| //===- 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 << "<OperandARM32>"; | |
| } | |
| protected: | |
| OperandARM32(OperandKindARM32 Kind, Type Ty) | |
| : Operand(static_cast<OperandKind>(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>()) | |
| 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>()) | |
| 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<OperandKind>(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>()) | |
| OperandARM32ShAmtImm(ShAmt); | |
| } | |
| static bool classof(const Operand *Operand) { | |
| return Operand->getKind() == static_cast<OperandKind>(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<OperandKind>(kFlexStart) <= Operand->getKind() && | |
| Operand->getKind() <= static_cast<OperandKind>(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<OperandKind>(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>()) | |
| 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<OperandKind>(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>()) | |
| 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<OperandKind>(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>()) | |
| 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<OperandKind>(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>()) StackVariable(Func, Ty, Index); | |
| } | |
| constexpr static auto StackVariableKind = | |
| static_cast<OperandKind>(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<InstKind>(Kind), Maxsrcs, Dest) {} | |
| static bool isClassof(const Inst *Instr, InstKindARM32 MyKind) { | |
| return Instr->getKind() == static_cast<InstKind>(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 <typename StreamType> | |
| inline StreamType &operator<<(StreamType &Stream, CondARM32::Cond Predicate) { | |
| Stream << InstARM32Pred::predString(Predicate); | |
| return Stream; | |
| } | |
| /// Instructions of the form x := op(y). | |
| template <InstARM32::InstKindARM32 K, bool NeedsWidthSuffix> | |
| 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>()) | |
| 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 <InstARM32::InstKindARM32 K> | |
| 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>()) | |
| 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 <InstARM32::InstKindARM32 K> | |
| class InstARM32UnaryopSignAwareFP : public InstARM32UnaryopFP<K> { | |
| 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>()) | |
| 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<K>(Func, Dest, Src, Predicate) {} | |
| }; | |
| /// Instructions of the form x := x op y. | |
| template <InstARM32::InstKindARM32 K> | |
| 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>()) | |
| 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 <InstARM32::InstKindARM32 K> | |
| 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>()) | |
| 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 <InstARM32::InstKindARM32 K> | |
| 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>()) | |
| 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 <InstARM32::InstKindARM32 K> | |
| 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>()) | |
| 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 <InstARM32::InstKindARM32 K> | |
| class InstARM32ThreeAddrSignAwareFP : public InstARM32ThreeAddrFP<K> { | |
| 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>()) | |
| InstARM32ThreeAddrSignAwareFP(Func, Dest, Src0, Src1); | |
| } | |
| static InstARM32ThreeAddrSignAwareFP * | |
| create(Cfg *Func, Variable *Dest, Variable *Src0, ConstantInteger32 *Src1) { | |
| return new (Func->allocate<InstARM32ThreeAddrSignAwareFP>()) | |
| 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<K>(Func, Dest, Src0, Src1) {} | |
| }; | |
| /// Instructions of the form x := a op1 (y op2 z). E.g., multiply accumulate. | |
| template <InstARM32::InstKindARM32 K> | |
| 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>()) | |
| 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 <InstARM32::InstKindARM32 K> | |
| 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>()) | |
| 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 <InstARM32::InstKindARM32 K> | |
| 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>()) | |
| 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<InstARM32::Adc>; | |
| using InstARM32Add = InstARM32ThreeAddrGPR<InstARM32::Add>; | |
| using InstARM32And = InstARM32ThreeAddrGPR<InstARM32::And>; | |
| using InstARM32Asr = InstARM32ThreeAddrGPR<InstARM32::Asr>; | |
| using InstARM32Bic = InstARM32ThreeAddrGPR<InstARM32::Bic>; | |
| using InstARM32Eor = InstARM32ThreeAddrGPR<InstARM32::Eor>; | |
| using InstARM32Lsl = InstARM32ThreeAddrGPR<InstARM32::Lsl>; | |
| using InstARM32Lsr = InstARM32ThreeAddrGPR<InstARM32::Lsr>; | |
| using InstARM32Mul = InstARM32ThreeAddrGPR<InstARM32::Mul>; | |
| using InstARM32Orr = InstARM32ThreeAddrGPR<InstARM32::Orr>; | |
| using InstARM32Rsb = InstARM32ThreeAddrGPR<InstARM32::Rsb>; | |
| using InstARM32Rsc = InstARM32ThreeAddrGPR<InstARM32::Rsc>; | |
| using InstARM32Sbc = InstARM32ThreeAddrGPR<InstARM32::Sbc>; | |
| using InstARM32Sdiv = InstARM32ThreeAddrGPR<InstARM32::Sdiv>; | |
| using InstARM32Sub = InstARM32ThreeAddrGPR<InstARM32::Sub>; | |
| using InstARM32Udiv = InstARM32ThreeAddrGPR<InstARM32::Udiv>; | |
| using InstARM32Vadd = InstARM32ThreeAddrFP<InstARM32::Vadd>; | |
| using InstARM32Vand = InstARM32ThreeAddrFP<InstARM32::Vand>; | |
| using InstARM32Vbsl = InstARM32ThreeAddrFP<InstARM32::Vbsl>; | |
| using InstARM32Vceq = InstARM32ThreeAddrFP<InstARM32::Vceq>; | |
| using InstARM32Vcge = InstARM32ThreeAddrSignAwareFP<InstARM32::Vcge>; | |
| using InstARM32Vcgt = InstARM32ThreeAddrSignAwareFP<InstARM32::Vcgt>; | |
| using InstARM32Vdiv = InstARM32ThreeAddrFP<InstARM32::Vdiv>; | |
| using InstARM32Veor = InstARM32ThreeAddrFP<InstARM32::Veor>; | |
| using InstARM32Vmla = InstARM32FourAddrFP<InstARM32::Vmla>; | |
| using InstARM32Vmls = InstARM32FourAddrFP<InstARM32::Vmls>; | |
| using InstARM32Vmovl = InstARM32ThreeAddrFP<InstARM32::Vmovl>; | |
| using InstARM32Vmovh = InstARM32ThreeAddrFP<InstARM32::Vmovh>; | |
| using InstARM32Vmovhl = InstARM32ThreeAddrFP<InstARM32::Vmovhl>; | |
| using InstARM32Vmovlh = InstARM32ThreeAddrFP<InstARM32::Vmovlh>; | |
| using InstARM32Vmul = InstARM32ThreeAddrFP<InstARM32::Vmul>; | |
| using InstARM32Vmvn = InstARM32UnaryopFP<InstARM32::Vmvn>; | |
| using InstARM32Vneg = InstARM32UnaryopSignAwareFP<InstARM32::Vneg>; | |
| using InstARM32Vorr = InstARM32ThreeAddrFP<InstARM32::Vorr>; | |
| using InstARM32Vqadd = InstARM32ThreeAddrSignAwareFP<InstARM32::Vqadd>; | |
| using InstARM32Vqsub = InstARM32ThreeAddrSignAwareFP<InstARM32::Vqsub>; | |
| using InstARM32Vqmovn2 = InstARM32ThreeAddrSignAwareFP<InstARM32::Vqmovn2>; | |
| using InstARM32Vmulh = InstARM32ThreeAddrSignAwareFP<InstARM32::Vmulh>; | |
| using InstARM32Vmlap = InstARM32ThreeAddrFP<InstARM32::Vmlap>; | |
| using InstARM32Vshl = InstARM32ThreeAddrSignAwareFP<InstARM32::Vshl>; | |
| using InstARM32Vshr = InstARM32ThreeAddrSignAwareFP<InstARM32::Vshr>; | |
| using InstARM32Vsub = InstARM32ThreeAddrFP<InstARM32::Vsub>; | |
| using InstARM32Ldr = InstARM32LoadBase<InstARM32::Ldr>; | |
| using InstARM32Ldrex = InstARM32LoadBase<InstARM32::Ldrex>; | |
| using InstARM32Vldr1d = InstARM32LoadBase<InstARM32::Vldr1d>; | |
| using InstARM32Vldr1q = InstARM32LoadBase<InstARM32::Vldr1q>; | |
| using InstARM32Vzip = InstARM32ThreeAddrFP<InstARM32::Vzip>; | |
| /// 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<InstARM32::Movt>; | |
| using InstARM32Movw = InstARM32UnaryopGPR<InstARM32::Movw, false>; | |
| using InstARM32Clz = InstARM32UnaryopGPR<InstARM32::Clz, false>; | |
| using InstARM32Mvn = InstARM32UnaryopGPR<InstARM32::Mvn, false>; | |
| using InstARM32Rbit = InstARM32UnaryopGPR<InstARM32::Rbit, false>; | |
| using InstARM32Rev = InstARM32UnaryopGPR<InstARM32::Rev, false>; | |
| // 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<InstARM32::Sxt, true>; | |
| using InstARM32Uxt = InstARM32UnaryopGPR<InstARM32::Uxt, true>; | |
| using InstARM32Vsqrt = InstARM32UnaryopFP<InstARM32::Vsqrt>; | |
| using InstARM32Mla = InstARM32FourAddrGPR<InstARM32::Mla>; | |
| using InstARM32Mls = InstARM32FourAddrGPR<InstARM32::Mls>; | |
| using InstARM32Cmn = InstARM32CmpLike<InstARM32::Cmn>; | |
| using InstARM32Cmp = InstARM32CmpLike<InstARM32::Cmp>; | |
| using InstARM32Tst = InstARM32CmpLike<InstARM32::Tst>; | |
| // 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>()) 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>()) | |
| 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>()) | |
| 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>()) | |
| 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>()) | |
| 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>()) | |
| 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>()) 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>()) 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>()) 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>()) | |
| 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>()) | |
| 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>()) | |
| 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>()) | |
| 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>()) 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>()) | |
| 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>()) | |
| 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>()) | |
| 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<Variable>(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>()) | |
| 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>()) | |
| 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>()) | |
| InstARM32Vcmp(Func, Src0, Src1, Predicate); | |
| } | |
| static InstARM32Vcmp *create(Cfg *Func, Variable *Src0, | |
| OperandARM32FlexFpZero *Src1, | |
| CondARM32::Cond Predicate) { | |
| return new (Func->allocate<InstARM32Vcmp>()) | |
| 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>()) 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>()) | |
| 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>()) 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>()) 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<InstARM32::Vcge>::Opcode = "vcge"; | |
| template <> | |
| constexpr const char *InstARM32ThreeAddrFP<InstARM32::Vcgt>::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<InstARM32::Vneg>::Opcode = "vneg"; | |
| template <> | |
| constexpr const char *InstARM32ThreeAddrFP<InstARM32::Vshl>::Opcode = "vshl"; | |
| template <> | |
| constexpr const char *InstARM32ThreeAddrFP<InstARM32::Vshr>::Opcode = "vshr"; | |
| template <> constexpr const char *InstARM32Vsub::Opcode = "vsub"; | |
| template <> | |
| constexpr const char *InstARM32ThreeAddrFP<InstARM32::Vqadd>::Opcode = "vqadd"; | |
| template <> | |
| constexpr const char *InstARM32ThreeAddrFP<InstARM32::Vqsub>::Opcode = "vqsub"; | |
| template <> | |
| constexpr const char *InstARM32ThreeAddrFP<InstARM32::Vqmovn2>::Opcode = | |
| "vqmovn2"; | |
| template <> | |
| constexpr const char *InstARM32ThreeAddrFP<InstARM32::Vmulh>::Opcode = "vmulh"; | |
| template <> | |
| constexpr const char *InstARM32ThreeAddrFP<InstARM32::Vmlap>::Opcode = "vmlap"; | |
| template <> | |
| constexpr const char *InstARM32ThreeAddrFP<InstARM32::Vzip>::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 |