//===- subzero/src/IceInstARM32.h - ARM32 machine instructions --*- C++ -*-===// // // The Subzero Code Generator // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// /// \file /// \brief Declares the InstARM32 and OperandARM32 classes and their subclasses. /// /// This represents the machine instructions and operands used for ARM32 code /// selection. /// //===----------------------------------------------------------------------===// #ifndef SUBZERO_SRC_ICEINSTARM32_H #define SUBZERO_SRC_ICEINSTARM32_H #include "IceConditionCodesARM32.h" #include "IceDefs.h" #include "IceInst.h" #include "IceInstARM32.def" #include "IceOperand.h" #include "IceRegistersARM32.h" namespace Ice { namespace ARM32 { /// Encoding of an ARM 32-bit instruction. using IValueT = uint32_t; /// An Offset value (+/-) used in an ARM 32-bit instruction. using IOffsetT = int32_t; class TargetARM32; /// OperandARM32 extends the Operand hierarchy. Its subclasses are /// OperandARM32Mem and OperandARM32Flex. class OperandARM32 : public Operand { OperandARM32() = delete; OperandARM32(const OperandARM32 &) = delete; OperandARM32 &operator=(const OperandARM32 &) = delete; public: enum OperandKindARM32 { k__Start = Operand::kTarget, kMem, kShAmtImm, kFlexStart, kFlexImm = kFlexStart, kFlexFpImm, kFlexFpZero, kFlexReg, kFlexEnd = kFlexReg }; enum ShiftKind { kNoShift = -1, #define X(enum, emit) enum, ICEINSTARM32SHIFT_TABLE #undef X }; using Operand::dump; void dump(const Cfg *, Ostream &Str) const override { if (BuildDefs::dump()) Str << ""; } protected: OperandARM32(OperandKindARM32 Kind, Type Ty) : Operand(static_cast(Kind), Ty) {} }; /// OperandARM32Mem represents a memory operand in any of the various ARM32 /// addressing modes. class OperandARM32Mem : public OperandARM32 { OperandARM32Mem() = delete; OperandARM32Mem(const OperandARM32Mem &) = delete; OperandARM32Mem &operator=(const OperandARM32Mem &) = delete; public: /// Memory operand addressing mode. /// The enum value also carries the encoding. // TODO(jvoung): unify with the assembler. enum AddrMode { // bit encoding P U 0 W Offset = (8 | 4 | 0) << 21, // offset (w/o writeback to base) PreIndex = (8 | 4 | 1) << 21, // pre-indexed addressing with writeback PostIndex = (0 | 4 | 0) << 21, // post-indexed addressing with writeback NegOffset = (8 | 0 | 0) << 21, // negative offset (w/o writeback to base) NegPreIndex = (8 | 0 | 1) << 21, // negative pre-indexed with writeback NegPostIndex = (0 | 0 | 0) << 21 // negative post-indexed with writeback }; /// Provide two constructors. /// NOTE: The Variable-typed operands have to be registers. /// /// (1) Reg + Imm. The Immediate actually has a limited number of bits /// for encoding, so check canHoldOffset first. It cannot handle general /// Constant operands like ConstantRelocatable, since a relocatable can /// potentially take up too many bits. static OperandARM32Mem *create(Cfg *Func, Type Ty, Variable *Base, ConstantInteger32 *ImmOffset, AddrMode Mode = Offset) { return new (Func->allocate()) OperandARM32Mem(Func, Ty, Base, ImmOffset, Mode); } /// (2) Reg +/- Reg with an optional shift of some kind and amount. static OperandARM32Mem *create(Cfg *Func, Type Ty, Variable *Base, Variable *Index, ShiftKind ShiftOp = kNoShift, uint16_t ShiftAmt = 0, AddrMode Mode = Offset) { return new (Func->allocate()) OperandARM32Mem(Func, Ty, Base, Index, ShiftOp, ShiftAmt, Mode); } Variable *getBase() const { return Base; } ConstantInteger32 *getOffset() const { return ImmOffset; } Variable *getIndex() const { return Index; } ShiftKind getShiftOp() const { return ShiftOp; } uint16_t getShiftAmt() const { return ShiftAmt; } AddrMode getAddrMode() const { return Mode; } bool isRegReg() const { return Index != nullptr; } bool isNegAddrMode() const { // Positive address modes have the "U" bit set, and negative modes don't. static_assert((PreIndex & (4 << 21)) != 0, "Positive addr modes should have U bit set."); static_assert((NegPreIndex & (4 << 21)) == 0, "Negative addr modes should have U bit clear."); return (Mode & (4 << 21)) == 0; } void emit(const Cfg *Func) const override; using OperandARM32::dump; void dump(const Cfg *Func, Ostream &Str) const override; static bool classof(const Operand *Operand) { return Operand->getKind() == static_cast(kMem); } /// Return true if a load/store instruction for an element of type Ty can /// encode the Offset directly in the immediate field of the 32-bit ARM /// instruction. For some types, if the load is Sign extending, then the range /// is reduced. static bool canHoldOffset(Type Ty, bool SignExt, int32_t Offset); private: OperandARM32Mem(Cfg *Func, Type Ty, Variable *Base, ConstantInteger32 *ImmOffset, AddrMode Mode); OperandARM32Mem(Cfg *Func, Type Ty, Variable *Base, Variable *Index, ShiftKind ShiftOp, uint16_t ShiftAmt, AddrMode Mode); Variable *Base; ConstantInteger32 *ImmOffset; Variable *Index; ShiftKind ShiftOp; uint16_t ShiftAmt; AddrMode Mode; }; /// OperandARM32ShAmtImm represents an Immediate that is used in one of the /// shift-by-immediate instructions (lsl, lsr, and asr), and shift-by-immediate /// shifted registers. class OperandARM32ShAmtImm : public OperandARM32 { OperandARM32ShAmtImm() = delete; OperandARM32ShAmtImm(const OperandARM32ShAmtImm &) = delete; OperandARM32ShAmtImm &operator=(const OperandARM32ShAmtImm &) = delete; public: static OperandARM32ShAmtImm *create(Cfg *Func, ConstantInteger32 *ShAmt) { return new (Func->allocate()) OperandARM32ShAmtImm(ShAmt); } static bool classof(const Operand *Operand) { return Operand->getKind() == static_cast(kShAmtImm); } void emit(const Cfg *Func) const override; using OperandARM32::dump; void dump(const Cfg *Func, Ostream &Str) const override; uint32_t getShAmtImm() const { return ShAmt->getValue(); } private: explicit OperandARM32ShAmtImm(ConstantInteger32 *SA); const ConstantInteger32 *const ShAmt; }; /// OperandARM32Flex represent the "flexible second operand" for data-processing /// instructions. It can be a rotatable 8-bit constant, or a register with an /// optional shift operand. The shift amount can even be a third register. class OperandARM32Flex : public OperandARM32 { OperandARM32Flex() = delete; OperandARM32Flex(const OperandARM32Flex &) = delete; OperandARM32Flex &operator=(const OperandARM32Flex &) = delete; public: static bool classof(const Operand *Operand) { return static_cast(kFlexStart) <= Operand->getKind() && Operand->getKind() <= static_cast(kFlexEnd); } protected: OperandARM32Flex(OperandKindARM32 Kind, Type Ty) : OperandARM32(Kind, Ty) {} }; /// Rotated immediate variant. class OperandARM32FlexImm : public OperandARM32Flex { OperandARM32FlexImm() = delete; OperandARM32FlexImm(const OperandARM32FlexImm &) = delete; OperandARM32FlexImm &operator=(const OperandARM32FlexImm &) = delete; public: /// Immed_8 rotated by an even number of bits (2 * RotateAmt). static OperandARM32FlexImm *create(Cfg *Func, Type Ty, uint32_t Imm, uint32_t RotateAmt); void emit(const Cfg *Func) const override; using OperandARM32::dump; void dump(const Cfg *Func, Ostream &Str) const override; static bool classof(const Operand *Operand) { return Operand->getKind() == static_cast(kFlexImm); } /// Return true if the Immediate can fit in the ARM flexible operand. Fills in /// the out-params RotateAmt and Immed_8 if Immediate fits. static bool canHoldImm(uint32_t Immediate, uint32_t *RotateAmt, uint32_t *Immed_8); uint32_t getImm() const { return Imm; } uint32_t getRotateAmt() const { return RotateAmt; } private: OperandARM32FlexImm(Cfg *Func, Type Ty, uint32_t Imm, uint32_t RotateAmt); uint32_t Imm; uint32_t RotateAmt; }; /// Modified Floating-point constant. class OperandARM32FlexFpImm : public OperandARM32Flex { OperandARM32FlexFpImm() = delete; OperandARM32FlexFpImm(const OperandARM32FlexFpImm &) = delete; OperandARM32FlexFpImm &operator=(const OperandARM32FlexFpImm &) = delete; public: static OperandARM32FlexFpImm *create(Cfg *Func, Type Ty, uint32_t ModifiedImm) { return new (Func->allocate()) OperandARM32FlexFpImm(Func, Ty, ModifiedImm); } void emit(const Cfg *Func) const override; using OperandARM32::dump; void dump(const Cfg *Func, Ostream &Str) const override; static bool classof(const Operand *Operand) { return Operand->getKind() == static_cast(kFlexFpImm); } static bool canHoldImm(const Operand *C, uint32_t *ModifiedImm); uint32_t getModifiedImm() const { return ModifiedImm; } private: OperandARM32FlexFpImm(Cfg *Func, Type Ty, uint32_t ModifiedImm); const uint32_t ModifiedImm; }; /// An operand for representing the 0.0 immediate in vcmp. class OperandARM32FlexFpZero : public OperandARM32Flex { OperandARM32FlexFpZero() = delete; OperandARM32FlexFpZero(const OperandARM32FlexFpZero &) = delete; OperandARM32FlexFpZero &operator=(const OperandARM32FlexFpZero &) = delete; public: static OperandARM32FlexFpZero *create(Cfg *Func, Type Ty) { return new (Func->allocate()) OperandARM32FlexFpZero(Func, Ty); } void emit(const Cfg *Func) const override; using OperandARM32::dump; void dump(const Cfg *Func, Ostream &Str) const override; static bool classof(const Operand *Operand) { return Operand->getKind() == static_cast(kFlexFpZero); } private: OperandARM32FlexFpZero(Cfg *Func, Type Ty); }; /// Shifted register variant. class OperandARM32FlexReg : public OperandARM32Flex { OperandARM32FlexReg() = delete; OperandARM32FlexReg(const OperandARM32FlexReg &) = delete; OperandARM32FlexReg &operator=(const OperandARM32FlexReg &) = delete; public: /// Register with immediate/reg shift amount and shift operation. static OperandARM32FlexReg *create(Cfg *Func, Type Ty, Variable *Reg, ShiftKind ShiftOp, Operand *ShiftAmt) { return new (Func->allocate()) OperandARM32FlexReg(Func, Ty, Reg, ShiftOp, ShiftAmt); } void emit(const Cfg *Func) const override; using OperandARM32::dump; void dump(const Cfg *Func, Ostream &Str) const override; static bool classof(const Operand *Operand) { return Operand->getKind() == static_cast(kFlexReg); } Variable *getReg() const { return Reg; } ShiftKind getShiftOp() const { return ShiftOp; } /// ShiftAmt can represent an immediate or a register. Operand *getShiftAmt() const { return ShiftAmt; } private: OperandARM32FlexReg(Cfg *Func, Type Ty, Variable *Reg, ShiftKind ShiftOp, Operand *ShiftAmt); Variable *Reg; ShiftKind ShiftOp; Operand *ShiftAmt; }; /// StackVariable represents a Var that isn't assigned a register (stack-only). /// It is assigned a stack slot, but the slot's offset may be too large to /// represent in the native addressing mode, and so it has a separate base /// register from SP/FP, where the offset from that base register is then in /// range. class StackVariable final : public Variable { StackVariable() = delete; StackVariable(const StackVariable &) = delete; StackVariable &operator=(const StackVariable &) = delete; public: static StackVariable *create(Cfg *Func, Type Ty, SizeT Index) { return new (Func->allocate()) StackVariable(Func, Ty, Index); } constexpr static auto StackVariableKind = static_cast(kVariable_Target); static bool classof(const Operand *Operand) { return Operand->getKind() == StackVariableKind; } void setBaseRegNum(RegNumT RegNum) { BaseRegNum = RegNum; } RegNumT getBaseRegNum() const override { return BaseRegNum; } // Inherit dump() and emit() from Variable. private: StackVariable(const Cfg *Func, Type Ty, SizeT Index) : Variable(Func, StackVariableKind, Ty, Index) {} RegNumT BaseRegNum; }; /// Base class for ARM instructions. While most ARM instructions can be /// conditionally executed, a few of them are not predicable (halt, memory /// barriers, etc.). class InstARM32 : public InstTarget { InstARM32() = delete; InstARM32(const InstARM32 &) = delete; InstARM32 &operator=(const InstARM32 &) = delete; public: // Defines form that assembly instruction should be synthesized. enum EmitForm { Emit_Text, Emit_Binary }; enum InstKindARM32 { k__Start = Inst::Target, Adc, Add, And, Asr, Bic, Br, Call, Clz, Cmn, Cmp, Dmb, Eor, Extract, Insert, Label, Ldr, Ldrex, Lsl, Lsr, Nop, Mla, Mls, Mov, Movt, Movw, Mul, Mvn, Orr, Pop, Push, Rbit, Ret, Rev, Rsb, Rsc, Sbc, Sdiv, Str, Strex, Sub, Sxt, Trap, Tst, Udiv, Umull, Uxt, Vabs, Vadd, Vand, Vbsl, Vceq, Vcge, Vcgt, Vcmp, Vcvt, Vdiv, Vdup, Veor, Vldr1d, Vldr1q, Vmla, Vmlap, Vmls, Vmovl, Vmovh, Vmovhl, Vmovlh, Vmrs, Vmul, Vmulh, Vmvn, Vneg, Vorr, Vqadd, Vqmovn2, Vqsub, Vshl, Vshr, Vsqrt, Vstr1, Vsub, Vzip }; static constexpr size_t InstSize = sizeof(uint32_t); static CondARM32::Cond getOppositeCondition(CondARM32::Cond Cond); /// Called inside derived methods emit() to communicate that multiple /// instructions are being generated. Used by emitIAS() methods to /// generate textual fixups for instructions that are not yet /// implemented. void startNextInst(const Cfg *Func) const; /// FPSign is used for certain vector instructions (particularly, right /// shifts) that require an operand sign specification. enum FPSign { FS_None, FS_Signed, FS_Unsigned, }; /// Shared emit routines for common forms of instructions. /// @{ static void emitThreeAddrFP(const char *Opcode, FPSign Sign, const InstARM32 *Instr, const Cfg *Func, Type OpType); static void emitFourAddrFP(const char *Opcode, FPSign Sign, const InstARM32 *Instr, const Cfg *Func); /// @} void dump(const Cfg *Func) const override; void emitIAS(const Cfg *Func) const override; protected: InstARM32(Cfg *Func, InstKindARM32 Kind, SizeT Maxsrcs, Variable *Dest) : InstTarget(Func, static_cast(Kind), Maxsrcs, Dest) {} static bool isClassof(const Inst *Instr, InstKindARM32 MyKind) { return Instr->getKind() == static_cast(MyKind); } // Generates text of assembly instruction using method emit(), and then adds // to the assembly buffer as a Fixup. void emitUsingTextFixup(const Cfg *Func) const; }; /// A predicable ARM instruction. class InstARM32Pred : public InstARM32 { InstARM32Pred() = delete; InstARM32Pred(const InstARM32Pred &) = delete; InstARM32Pred &operator=(const InstARM32Pred &) = delete; public: InstARM32Pred(Cfg *Func, InstKindARM32 Kind, SizeT Maxsrcs, Variable *Dest, CondARM32::Cond Predicate) : InstARM32(Func, Kind, Maxsrcs, Dest), Predicate(Predicate) {} CondARM32::Cond getPredicate() const { return Predicate; } void setPredicate(CondARM32::Cond Pred) { Predicate = Pred; } static const char *predString(CondARM32::Cond Predicate); void dumpOpcodePred(Ostream &Str, const char *Opcode, Type Ty) const; /// Shared emit routines for common forms of instructions. static void emitUnaryopGPR(const char *Opcode, const InstARM32Pred *Instr, const Cfg *Func, bool NeedsWidthSuffix); static void emitUnaryopFP(const char *Opcode, FPSign Sign, const InstARM32Pred *Instr, const Cfg *Func); static void emitTwoAddr(const char *Opcode, const InstARM32Pred *Instr, const Cfg *Func); static void emitThreeAddr(const char *Opcode, const InstARM32Pred *Instr, const Cfg *Func, bool SetFlags); static void emitFourAddr(const char *Opcode, const InstARM32Pred *Instr, const Cfg *Func); static void emitCmpLike(const char *Opcode, const InstARM32Pred *Instr, const Cfg *Func); protected: CondARM32::Cond Predicate; }; template inline StreamType &operator<<(StreamType &Stream, CondARM32::Cond Predicate) { Stream << InstARM32Pred::predString(Predicate); return Stream; } /// Instructions of the form x := op(y). template class InstARM32UnaryopGPR : public InstARM32Pred { InstARM32UnaryopGPR() = delete; InstARM32UnaryopGPR(const InstARM32UnaryopGPR &) = delete; InstARM32UnaryopGPR &operator=(const InstARM32UnaryopGPR &) = delete; public: static InstARM32UnaryopGPR *create(Cfg *Func, Variable *Dest, Operand *Src, CondARM32::Cond Predicate) { return new (Func->allocate()) InstARM32UnaryopGPR(Func, Dest, Src, Predicate); } void emit(const Cfg *Func) const override { if (!BuildDefs::dump()) return; emitUnaryopGPR(Opcode, this, Func, NeedsWidthSuffix); } void emitIAS(const Cfg *Func) const override; void dump(const Cfg *Func) const override { if (!BuildDefs::dump()) return; Ostream &Str = Func->getContext()->getStrDump(); dumpDest(Func); Str << " = "; dumpOpcodePred(Str, Opcode, getDest()->getType()); Str << " "; dumpSources(Func); } static bool classof(const Inst *Instr) { return isClassof(Instr, K); } private: InstARM32UnaryopGPR(Cfg *Func, Variable *Dest, Operand *Src, CondARM32::Cond Predicate) : InstARM32Pred(Func, K, 1, Dest, Predicate) { addSource(Src); } static const char *const Opcode; }; /// Instructions of the form x := op(y), for vector/FP. template class InstARM32UnaryopFP : public InstARM32Pred { InstARM32UnaryopFP() = delete; InstARM32UnaryopFP(const InstARM32UnaryopFP &) = delete; InstARM32UnaryopFP &operator=(const InstARM32UnaryopFP &) = delete; public: static InstARM32UnaryopFP *create(Cfg *Func, Variable *Dest, Variable *Src, CondARM32::Cond Predicate) { return new (Func->allocate()) InstARM32UnaryopFP(Func, Dest, Src, Predicate); } void emit(const Cfg *Func) const override { if (!BuildDefs::dump()) return; emitUnaryopFP(Opcode, Sign, this, Func); } void emitIAS(const Cfg *Func) const override; void dump(const Cfg *Func) const override { if (!BuildDefs::dump()) return; Ostream &Str = Func->getContext()->getStrDump(); dumpDest(Func); Str << " = "; dumpOpcodePred(Str, Opcode, getDest()->getType()); Str << " "; dumpSources(Func); } static bool classof(const Inst *Instr) { return isClassof(Instr, K); } protected: InstARM32UnaryopFP(Cfg *Func, Variable *Dest, Operand *Src, CondARM32::Cond Predicate) : InstARM32Pred(Func, K, 1, Dest, Predicate) { addSource(Src); } FPSign Sign = FS_None; static const char *const Opcode; }; template class InstARM32UnaryopSignAwareFP : public InstARM32UnaryopFP { InstARM32UnaryopSignAwareFP() = delete; InstARM32UnaryopSignAwareFP(const InstARM32UnaryopSignAwareFP &) = delete; InstARM32UnaryopSignAwareFP & operator=(const InstARM32UnaryopSignAwareFP &) = delete; public: static InstARM32UnaryopSignAwareFP * create(Cfg *Func, Variable *Dest, Variable *Src, CondARM32::Cond Predicate) { return new (Func->allocate()) InstARM32UnaryopSignAwareFP(Func, Dest, Src, Predicate); } void emitIAS(const Cfg *Func) const override; void setSignType(InstARM32::FPSign SignType) { this->Sign = SignType; } private: InstARM32UnaryopSignAwareFP(Cfg *Func, Variable *Dest, Operand *Src, CondARM32::Cond Predicate) : InstARM32UnaryopFP(Func, Dest, Src, Predicate) {} }; /// Instructions of the form x := x op y. template class InstARM32TwoAddrGPR : public InstARM32Pred { InstARM32TwoAddrGPR() = delete; InstARM32TwoAddrGPR(const InstARM32TwoAddrGPR &) = delete; InstARM32TwoAddrGPR &operator=(const InstARM32TwoAddrGPR &) = delete; public: /// Dest must be a register. static InstARM32TwoAddrGPR *create(Cfg *Func, Variable *Dest, Operand *Src, CondARM32::Cond Predicate) { return new (Func->allocate()) InstARM32TwoAddrGPR(Func, Dest, Src, Predicate); } void emit(const Cfg *Func) const override { if (!BuildDefs::dump()) return; emitTwoAddr(Opcode, this, Func); } void emitIAS(const Cfg *Func) const override; void dump(const Cfg *Func) const override { if (!BuildDefs::dump()) return; Ostream &Str = Func->getContext()->getStrDump(); dumpDest(Func); Str << " = "; dumpOpcodePred(Str, Opcode, getDest()->getType()); Str << " "; dumpSources(Func); } static bool classof(const Inst *Instr) { return isClassof(Instr, K); } private: InstARM32TwoAddrGPR(Cfg *Func, Variable *Dest, Operand *Src, CondARM32::Cond Predicate) : InstARM32Pred(Func, K, 2, Dest, Predicate) { addSource(Dest); addSource(Src); } static const char *const Opcode; }; /// Base class for load instructions. template class InstARM32LoadBase : public InstARM32Pred { InstARM32LoadBase() = delete; InstARM32LoadBase(const InstARM32LoadBase &) = delete; InstARM32LoadBase &operator=(const InstARM32LoadBase &) = delete; public: static InstARM32LoadBase *create(Cfg *Func, Variable *Dest, Operand *Source, CondARM32::Cond Predicate) { return new (Func->allocate()) InstARM32LoadBase(Func, Dest, Source, Predicate); } void emit(const Cfg *Func) const override; void emitIAS(const Cfg *Func) const override; void dump(const Cfg *Func) const override { if (!BuildDefs::dump()) return; Ostream &Str = Func->getContext()->getStrDump(); dumpOpcodePred(Str, Opcode, getDest()->getType()); Str << " "; dumpDest(Func); Str << ", "; dumpSources(Func); } static bool classof(const Inst *Instr) { return isClassof(Instr, K); } private: InstARM32LoadBase(Cfg *Func, Variable *Dest, Operand *Source, CondARM32::Cond Predicate) : InstARM32Pred(Func, K, 1, Dest, Predicate) { addSource(Source); } static const char *const Opcode; }; /// Instructions of the form x := y op z. May have the side-effect of setting /// status flags. template class InstARM32ThreeAddrGPR : public InstARM32Pred { InstARM32ThreeAddrGPR() = delete; InstARM32ThreeAddrGPR(const InstARM32ThreeAddrGPR &) = delete; InstARM32ThreeAddrGPR &operator=(const InstARM32ThreeAddrGPR &) = delete; public: /// Create an ordinary binary-op instruction like add, and sub. Dest and Src1 /// must be registers. static InstARM32ThreeAddrGPR *create(Cfg *Func, Variable *Dest, Variable *Src0, Operand *Src1, CondARM32::Cond Predicate, bool SetFlags = false) { return new (Func->allocate()) InstARM32ThreeAddrGPR(Func, Dest, Src0, Src1, Predicate, SetFlags); } void emit(const Cfg *Func) const override { if (!BuildDefs::dump()) return; emitThreeAddr(Opcode, this, Func, SetFlags); } void emitIAS(const Cfg *Func) const override; void dump(const Cfg *Func) const override { if (!BuildDefs::dump()) return; Ostream &Str = Func->getContext()->getStrDump(); dumpDest(Func); Str << " = "; dumpOpcodePred(Str, Opcode, getDest()->getType()); Str << (SetFlags ? ".s " : " "); dumpSources(Func); } static bool classof(const Inst *Instr) { return isClassof(Instr, K); } private: InstARM32ThreeAddrGPR(Cfg *Func, Variable *Dest, Variable *Src0, Operand *Src1, CondARM32::Cond Predicate, bool SetFlags) : InstARM32Pred(Func, K, 2, Dest, Predicate), SetFlags(SetFlags) { HasSideEffects = SetFlags; addSource(Src0); addSource(Src1); } static const char *const Opcode; bool SetFlags; }; /// Instructions of the form x := y op z, for vector/FP. We leave these as /// unconditional: "ARM deprecates the conditional execution of any instruction /// encoding provided by the Advanced SIMD Extension that is not also provided /// by the floating-point (VFP) extension". They do not set flags. template class InstARM32ThreeAddrFP : public InstARM32 { InstARM32ThreeAddrFP() = delete; InstARM32ThreeAddrFP(const InstARM32ThreeAddrFP &) = delete; InstARM32ThreeAddrFP &operator=(const InstARM32ThreeAddrFP &) = delete; public: /// Create a vector/FP binary-op instruction like vadd, and vsub. Everything /// must be a register. static InstARM32ThreeAddrFP *create(Cfg *Func, Variable *Dest, Variable *Src0, Variable *Src1) { return new (Func->allocate()) InstARM32ThreeAddrFP(Func, Dest, Src0, Src1); } void emit(const Cfg *Func) const override { if (!BuildDefs::dump()) return; const Type OpType = (isVectorCompare() ? getSrc(0) : getDest())->getType(); emitThreeAddrFP(Opcode, Sign, this, Func, OpType); } void emitIAS(const Cfg *Func) const override; void dump(const Cfg *Func) const override { if (!BuildDefs::dump()) return; Ostream &Str = Func->getContext()->getStrDump(); dumpDest(Func); const Type OpType = (isVectorCompare() ? getSrc(0) : getDest())->getType(); Str << " = " << Opcode << "." << OpType << " "; dumpSources(Func); } static bool classof(const Inst *Instr) { return isClassof(Instr, K); } protected: FPSign Sign = FS_None; InstARM32ThreeAddrFP(Cfg *Func, Variable *Dest, Variable *Src0, Operand *Src1) : InstARM32(Func, K, 2, Dest) { addSource(Src0); addSource(Src1); } static const char *const Opcode; private: static constexpr bool isVectorCompare() { return K == InstARM32::Vceq || K == InstARM32::Vcgt || K == InstARM32::Vcge; } }; template class InstARM32ThreeAddrSignAwareFP : public InstARM32ThreeAddrFP { InstARM32ThreeAddrSignAwareFP() = delete; InstARM32ThreeAddrSignAwareFP(const InstARM32ThreeAddrSignAwareFP &) = delete; InstARM32ThreeAddrSignAwareFP & operator=(const InstARM32ThreeAddrSignAwareFP &) = delete; public: /// Create a vector/FP binary-op instruction like vadd, and vsub. Everything /// must be a register. static InstARM32ThreeAddrSignAwareFP *create(Cfg *Func, Variable *Dest, Variable *Src0, Variable *Src1) { return new (Func->allocate()) InstARM32ThreeAddrSignAwareFP(Func, Dest, Src0, Src1); } static InstARM32ThreeAddrSignAwareFP * create(Cfg *Func, Variable *Dest, Variable *Src0, ConstantInteger32 *Src1) { return new (Func->allocate()) InstARM32ThreeAddrSignAwareFP(Func, Dest, Src0, Src1); } void emitIAS(const Cfg *Func) const override; void setSignType(InstARM32::FPSign SignType) { this->Sign = SignType; } private: InstARM32ThreeAddrSignAwareFP(Cfg *Func, Variable *Dest, Variable *Src0, Operand *Src1) : InstARM32ThreeAddrFP(Func, Dest, Src0, Src1) {} }; /// Instructions of the form x := a op1 (y op2 z). E.g., multiply accumulate. template class InstARM32FourAddrGPR : public InstARM32Pred { InstARM32FourAddrGPR() = delete; InstARM32FourAddrGPR(const InstARM32FourAddrGPR &) = delete; InstARM32FourAddrGPR &operator=(const InstARM32FourAddrGPR &) = delete; public: // Every operand must be a register. static InstARM32FourAddrGPR *create(Cfg *Func, Variable *Dest, Variable *Src0, Variable *Src1, Variable *Src2, CondARM32::Cond Predicate) { return new (Func->allocate()) InstARM32FourAddrGPR(Func, Dest, Src0, Src1, Src2, Predicate); } void emit(const Cfg *Func) const override { if (!BuildDefs::dump()) return; emitFourAddr(Opcode, this, Func); } void emitIAS(const Cfg *Func) const override; void dump(const Cfg *Func) const override { if (!BuildDefs::dump()) return; Ostream &Str = Func->getContext()->getStrDump(); dumpDest(Func); Str << " = "; dumpOpcodePred(Str, Opcode, getDest()->getType()); Str << " "; dumpSources(Func); } static bool classof(const Inst *Instr) { return isClassof(Instr, K); } private: InstARM32FourAddrGPR(Cfg *Func, Variable *Dest, Variable *Src0, Variable *Src1, Variable *Src2, CondARM32::Cond Predicate) : InstARM32Pred(Func, K, 3, Dest, Predicate) { addSource(Src0); addSource(Src1); addSource(Src2); } static const char *const Opcode; }; /// Instructions of the form x := x op1 (y op2 z). E.g., multiply accumulate. /// We leave these as unconditional: "ARM deprecates the conditional execution /// of any instruction encoding provided by the Advanced SIMD Extension that is /// not also provided by the floating-point (VFP) extension". They do not set /// flags. template class InstARM32FourAddrFP : public InstARM32 { InstARM32FourAddrFP() = delete; InstARM32FourAddrFP(const InstARM32FourAddrFP &) = delete; InstARM32FourAddrFP &operator=(const InstARM32FourAddrFP &) = delete; public: // Every operand must be a register. static InstARM32FourAddrFP *create(Cfg *Func, Variable *Dest, Variable *Src0, Variable *Src1) { return new (Func->allocate()) InstARM32FourAddrFP(Func, Dest, Src0, Src1); } void emit(const Cfg *Func) const override { if (!BuildDefs::dump()) return; emitFourAddrFP(Opcode, Sign, this, Func); } void emitIAS(const Cfg *Func) const override; void dump(const Cfg *Func) const override { if (!BuildDefs::dump()) return; Ostream &Str = Func->getContext()->getStrDump(); dumpDest(Func); Str << " = "; Str << Opcode << "." << getDest()->getType() << " "; dumpDest(Func); Str << ", "; dumpSources(Func); } static bool classof(const Inst *Instr) { return isClassof(Instr, K); } private: InstARM32FourAddrFP(Cfg *Func, Variable *Dest, Variable *Src0, Variable *Src1) : InstARM32(Func, K, 3, Dest) { addSource(Dest); addSource(Src0); addSource(Src1); } FPSign Sign = FS_None; static const char *const Opcode; }; /// Instructions of the form x cmpop y (setting flags). template class InstARM32CmpLike : public InstARM32Pred { InstARM32CmpLike() = delete; InstARM32CmpLike(const InstARM32CmpLike &) = delete; InstARM32CmpLike &operator=(const InstARM32CmpLike &) = delete; public: static InstARM32CmpLike *create(Cfg *Func, Variable *Src0, Operand *Src1, CondARM32::Cond Predicate) { return new (Func->allocate()) InstARM32CmpLike(Func, Src0, Src1, Predicate); } void emit(const Cfg *Func) const override { if (!BuildDefs::dump()) return; emitCmpLike(Opcode, this, Func); } void emitIAS(const Cfg *Func) const override; void dump(const Cfg *Func) const override { if (!BuildDefs::dump()) return; Ostream &Str = Func->getContext()->getStrDump(); dumpOpcodePred(Str, Opcode, getSrc(0)->getType()); Str << " "; dumpSources(Func); } static bool classof(const Inst *Instr) { return isClassof(Instr, K); } private: InstARM32CmpLike(Cfg *Func, Variable *Src0, Operand *Src1, CondARM32::Cond Predicate) : InstARM32Pred(Func, K, 2, nullptr, Predicate) { HasSideEffects = true; addSource(Src0); addSource(Src1); } static const char *const Opcode; }; using InstARM32Adc = InstARM32ThreeAddrGPR; using InstARM32Add = InstARM32ThreeAddrGPR; using InstARM32And = InstARM32ThreeAddrGPR; using InstARM32Asr = InstARM32ThreeAddrGPR; using InstARM32Bic = InstARM32ThreeAddrGPR; using InstARM32Eor = InstARM32ThreeAddrGPR; using InstARM32Lsl = InstARM32ThreeAddrGPR; using InstARM32Lsr = InstARM32ThreeAddrGPR; using InstARM32Mul = InstARM32ThreeAddrGPR; using InstARM32Orr = InstARM32ThreeAddrGPR; using InstARM32Rsb = InstARM32ThreeAddrGPR; using InstARM32Rsc = InstARM32ThreeAddrGPR; using InstARM32Sbc = InstARM32ThreeAddrGPR; using InstARM32Sdiv = InstARM32ThreeAddrGPR; using InstARM32Sub = InstARM32ThreeAddrGPR; using InstARM32Udiv = InstARM32ThreeAddrGPR; using InstARM32Vadd = InstARM32ThreeAddrFP; using InstARM32Vand = InstARM32ThreeAddrFP; using InstARM32Vbsl = InstARM32ThreeAddrFP; using InstARM32Vceq = InstARM32ThreeAddrFP; using InstARM32Vcge = InstARM32ThreeAddrSignAwareFP; using InstARM32Vcgt = InstARM32ThreeAddrSignAwareFP; using InstARM32Vdiv = InstARM32ThreeAddrFP; using InstARM32Veor = InstARM32ThreeAddrFP; using InstARM32Vmla = InstARM32FourAddrFP; using InstARM32Vmls = InstARM32FourAddrFP; using InstARM32Vmovl = InstARM32ThreeAddrFP; using InstARM32Vmovh = InstARM32ThreeAddrFP; using InstARM32Vmovhl = InstARM32ThreeAddrFP; using InstARM32Vmovlh = InstARM32ThreeAddrFP; using InstARM32Vmul = InstARM32ThreeAddrFP; using InstARM32Vmvn = InstARM32UnaryopFP; using InstARM32Vneg = InstARM32UnaryopSignAwareFP; using InstARM32Vorr = InstARM32ThreeAddrFP; using InstARM32Vqadd = InstARM32ThreeAddrSignAwareFP; using InstARM32Vqsub = InstARM32ThreeAddrSignAwareFP; using InstARM32Vqmovn2 = InstARM32ThreeAddrSignAwareFP; using InstARM32Vmulh = InstARM32ThreeAddrSignAwareFP; using InstARM32Vmlap = InstARM32ThreeAddrFP; using InstARM32Vshl = InstARM32ThreeAddrSignAwareFP; using InstARM32Vshr = InstARM32ThreeAddrSignAwareFP; using InstARM32Vsub = InstARM32ThreeAddrFP; using InstARM32Ldr = InstARM32LoadBase; using InstARM32Ldrex = InstARM32LoadBase; using InstARM32Vldr1d = InstARM32LoadBase; using InstARM32Vldr1q = InstARM32LoadBase; using InstARM32Vzip = InstARM32ThreeAddrFP; /// MovT leaves the bottom bits alone so dest is also a source. This helps /// indicate that a previous MovW setting dest is not dead code. using InstARM32Movt = InstARM32TwoAddrGPR; using InstARM32Movw = InstARM32UnaryopGPR; using InstARM32Clz = InstARM32UnaryopGPR; using InstARM32Mvn = InstARM32UnaryopGPR; using InstARM32Rbit = InstARM32UnaryopGPR; using InstARM32Rev = InstARM32UnaryopGPR; // Technically, the uxt{b,h} and sxt{b,h} instructions have a rotation operand // as well (rotate source by 8, 16, 24 bits prior to extending), but we aren't // using that for now, so just model as a Unaryop. using InstARM32Sxt = InstARM32UnaryopGPR; using InstARM32Uxt = InstARM32UnaryopGPR; using InstARM32Vsqrt = InstARM32UnaryopFP; using InstARM32Mla = InstARM32FourAddrGPR; using InstARM32Mls = InstARM32FourAddrGPR; using InstARM32Cmn = InstARM32CmpLike; using InstARM32Cmp = InstARM32CmpLike; using InstARM32Tst = InstARM32CmpLike; // InstARM32Label represents an intra-block label that is the target of an // intra-block branch. The offset between the label and the branch must be fit // in the instruction immediate (considered "near"). class InstARM32Label : public InstARM32 { InstARM32Label() = delete; InstARM32Label(const InstARM32Label &) = delete; InstARM32Label &operator=(const InstARM32Label &) = delete; public: static InstARM32Label *create(Cfg *Func, TargetARM32 *Target) { return new (Func->allocate()) InstARM32Label(Func, Target); } uint32_t getEmitInstCount() const override { return 0; } GlobalString getLabelName() const { return Name; } SizeT getNumber() const { return Number; } void emit(const Cfg *Func) const override; void emitIAS(const Cfg *Func) const override; void dump(const Cfg *Func) const override; void setRelocOffset(RelocOffset *Value) { OffsetReloc = Value; } private: InstARM32Label(Cfg *Func, TargetARM32 *Target); RelocOffset *OffsetReloc = nullptr; SizeT Number; // used for unique label generation. GlobalString Name; }; /// Direct branch instruction. class InstARM32Br : public InstARM32Pred { InstARM32Br() = delete; InstARM32Br(const InstARM32Br &) = delete; InstARM32Br &operator=(const InstARM32Br &) = delete; public: /// Create a conditional branch to one of two nodes. static InstARM32Br *create(Cfg *Func, CfgNode *TargetTrue, CfgNode *TargetFalse, CondARM32::Cond Predicate) { assert(Predicate != CondARM32::AL); constexpr InstARM32Label *NoLabel = nullptr; return new (Func->allocate()) InstARM32Br(Func, TargetTrue, TargetFalse, NoLabel, Predicate); } /// Create an unconditional branch to a node. static InstARM32Br *create(Cfg *Func, CfgNode *Target) { constexpr CfgNode *NoCondTarget = nullptr; constexpr InstARM32Label *NoLabel = nullptr; return new (Func->allocate()) InstARM32Br(Func, NoCondTarget, Target, NoLabel, CondARM32::AL); } /// Create a non-terminator conditional branch to a node, with a fallthrough /// to the next instruction in the current node. This is used for switch /// lowering. static InstARM32Br *create(Cfg *Func, CfgNode *Target, CondARM32::Cond Predicate) { assert(Predicate != CondARM32::AL); constexpr CfgNode *NoUncondTarget = nullptr; constexpr InstARM32Label *NoLabel = nullptr; return new (Func->allocate()) InstARM32Br(Func, Target, NoUncondTarget, NoLabel, Predicate); } // Create a conditional intra-block branch (or unconditional, if // Condition==AL) to a label in the current block. static InstARM32Br *create(Cfg *Func, InstARM32Label *Label, CondARM32::Cond Predicate) { constexpr CfgNode *NoCondTarget = nullptr; constexpr CfgNode *NoUncondTarget = nullptr; return new (Func->allocate()) InstARM32Br(Func, NoCondTarget, NoUncondTarget, Label, Predicate); } const CfgNode *getTargetTrue() const { return TargetTrue; } const CfgNode *getTargetFalse() const { return TargetFalse; } bool optimizeBranch(const CfgNode *NextNode); uint32_t getEmitInstCount() const override { uint32_t Sum = 0; if (Label) ++Sum; if (getTargetTrue()) ++Sum; if (getTargetFalse()) ++Sum; return Sum; } bool isUnconditionalBranch() const override { return getPredicate() == CondARM32::AL; } bool repointEdges(CfgNode *OldNode, CfgNode *NewNode) override; void emit(const Cfg *Func) const override; void emitIAS(const Cfg *Func) const override; void dump(const Cfg *Func) const override; static bool classof(const Inst *Instr) { return isClassof(Instr, Br); } private: InstARM32Br(Cfg *Func, const CfgNode *TargetTrue, const CfgNode *TargetFalse, const InstARM32Label *Label, CondARM32::Cond Predicate); const CfgNode *TargetTrue; const CfgNode *TargetFalse; const InstARM32Label *Label; // Intra-block branch target }; /// Call instruction (bl/blx). Arguments should have already been pushed. /// Technically bl and the register form of blx can be predicated, but we'll /// leave that out until needed. class InstARM32Call : public InstARM32 { InstARM32Call() = delete; InstARM32Call(const InstARM32Call &) = delete; InstARM32Call &operator=(const InstARM32Call &) = delete; public: static InstARM32Call *create(Cfg *Func, Variable *Dest, Operand *CallTarget) { return new (Func->allocate()) InstARM32Call(Func, Dest, CallTarget); } Operand *getCallTarget() const { return getSrc(0); } void emit(const Cfg *Func) const override; void emitIAS(const Cfg *Func) const override; void dump(const Cfg *Func) const override; static bool classof(const Inst *Instr) { return isClassof(Instr, Call); } private: InstARM32Call(Cfg *Func, Variable *Dest, Operand *CallTarget); }; class InstARM32RegisterStackOp : public InstARM32 { InstARM32RegisterStackOp() = delete; InstARM32RegisterStackOp(const InstARM32RegisterStackOp &) = delete; InstARM32RegisterStackOp & operator=(const InstARM32RegisterStackOp &) = delete; public: void emit(const Cfg *Func) const override; void emitIAS(const Cfg *Func) const override; void dump(const Cfg *Func) const override; protected: InstARM32RegisterStackOp(Cfg *Func, InstKindARM32 Kind, SizeT Maxsrcs, Variable *Dest) : InstARM32(Func, Kind, Maxsrcs, Dest) {} void emitUsingForm(const Cfg *Func, const EmitForm Form) const; void emitGPRsAsText(const Cfg *Func) const; void emitSRegsAsText(const Cfg *Func, const Variable *BaseReg, SizeT Regcount) const; void emitSRegsOp(const Cfg *Func, const EmitForm, const Variable *BaseReg, SizeT RegCount, SizeT InstIndex) const; virtual const char *getDumpOpcode() const { return getGPROpcode(); } virtual const char *getGPROpcode() const = 0; virtual const char *getSRegOpcode() const = 0; virtual Variable *getStackReg(SizeT Index) const = 0; virtual SizeT getNumStackRegs() const = 0; virtual void emitSingleGPR(const Cfg *Func, const EmitForm Form, const Variable *Reg) const = 0; virtual void emitMultipleGPRs(const Cfg *Func, const EmitForm Form, IValueT Registers) const = 0; virtual void emitSRegs(const Cfg *Func, const EmitForm Form, const Variable *BaseReg, SizeT RegCount) const = 0; }; /// Pops a list of registers. It may be a list of GPRs, or a list of VFP "s" /// regs, but not both. In any case, the list must be sorted. class InstARM32Pop final : public InstARM32RegisterStackOp { InstARM32Pop() = delete; InstARM32Pop(const InstARM32Pop &) = delete; InstARM32Pop &operator=(const InstARM32Pop &) = delete; public: static InstARM32Pop *create(Cfg *Func, const VarList &Dests) { return new (Func->allocate()) InstARM32Pop(Func, Dests); } static bool classof(const Inst *Instr) { return isClassof(Instr, Pop); } private: InstARM32Pop(Cfg *Func, const VarList &Dests); virtual const char *getGPROpcode() const final; virtual const char *getSRegOpcode() const final; Variable *getStackReg(SizeT Index) const final; SizeT getNumStackRegs() const final; void emitSingleGPR(const Cfg *Func, const EmitForm Form, const Variable *Reg) const final; void emitMultipleGPRs(const Cfg *Func, const EmitForm Form, IValueT Registers) const final; void emitSRegs(const Cfg *Func, const EmitForm Form, const Variable *BaseReg, SizeT RegCount) const final; VarList Dests; }; /// Pushes a list of registers. Just like Pop (see above), the list may be of /// GPRs, or VFP "s" registers, but not both. class InstARM32Push final : public InstARM32RegisterStackOp { InstARM32Push() = delete; InstARM32Push(const InstARM32Push &) = delete; InstARM32Push &operator=(const InstARM32Push &) = delete; public: static InstARM32Push *create(Cfg *Func, const VarList &Srcs) { return new (Func->allocate()) InstARM32Push(Func, Srcs); } static bool classof(const Inst *Instr) { return isClassof(Instr, Push); } private: InstARM32Push(Cfg *Func, const VarList &Srcs); const char *getGPROpcode() const final; const char *getSRegOpcode() const final; Variable *getStackReg(SizeT Index) const final; SizeT getNumStackRegs() const final; void emitSingleGPR(const Cfg *Func, const EmitForm Form, const Variable *Reg) const final; void emitMultipleGPRs(const Cfg *Func, const EmitForm Form, IValueT Registers) const final; void emitSRegs(const Cfg *Func, const EmitForm Form, const Variable *BaseReg, SizeT RegCount) const final; }; /// Ret pseudo-instruction. This is actually a "bx" instruction with an "lr" /// register operand, but epilogue lowering will search for a Ret instead of a /// generic "bx". This instruction also takes a Source operand (for non-void /// returning functions) for liveness analysis, though a FakeUse before the ret /// would do just as well. /// /// NOTE: Even though "bx" can be predicated, for now leave out the predication /// since it's not yet known to be useful for Ret. That may complicate finding /// the terminator instruction if it's not guaranteed to be executed. class InstARM32Ret : public InstARM32 { InstARM32Ret() = delete; InstARM32Ret(const InstARM32Ret &) = delete; InstARM32Ret &operator=(const InstARM32Ret &) = delete; public: static InstARM32Ret *create(Cfg *Func, Variable *LR, Variable *Source = nullptr) { return new (Func->allocate()) InstARM32Ret(Func, LR, Source); } void emit(const Cfg *Func) const override; void emitIAS(const Cfg *Func) const override; void dump(const Cfg *Func) const override; static bool classof(const Inst *Instr) { return isClassof(Instr, Ret); } private: InstARM32Ret(Cfg *Func, Variable *LR, Variable *Source); }; /// Store instruction. It's important for liveness that there is no Dest operand /// (OperandARM32Mem instead of Dest Variable). class InstARM32Str final : public InstARM32Pred { InstARM32Str() = delete; InstARM32Str(const InstARM32Str &) = delete; InstARM32Str &operator=(const InstARM32Str &) = delete; public: /// Value must be a register. static InstARM32Str *create(Cfg *Func, Variable *Value, OperandARM32Mem *Mem, CondARM32::Cond Predicate) { return new (Func->allocate()) InstARM32Str(Func, Value, Mem, Predicate); } void emit(const Cfg *Func) const override; void emitIAS(const Cfg *Func) const override; void dump(const Cfg *Func) const override; static bool classof(const Inst *Instr) { return isClassof(Instr, Str); } private: InstARM32Str(Cfg *Func, Variable *Value, OperandARM32Mem *Mem, CondARM32::Cond Predicate); }; /// Exclusive Store instruction. Like its non-exclusive sibling, it's important /// for liveness that there is no Dest operand (OperandARM32Mem instead of Dest /// Variable). class InstARM32Strex final : public InstARM32Pred { InstARM32Strex() = delete; InstARM32Strex(const InstARM32Strex &) = delete; InstARM32Strex &operator=(const InstARM32Strex &) = delete; public: /// Value must be a register. static InstARM32Strex *create(Cfg *Func, Variable *Dest, Variable *Value, OperandARM32Mem *Mem, CondARM32::Cond Predicate) { return new (Func->allocate()) InstARM32Strex(Func, Dest, Value, Mem, Predicate); } void emit(const Cfg *Func) const override; void emitIAS(const Cfg *Func) const override; void dump(const Cfg *Func) const override; static bool classof(const Inst *Instr) { return isClassof(Instr, Strex); } private: InstARM32Strex(Cfg *Func, Variable *Dest, Variable *Value, OperandARM32Mem *Mem, CondARM32::Cond Predicate); }; /// Sub-vector store instruction. It's important for liveness that there is no /// Dest operand (OperandARM32Mem instead of Dest Variable). class InstARM32Vstr1 final : public InstARM32Pred { InstARM32Vstr1() = delete; InstARM32Vstr1(const InstARM32Vstr1 &) = delete; InstARM32Vstr1 &operator=(const InstARM32Vstr1 &) = delete; public: /// Value must be a register. static InstARM32Vstr1 *create(Cfg *Func, Variable *Value, OperandARM32Mem *Mem, CondARM32::Cond Predicate, SizeT Size) { return new (Func->allocate()) InstARM32Vstr1(Func, Value, Mem, Predicate, Size); } void emit(const Cfg *Func) const override; void emitIAS(const Cfg *Func) const override; void dump(const Cfg *Func) const override; static bool classof(const Inst *Instr) { return isClassof(Instr, Vstr1); } private: InstARM32Vstr1(Cfg *Func, Variable *Value, OperandARM32Mem *Mem, CondARM32::Cond Predicate, SizeT Size); SizeT Size; }; /// Vector element duplication/replication instruction. class InstARM32Vdup final : public InstARM32Pred { InstARM32Vdup() = delete; InstARM32Vdup(const InstARM32Vdup &) = delete; InstARM32Vdup &operator=(const InstARM32Vdup &) = delete; public: /// Value must be a register. static InstARM32Vdup *create(Cfg *Func, Variable *Dest, Variable *Src, IValueT Idx) { return new (Func->allocate()) InstARM32Vdup(Func, Dest, Src, Idx); } void emit(const Cfg *Func) const override; void emitIAS(const Cfg *Func) const override; void dump(const Cfg *Func) const override; static bool classof(const Inst *Instr) { return isClassof(Instr, Vdup); } private: InstARM32Vdup(Cfg *Func, Variable *Dest, Variable *Src, IValueT Idx); const IValueT Idx; }; class InstARM32Trap : public InstARM32 { InstARM32Trap() = delete; InstARM32Trap(const InstARM32Trap &) = delete; InstARM32Trap &operator=(const InstARM32Trap &) = delete; public: static InstARM32Trap *create(Cfg *Func) { return new (Func->allocate()) InstARM32Trap(Func); } void emit(const Cfg *Func) const override; void emitIAS(const Cfg *Func) const override; void dump(const Cfg *Func) const override; static bool classof(const Inst *Instr) { return isClassof(Instr, Trap); } private: explicit InstARM32Trap(Cfg *Func); }; /// Unsigned Multiply Long: d.lo, d.hi := x * y class InstARM32Umull : public InstARM32Pred { InstARM32Umull() = delete; InstARM32Umull(const InstARM32Umull &) = delete; InstARM32Umull &operator=(const InstARM32Umull &) = delete; public: /// Everything must be a register. static InstARM32Umull *create(Cfg *Func, Variable *DestLo, Variable *DestHi, Variable *Src0, Variable *Src1, CondARM32::Cond Predicate) { return new (Func->allocate()) InstARM32Umull(Func, DestLo, DestHi, Src0, Src1, Predicate); } void emit(const Cfg *Func) const override; void emitIAS(const Cfg *Func) const override; void dump(const Cfg *Func) const override; static bool classof(const Inst *Instr) { return isClassof(Instr, Umull); } private: InstARM32Umull(Cfg *Func, Variable *DestLo, Variable *DestHi, Variable *Src0, Variable *Src1, CondARM32::Cond Predicate); Variable *DestHi; }; /// Handles fp2int, int2fp, and fp2fp conversions. class InstARM32Vcvt final : public InstARM32Pred { InstARM32Vcvt() = delete; InstARM32Vcvt(const InstARM32Vcvt &) = delete; InstARM32Vcvt &operator=(const InstARM32Vcvt &) = delete; public: enum VcvtVariant { S2si, S2ui, Si2s, Ui2s, D2si, D2ui, Si2d, Ui2d, S2d, D2s, Vs2si, Vs2ui, Vsi2s, Vui2s, }; static InstARM32Vcvt *create(Cfg *Func, Variable *Dest, Variable *Src, VcvtVariant Variant, CondARM32::Cond Predicate) { return new (Func->allocate()) InstARM32Vcvt(Func, Dest, Src, Variant, Predicate); } void emit(const Cfg *Func) const override; void emitIAS(const Cfg *Func) const override; void dump(const Cfg *Func) const override; static bool classof(const Inst *Instr) { return isClassof(Instr, Vcvt); } private: InstARM32Vcvt(Cfg *Func, Variable *Dest, Variable *Src, VcvtVariant Variant, CondARM32::Cond Predicate); const VcvtVariant Variant; }; /// Handles (some of) vmov's various formats. class InstARM32Mov final : public InstARM32Pred { InstARM32Mov() = delete; InstARM32Mov(const InstARM32Mov &) = delete; InstARM32Mov &operator=(const InstARM32Mov &) = delete; public: static InstARM32Mov *create(Cfg *Func, Variable *Dest, Operand *Src, CondARM32::Cond Predicate) { return new (Func->allocate()) InstARM32Mov(Func, Dest, Src, Predicate); } bool isRedundantAssign() const override { return !isMultiDest() && !isMultiSource() && getPredicate() == CondARM32::AL && checkForRedundantAssign(getDest(), getSrc(0)); } bool isVarAssign() const override { return llvm::isa(getSrc(0)); } void emit(const Cfg *Func) const override; void emitIAS(const Cfg *Func) const override; void dump(const Cfg *Func) const override; static bool classof(const Inst *Instr) { return isClassof(Instr, Mov); } bool isMultiDest() const { return DestHi != nullptr; } bool isMultiSource() const { assert(getSrcSize() == 1 || getSrcSize() == 2); return getSrcSize() == 2; } Variable *getDestHi() const { return DestHi; } private: InstARM32Mov(Cfg *Func, Variable *Dest, Operand *Src, CondARM32::Cond Predicate); void emitMultiDestSingleSource(const Cfg *Func) const; void emitSingleDestMultiSource(const Cfg *Func) const; void emitSingleDestSingleSource(const Cfg *Func) const; Variable *DestHi = nullptr; }; /// Generates vmov Rd, Dn[x] instructions, and their related floating point /// versions. class InstARM32Extract final : public InstARM32Pred { InstARM32Extract() = delete; InstARM32Extract(const InstARM32Extract &) = delete; InstARM32Extract &operator=(const InstARM32Extract &) = delete; public: static InstARM32Extract *create(Cfg *Func, Variable *Dest, Variable *Src0, uint32_t Index, CondARM32::Cond Predicate) { return new (Func->allocate()) InstARM32Extract(Func, Dest, Src0, Index, Predicate); } void emit(const Cfg *Func) const override; void emitIAS(const Cfg *Func) const override; static bool classof(const Inst *Inst) { return isClassof(Inst, Extract); } private: InstARM32Extract(Cfg *Func, Variable *Dest, Variable *Src0, uint32_t Index, CondARM32::Cond Predicate) : InstARM32Pred(Func, InstARM32::Extract, 1, Dest, Predicate), Index(Index) { assert(Index < typeNumElements(Src0->getType())); addSource(Src0); } const uint32_t Index; }; /// Generates vmov Dn[x], Rd instructions, and their related floating point /// versions. class InstARM32Insert final : public InstARM32Pred { InstARM32Insert() = delete; InstARM32Insert(const InstARM32Insert &) = delete; InstARM32Insert &operator=(const InstARM32Insert &) = delete; public: static InstARM32Insert *create(Cfg *Func, Variable *Dest, Variable *Src0, uint32_t Index, CondARM32::Cond Predicate) { return new (Func->allocate()) InstARM32Insert(Func, Dest, Src0, Index, Predicate); } void emit(const Cfg *Func) const override; void emitIAS(const Cfg *Func) const override; static bool classof(const Inst *Inst) { return isClassof(Inst, Insert); } private: InstARM32Insert(Cfg *Func, Variable *Dest, Variable *Src0, uint32_t Index, CondARM32::Cond Predicate) : InstARM32Pred(Func, InstARM32::Insert, 1, Dest, Predicate), Index(Index) { assert(Index < typeNumElements(Dest->getType())); addSource(Src0); } const uint32_t Index; }; class InstARM32Vcmp final : public InstARM32Pred { InstARM32Vcmp() = delete; InstARM32Vcmp(const InstARM32Vcmp &) = delete; InstARM32Vcmp &operator=(const InstARM32Vcmp &) = delete; public: static InstARM32Vcmp *create(Cfg *Func, Variable *Src0, Variable *Src1, CondARM32::Cond Predicate) { return new (Func->allocate()) InstARM32Vcmp(Func, Src0, Src1, Predicate); } static InstARM32Vcmp *create(Cfg *Func, Variable *Src0, OperandARM32FlexFpZero *Src1, CondARM32::Cond Predicate) { return new (Func->allocate()) InstARM32Vcmp(Func, Src0, Src1, Predicate); } void emit(const Cfg *Func) const override; void emitIAS(const Cfg *Func) const override; void dump(const Cfg *Func) const override; static bool classof(const Inst *Instr) { return isClassof(Instr, Vcmp); } private: InstARM32Vcmp(Cfg *Func, Variable *Src0, Operand *Src1, CondARM32::Cond Predicate); }; /// Copies the FP Status and Control Register the core flags. class InstARM32Vmrs final : public InstARM32Pred { InstARM32Vmrs() = delete; InstARM32Vmrs(const InstARM32Vmrs &) = delete; InstARM32Vmrs &operator=(const InstARM32Vmrs &) = delete; public: static InstARM32Vmrs *create(Cfg *Func, CondARM32::Cond Predicate) { return new (Func->allocate()) InstARM32Vmrs(Func, Predicate); } void emit(const Cfg *Func) const override; void emitIAS(const Cfg *Func) const override; void dump(const Cfg *Func) const override; static bool classof(const Inst *Instr) { return isClassof(Instr, Vmrs); } private: InstARM32Vmrs(Cfg *Func, CondARM32::Cond Predicate); }; class InstARM32Vabs final : public InstARM32Pred { InstARM32Vabs() = delete; InstARM32Vabs(const InstARM32Vabs &) = delete; InstARM32Vabs &operator=(const InstARM32Vabs &) = delete; public: static InstARM32Vabs *create(Cfg *Func, Variable *Dest, Variable *Src, CondARM32::Cond Predicate) { return new (Func->allocate()) InstARM32Vabs(Func, Dest, Src, Predicate); } void emit(const Cfg *Func) const override; void emitIAS(const Cfg *Func) const override; void dump(const Cfg *Func) const override; static bool classof(const Inst *Instr) { return isClassof(Instr, Vabs); } private: InstARM32Vabs(Cfg *Func, Variable *Dest, Variable *Src, CondARM32::Cond Predicate); }; class InstARM32Dmb final : public InstARM32Pred { InstARM32Dmb() = delete; InstARM32Dmb(const InstARM32Dmb &) = delete; InstARM32Dmb &operator=(const InstARM32Dmb &) = delete; public: static InstARM32Dmb *create(Cfg *Func) { return new (Func->allocate()) InstARM32Dmb(Func); } void emit(const Cfg *Func) const override; void emitIAS(const Cfg *Func) const override; void dump(const Cfg *Func) const override; static bool classof(const Inst *Instr) { return isClassof(Instr, Dmb); } private: explicit InstARM32Dmb(Cfg *Func); }; class InstARM32Nop final : public InstARM32Pred { InstARM32Nop() = delete; InstARM32Nop(const InstARM32Nop &) = delete; InstARM32Nop &operator=(const InstARM32Nop &) = delete; public: static InstARM32Nop *create(Cfg *Func) { return new (Func->allocate()) InstARM32Nop(Func); } void emit(const Cfg *Func) const override; void emitIAS(const Cfg *Func) const override; void dump(const Cfg *Func) const override; static bool classof(const Inst *Instr) { return isClassof(Instr, Nop); } private: explicit InstARM32Nop(Cfg *Func); }; // Declare partial template specializations of emit() methods that already have // default implementations. Without this, there is the possibility of ODR // violations and link errors. template <> void InstARM32Ldr::emit(const Cfg *Func) const; template <> void InstARM32Movw::emit(const Cfg *Func) const; template <> void InstARM32Movt::emit(const Cfg *Func) const; template <> void InstARM32Vldr1d::emit(const Cfg *Func) const; template <> void InstARM32Vldr1q::emit(const Cfg *Func) const; // Two-addr ops template <> constexpr const char *InstARM32Movt::Opcode = "movt"; // Unary ops template <> constexpr const char *InstARM32Movw::Opcode = "movw"; template <> constexpr const char *InstARM32Clz::Opcode = "clz"; template <> constexpr const char *InstARM32Mvn::Opcode = "mvn"; template <> constexpr const char *InstARM32Rbit::Opcode = "rbit"; template <> constexpr const char *InstARM32Rev::Opcode = "rev"; template <> constexpr const char *InstARM32Sxt::Opcode = "sxt"; // still requires b/h template <> constexpr const char *InstARM32Uxt::Opcode = "uxt"; // still requires b/h // FP template <> constexpr const char *InstARM32Vsqrt::Opcode = "vsqrt"; // Mov-like ops template <> constexpr const char *InstARM32Ldr::Opcode = "ldr"; template <> constexpr const char *InstARM32Ldrex::Opcode = "ldrex"; template <> constexpr const char *InstARM32Vldr1d::Opcode = "vldr1d"; template <> constexpr const char *InstARM32Vldr1q::Opcode = "vldr1q"; // Three-addr ops template <> constexpr const char *InstARM32Adc::Opcode = "adc"; template <> constexpr const char *InstARM32Add::Opcode = "add"; template <> constexpr const char *InstARM32And::Opcode = "and"; template <> constexpr const char *InstARM32Asr::Opcode = "asr"; template <> constexpr const char *InstARM32Bic::Opcode = "bic"; template <> constexpr const char *InstARM32Eor::Opcode = "eor"; template <> constexpr const char *InstARM32Lsl::Opcode = "lsl"; template <> constexpr const char *InstARM32Lsr::Opcode = "lsr"; template <> constexpr const char *InstARM32Mul::Opcode = "mul"; template <> constexpr const char *InstARM32Orr::Opcode = "orr"; template <> constexpr const char *InstARM32Rsb::Opcode = "rsb"; template <> constexpr const char *InstARM32Rsc::Opcode = "rsc"; template <> constexpr const char *InstARM32Sbc::Opcode = "sbc"; template <> constexpr const char *InstARM32Sdiv::Opcode = "sdiv"; template <> constexpr const char *InstARM32Sub::Opcode = "sub"; template <> constexpr const char *InstARM32Udiv::Opcode = "udiv"; // FP template <> constexpr const char *InstARM32Vadd::Opcode = "vadd"; template <> constexpr const char *InstARM32Vand::Opcode = "vand"; template <> constexpr const char *InstARM32Vbsl::Opcode = "vbsl"; template <> constexpr const char *InstARM32Vceq::Opcode = "vceq"; template <> constexpr const char *InstARM32ThreeAddrFP::Opcode = "vcge"; template <> constexpr const char *InstARM32ThreeAddrFP::Opcode = "vcgt"; template <> constexpr const char *InstARM32Vdiv::Opcode = "vdiv"; template <> constexpr const char *InstARM32Veor::Opcode = "veor"; template <> constexpr const char *InstARM32Vmla::Opcode = "vmla"; template <> constexpr const char *InstARM32Vmls::Opcode = "vmls"; template <> constexpr const char *InstARM32Vmul::Opcode = "vmul"; template <> constexpr const char *InstARM32Vmvn::Opcode = "vmvn"; template <> constexpr const char *InstARM32Vmovl::Opcode = "vmovl"; template <> constexpr const char *InstARM32Vmovh::Opcode = "vmovh"; template <> constexpr const char *InstARM32Vmovhl::Opcode = "vmovhl"; template <> constexpr const char *InstARM32Vmovlh::Opcode = "vmovlh"; template <> constexpr const char *InstARM32Vorr::Opcode = "vorr"; template <> constexpr const char *InstARM32UnaryopFP::Opcode = "vneg"; template <> constexpr const char *InstARM32ThreeAddrFP::Opcode = "vshl"; template <> constexpr const char *InstARM32ThreeAddrFP::Opcode = "vshr"; template <> constexpr const char *InstARM32Vsub::Opcode = "vsub"; template <> constexpr const char *InstARM32ThreeAddrFP::Opcode = "vqadd"; template <> constexpr const char *InstARM32ThreeAddrFP::Opcode = "vqsub"; template <> constexpr const char *InstARM32ThreeAddrFP::Opcode = "vqmovn2"; template <> constexpr const char *InstARM32ThreeAddrFP::Opcode = "vmulh"; template <> constexpr const char *InstARM32ThreeAddrFP::Opcode = "vmlap"; template <> constexpr const char *InstARM32ThreeAddrFP::Opcode = "vzip"; // Four-addr ops template <> constexpr const char *InstARM32Mla::Opcode = "mla"; template <> constexpr const char *InstARM32Mls::Opcode = "mls"; // Cmp-like ops template <> constexpr const char *InstARM32Cmn::Opcode = "cmn"; template <> constexpr const char *InstARM32Cmp::Opcode = "cmp"; template <> constexpr const char *InstARM32Tst::Opcode = "tst"; } // end of namespace ARM32 } // end of namespace Ice #endif // SUBZERO_SRC_ICEINSTARM32_H