Index: src/IceInstARM32.h |
diff --git a/src/IceInstARM32.h b/src/IceInstARM32.h |
index e4e6c49ab8be20e2ba81dc0e380eb930df4205cb..2e3f764d2953740ccfbbf3c4aa526af9c4c96e14 100644 |
--- a/src/IceInstARM32.h |
+++ b/src/IceInstARM32.h |
@@ -25,15 +25,22 @@ namespace Ice { |
class TargetARM32; |
-// OperandARM32 extends the Operand hierarchy. |
-// TODO(jvoung): Add the OperandARM32Mem and OperandARM32Flex. |
+// 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 }; |
+ enum OperandKindARM32 { |
+ k__Start = Operand::kTarget, |
+ kMem, |
+ kFlexStart, |
+ kFlexImm = kFlexStart, |
+ kFlexReg, |
+ kFlexEnd = kFlexReg |
+ }; |
enum ShiftKind { |
kNoShift = -1, |
@@ -56,18 +63,178 @@ protected: |
// OperandARM32Mem represents a memory operand in any of the various ARM32 |
// addressing modes. |
-// TODO(jvoung): Fill out more. |
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 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 = nullptr, |
+ 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. |
+ // Note that this mode is disallowed in the NaCl sandbox. |
+ 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 { return Mode >= NegOffset; } |
+ |
+ 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); |
+ ~OperandARM32Mem() override {} |
+ Variable *Base; |
+ ConstantInteger32 *ImmOffset; |
+ Variable *Index; |
+ ShiftKind ShiftOp; |
+ uint16_t ShiftAmt; |
+ AddrMode Mode; |
+}; |
+ |
+// 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) {} |
+ ~OperandARM32Flex() override {} |
+}; |
+ |
+// 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) { |
+ return new (Func->allocate<OperandARM32FlexImm>()) |
+ OperandARM32FlexImm(Func, Ty, Imm, 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); |
+ ~OperandARM32FlexImm() override {} |
+ |
+ uint32_t Imm; |
+ uint32_t RotateAmt; |
+}; |
+ |
+// 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); |
+ ~OperandARM32FlexReg() override {} |
+ |
+ Variable *Reg; |
+ ShiftKind ShiftOp; |
+ Operand *ShiftAmt; |
}; |
class InstARM32 : public InstTarget { |
@@ -76,7 +243,15 @@ class InstARM32 : public InstTarget { |
InstARM32 &operator=(const InstARM32 &) = delete; |
public: |
- enum InstKindARM32 { k__Start = Inst::Target, Ret }; |
+ enum InstKindARM32 { |
+ k__Start = Inst::Target, |
+ Mov, |
+ Movt, |
+ Movw, |
+ Mvn, |
+ Ret, |
+ Ldr |
+ }; |
static const char *getWidthString(Type Ty); |
@@ -91,6 +266,168 @@ protected: |
} |
}; |
+void emitTwoAddr(const char *Opcode, const Inst *Inst, const Cfg *Func); |
+ |
+// TODO(jvoung): add condition codes if instruction can be predicated. |
+ |
+// Instructions of the form x := op(y). |
+template <InstARM32::InstKindARM32 K> |
+class InstARM32UnaryopGPR : public InstARM32 { |
+ InstARM32UnaryopGPR() = delete; |
+ InstARM32UnaryopGPR(const InstARM32UnaryopGPR &) = delete; |
+ InstARM32UnaryopGPR &operator=(const InstARM32UnaryopGPR &) = delete; |
+ |
+public: |
+ static InstARM32UnaryopGPR *create(Cfg *Func, Variable *Dest, Operand *Src) { |
+ return new (Func->allocate<InstARM32UnaryopGPR>()) |
+ InstARM32UnaryopGPR(Func, Dest, Src); |
+ } |
+ void emit(const Cfg *Func) const override { |
+ if (!ALLOW_DUMP) |
+ return; |
+ Ostream &Str = Func->getContext()->getStrEmit(); |
+ assert(getSrcSize() == 1); |
+ Str << "\t" << Opcode << "\t"; |
+ getDest()->emit(Func); |
+ Str << ", "; |
+ getSrc(0)->emit(Func); |
+ } |
+ void emitIAS(const Cfg *Func) const override { |
+ (void)Func; |
+ llvm_unreachable("Not yet implemented"); |
+ } |
+ void dump(const Cfg *Func) const override { |
+ if (!ALLOW_DUMP) |
+ return; |
+ Ostream &Str = Func->getContext()->getStrDump(); |
+ dumpDest(Func); |
+ Str << " = " << Opcode << "." << getDest()->getType() << " "; |
+ dumpSources(Func); |
+ } |
+ static bool classof(const Inst *Inst) { return isClassof(Inst, K); } |
+ |
+private: |
+ InstARM32UnaryopGPR(Cfg *Func, Variable *Dest, Operand *Src) |
+ : InstARM32(Func, K, 1, Dest) { |
+ addSource(Src); |
+ } |
+ ~InstARM32UnaryopGPR() override {} |
+ static const char *Opcode; |
+}; |
+ |
+// Instructions of the form x := x op y. |
+template <InstARM32::InstKindARM32 K> |
+class InstARM32TwoAddrGPR : public InstARM32 { |
+ 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) { |
+ return new (Func->allocate<InstARM32TwoAddrGPR>()) |
+ InstARM32TwoAddrGPR(Func, Dest, Src); |
+ } |
+ void emit(const Cfg *Func) const override { |
+ if (!ALLOW_DUMP) |
+ return; |
+ emitTwoAddr(Opcode, this, Func); |
+ } |
+ void emitIAS(const Cfg *Func) const override { |
+ (void)Func; |
+ llvm::report_fatal_error("Not yet implemented"); |
+ } |
+ void dump(const Cfg *Func) const override { |
+ if (!ALLOW_DUMP) |
+ return; |
+ Ostream &Str = Func->getContext()->getStrDump(); |
+ dumpDest(Func); |
+ Str << " = " << Opcode << "." << getDest()->getType() << " "; |
+ dumpSources(Func); |
+ } |
+ static bool classof(const Inst *Inst) { return isClassof(Inst, K); } |
+ |
+private: |
+ InstARM32TwoAddrGPR(Cfg *Func, Variable *Dest, Operand *Src) |
+ : InstARM32(Func, K, 2, Dest) { |
+ addSource(Dest); |
+ addSource(Src); |
+ } |
+ ~InstARM32TwoAddrGPR() override {} |
+ static const char *Opcode; |
+}; |
+ |
+// Base class for assignment instructions. |
+// These can be tested for redundancy (and elided if redundant). |
+template <InstARM32::InstKindARM32 K> |
+class InstARM32Movlike : public InstARM32 { |
+ InstARM32Movlike() = delete; |
+ InstARM32Movlike(const InstARM32Movlike &) = delete; |
+ InstARM32Movlike &operator=(const InstARM32Movlike &) = delete; |
+ |
+public: |
+ static InstARM32Movlike *create(Cfg *Func, Variable *Dest, Operand *Source) { |
+ return new (Func->allocate<InstARM32Movlike>()) |
+ InstARM32Movlike(Func, Dest, Source); |
+ } |
+ bool isRedundantAssign() const override { |
+ return checkForRedundantAssign(getDest(), getSrc(0)); |
+ } |
+ bool isSimpleAssign() const override { return true; } |
+ void emit(const Cfg *Func) const override; |
+ void emitIAS(const Cfg *Func) const override; |
+ void dump(const Cfg *Func) const override { |
+ if (!ALLOW_DUMP) |
+ return; |
+ Ostream &Str = Func->getContext()->getStrDump(); |
+ Str << Opcode << "." << getDest()->getType() << " "; |
+ dumpDest(Func); |
+ Str << ", "; |
+ dumpSources(Func); |
+ } |
+ static bool classof(const Inst *Inst) { return isClassof(Inst, K); } |
+ |
+private: |
+ InstARM32Movlike(Cfg *Func, Variable *Dest, Operand *Source) |
+ : InstARM32(Func, K, 1, Dest) { |
+ addSource(Source); |
+ } |
+ ~InstARM32Movlike() override {} |
+ |
+ static const char *Opcode; |
+}; |
+ |
+// Move instruction (variable <- flex). This is more of a pseudo-inst. |
+// If var is a register, then we use "mov". If var is stack, then we use |
+// "str" to store to the stack. |
+typedef InstARM32Movlike<InstARM32::Mov> InstARM32Mov; |
+// 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. |
+typedef InstARM32TwoAddrGPR<InstARM32::Movt> InstARM32Movt; |
+typedef InstARM32UnaryopGPR<InstARM32::Movw> InstARM32Movw; |
+typedef InstARM32UnaryopGPR<InstARM32::Mvn> InstARM32Mvn; |
+ |
+// Load instruction. |
+class InstARM32Ldr : public InstARM32 { |
+ InstARM32Ldr() = delete; |
+ InstARM32Ldr(const InstARM32Ldr &) = delete; |
+ InstARM32Ldr &operator=(const InstARM32Ldr &) = delete; |
+ |
+public: |
+ // Dest must be a register. |
+ static InstARM32Ldr *create(Cfg *Func, Variable *Dest, OperandARM32Mem *Mem) { |
+ return new (Func->allocate<InstARM32Ldr>()) InstARM32Ldr(Func, Dest, Mem); |
+ } |
+ 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 *Inst) { return isClassof(Inst, Ldr); } |
+ |
+private: |
+ InstARM32Ldr(Cfg *Func, Variable *Dest, OperandARM32Mem *Mem); |
+ ~InstARM32Ldr() override {} |
+}; |
+ |
// 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 |
@@ -116,6 +453,13 @@ private: |
~InstARM32Ret() override {} |
}; |
+// 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 InstARM32Movw::emit(const Cfg *Func) const; |
+template <> void InstARM32Movt::emit(const Cfg *Func) const; |
+ |
} // end of namespace Ice |
#endif // SUBZERO_SRC_ICEINSTARM32_H |