| /* |
| * Copyright (c) 2018 ARM Limited |
| * All rights reserved |
| * |
| * The license below extends only to copyright in the software and shall |
| * not be construed as granting a license to any other intellectual |
| * property including but not limited to intellectual property relating |
| * to a hardware implementation of the functionality of the software |
| * licensed hereunder. You may use the software subject to the license |
| * terms below provided that you ensure that this notice is replicated |
| * unmodified and in its entirety in all distributions of the software, |
| * modified or unmodified, in source code or in binary form. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer; |
| * redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution; |
| * neither the name of the copyright holders nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| * Authors: Giacomo Gabrielli |
| */ |
| |
| #ifndef __ARCH_ARM_SVE_MACROMEM_HH__ |
| #define __ARCH_ARM_SVE_MACROMEM_HH__ |
| |
| #include "arch/arm/generated/decoder.hh" |
| #include "arch/arm/insts/pred_inst.hh" |
| |
| namespace ArmISA { |
| |
| template <typename Element, |
| template <typename> class MicroopLdMemType, |
| template <typename> class MicroopDeIntrlvType> |
| class SveLdStructSS : public PredMacroOp |
| { |
| protected: |
| IntRegIndex dest; |
| IntRegIndex gp; |
| IntRegIndex base; |
| IntRegIndex offset; |
| uint8_t numregs; |
| |
| public: |
| SveLdStructSS(const char* mnem, ExtMachInst machInst, OpClass __opClass, |
| IntRegIndex _dest, IntRegIndex _gp, IntRegIndex _base, |
| IntRegIndex _offset, uint8_t _numregs) |
| : PredMacroOp(mnem, machInst, __opClass), |
| dest(_dest), gp(_gp), base(_base), offset(_offset), numregs(_numregs) |
| { |
| numMicroops = numregs * 2; |
| |
| microOps = new StaticInstPtr[numMicroops]; |
| |
| for (int i = 0; i < numregs; ++i) { |
| microOps[i] = new MicroopLdMemType<Element>( |
| mnem, machInst, static_cast<IntRegIndex>(INTRLVREG0 + i), |
| _gp, _base, _offset, _numregs, i); |
| } |
| for (int i = 0; i < numregs; ++i) { |
| microOps[i + numregs] = new MicroopDeIntrlvType<Element>( |
| mnem, machInst, static_cast<IntRegIndex>((_dest + i) % 32), |
| _numregs, i, this); |
| } |
| |
| microOps[0]->setFirstMicroop(); |
| microOps[numMicroops - 1]->setLastMicroop(); |
| |
| for (StaticInstPtr *uop = microOps; !(*uop)->isLastMicroop(); uop++) { |
| (*uop)->setDelayedCommit(); |
| } |
| } |
| |
| Fault |
| execute(ExecContext *, Trace::InstRecord *) const |
| { |
| panic("Execute method called when it shouldn't!"); |
| return NoFault; |
| } |
| |
| std::string |
| generateDisassembly(Addr pc, const SymbolTable *symtab) const |
| { |
| std::stringstream ss; |
| printMnemonic(ss, "", false); |
| ccprintf(ss, "{"); |
| for (int i = 0; i < numregs; ++i) { |
| printVecReg(ss, (dest + i) % 32, true); |
| if (i < numregs - 1) |
| ccprintf(ss, ", "); |
| } |
| ccprintf(ss, "}, "); |
| printVecPredReg(ss, gp); |
| ccprintf(ss, "/z, ["); |
| printIntReg(ss, base); |
| ccprintf(ss, ", "); |
| printIntReg(ss, offset); |
| ccprintf(ss, "]"); |
| return ss.str(); |
| } |
| }; |
| |
| template <typename Element, |
| template <typename> class MicroopStMemType, |
| template <typename> class MicroopIntrlvType> |
| class SveStStructSS : public PredMacroOp |
| { |
| protected: |
| IntRegIndex dest; |
| IntRegIndex gp; |
| IntRegIndex base; |
| IntRegIndex offset; |
| uint8_t numregs; |
| |
| public: |
| SveStStructSS(const char* mnem, ExtMachInst machInst, OpClass __opClass, |
| IntRegIndex _dest, IntRegIndex _gp, IntRegIndex _base, |
| IntRegIndex _offset, uint8_t _numregs) |
| : PredMacroOp(mnem, machInst, __opClass), |
| dest(_dest), gp(_gp), base(_base), offset(_offset), numregs(_numregs) |
| { |
| numMicroops = numregs * 2; |
| |
| microOps = new StaticInstPtr[numMicroops]; |
| |
| for (int i = 0; i < numregs; ++i) { |
| microOps[i] = new MicroopIntrlvType<Element>( |
| mnem, machInst, static_cast<IntRegIndex>(INTRLVREG0 + i), |
| _dest, _numregs, i, this); |
| } |
| |
| for (int i = 0; i < numregs; ++i) { |
| microOps[i + numregs] = new MicroopStMemType<Element>( |
| mnem, machInst, static_cast<IntRegIndex>(INTRLVREG0 + i), |
| _gp, _base, _offset, _numregs, i); |
| } |
| |
| microOps[0]->setFirstMicroop(); |
| microOps[numMicroops - 1]->setLastMicroop(); |
| |
| for (StaticInstPtr *uop = microOps; !(*uop)->isLastMicroop(); uop++) { |
| (*uop)->setDelayedCommit(); |
| } |
| } |
| |
| Fault |
| execute(ExecContext *, Trace::InstRecord *) const |
| { |
| panic("Execute method called when it shouldn't!"); |
| return NoFault; |
| } |
| |
| std::string |
| generateDisassembly(Addr pc, const SymbolTable *symtab) const |
| { |
| std::stringstream ss; |
| printMnemonic(ss, "", false); |
| ccprintf(ss, "{"); |
| for (int i = 0; i < numregs; ++i) { |
| printVecReg(ss, (dest + i) % 32, true); |
| if (i < numregs - 1) |
| ccprintf(ss, ", "); |
| } |
| ccprintf(ss, "}, "); |
| printVecPredReg(ss, gp); |
| ccprintf(ss, ", ["); |
| printIntReg(ss, base); |
| ccprintf(ss, ", "); |
| printIntReg(ss, offset); |
| ccprintf(ss, "]"); |
| return ss.str(); |
| } |
| }; |
| |
| |
| template <typename Element, |
| template <typename> class MicroopLdMemType, |
| template <typename> class MicroopDeIntrlvType> |
| class SveLdStructSI : public PredMacroOp |
| { |
| protected: |
| IntRegIndex dest; |
| IntRegIndex gp; |
| IntRegIndex base; |
| int64_t imm; |
| uint8_t numregs; |
| |
| public: |
| SveLdStructSI(const char* mnem, ExtMachInst machInst, OpClass __opClass, |
| IntRegIndex _dest, IntRegIndex _gp, IntRegIndex _base, |
| int64_t _imm, uint8_t _numregs) |
| : PredMacroOp(mnem, machInst, __opClass), |
| dest(_dest), gp(_gp), base(_base), imm(_imm), numregs(_numregs) |
| { |
| numMicroops = numregs * 2; |
| |
| microOps = new StaticInstPtr[numMicroops]; |
| |
| for (int i = 0; i < numregs; ++i) { |
| microOps[i] = new MicroopLdMemType<Element>( |
| mnem, machInst, static_cast<IntRegIndex>(INTRLVREG0 + i), |
| _gp, _base, _imm, _numregs, i); |
| } |
| for (int i = 0; i < numregs; ++i) { |
| microOps[i + numregs] = new MicroopDeIntrlvType<Element>( |
| mnem, machInst, static_cast<IntRegIndex>((_dest + i) % 32), |
| _numregs, i, this); |
| } |
| |
| microOps[0]->setFirstMicroop(); |
| microOps[numMicroops - 1]->setLastMicroop(); |
| |
| for (StaticInstPtr *uop = microOps; !(*uop)->isLastMicroop(); uop++) { |
| (*uop)->setDelayedCommit(); |
| } |
| } |
| |
| Fault |
| execute(ExecContext *, Trace::InstRecord *) const |
| { |
| panic("Execute method called when it shouldn't!"); |
| return NoFault; |
| } |
| |
| std::string |
| generateDisassembly(Addr pc, const SymbolTable *symtab) const |
| { |
| std::stringstream ss; |
| printMnemonic(ss, "", false); |
| ccprintf(ss, "{"); |
| for (int i = 0; i < numregs; ++i) { |
| printVecReg(ss, (dest + i) % 32, true); |
| if (i < numregs - 1) |
| ccprintf(ss, ", "); |
| } |
| ccprintf(ss, "}, "); |
| printVecPredReg(ss, gp); |
| ccprintf(ss, "/z, ["); |
| printIntReg(ss, base); |
| if (imm != 0) { |
| ccprintf(ss, ", #%d, MUL VL", imm); |
| } |
| ccprintf(ss, "]"); |
| return ss.str(); |
| } |
| }; |
| |
| template <typename Element, |
| template <typename> class MicroopStMemType, |
| template <typename> class MicroopIntrlvType> |
| class SveStStructSI : public PredMacroOp |
| { |
| protected: |
| IntRegIndex dest; |
| IntRegIndex gp; |
| IntRegIndex base; |
| int64_t imm; |
| uint8_t numregs; |
| |
| public: |
| SveStStructSI(const char* mnem, ExtMachInst machInst, OpClass __opClass, |
| IntRegIndex _dest, IntRegIndex _gp, IntRegIndex _base, |
| int64_t _imm, uint8_t _numregs) |
| : PredMacroOp(mnem, machInst, __opClass), |
| dest(_dest), gp(_gp), base(_base), imm(_imm), numregs(_numregs) |
| { |
| numMicroops = numregs * 2; |
| |
| microOps = new StaticInstPtr[numMicroops]; |
| |
| for (int i = 0; i < numregs; ++i) { |
| microOps[i] = new MicroopIntrlvType<Element>( |
| mnem, machInst, static_cast<IntRegIndex>(INTRLVREG0 + i), |
| _dest, _numregs, i, this); |
| } |
| |
| for (int i = 0; i < numregs; ++i) { |
| microOps[i + numregs] = new MicroopStMemType<Element>( |
| mnem, machInst, static_cast<IntRegIndex>(INTRLVREG0 + i), |
| _gp, _base, _imm, _numregs, i); |
| } |
| |
| microOps[0]->setFirstMicroop(); |
| microOps[numMicroops - 1]->setLastMicroop(); |
| |
| for (StaticInstPtr *uop = microOps; !(*uop)->isLastMicroop(); uop++) { |
| (*uop)->setDelayedCommit(); |
| } |
| } |
| |
| Fault |
| execute(ExecContext *, Trace::InstRecord *) const |
| { |
| panic("Execute method called when it shouldn't!"); |
| return NoFault; |
| } |
| |
| std::string |
| generateDisassembly(Addr pc, const SymbolTable *symtab) const |
| { |
| std::stringstream ss; |
| printMnemonic(ss, "", false); |
| ccprintf(ss, "{"); |
| for (int i = 0; i < numregs; ++i) { |
| printVecReg(ss, (dest + i) % 32, true); |
| if (i < numregs - 1) |
| ccprintf(ss, ", "); |
| } |
| ccprintf(ss, "}, "); |
| printVecPredReg(ss, gp); |
| ccprintf(ss, ", ["); |
| printIntReg(ss, base); |
| if (imm != 0) { |
| ccprintf(ss, ", #%d, MUL VL", imm); |
| } |
| ccprintf(ss, "]"); |
| return ss.str(); |
| } |
| }; |
| |
| template <typename RegElemType, typename MemElemType, |
| template <typename, typename> class MicroopType, |
| template <typename> class FirstFaultWritebackMicroopType> |
| class SveIndexedMemVI : public PredMacroOp |
| { |
| protected: |
| IntRegIndex dest; |
| IntRegIndex gp; |
| IntRegIndex base; |
| uint64_t imm; |
| |
| public: |
| SveIndexedMemVI(const char *mnem, ExtMachInst machInst, OpClass __opClass, |
| IntRegIndex _dest, IntRegIndex _gp, IntRegIndex _base, |
| uint64_t _imm, bool firstFault) |
| : PredMacroOp(mnem, machInst, __opClass), |
| dest(_dest), gp(_gp), base(_base), imm(_imm) |
| { |
| bool isLoad = (__opClass == MemReadOp); |
| assert(!firstFault || isLoad); |
| |
| int num_elems = ((machInst.sveLen + 1) * 16) / sizeof(RegElemType); |
| |
| numMicroops = num_elems; |
| if (isLoad) { |
| if (firstFault) { |
| numMicroops += 2; |
| } else { |
| numMicroops++; |
| } |
| } |
| |
| microOps = new StaticInstPtr[numMicroops]; |
| |
| StaticInstPtr *uop = microOps; |
| |
| if (isLoad) { |
| // The first microop of a gather load copies the source vector |
| // register used for address calculation to an auxiliary register, |
| // with all subsequent microops reading from the latter. This is |
| // needed to properly handle cases where the source vector |
| // register is the same as the destination register |
| *uop = new ArmISAInst::SveGatherLoadCpySrcVecMicroop( |
| mnem, machInst, _base, this); |
| uop++; |
| } |
| |
| for (int i = 0; i < num_elems; i++, uop++) { |
| *uop = new MicroopType<RegElemType, MemElemType>( |
| mnem, machInst, __opClass, _dest, _gp, |
| isLoad ? (IntRegIndex) VECREG_UREG0 : _base, _imm, i, |
| num_elems, firstFault); |
| } |
| |
| if (firstFault) { |
| *uop = new FirstFaultWritebackMicroopType<RegElemType>( |
| mnem, machInst, __opClass, num_elems, this); |
| } else { |
| --uop; |
| } |
| |
| (*uop)->setLastMicroop(); |
| microOps[0]->setFirstMicroop(); |
| |
| for (StaticInstPtr *uop = microOps; !(*uop)->isLastMicroop(); uop++) { |
| (*uop)->setDelayedCommit(); |
| } |
| } |
| |
| Fault |
| execute(ExecContext *, Trace::InstRecord *) const |
| { |
| panic("Execute method called when it shouldn't!"); |
| return NoFault; |
| } |
| |
| std::string |
| generateDisassembly(Addr pc, const SymbolTable *symtab) const |
| { |
| // TODO: add suffix to transfer and base registers |
| std::stringstream ss; |
| printMnemonic(ss, "", false); |
| ccprintf(ss, "{"); |
| printVecReg(ss, dest, true); |
| ccprintf(ss, "}, "); |
| printVecPredReg(ss, gp); |
| ccprintf(ss, "/z, ["); |
| printVecReg(ss, base, true); |
| if (imm != 0) { |
| ccprintf(ss, ", #%d", imm * sizeof(MemElemType)); |
| } |
| ccprintf(ss, "]"); |
| return ss.str(); |
| } |
| }; |
| |
| template <typename RegElemType, typename MemElemType, |
| template <typename, typename> class MicroopType, |
| template <typename> class FirstFaultWritebackMicroopType> |
| class SveIndexedMemSV : public PredMacroOp |
| { |
| protected: |
| IntRegIndex dest; |
| IntRegIndex gp; |
| IntRegIndex base; |
| IntRegIndex offset; |
| |
| bool offsetIs32; |
| bool offsetIsSigned; |
| bool offsetIsScaled; |
| |
| public: |
| SveIndexedMemSV(const char *mnem, ExtMachInst machInst, OpClass __opClass, |
| IntRegIndex _dest, IntRegIndex _gp, IntRegIndex _base, |
| IntRegIndex _offset, bool _offsetIs32, |
| bool _offsetIsSigned, bool _offsetIsScaled, |
| bool firstFault) |
| : PredMacroOp(mnem, machInst, __opClass), |
| dest(_dest), gp(_gp), base(_base), offset(_offset), |
| offsetIs32(_offsetIs32), offsetIsSigned(_offsetIsSigned), |
| offsetIsScaled(_offsetIsScaled) |
| { |
| bool isLoad = (__opClass == MemReadOp); |
| assert(!firstFault || isLoad); |
| |
| int num_elems = ((machInst.sveLen + 1) * 16) / sizeof(RegElemType); |
| |
| numMicroops = num_elems; |
| if (isLoad) { |
| if (firstFault) { |
| numMicroops += 2; |
| } else { |
| numMicroops++; |
| } |
| } |
| |
| microOps = new StaticInstPtr[numMicroops]; |
| |
| StaticInstPtr *uop = microOps; |
| |
| if (isLoad) { |
| // The first microop of a gather load copies the source vector |
| // register used for address calculation to an auxiliary register, |
| // with all subsequent microops reading from the latter. This is |
| // needed to properly handle cases where the source vector |
| // register is the same as the destination register |
| *uop = new ArmISAInst::SveGatherLoadCpySrcVecMicroop( |
| mnem, machInst, _offset, this); |
| uop++; |
| } |
| |
| for (int i = 0; i < num_elems; i++, uop++) { |
| *uop = new MicroopType<RegElemType, MemElemType>( |
| mnem, machInst, __opClass, _dest, _gp, _base, |
| isLoad ? (IntRegIndex) VECREG_UREG0 : _offset, _offsetIs32, |
| _offsetIsSigned, _offsetIsScaled, i, num_elems, firstFault); |
| } |
| |
| if (firstFault) { |
| *uop = new FirstFaultWritebackMicroopType<RegElemType>( |
| mnem, machInst, __opClass, num_elems, this); |
| } else { |
| --uop; |
| } |
| |
| (*uop)->setLastMicroop(); |
| microOps[0]->setFirstMicroop(); |
| |
| for (StaticInstPtr *uop = microOps; !(*uop)->isLastMicroop(); uop++) { |
| (*uop)->setDelayedCommit(); |
| } |
| } |
| |
| Fault |
| execute(ExecContext *, Trace::InstRecord *) const |
| { |
| panic("Execute method called when it shouldn't!"); |
| return NoFault; |
| } |
| |
| std::string |
| generateDisassembly(Addr pc, const SymbolTable *symtab) const |
| { |
| // TODO: add suffix to transfer and base registers |
| std::stringstream ss; |
| printMnemonic(ss, "", false); |
| ccprintf(ss, "{"); |
| printVecReg(ss, dest, true); |
| ccprintf(ss, "}, "); |
| printVecPredReg(ss, gp); |
| ccprintf(ss, "/z, ["); |
| printIntReg(ss, base); |
| ccprintf(ss, ", "); |
| printVecReg(ss, offset, true); |
| ccprintf(ss, "]"); |
| return ss.str(); |
| } |
| }; |
| |
| } // namespace ArmISA |
| |
| #endif // __ARCH_ARM_SVE_MACROMEM_HH__ |