| //===- subzero/src/IceAssemblerARM32.h - Assembler for ARM32 ----*- C++ -*-===// | |
| // | |
| // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| // | |
| // Modified by the Subzero authors. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // 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 Assembler class for ARM32. | |
| /// | |
| /// Note: All references to ARM "section" documentation refers to the "ARM | |
| /// Architecture Reference Manual, ARMv7-A and ARMv7-R edition". See: | |
| /// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0406c | |
| /// | |
| //===----------------------------------------------------------------------===// | |
| #ifndef SUBZERO_SRC_ICEASSEMBLERARM32_H | |
| #define SUBZERO_SRC_ICEASSEMBLERARM32_H | |
| #include "IceAssembler.h" | |
| #include "IceConditionCodesARM32.h" | |
| #include "IceDefs.h" | |
| #include "IceFixups.h" | |
| #include "IceInstARM32.h" | |
| #include "IceRegistersARM32.h" | |
| #include "IceTargetLowering.h" | |
| namespace Ice { | |
| namespace ARM32 { | |
| /// Handles encoding of bottom/top 16 bits of an address using movw/movt. | |
| class MoveRelocatableFixup final : public AssemblerFixup { | |
| MoveRelocatableFixup &operator=(const MoveRelocatableFixup &) = delete; | |
| MoveRelocatableFixup(const MoveRelocatableFixup &) = default; | |
| public: | |
| MoveRelocatableFixup() = default; | |
| size_t emit(GlobalContext *Ctx, const Assembler &Asm) const final; | |
| void emitOffset(Assembler *Asm) const; | |
| }; | |
| /// Handles encoding of branch and link to global location. | |
| class BlRelocatableFixup final : public AssemblerFixup { | |
| BlRelocatableFixup(const BlRelocatableFixup &) = delete; | |
| BlRelocatableFixup &operator=(const BlRelocatableFixup &) = delete; | |
| public: | |
| BlRelocatableFixup() = default; | |
| size_t emit(GlobalContext *Ctx, const Assembler &Asm) const final; | |
| void emitOffset(Assembler *Asm) const; | |
| }; | |
| class AssemblerARM32 : public Assembler { | |
| AssemblerARM32(const AssemblerARM32 &) = delete; | |
| AssemblerARM32 &operator=(const AssemblerARM32 &) = delete; | |
| public: | |
| // Rotation values. | |
| enum RotationValue { | |
| kRotateNone, // Omitted | |
| kRotate8, // ror #8 | |
| kRotate16, // ror #16 | |
| kRotate24 // ror #24 | |
| }; | |
| // Encoding of the number of D registers in a list of D registers. | |
| enum DRegListSize { | |
| DRegListSize1 = 7, // 0b0111 | |
| DRegListSize2 = 10, // 0b1010 | |
| DRegListSIze3 = 6, // 0b0110 | |
| DRegListSize4 = 2 // 0b0010 | |
| }; | |
| class TargetInfo { | |
| TargetInfo(const TargetInfo &) = delete; | |
| TargetInfo &operator=(const TargetInfo &) = delete; | |
| public: | |
| TargetInfo(bool HasFramePointer, RegNumT FrameOrStackReg) | |
| : HasFramePointer(HasFramePointer), FrameOrStackReg(FrameOrStackReg) {} | |
| explicit TargetInfo(const TargetLowering *Target) | |
| : HasFramePointer(Target->hasFramePointer()), | |
| FrameOrStackReg(Target->getFrameOrStackReg()) {} | |
| const bool HasFramePointer; | |
| const RegNumT FrameOrStackReg; | |
| }; | |
| explicit AssemblerARM32(bool IsNonsfi, bool use_far_branches = false) | |
| : Assembler(Asm_ARM32), IsNonsfi(IsNonsfi) { | |
| // TODO(kschimpf): Add mode if needed when branches are handled. | |
| (void)use_far_branches; | |
| } | |
| ~AssemblerARM32() override { | |
| if (BuildDefs::asserts()) { | |
| for (const Label *Label : CfgNodeLabels) { | |
| Label->finalCheck(); | |
| } | |
| for (const Label *Label : LocalLabels) { | |
| Label->finalCheck(); | |
| } | |
| } | |
| } | |
| MoveRelocatableFixup *createMoveFixup(bool IsMovW, const Constant *Value); | |
| BlRelocatableFixup *createBlFixup(const ConstantRelocatable *BlTarget); | |
| void alignFunction() override { | |
| const SizeT Align = 1 << getBundleAlignLog2Bytes(); | |
| SizeT BytesNeeded = Utils::OffsetToAlignment(Buffer.getPosition(), Align); | |
| constexpr SizeT InstSize = sizeof(IValueT); | |
| assert(BytesNeeded % InstARM32::InstSize == 0); | |
| while (BytesNeeded > 0) { | |
| trap(); | |
| BytesNeeded -= InstSize; | |
| } | |
| } | |
| SizeT getBundleAlignLog2Bytes() const override { return 4; } | |
| const char *getAlignDirective() const override { return ".p2alignl"; } | |
| llvm::ArrayRef<uint8_t> getNonExecBundlePadding() const override; | |
| void padWithNop(intptr_t Padding) override; | |
| Ice::Label *getCfgNodeLabel(SizeT NodeNumber) override { | |
| assert(NodeNumber < CfgNodeLabels.size()); | |
| return CfgNodeLabels[NodeNumber]; | |
| } | |
| Label *getOrCreateCfgNodeLabel(SizeT NodeNumber) { | |
| return getOrCreateLabel(NodeNumber, CfgNodeLabels); | |
| } | |
| Label *getOrCreateLocalLabel(SizeT Number) { | |
| return getOrCreateLabel(Number, LocalLabels); | |
| } | |
| void bindLocalLabel(const InstARM32Label *InstL, SizeT Number) { | |
| if (BuildDefs::dump() && !getFlags().getDisableHybridAssembly()) { | |
| constexpr SizeT InstSize = 0; | |
| emitTextInst(InstL->getLabelName() + ":", InstSize); | |
| } | |
| Label *L = getOrCreateLocalLabel(Number); | |
| if (!getPreliminary()) | |
| this->bind(L); | |
| } | |
| bool fixupIsPCRel(FixupKind Kind) const override { | |
| if (Kind == llvm::ELF::R_ARM_MOVW_PREL_NC) | |
| return true; | |
| if (Kind == llvm::ELF::R_ARM_MOVT_PREL) | |
| return true; | |
| return false; | |
| } | |
| /// Accessors to keep track of the number of bytes generated inside | |
| /// InstARM32::emit() methods, when run inside of | |
| /// InstARM32::emitUsingTextFixup(). | |
| void resetEmitTextSize() { EmitTextSize = 0; } | |
| void incEmitTextSize(size_t Amount) { EmitTextSize += Amount; } | |
| void decEmitTextSize(size_t Amount) { EmitTextSize -= Amount; } | |
| size_t getEmitTextSize() const { return EmitTextSize; } | |
| void bind(Label *label); | |
| // List of instructions implemented by integrated assembler. | |
| void adc(const Operand *OpRd, const Operand *OpRn, const Operand *OpSrc1, | |
| bool SetFlags, CondARM32::Cond Cond); | |
| void add(const Operand *OpRd, const Operand *OpRn, const Operand *OpSrc1, | |
| bool SetFlags, CondARM32::Cond Cond); | |
| void and_(const Operand *OpRd, const Operand *OpRn, const Operand *OpSrc1, | |
| bool SetFlags, CondARM32::Cond Cond); | |
| void asr(const Operand *OpRd, const Operand *OpRn, const Operand *OpSrc1, | |
| bool SetFlags, CondARM32::Cond Cond); | |
| void b(Label *L, CondARM32::Cond Cond); | |
| void bkpt(uint16_t Imm16); | |
| void bic(const Operand *OpRd, const Operand *OpRn, const Operand *OpSrc1, | |
| bool SetFlags, CondARM32::Cond Cond); | |
| void bl(const ConstantRelocatable *Target); | |
| void blx(const Operand *Target); | |
| void bx(RegARM32::GPRRegister Rm, CondARM32::Cond Cond = CondARM32::AL); | |
| void clz(const Operand *OpRd, const Operand *OpSrc, CondARM32::Cond Cond); | |
| void cmn(const Operand *OpRn, const Operand *OpSrc1, CondARM32::Cond Cond); | |
| void cmp(const Operand *OpRn, const Operand *OpSrc1, CondARM32::Cond Cond); | |
| void dmb(IValueT Option); // Option is a 4-bit value. | |
| void eor(const Operand *OpRd, const Operand *OpRn, const Operand *OpSrc1, | |
| bool SetFlags, CondARM32::Cond Cond); | |
| void ldr(const Operand *OpRt, const Operand *OpAddress, CondARM32::Cond Cond, | |
| const TargetInfo &TInfo); | |
| void ldr(const Operand *OpRt, const Operand *OpAddress, CondARM32::Cond Cond, | |
| const TargetLowering *Lowering) { | |
| const TargetInfo TInfo(Lowering); | |
| ldr(OpRt, OpAddress, Cond, TInfo); | |
| } | |
| void ldrex(const Operand *OpRt, const Operand *OpAddress, | |
| CondARM32::Cond Cond, const TargetInfo &TInfo); | |
| void ldrex(const Operand *OpRt, const Operand *OpAddress, | |
| CondARM32::Cond Cond, const TargetLowering *Lowering) { | |
| const TargetInfo TInfo(Lowering); | |
| ldrex(OpRt, OpAddress, Cond, TInfo); | |
| } | |
| void lsl(const Operand *OpRd, const Operand *OpRn, const Operand *OpSrc1, | |
| bool SetFlags, CondARM32::Cond Cond); | |
| void lsr(const Operand *OpRd, const Operand *OpRn, const Operand *OpSrc1, | |
| bool SetFlags, CondARM32::Cond Cond); | |
| void mov(const Operand *OpRd, const Operand *OpSrc, CondARM32::Cond Cond); | |
| void movw(const Operand *OpRd, const Operand *OpSrc, CondARM32::Cond Cond); | |
| void movt(const Operand *OpRd, const Operand *OpSrc, CondARM32::Cond Cond); | |
| void mla(const Operand *OpRd, const Operand *OpRn, const Operand *OpRm, | |
| const Operand *OpRa, CondARM32::Cond Cond); | |
| void mls(const Operand *OpRd, const Operand *OpRn, const Operand *OpRm, | |
| const Operand *OpRa, CondARM32::Cond Cond); | |
| void mul(const Operand *OpRd, const Operand *OpRn, const Operand *OpSrc1, | |
| bool SetFlags, CondARM32::Cond Cond); | |
| void mvn(const Operand *OpRd, const Operand *OpScc, CondARM32::Cond Cond); | |
| void nop(); | |
| void orr(const Operand *OpRd, const Operand *OpRn, const Operand *OpSrc1, | |
| bool SetFlags, CondARM32::Cond Cond); | |
| void pop(const Variable *OpRt, CondARM32::Cond Cond); | |
| // Note: Registers is a bitset, where bit n corresponds to register Rn. | |
| void popList(const IValueT Registers, CondARM32::Cond Cond); | |
| void push(const Operand *OpRt, CondARM32::Cond Cond); | |
| // Note: Registers is a bitset, where bit n corresponds to register Rn. | |
| void pushList(const IValueT Registers, CondARM32::Cond Cond); | |
| void rbit(const Operand *OpRd, const Operand *OpRm, CondARM32::Cond Cond); | |
| void rev(const Operand *OpRd, const Operand *OpRm, CondARM32::Cond Cond); | |
| void rsb(const Operand *OpRd, const Operand *OpRn, const Operand *OpSrc1, | |
| bool SetFlags, CondARM32::Cond Cond); | |
| void rsc(const Operand *OpRd, const Operand *OpRn, const Operand *OpSrc1, | |
| bool SetFlags, CondARM32::Cond Cond); | |
| void sbc(const Operand *OpRd, const Operand *OpRn, const Operand *OpSrc1, | |
| bool SetFlags, CondARM32::Cond Cond); | |
| void sdiv(const Operand *OpRd, const Operand *OpRn, const Operand *OpSrc1, | |
| CondARM32::Cond Cond); | |
| void str(const Operand *OpRt, const Operand *OpAddress, CondARM32::Cond Cond, | |
| const TargetInfo &TInfo); | |
| void str(const Operand *OpRt, const Operand *OpAddress, CondARM32::Cond Cond, | |
| const TargetLowering *Lowering) { | |
| const TargetInfo TInfo(Lowering); | |
| str(OpRt, OpAddress, Cond, TInfo); | |
| } | |
| void strex(const Operand *OpRd, const Operand *OpRt, const Operand *OpAddress, | |
| CondARM32::Cond Cond, const TargetInfo &TInfo); | |
| void strex(const Operand *OpRd, const Operand *OpRt, const Operand *OpAddress, | |
| CondARM32::Cond Cond, const TargetLowering *Lowering) { | |
| const TargetInfo TInfo(Lowering); | |
| strex(OpRd, OpRt, OpAddress, Cond, TInfo); | |
| } | |
| void sub(const Operand *OpRd, const Operand *OpRn, const Operand *OpSrc1, | |
| bool SetFlags, CondARM32::Cond Cond); | |
| // Implements sxtb/sxth depending on type of OpSrc0. | |
| void sxt(const Operand *OpRd, const Operand *OpSrc0, CondARM32::Cond Cond); | |
| void trap(); | |
| void tst(const Operand *OpRn, const Operand *OpSrc1, CondARM32::Cond Cond); | |
| void udiv(const Operand *OpRd, const Operand *OpRn, const Operand *OpSrc1, | |
| CondARM32::Cond Cond); | |
| void umull(const Operand *OpRdLo, const Operand *OpRdHi, const Operand *OpRn, | |
| const Operand *OpRm, CondARM32::Cond Cond); | |
| // Implements uxtb/uxth depending on type of OpSrc0. | |
| void uxt(const Operand *OpRd, const Operand *OpSrc0, CondARM32::Cond Cond); | |
| void vabss(const Operand *OpSd, const Operand *OpSm, CondARM32::Cond Cond); | |
| void vabsd(const Operand *OpDd, const Operand *OpDm, CondARM32::Cond Cond); | |
| void vabsq(const Operand *OpQd, const Operand *OpQm); | |
| void vaddd(const Operand *OpDd, const Operand *OpDn, const Operand *OpDm, | |
| CondARM32::Cond Cond); | |
| void vadds(const Operand *OpSd, const Operand *OpSn, const Operand *OpSm, | |
| CondARM32::Cond Cond); | |
| // Integer vector add. | |
| void vaddqi(Type ElmtTy, const Operand *OpQd, const Operand *OpQm, | |
| const Operand *OpQn); | |
| // Float vector add. | |
| void vaddqf(const Operand *OpQd, const Operand *OpQm, const Operand *OpQn); | |
| void vandq(const Operand *OpQd, const Operand *OpQm, const Operand *OpQn); | |
| void vbslq(const Operand *OpQd, const Operand *OpQm, const Operand *OpQn); | |
| void vceqqi(const Type ElmtTy, const Operand *OpQd, const Operand *OpQm, | |
| const Operand *OpQn); | |
| void vceqqs(const Operand *OpQd, const Operand *OpQm, const Operand *OpQn); | |
| void vcgeqi(const Type ElmtTy, const Operand *OpQd, const Operand *OpQm, | |
| const Operand *OpQn); | |
| void vcugeqi(const Type ElmtTy, const Operand *OpQd, const Operand *OpQm, | |
| const Operand *OpQn); | |
| void vcgeqs(const Operand *OpQd, const Operand *OpQm, const Operand *OpQn); | |
| void vcgtqi(const Type ElmtTy, const Operand *OpQd, const Operand *OpQm, | |
| const Operand *OpQn); | |
| void vcugtqi(const Type ElmtTy, const Operand *OpQd, const Operand *OpQm, | |
| const Operand *OpQn); | |
| void vcgtqs(const Operand *OpQd, const Operand *OpQm, const Operand *OpQn); | |
| void vcmpd(const Operand *OpDd, const Operand *OpDm, CondARM32::Cond cond); | |
| // Second argument of compare is zero (+0.0). | |
| void vcmpdz(const Operand *OpDd, CondARM32::Cond cond); | |
| void vcmps(const Operand *OpSd, const Operand *OpSm, CondARM32::Cond cond); | |
| // Second argument of compare is zero (+0.0). | |
| void vcmpsz(const Operand *OpSd, CondARM32::Cond cond); | |
| void vcvtds(const Operand *OpDd, const Operand *OpSm, CondARM32::Cond Cond); | |
| // vcvt<c>.S32.F32 | |
| void vcvtis(const Operand *OpSd, const Operand *OpSm, CondARM32::Cond Cond); | |
| // vcvt<c>.S32.F64 | |
| void vcvtid(const Operand *OpSd, const Operand *OpDm, CondARM32::Cond Cond); | |
| // vcvt<c>.F64.S32 | |
| void vcvtdi(const Operand *OpDd, const Operand *OpSm, CondARM32::Cond Cond); | |
| // vcvt<c>.F64.U32 | |
| void vcvtdu(const Operand *OpDd, const Operand *OpSm, CondARM32::Cond Cond); | |
| void vcvtsd(const Operand *OpSd, const Operand *OpDm, CondARM32::Cond Cond); | |
| // vcvt<c>.F32.S32 | |
| void vcvtsi(const Operand *OpSd, const Operand *OpSm, CondARM32::Cond Cond); | |
| // vcvt<c>.F32.U32 | |
| void vcvtsu(const Operand *OpSd, const Operand *OpSm, CondARM32::Cond Cond); | |
| // vcvt<c>.U32.F64 | |
| void vcvtud(const Operand *OpSd, const Operand *OpDm, CondARM32::Cond Cond); | |
| // vcvt<c>.u32.f32 | |
| void vcvtus(const Operand *OpSd, const Operand *OpSm, CondARM32::Cond Cond); | |
| void vcvtqsi(const Operand *OpQd, const Operand *OpQm); | |
| void vcvtqsu(const Operand *OpQd, const Operand *OpQm); | |
| void vcvtqis(const Operand *OpQd, const Operand *OpQm); | |
| void vcvtqus(const Operand *OpQd, const Operand *OpQm); | |
| void vdivd(const Operand *OpDd, const Operand *OpDn, const Operand *OpDm, | |
| CondARM32::Cond Cond); | |
| void vdivs(const Operand *OpSd, const Operand *OpSn, const Operand *OpSm, | |
| CondARM32::Cond Cond); | |
| void veord(const Operand *OpDd, const Operand *OpDn, const Operand *OpDm); | |
| void veorq(const Operand *OpQd, const Operand *OpQn, const Operand *OpQm); | |
| void vldrd(const Operand *OpDd, const Operand *OpAddress, | |
| CondARM32::Cond Cond, const TargetInfo &TInfo); | |
| void vldrd(const Operand *OpDd, const Operand *OpAddress, | |
| CondARM32::Cond Cond, const TargetLowering *Lowering) { | |
| const TargetInfo TInfo(Lowering); | |
| vldrd(OpDd, OpAddress, Cond, TInfo); | |
| } | |
| void vldrs(const Operand *OpSd, const Operand *OpAddress, | |
| CondARM32::Cond Cond, const TargetInfo &TInfo); | |
| void vldrs(const Operand *OpSd, const Operand *OpAddress, | |
| CondARM32::Cond Cond, const TargetLowering *Lowering) { | |
| const TargetInfo TInfo(Lowering); | |
| vldrs(OpSd, OpAddress, Cond, TInfo); | |
| } | |
| void vldrq(const Operand *OpQd, const Operand *OpAddress, | |
| CondARM32::Cond Cond, const TargetInfo &TInfo); | |
| void vldrq(const Operand *OpQd, const Operand *OpAddress, | |
| CondARM32::Cond Cond, const TargetLowering *Lowering) { | |
| const TargetInfo TInfo(Lowering); | |
| vldrq(OpQd, OpAddress, Cond, TInfo); | |
| } | |
| // ElmtSize = #bits in vector element. | |
| void vld1qr(size_t ElmtSize, const Operand *OpQd, const Operand *OpRn, | |
| const TargetInfo &TInfo); | |
| void vld1(size_t ElmtSize, const Operand *OpQd, const Operand *OpRn, | |
| const TargetInfo &TInfo); | |
| void vld1qr(size_t ElmtSize, const Operand *OpQd, const Operand *OpRn, | |
| const TargetLowering *Lowering) { | |
| const TargetInfo TInfo(Lowering); | |
| vld1qr(ElmtSize, OpQd, OpRn, TInfo); | |
| } | |
| void vld1(size_t ElmtSize, const Operand *OpQd, const Operand *OpRn, | |
| const TargetLowering *Lowering) { | |
| const TargetInfo TInfo(Lowering); | |
| vld1(ElmtSize, OpQd, OpRn, TInfo); | |
| } | |
| // Qn[i] = Imm for all i in vector. Returns true iff Imm can be defined as an | |
| // Imm8 using AdvSIMDExpandImm(). | |
| bool vmovqc(const Operand *OpQd, const ConstantInteger32 *Imm); | |
| // Dn = FpImm | |
| void vmovd(const Operand *OpDn, const OperandARM32FlexFpImm *OpFpImm, | |
| CondARM32::Cond Cond); | |
| // Dd = Dm | |
| void vmovdd(const Operand *OpDd, const Variable *OpDm, CondARM32::Cond Cond); | |
| // Dm = Rt:Rt2 | |
| void vmovdrr(const Operand *OpDm, const Operand *OpRt, const Operand *OpRt2, | |
| CondARM32::Cond Cond); | |
| // Qd[Index] = Rt | |
| void vmovqir(const Operand *OpQd, uint32_t Index, const Operand *OpRt, | |
| CondARM32::Cond Cond); | |
| // Qd[Index] = Sm | |
| void vmovqis(const Operand *OpQd, uint32_t Indx, const Operand *OpSm, | |
| CondARM32::Cond Cond); | |
| // Rt = Qm[Index] | |
| void vmovrqi(const Operand *OpRt, const Operand *OpQd, uint32_t Index, | |
| CondARM32::Cond Cond); | |
| // Rt:Rt2 = Dm | |
| void vmovrrd(const Operand *OpRt, const Operand *OpRt2, const Operand *OpDm, | |
| CondARM32::Cond Cond); | |
| // Rt = Sn | |
| void vmovrs(const Operand *OpRt, const Operand *OpSn, CondARM32::Cond Cond); | |
| // Sn = FpImm | |
| void vmovs(const Operand *OpSn, const OperandARM32FlexFpImm *OpFpImm, | |
| CondARM32::Cond Cond); | |
| // Sd = Sm | |
| void vmovss(const Operand *OpSd, const Variable *OpSm, CondARM32::Cond Cond); | |
| // Sd = Qm[Index] | |
| void vmovsqi(const Operand *OpSd, const Operand *OpQm, uint32_t Index, | |
| CondARM32::Cond Cond); | |
| // Sn = Rt | |
| void vmovsr(const Operand *OpSn, const Operand *OpRt, CondARM32::Cond Cond); | |
| void vmlad(const Operand *OpDd, const Operand *OpDn, const Operand *OpDm, | |
| CondARM32::Cond Cond); | |
| void vmlas(const Operand *OpSd, const Operand *OpSn, const Operand *OpSm, | |
| CondARM32::Cond Cond); | |
| void vmlsd(const Operand *OpDd, const Operand *OpDn, const Operand *OpDm, | |
| CondARM32::Cond Cond); | |
| void vmlss(const Operand *OpSd, const Operand *OpSn, const Operand *OpSm, | |
| CondARM32::Cond Cond); | |
| // Uses APSR_nzcv as register | |
| void vmrsAPSR_nzcv(CondARM32::Cond Cond); | |
| void vmuld(const Operand *OpDd, const Operand *OpDn, const Operand *OpDm, | |
| CondARM32::Cond Cond); | |
| // Integer vector multiply. | |
| void vmulqi(Type ElmtTy, const Operand *OpQd, const Operand *OpQn, | |
| const Operand *OpQm); | |
| // Integer vector multiply high. | |
| void vmulh(Type ElmtTy, const Operand *OpQd, const Operand *OpQn, | |
| const Operand *OpQm, bool Unsigned); | |
| // Integer vector multiply add pairwise. | |
| void vmlap(Type ElmtTy, const Operand *OpQd, const Operand *OpQn, | |
| const Operand *OpQm); | |
| // Vector element replication. | |
| void vdup(Type ElmtTy, const Operand *OpQd, const Operand *OpQn, IValueT Idx); | |
| // Vector interleave lower halves. | |
| void vzip(Type ElmtTy, const Operand *OpQd, const Operand *OpQn, | |
| const Operand *OpQm); | |
| // Float vector multiply. | |
| void vmulqf(const Operand *OpQd, const Operand *OpQn, const Operand *OpQm); | |
| void vmuls(const Operand *OpSd, const Operand *OpSn, const Operand *OpSm, | |
| CondARM32::Cond Cond); | |
| void vmvnq(const Operand *OpQd, const Operand *OpQm); | |
| void vmovlq(const Operand *OpQd, const Operand *OpQn, const Operand *OpQm); | |
| void vmovhq(const Operand *OpQd, const Operand *OpQn, const Operand *OpQm); | |
| void vmovhlq(const Operand *OpQd, const Operand *OpQn, const Operand *OpQm); | |
| void vmovlhq(const Operand *OpQd, const Operand *OpQn, const Operand *OpQm); | |
| void vnegqs(const Operand *OpQd, const Operand *OpQm); | |
| void vnegqs(Type ElmtTy, const Operand *OpQd, const Operand *OpQm); | |
| void vorrq(const Operand *OpQd, const Operand *OpQm, const Operand *OpQn); | |
| void vpop(const Variable *OpBaseReg, SizeT NumConsecRegs, | |
| CondARM32::Cond Cond); | |
| void vpush(const Variable *OpBaseReg, SizeT NumConsecRegs, | |
| CondARM32::Cond Cond); | |
| void vshlqi(Type ElmtTy, const Operand *OpQd, const Operand *OpQm, | |
| const Operand *OpQn); | |
| void vshlqu(Type ElmtTy, const Operand *OpQd, const Operand *OpQm, | |
| const Operand *OpQn); | |
| void vshlqc(Type ElmtTy, const Operand *OpQd, const Operand *OpQm, | |
| const ConstantInteger32 *OpQn); | |
| void vshrqc(Type ElmtTy, const Operand *OpQd, const Operand *OpQm, | |
| const ConstantInteger32 *OpQn, InstARM32::FPSign Sign); | |
| void vsqrtd(const Operand *OpDd, const Operand *OpDm, CondARM32::Cond Cond); | |
| void vsqrts(const Operand *OpSd, const Operand *OpSm, CondARM32::Cond Cond); | |
| void vstrd(const Operand *OpDd, const Operand *OpAddress, | |
| CondARM32::Cond Cond, const TargetInfo &TInfo); | |
| void vstrd(const Operand *OpDd, const Operand *OpAddress, | |
| CondARM32::Cond Cond, const TargetLowering *Lowering) { | |
| const TargetInfo TInfo(Lowering); | |
| vstrd(OpDd, OpAddress, Cond, TInfo); | |
| } | |
| void vstrs(const Operand *OpSd, const Operand *OpAddress, | |
| CondARM32::Cond Cond, const TargetInfo &TInfo); | |
| void vstrs(const Operand *OpSd, const Operand *OpAddress, | |
| CondARM32::Cond Cond, const TargetLowering *Lowering) { | |
| const TargetInfo TInfo(Lowering); | |
| vstrs(OpSd, OpAddress, Cond, TInfo); | |
| } | |
| void vstrq(const Operand *OpQd, const Operand *OpAddress, | |
| CondARM32::Cond Cond, const TargetInfo &TInfo); | |
| void vstrq(const Operand *OpQd, const Operand *OpAddress, | |
| CondARM32::Cond Cond, const TargetLowering *Lowering) { | |
| const TargetInfo TInfo(Lowering); | |
| vstrq(OpQd, OpAddress, Cond, TInfo); | |
| } | |
| // ElmtSize = #bits in vector element. | |
| void vst1qr(size_t ElmtSize, const Operand *OpQd, const Operand *OpAddress, | |
| const TargetInfo &TInfo); | |
| void vst1qr(size_t ElmtSize, const Operand *OpQd, const Operand *OpRn, | |
| const TargetLowering *Lowering) { | |
| const TargetInfo TInfo(Lowering); | |
| vst1qr(ElmtSize, OpQd, OpRn, TInfo); | |
| } | |
| void vst1(size_t ElmtSize, const Operand *OpQd, const Operand *OpAddress, | |
| const TargetInfo &TInfo); | |
| void vst1(size_t ElmtSize, const Operand *OpQd, const Operand *OpRn, | |
| const TargetLowering *Lowering) { | |
| const TargetInfo TInfo(Lowering); | |
| vst1(ElmtSize, OpQd, OpRn, TInfo); | |
| } | |
| void vsubd(const Operand *OpDd, const Operand *OpDn, const Operand *OpDm, | |
| CondARM32::Cond Cond); | |
| // Integer vector subtract. | |
| void vsubqi(Type ElmtTy, const Operand *OpQd, const Operand *OpQm, | |
| const Operand *OpQn); | |
| // Integer vector saturating subtract. | |
| void vqsubqi(Type ElmtTy, const Operand *OpQd, const Operand *OpQm, | |
| const Operand *OpQn); | |
| void vqsubqu(Type ElmtTy, const Operand *OpQd, const Operand *OpQm, | |
| const Operand *OpQn); | |
| // Integer vector saturating add. | |
| void vqaddqi(Type ElmtTy, const Operand *OpQd, const Operand *OpQm, | |
| const Operand *OpQn); | |
| void vqaddqu(Type ElmtTy, const Operand *OpQd, const Operand *OpQm, | |
| const Operand *OpQn); | |
| // Integer vector packing with optional saturation. | |
| void vqmovn2(Type ElmtTy, const Operand *OpQd, const Operand *OpQm, | |
| const Operand *OpQn, bool Unsigned, bool Saturating); | |
| // Float vector subtract | |
| void vsubqf(const Operand *OpQd, const Operand *OpQm, const Operand *OpQn); | |
| void vsubs(const Operand *OpSd, const Operand *OpSn, const Operand *OpSm, | |
| CondARM32::Cond Cond); | |
| static bool classof(const Assembler *Asm) { | |
| return Asm->getKind() == Asm_ARM32; | |
| } | |
| void emitTextInst(const std::string &Text, SizeT InstSize); | |
| private: | |
| ENABLE_MAKE_UNIQUE; | |
| const bool IsNonsfi; | |
| // A vector of pool-allocated x86 labels for CFG nodes. | |
| using LabelVector = std::vector<Label *>; | |
| LabelVector CfgNodeLabels; | |
| // A vector of pool-allocated x86 labels for Local labels. | |
| LabelVector LocalLabels; | |
| // Number of bytes emitted by InstARM32::emit() methods, when run inside | |
| // InstARM32::emitUsingTextFixup(). | |
| size_t EmitTextSize = 0; | |
| // Load/store multiple addressing mode. | |
| enum BlockAddressMode { | |
| // bit encoding P U W | |
| DA = (0 | 0 | 0) << 21, // decrement after | |
| IA = (0 | 4 | 0) << 21, // increment after | |
| DB = (8 | 0 | 0) << 21, // decrement before | |
| IB = (8 | 4 | 0) << 21, // increment before | |
| DA_W = (0 | 0 | 1) << 21, // decrement after with writeback to base | |
| IA_W = (0 | 4 | 1) << 21, // increment after with writeback to base | |
| DB_W = (8 | 0 | 1) << 21, // decrement before with writeback to base | |
| IB_W = (8 | 4 | 1) << 21 // increment before with writeback to base | |
| }; | |
| Label *getOrCreateLabel(SizeT Number, LabelVector &Labels); | |
| void bindCfgNodeLabel(const CfgNode *Node) override; | |
| // SIMD encoding for the vector ElmtTy. | |
| static IValueT encodeElmtType(Type ElmtTy); | |
| void emitInst(IValueT Value) { | |
| AssemblerBuffer::EnsureCapacity _(&Buffer); | |
| Buffer.emit<IValueT>(Value); | |
| } | |
| // List of possible checks to apply when calling emitType01() (below). | |
| enum EmitChecks { NoChecks, RdIsPcAndSetFlags }; | |
| // Pattern cccctttoooosnnnnddddiiiiiiiiiiii where cccc=Cond, ttt=InstType, | |
| // s=SetFlags, oooo=Opcode, nnnn=Rn, dddd=Rd, iiiiiiiiiiii=imm12 (See ARM | |
| // section A5.2.3). | |
| void emitType01(CondARM32::Cond Cond, IValueT InstType, IValueT Opcode, | |
| bool SetFlags, IValueT Rn, IValueT Rd, IValueT imm12, | |
| EmitChecks RuleChecks, const char *InstName); | |
| // Converts appropriate representation on a data operation, and then calls | |
| // emitType01 above. | |
| void emitType01(CondARM32::Cond Cond, IValueT Opcode, const Operand *OpRd, | |
| const Operand *OpRn, const Operand *OpSrc1, bool SetFlags, | |
| EmitChecks RuleChecks, const char *InstName); | |
| // Same as above, but the value for Rd and Rn have already been converted | |
| // into instruction values. | |
| void emitType01(CondARM32::Cond Cond, IValueT Opcode, IValueT OpRd, | |
| IValueT OpRn, const Operand *OpSrc1, bool SetFlags, | |
| EmitChecks RuleChecks, const char *InstName); | |
| void emitType05(CondARM32::Cond Cond, int32_t Offset, bool Link); | |
| // Emit ccccoooaabalnnnnttttaaaaaaaaaaaa where cccc=Cond, | |
| // ooo=InstType, l=isLoad, b=isByte, and | |
| // aaa0a0aaaa0000aaaaaaaaaaaa=Address. Note that Address is assumed to be | |
| // defined by decodeAddress() in IceAssemblerARM32.cpp. | |
| void emitMemOp(CondARM32::Cond Cond, IValueT InstType, bool IsLoad, | |
| bool IsByte, IValueT Rt, IValueT Address); | |
| // Emit ccccxxxxxxxxxxxxddddxxxxxxxxmmmm where cccc=Cond, | |
| // xxxxxxxxxxxx0000xxxxxxxx0000=Opcode, dddd=Rd, and mmmm=Rm. | |
| void emitRdRm(CondARM32::Cond Cond, IValueT Opcode, const Operand *OpRd, | |
| const Operand *OpRm, const char *InstName); | |
| // Emit ldr/ldrb/str/strb instruction with given address. | |
| void emitMemOp(CondARM32::Cond Cond, bool IsLoad, bool IsByte, IValueT Rt, | |
| const Operand *OpAddress, const TargetInfo &TInfo, | |
| const char *InstName); | |
| // Emit ldrh/ldrd/strh/strd instruction with given address using encoding 3. | |
| void emitMemOpEnc3(CondARM32::Cond Cond, IValueT Opcode, IValueT Rt, | |
| const Operand *OpAddress, const TargetInfo &TInfo, | |
| const char *InstName); | |
| // Emit cccc00011xxlnnnndddd11111001tttt where cccc=Cond, xx encodes type | |
| // size, l=IsLoad, nnnn=Rn (as defined by OpAddress), and tttt=Rt. | |
| void emitMemExOp(CondARM32::Cond, Type Ty, bool IsLoad, const Operand *OpRd, | |
| IValueT Rt, const Operand *OpAddress, | |
| const TargetInfo &TInfo, const char *InstName); | |
| // Pattern cccc100aaaalnnnnrrrrrrrrrrrrrrrr where cccc=Cond, | |
| // aaaa<<21=AddressMode, l=IsLoad, nnnn=BaseReg, and | |
| // rrrrrrrrrrrrrrrr is bitset of Registers. | |
| void emitMultiMemOp(CondARM32::Cond Cond, BlockAddressMode AddressMode, | |
| bool IsLoad, IValueT BaseReg, IValueT Registers); | |
| // Pattern ccccxxxxxDxxxxxxddddxxxxiiiiiiii where cccc=Cond, ddddD=BaseReg, | |
| // iiiiiiii=NumConsecRegs, and xxxxx0xxxxxx0000xxxx00000000=Opcode. | |
| void emitVStackOp(CondARM32::Cond Cond, IValueT Opcode, | |
| const Variable *OpBaseReg, SizeT NumConsecRegs); | |
| // Pattern cccc111xxDxxxxxxdddd101xxxMxmmmm where cccc=Cond, ddddD=Sd, | |
| // Mmmmm=Dm, and xx0xxxxxxdddd000xxx0x0000=Opcode. | |
| void emitVFPsd(CondARM32::Cond Cond, IValueT Opcode, IValueT Sd, IValueT Dm); | |
| // Pattern cccc111xxDxxxxxxdddd101xxxMxmmmm where cccc=Cond, Ddddd=Dd, | |
| // mmmmM=Sm, and xx0xxxxxxdddd000xxx0x0000=Opcode. | |
| void emitVFPds(CondARM32::Cond Cond, IValueT Opcode, IValueT Dd, IValueT Sm); | |
| // Pattern 111100000D00nnnnddddttttssaammmm | Opcode where Ddddd=Dd, nnnn=Rn, | |
| // mmmmm=Rm, tttt=NumDRegs, ElmtSize in {8, 16, 32, 64) and defines ss, and | |
| // aa=Align. | |
| void emitVMem1Op(IValueT Opcode, IValueT Dd, IValueT Rn, IValueT Rm, | |
| DRegListSize NumDRegs, size_t ElmtSize, IValueT Align, | |
| const char *InstName); | |
| // Pattern 111100000D00nnnnddddss00aaaammmm | Opcode where Ddddd=Dd, nnnn=Rn, | |
| // mmmmm=Rm, ElmtSize in {8, 16, 32) and defines ss, and aa=Align. | |
| void emitVMem1Op(IValueT Opcode, IValueT Dd, IValueT Rn, IValueT Rm, | |
| size_t ElmtSize, IValueT Align, const char *InstName); | |
| // Pattern cccc011100x1dddd1111mmmm0001nnn where cccc=Cond, | |
| // x=Opcode, dddd=Rd, nnnn=Rn, mmmm=Rm. | |
| void emitDivOp(CondARM32::Cond Cond, IValueT Opcode, IValueT Rd, IValueT Rn, | |
| IValueT Rm); | |
| // cccc1110iiiennnntttt1011Njj10000 where cccc=Cond, tttt=Rt, Ndddd=2*Qn=Dn, | |
| // iii=Opcode1, jj=Opcode2, Opcode1Opcode2 encodes Index and the | |
| // corresponding element size of the vector element, and e=IsExtract. | |
| void emitInsertExtractInt(CondARM32::Cond Cond, const Operand *OpQn, | |
| uint32_t Index, const Operand *OpRt, bool IsExtract, | |
| const char *InstName); | |
| // cccc11101D110000dddd101001M0mmmm where cccc=Cond, ddddD=Sd, and mmmmM=Sm. | |
| // Assigns Sd the value of Sm. | |
| void emitMoveSS(CondARM32::Cond Cond, IValueT Sd, IValueT Sm); | |
| // Pattern ccccxxxxxxxfnnnnddddssss1001mmmm where cccc=Cond, dddd=Rd, nnnn=Rn, | |
| // mmmm=Rm, ssss=Rs, f=SetFlags and xxxxxxx=Opcode. | |
| void emitMulOp(CondARM32::Cond Cond, IValueT Opcode, IValueT Rd, IValueT Rn, | |
| IValueT Rm, IValueT Rs, bool SetFlags); | |
| // Pattern cccc0001101s0000ddddxxxxxtt0mmmm where cccc=Cond, s=SetFlags, | |
| // dddd=Rd, mmmm=Rm, tt=Shift, and xxxxx is defined by OpSrc1. OpSrc1 defines | |
| // either xxxxx=Imm5, or xxxxx=ssss0 where ssss=Rs. | |
| void emitShift(const CondARM32::Cond Cond, | |
| const OperandARM32::ShiftKind Shift, const Operand *OpRd, | |
| const Operand *OpRm, const Operand *OpSrc1, | |
| const bool SetFlags, const char *InstName); | |
| // Implements various forms of signed/unsigned extend value, using pattern | |
| // ccccxxxxxxxxnnnnddddrr000111mmmm where cccc=Cond, xxxxxxxx<<20=Opcode, | |
| // nnnn=Rn, dddd=Rd, rr=Rotation, and mmmm=Rm. | |
| void emitSignExtend(CondARM32::Cond, IValueT Opcode, const Operand *OpRd, | |
| const Operand *OpSrc0, const char *InstName); | |
| // Implements various forms of vector (SIMD) operations. Implements pattern | |
| // 111100100D00nnnndddn00F0NQM0mmmm where Dddd=Dd, Nnnn=Dn, Mmmm=Dm, | |
| // Q=UseQRegs, F=IsFloatTy, and Opcode is unioned into the pattern. | |
| void emitSIMDBase(IValueT Opcode, IValueT Dd, IValueT Dn, IValueT Dm, | |
| bool UseQRegs, bool IsFloatTy); | |
| // Same as emitSIMDBase above, except ElmtShift=20 and ElmtSize is computed | |
| // from ElmtTy. | |
| void emitSIMD(IValueT Opcode, Type ElmtTy, IValueT Dd, IValueT Dn, IValueT Dm, | |
| bool UseQRegs); | |
| // Implements various integer forms of vector (SIMD) operations using Q | |
| // registers. Implements pattern 111100100D00nnn0ddd000F0N1M0mmm0 where | |
| // Dddd=Qd, Nnnn=Qn, Mmmm=Qm, F=IsFloatTy, and Opcode is unioned into the | |
| // pattern. | |
| void emitSIMDqqqBase(IValueT Opcode, const Operand *OpQd, const Operand *OpQn, | |
| const Operand *OpQm, bool IsFloatTy, | |
| const char *OpcodeName); | |
| // Same as emitSIMD above, except ElmtShift=20 and ElmtSize is computed from | |
| // ElmtTy. | |
| void emitSIMDqqq(IValueT Opcode, Type ElmtTy, const Operand *OpQd, | |
| const Operand *OpQn, const Operand *OpQm, | |
| const char *OpcodeName); | |
| // Implements various forms of vector (SIMD) shifts using Q registers. | |
| // Implements pattern 111100101Diiiiiidddd010101M1mmmm where Dddd=Qd, Mmmm=Qm, | |
| // iiiiii=Imm6, and Opcode is unioned into the pattern. | |
| void emitSIMDShiftqqc(IValueT Opcode, const Operand *OpQd, | |
| const Operand *OpQm, const IValueT Imm6, | |
| const char *OpcodeName); | |
| // Implements various forms of vector (SIMD) casts between (signed and | |
| // unsigned) integer and floating point types (f32). Implements pattern | |
| // 111100111D11ss11dddd011ooQM0mmmm where Dddd=Qd, Mmmm=Qm, 10=ss, op=00, 1=Q, | |
| // and Opcode is unioned into the pattern. | |
| void emitSIMDCvtqq(IValueT Opcode, const Operand *OpQd, const Operand *OpQm, | |
| const char *CvtName); | |
| // Pattern cccctttxxxxnnnn0000iiiiiiiiiiii where cccc=Cond, nnnn=Rn, | |
| // ttt=Instruction type (derived from OpSrc1), iiiiiiiiiiii is derived from | |
| // OpSrc1, and xxxx=Opcode. | |
| void emitCompareOp(CondARM32::Cond Cond, IValueT Opcode, const Operand *OpRn, | |
| const Operand *OpSrc1, const char *CmpName); | |
| void emitBranch(Label *L, CondARM32::Cond, bool Link); | |
| // Returns the offset encoded in the branch instruction Inst. | |
| static IOffsetT decodeBranchOffset(IValueT Inst); | |
| // Implements movw/movt, generating pattern ccccxxxxxxxsiiiiddddiiiiiiiiiiii | |
| // where cccc=Cond, xxxxxxx<<21=Opcode, dddd=Rd, s=SetFlags, and | |
| // iiiiiiiiiiiiiiii=Imm16. | |
| void emitMovwt(CondARM32::Cond Cond, bool IsMovw, const Operand *OpRd, | |
| const Operand *OpSrc, const char *MovName); | |
| // Emit VFP instruction with 3 D registers. | |
| void emitVFPddd(CondARM32::Cond Cond, IValueT Opcode, const Operand *OpDd, | |
| const Operand *OpDn, const Operand *OpDm, | |
| const char *InstName); | |
| void emitVFPddd(CondARM32::Cond Cond, IValueT Opcode, IValueT Dd, IValueT Dn, | |
| IValueT Dm); | |
| // Emit VFP instruction with 3 S registers. | |
| void emitVFPsss(CondARM32::Cond Cond, IValueT Opcode, IValueT Sd, IValueT Sn, | |
| IValueT Sm); | |
| void emitVFPsss(CondARM32::Cond Cond, IValueT Opcode, const Operand *OpSd, | |
| const Operand *OpSn, const Operand *OpSm, | |
| const char *InstName); | |
| }; | |
| } // end of namespace ARM32 | |
| } // end of namespace Ice | |
| #endif // SUBZERO_SRC_ICEASSEMBLERARM32_H |