//===- subzero/src/IceAssemblerARM32.cpp - 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 Implements the Assembler class for ARM32. /// //===----------------------------------------------------------------------===// #include "IceAssemblerARM32.h" #include "IceCfgNode.h" #include "IceUtils.h" namespace { using namespace Ice; using namespace Ice::ARM32; using WordType = uint32_t; static constexpr IValueT kWordSize = sizeof(WordType); // The following define individual bits. static constexpr IValueT B0 = 1; static constexpr IValueT B1 = 1 << 1; static constexpr IValueT B2 = 1 << 2; static constexpr IValueT B3 = 1 << 3; static constexpr IValueT B4 = 1 << 4; static constexpr IValueT B5 = 1 << 5; static constexpr IValueT B6 = 1 << 6; static constexpr IValueT B7 = 1 << 7; static constexpr IValueT B8 = 1 << 8; static constexpr IValueT B9 = 1 << 9; static constexpr IValueT B10 = 1 << 10; static constexpr IValueT B11 = 1 << 11; static constexpr IValueT B12 = 1 << 12; static constexpr IValueT B13 = 1 << 13; static constexpr IValueT B14 = 1 << 14; static constexpr IValueT B15 = 1 << 15; static constexpr IValueT B16 = 1 << 16; static constexpr IValueT B17 = 1 << 17; static constexpr IValueT B18 = 1 << 18; static constexpr IValueT B19 = 1 << 19; static constexpr IValueT B20 = 1 << 20; static constexpr IValueT B21 = 1 << 21; static constexpr IValueT B22 = 1 << 22; static constexpr IValueT B23 = 1 << 23; static constexpr IValueT B24 = 1 << 24; static constexpr IValueT B25 = 1 << 25; static constexpr IValueT B26 = 1 << 26; static constexpr IValueT B27 = 1 << 27; // Constants used for the decoding or encoding of the individual fields of // instructions. Based on ARM section A5.1. static constexpr IValueT L = 1 << 20; // load (or store) static constexpr IValueT W = 1 << 21; // writeback base register // (or leave unchanged) static constexpr IValueT B = 1 << 22; // unsigned byte (or word) static constexpr IValueT U = 1 << 23; // positive (or negative) // offset/index static constexpr IValueT P = 1 << 24; // offset/pre-indexed // addressing (or // post-indexed addressing) static constexpr IValueT kConditionShift = 28; static constexpr IValueT kLinkShift = 24; static constexpr IValueT kOpcodeShift = 21; static constexpr IValueT kRdShift = 12; static constexpr IValueT kRmShift = 0; static constexpr IValueT kRnShift = 16; static constexpr IValueT kRsShift = 8; static constexpr IValueT kSShift = 20; static constexpr IValueT kTypeShift = 25; // Immediate instruction fields encoding. static constexpr IValueT kImmed8Bits = 8; static constexpr IValueT kImmed8Shift = 0; static constexpr IValueT kRotateBits = 4; static constexpr IValueT kRotateShift = 8; // Shift instruction register fields encodings. static constexpr IValueT kShiftImmShift = 7; static constexpr IValueT kShiftImmBits = 5; static constexpr IValueT kShiftShift = 5; static constexpr IValueT kImmed12Bits = 12; static constexpr IValueT kImm12Shift = 0; // Rotation instructions (uxtb etc.). static constexpr IValueT kRotationShift = 10; // MemEx instructions. static constexpr IValueT kMemExOpcodeShift = 20; // Div instruction register field encodings. static constexpr IValueT kDivRdShift = 16; static constexpr IValueT kDivRmShift = 8; static constexpr IValueT kDivRnShift = 0; // Type of instruction encoding (bits 25-27). See ARM section A5.1 static constexpr IValueT kInstTypeDataRegister = 0; // i.e. 000 static constexpr IValueT kInstTypeDataRegShift = 0; // i.e. 000 static constexpr IValueT kInstTypeDataImmediate = 1; // i.e. 001 static constexpr IValueT kInstTypeMemImmediate = 2; // i.e. 010 static constexpr IValueT kInstTypeRegisterShift = 3; // i.e. 011 // Limit on number of registers in a vpush/vpop. static constexpr SizeT VpushVpopMaxConsecRegs = 16; // Offset modifier to current PC for next instruction. The offset is off by 8 // due to the way the ARM CPUs read PC. static constexpr IOffsetT kPCReadOffset = 8; // Mask to pull out PC offset from branch (b) instruction. static constexpr int kBranchOffsetBits = 24; static constexpr IOffsetT kBranchOffsetMask = 0x00ffffff; IValueT encodeBool(bool B) { return B ? 1 : 0; } IValueT encodeRotation(ARM32::AssemblerARM32::RotationValue Value) { return static_cast(Value); } IValueT encodeGPRRegister(RegARM32::GPRRegister Rn) { return static_cast(Rn); } RegARM32::GPRRegister decodeGPRRegister(IValueT R) { return static_cast(R); } IValueT encodeCondition(CondARM32::Cond Cond) { return static_cast(Cond); } IValueT encodeShift(OperandARM32::ShiftKind Shift) { // Follows encoding in ARM section A8.4.1 "Constant shifts". switch (Shift) { case OperandARM32::kNoShift: case OperandARM32::LSL: return 0; // 0b00 case OperandARM32::LSR: return 1; // 0b01 case OperandARM32::ASR: return 2; // 0b10 case OperandARM32::ROR: case OperandARM32::RRX: return 3; // 0b11 } llvm::report_fatal_error("Unknown Shift value"); return 0; } // Returns the bits in the corresponding masked value. IValueT mask(IValueT Value, IValueT Shift, IValueT Bits) { return (Value >> Shift) & ((1 << Bits) - 1); } // Extract out a Bit in Value. bool isBitSet(IValueT Bit, IValueT Value) { return (Value & Bit) == Bit; } // Returns the GPR register at given Shift in Value. RegARM32::GPRRegister getGPRReg(IValueT Shift, IValueT Value) { return decodeGPRRegister((Value >> Shift) & 0xF); } IValueT getEncodedGPRegNum(const Variable *Var) { assert(Var->hasReg()); const auto Reg = Var->getRegNum(); return llvm::isa(Var) ? RegARM32::getI64PairFirstGPRNum(Reg) : RegARM32::getEncodedGPR(Reg); } IValueT getEncodedSRegNum(const Variable *Var) { assert(Var->hasReg()); return RegARM32::getEncodedSReg(Var->getRegNum()); } IValueT getEncodedDRegNum(const Variable *Var) { return RegARM32::getEncodedDReg(Var->getRegNum()); } IValueT getEncodedQRegNum(const Variable *Var) { return RegARM32::getEncodedQReg(Var->getRegNum()); } IValueT mapQRegToDReg(IValueT EncodedQReg) { IValueT DReg = EncodedQReg << 1; assert(DReg < RegARM32::getNumDRegs()); return DReg; } IValueT mapQRegToSReg(IValueT EncodedQReg) { IValueT SReg = EncodedQReg << 2; assert(SReg < RegARM32::getNumSRegs()); return SReg; } IValueT getYInRegXXXXY(IValueT RegXXXXY) { return RegXXXXY & 0x1; } IValueT getXXXXInRegXXXXY(IValueT RegXXXXY) { return RegXXXXY >> 1; } IValueT getYInRegYXXXX(IValueT RegYXXXX) { return RegYXXXX >> 4; } IValueT getXXXXInRegYXXXX(IValueT RegYXXXX) { return RegYXXXX & 0x0f; } // Figures out Op/Cmode values for given Value. Returns true if able to encode. bool encodeAdvSIMDExpandImm(IValueT Value, Type ElmtTy, IValueT &Op, IValueT &Cmode, IValueT &Imm8) { // TODO(kschimpf): Handle other shifted 8-bit values. constexpr IValueT Imm8Mask = 0xFF; if ((Value & IValueT(~Imm8Mask)) != 0) return false; Imm8 = Value; switch (ElmtTy) { case IceType_i8: Op = 0; Cmode = 14; // 0b1110 return true; case IceType_i16: Op = 0; Cmode = 8; // 0b1000 return true; case IceType_i32: Op = 0; Cmode = 0; // 0b0000 return true; default: return false; } } // Defines layouts of an operand representing a (register) memory address, // possibly modified by an immediate value. enum EncodedImmAddress { // Address modified by a rotated immediate 8-bit value. RotatedImm8Address, // Alternate encoding for RotatedImm8Address, where the offset is divided by 4 // before encoding. RotatedImm8Div4Address, // Address modified by an immediate 12-bit value. Imm12Address, // Alternate encoding 3, for an address modified by a rotated immediate 8-bit // value. RotatedImm8Enc3Address, // Encoding where no immediate offset is used. NoImmOffsetAddress }; // The way an operand is encoded into a sequence of bits in functions // encodeOperand and encodeAddress below. enum EncodedOperand { // Unable to encode, value left undefined. CantEncode = 0, // Value is register found. EncodedAsRegister, // Value=rrrriiiiiiii where rrrr is the rotation, and iiiiiiii is the imm8 // value. EncodedAsRotatedImm8, // EncodedAsImmRegOffset is a memory operand that can take three forms, based // on type EncodedImmAddress: // // ***** RotatedImm8Address ***** // // Value=0000000pu0w0nnnn0000iiiiiiiiiiii where nnnn is the base register Rn, // p=1 if pre-indexed addressing, u=1 if offset positive, w=1 if writeback to // Rn should be used, and iiiiiiiiiiii defines the rotated Imm8 value. // // ***** RotatedImm8Div4Address ***** // // Value=00000000pu0w0nnnn0000iiii0000jjjj where nnnn=Rn, iiiijjjj=Imm8, p=1 // if pre-indexed addressing, u=1 if offset positive, and w=1 if writeback to // Rn. // // ***** Imm12Address ***** // // Value=0000000pu0w0nnnn0000iiiiiiiiiiii where nnnn is the base register Rn, // p=1 if pre-indexed addressing, u=1 if offset positive, w=1 if writeback to // Rn should be used, and iiiiiiiiiiii defines the immediate 12-bit value. // // ***** NoImmOffsetAddress ***** // // Value=000000001000nnnn0000000000000000 where nnnn=Rn. EncodedAsImmRegOffset, // Value=0000000pu0w00nnnnttttiiiiiss0mmmm where nnnn is the base register Rn, // mmmm is the index register Rm, iiiii is the shift amount, ss is the shift // kind, p=1 if pre-indexed addressing, u=1 if offset positive, and w=1 if // writeback to Rn. EncodedAsShiftRotateImm5, // Value=000000000000000000000iiiii0000000 where iiii defines the Imm5 value // to shift. EncodedAsShiftImm5, // Value=iiiiiss0mmmm where mmmm is the register to rotate, ss is the shift // kind, and iiiii is the shift amount. EncodedAsShiftedRegister, // Value=ssss0tt1mmmm where mmmm=Rm, tt is an encoded ShiftKind, and ssss=Rms. EncodedAsRegShiftReg, // Value is 32bit integer constant. EncodedAsConstI32 }; // Sets Encoding to a rotated Imm8 encoding of Value, if possible. IValueT encodeRotatedImm8(IValueT RotateAmt, IValueT Immed8) { assert(RotateAmt < (1 << kRotateBits)); assert(Immed8 < (1 << kImmed8Bits)); return (RotateAmt << kRotateShift) | (Immed8 << kImmed8Shift); } // Encodes iiiiitt0mmmm for data-processing (2nd) operands where iiiii=Imm5, // tt=Shift, and mmmm=Rm. IValueT encodeShiftRotateImm5(IValueT Rm, OperandARM32::ShiftKind Shift, IOffsetT imm5) { (void)kShiftImmBits; assert(imm5 < (1 << kShiftImmBits)); return (imm5 << kShiftImmShift) | (encodeShift(Shift) << kShiftShift) | Rm; } // Encodes mmmmtt01ssss for data-processing operands where mmmm=Rm, ssss=Rs, and // tt=Shift. IValueT encodeShiftRotateReg(IValueT Rm, OperandARM32::ShiftKind Shift, IValueT Rs) { return (Rs << kRsShift) | (encodeShift(Shift) << kShiftShift) | B4 | (Rm << kRmShift); } // Defines the set of registers expected in an operand. enum RegSetWanted { WantGPRegs, WantSRegs, WantDRegs, WantQRegs }; EncodedOperand encodeOperand(const Operand *Opnd, IValueT &Value, RegSetWanted WantedRegSet) { Value = 0; // Make sure initialized. if (const auto *Var = llvm::dyn_cast(Opnd)) { if (Var->hasReg()) { switch (WantedRegSet) { case WantGPRegs: Value = getEncodedGPRegNum(Var); break; case WantSRegs: Value = getEncodedSRegNum(Var); break; case WantDRegs: Value = getEncodedDRegNum(Var); break; case WantQRegs: Value = getEncodedQRegNum(Var); break; } return EncodedAsRegister; } return CantEncode; } if (const auto *FlexImm = llvm::dyn_cast(Opnd)) { const IValueT Immed8 = FlexImm->getImm(); const IValueT Rotate = FlexImm->getRotateAmt(); if (!((Rotate < (1 << kRotateBits)) && (Immed8 < (1 << kImmed8Bits)))) return CantEncode; Value = (Rotate << kRotateShift) | (Immed8 << kImmed8Shift); return EncodedAsRotatedImm8; } if (const auto *Const = llvm::dyn_cast(Opnd)) { Value = Const->getValue(); return EncodedAsConstI32; } if (const auto *FlexReg = llvm::dyn_cast(Opnd)) { Operand *Amt = FlexReg->getShiftAmt(); IValueT Rm; if (encodeOperand(FlexReg->getReg(), Rm, WantGPRegs) != EncodedAsRegister) return CantEncode; if (const auto *Var = llvm::dyn_cast(Amt)) { IValueT Rs; if (encodeOperand(Var, Rs, WantGPRegs) != EncodedAsRegister) return CantEncode; Value = encodeShiftRotateReg(Rm, FlexReg->getShiftOp(), Rs); return EncodedAsRegShiftReg; } // If reached, the amount is a shifted amount by some 5-bit immediate. uint32_t Imm5; if (const auto *ShAmt = llvm::dyn_cast(Amt)) { Imm5 = ShAmt->getShAmtImm(); } else if (const auto *IntConst = llvm::dyn_cast(Amt)) { int32_t Val = IntConst->getValue(); if (Val < 0) return CantEncode; Imm5 = static_cast(Val); } else return CantEncode; Value = encodeShiftRotateImm5(Rm, FlexReg->getShiftOp(), Imm5); return EncodedAsShiftedRegister; } if (const auto *ShImm = llvm::dyn_cast(Opnd)) { const IValueT Immed5 = ShImm->getShAmtImm(); assert(Immed5 < (1 << kShiftImmBits)); Value = (Immed5 << kShiftImmShift); return EncodedAsShiftImm5; } return CantEncode; } IValueT encodeImmRegOffset(IValueT Reg, IOffsetT Offset, OperandARM32Mem::AddrMode Mode, IOffsetT MaxOffset, IValueT OffsetShift) { IValueT Value = Mode | (Reg << kRnShift); if (Offset < 0) { Offset = -Offset; Value ^= U; // Flip U to adjust sign. } assert(Offset <= MaxOffset); (void)MaxOffset; return Value | (Offset >> OffsetShift); } // Encodes immediate register offset using encoding 3. IValueT encodeImmRegOffsetEnc3(IValueT Rn, IOffsetT Imm8, OperandARM32Mem::AddrMode Mode) { IValueT Value = Mode | (Rn << kRnShift); if (Imm8 < 0) { Imm8 = -Imm8; Value = (Value ^ U); } assert(Imm8 < (1 << 8)); Value = Value | B22 | ((Imm8 & 0xf0) << 4) | (Imm8 & 0x0f); return Value; } IValueT encodeImmRegOffset(EncodedImmAddress ImmEncoding, IValueT Reg, IOffsetT Offset, OperandARM32Mem::AddrMode Mode) { switch (ImmEncoding) { case RotatedImm8Address: { constexpr IOffsetT MaxOffset = (1 << 8) - 1; constexpr IValueT NoRightShift = 0; return encodeImmRegOffset(Reg, Offset, Mode, MaxOffset, NoRightShift); } case RotatedImm8Div4Address: { assert((Offset & 0x3) == 0); constexpr IOffsetT MaxOffset = (1 << 8) - 1; constexpr IValueT RightShift2 = 2; return encodeImmRegOffset(Reg, Offset, Mode, MaxOffset, RightShift2); } case Imm12Address: { constexpr IOffsetT MaxOffset = (1 << 12) - 1; constexpr IValueT NoRightShift = 0; return encodeImmRegOffset(Reg, Offset, Mode, MaxOffset, NoRightShift); } case RotatedImm8Enc3Address: return encodeImmRegOffsetEnc3(Reg, Offset, Mode); case NoImmOffsetAddress: { assert(Offset == 0); assert(Mode == OperandARM32Mem::Offset); return Reg << kRnShift; } } llvm_unreachable("(silence g++ warning)"); } // Encodes memory address Opnd, and encodes that information into Value, based // on how ARM represents the address. Returns how the value was encoded. EncodedOperand encodeAddress(const Operand *Opnd, IValueT &Value, const AssemblerARM32::TargetInfo &TInfo, EncodedImmAddress ImmEncoding) { Value = 0; // Make sure initialized. if (const auto *Var = llvm::dyn_cast(Opnd)) { // Should be a stack variable, with an offset. if (Var->hasReg()) return CantEncode; IOffsetT Offset = Var->getStackOffset(); if (!Utils::IsAbsoluteUint(12, Offset)) return CantEncode; const auto BaseRegNum = Var->hasReg() ? Var->getBaseRegNum() : TInfo.FrameOrStackReg; Value = encodeImmRegOffset(ImmEncoding, BaseRegNum, Offset, OperandARM32Mem::Offset); return EncodedAsImmRegOffset; } if (const auto *Mem = llvm::dyn_cast(Opnd)) { Variable *Var = Mem->getBase(); if (!Var->hasReg()) return CantEncode; IValueT Rn = getEncodedGPRegNum(Var); if (Mem->isRegReg()) { const Variable *Index = Mem->getIndex(); if (Var == nullptr) return CantEncode; Value = (Rn << kRnShift) | Mem->getAddrMode() | encodeShiftRotateImm5(getEncodedGPRegNum(Index), Mem->getShiftOp(), Mem->getShiftAmt()); return EncodedAsShiftRotateImm5; } // Encoded as immediate register offset. ConstantInteger32 *Offset = Mem->getOffset(); Value = encodeImmRegOffset(ImmEncoding, Rn, Offset->getValue(), Mem->getAddrMode()); return EncodedAsImmRegOffset; } return CantEncode; } // Checks that Offset can fit in imm24 constant of branch (b) instruction. void assertCanEncodeBranchOffset(IOffsetT Offset) { (void)Offset; (void)kBranchOffsetBits; assert(Utils::IsAligned(Offset, 4) && Utils::IsInt(kBranchOffsetBits, Offset >> 2)); } IValueT encodeBranchOffset(IOffsetT Offset, IValueT Inst) { // Adjust offset to the way ARM CPUs read PC. Offset -= kPCReadOffset; assertCanEncodeBranchOffset(Offset); // Properly preserve only the bits supported in the instruction. Offset >>= 2; Offset &= kBranchOffsetMask; return (Inst & ~kBranchOffsetMask) | Offset; } IValueT encodeRegister(const Operand *OpReg, RegSetWanted WantedRegSet, const char *RegName, const char *InstName) { IValueT Reg = 0; if (encodeOperand(OpReg, Reg, WantedRegSet) != EncodedAsRegister) llvm::report_fatal_error(std::string(InstName) + ": Can't find register " + RegName); return Reg; } IValueT encodeGPRegister(const Operand *OpReg, const char *RegName, const char *InstName) { return encodeRegister(OpReg, WantGPRegs, RegName, InstName); } IValueT encodeSRegister(const Operand *OpReg, const char *RegName, const char *InstName) { return encodeRegister(OpReg, WantSRegs, RegName, InstName); } IValueT encodeDRegister(const Operand *OpReg, const char *RegName, const char *InstName) { return encodeRegister(OpReg, WantDRegs, RegName, InstName); } IValueT encodeQRegister(const Operand *OpReg, const char *RegName, const char *InstName) { return encodeRegister(OpReg, WantQRegs, RegName, InstName); } void verifyPOrNotW(IValueT Address, const char *InstName) { if (BuildDefs::minimal()) return; if (!isBitSet(P, Address) && isBitSet(W, Address)) llvm::report_fatal_error(std::string(InstName) + ": P=0 when W=1 not allowed"); } void verifyRegsNotEq(IValueT Reg1, const char *Reg1Name, IValueT Reg2, const char *Reg2Name, const char *InstName) { if (BuildDefs::minimal()) return; if (Reg1 == Reg2) llvm::report_fatal_error(std::string(InstName) + ": " + Reg1Name + "=" + Reg2Name + " not allowed"); } void verifyRegNotPc(IValueT Reg, const char *RegName, const char *InstName) { verifyRegsNotEq(Reg, RegName, RegARM32::Encoded_Reg_pc, "pc", InstName); } void verifyAddrRegNotPc(IValueT RegShift, IValueT Address, const char *RegName, const char *InstName) { if (BuildDefs::minimal()) return; if (getGPRReg(RegShift, Address) == RegARM32::Encoded_Reg_pc) llvm::report_fatal_error(std::string(InstName) + ": " + RegName + "=pc not allowed"); } void verifyRegNotPcWhenSetFlags(IValueT Reg, bool SetFlags, const char *InstName) { if (BuildDefs::minimal()) return; if (SetFlags && (Reg == RegARM32::Encoded_Reg_pc)) llvm::report_fatal_error(std::string(InstName) + ": " + RegARM32::getRegName(RegARM32::Reg_pc) + "=pc not allowed when CC=1"); } enum SIMDShiftType { ST_Vshl, ST_Vshr }; IValueT encodeSIMDShiftImm6(SIMDShiftType Shift, Type ElmtTy, const IValueT Imm) { assert(Imm > 0); const SizeT MaxShift = getScalarIntBitWidth(ElmtTy); assert(Imm < 2 * MaxShift); assert(ElmtTy == IceType_i8 || ElmtTy == IceType_i16 || ElmtTy == IceType_i32); const IValueT VshlImm = Imm - MaxShift; const IValueT VshrImm = 2 * MaxShift - Imm; return ((Shift == ST_Vshl) ? VshlImm : VshrImm) & (2 * MaxShift - 1); } IValueT encodeSIMDShiftImm6(SIMDShiftType Shift, Type ElmtTy, const ConstantInteger32 *Imm6) { const IValueT Imm = Imm6->getValue(); return encodeSIMDShiftImm6(Shift, ElmtTy, Imm); } } // end of anonymous namespace namespace Ice { namespace ARM32 { size_t MoveRelocatableFixup::emit(GlobalContext *Ctx, const Assembler &Asm) const { if (!BuildDefs::dump()) return InstARM32::InstSize; Ostream &Str = Ctx->getStrEmit(); IValueT Inst = Asm.load(position()); const bool IsMovw = kind() == llvm::ELF::R_ARM_MOVW_ABS_NC || kind() == llvm::ELF::R_ARM_MOVW_PREL_NC; const auto Symbol = symbol().toString(); const bool NeedsPCRelSuffix = (Asm.fixupIsPCRel(kind()) || Symbol == GlobalOffsetTable); Str << "\t" "mov" << (IsMovw ? "w" : "t") << "\t" << RegARM32::getRegName(RegNumT::fixme((Inst >> kRdShift) & 0xF)) << ", #:" << (IsMovw ? "lower" : "upper") << "16:" << Symbol << (NeedsPCRelSuffix ? " - ." : "") << "\t@ .word " // TODO(jpp): This is broken, it also needs to add a magic constant. << llvm::format_hex_no_prefix(Inst, 8) << "\n"; return InstARM32::InstSize; } IValueT AssemblerARM32::encodeElmtType(Type ElmtTy) { switch (ElmtTy) { case IceType_i8: return 0; case IceType_i16: return 1; case IceType_i32: case IceType_f32: return 2; case IceType_i64: return 3; default: llvm::report_fatal_error("SIMD op: Don't understand element type " + typeStdString(ElmtTy)); } } // This fixup points to an ARM32 instruction with the following format: void MoveRelocatableFixup::emitOffset(Assembler *Asm) const { // cccc00110T00iiiiddddiiiiiiiiiiii where cccc=Cond, dddd=Rd, // iiiiiiiiiiiiiiii = Imm16, and T=1 for movt. const IValueT Inst = Asm->load(position()); constexpr IValueT Imm16Mask = 0x000F0FFF; const IValueT Imm16 = offset() & 0xffff; Asm->store(position(), (Inst & ~Imm16Mask) | ((Imm16 >> 12) << 16) | (Imm16 & 0xfff)); } MoveRelocatableFixup *AssemblerARM32::createMoveFixup(bool IsMovW, const Constant *Value) { MoveRelocatableFixup *F = new (allocate()) MoveRelocatableFixup(); F->set_kind(IsMovW ? (IsNonsfi ? llvm::ELF::R_ARM_MOVW_PREL_NC : llvm::ELF::R_ARM_MOVW_ABS_NC) : (IsNonsfi ? llvm::ELF::R_ARM_MOVT_PREL : llvm::ELF::R_ARM_MOVT_ABS)); F->set_value(Value); Buffer.installFixup(F); return F; } size_t BlRelocatableFixup::emit(GlobalContext *Ctx, const Assembler &Asm) const { if (!BuildDefs::dump()) return InstARM32::InstSize; Ostream &Str = Ctx->getStrEmit(); IValueT Inst = Asm.load(position()); Str << "\t" "bl\t" << symbol() << "\t@ .word " << llvm::format_hex_no_prefix(Inst, 8) << "\n"; return InstARM32::InstSize; } void BlRelocatableFixup::emitOffset(Assembler *Asm) const { // cccc101liiiiiiiiiiiiiiiiiiiiiiii where cccc=Cond, l=Link, and // iiiiiiiiiiiiiiiiiiiiiiii= // EncodedBranchOffset(cccc101l000000000000000000000000, Offset); const IValueT Inst = Asm->load(position()); constexpr IValueT OffsetMask = 0x00FFFFFF; Asm->store(position(), encodeBranchOffset(offset(), Inst & ~OffsetMask)); } void AssemblerARM32::padWithNop(intptr_t Padding) { constexpr intptr_t InstWidth = sizeof(IValueT); assert(Padding % InstWidth == 0 && "Padding not multiple of instruction size"); for (intptr_t i = 0; i < Padding; i += InstWidth) nop(); } BlRelocatableFixup * AssemblerARM32::createBlFixup(const ConstantRelocatable *BlTarget) { BlRelocatableFixup *F = new (allocate()) BlRelocatableFixup(); F->set_kind(llvm::ELF::R_ARM_CALL); F->set_value(BlTarget); Buffer.installFixup(F); return F; } void AssemblerARM32::bindCfgNodeLabel(const CfgNode *Node) { if (BuildDefs::dump() && !getFlags().getDisableHybridAssembly()) { // Generate label name so that branches can find it. constexpr SizeT InstSize = 0; emitTextInst(Node->getAsmName() + ":", InstSize); } SizeT NodeNumber = Node->getIndex(); assert(!getPreliminary()); Label *L = getOrCreateCfgNodeLabel(NodeNumber); this->bind(L); } Label *AssemblerARM32::getOrCreateLabel(SizeT Number, LabelVector &Labels) { Label *L = nullptr; if (Number == Labels.size()) { L = new (this->allocate