HELLO·Android
系统源代码
IT资讯
技术文章
我的收藏
注册
登录
-
我收藏的文章
创建代码块
我的代码块
我的账号
Android 10
|
10.0.0_r6
下载
查看原文件
收藏
根目录
external
swiftshader
third_party
subzero
src
IceAssemblerARM32.cpp
//===- 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
()) Label(); Labels.push_back(L); return L; } if (Number > Labels.size()) { Labels.resize(Number + 1); } L = Labels[Number]; if (!L) { L = new (this->allocate
()) Label(); Labels[Number] = L; } return L; } // Pull out offset from branch Inst. IOffsetT AssemblerARM32::decodeBranchOffset(IValueT Inst) { // Sign-extend, left-shift by 2, and adjust to the way ARM CPUs read PC. const IOffsetT Offset = (Inst & kBranchOffsetMask) << 8; return (Offset >> 6) + kPCReadOffset; } void AssemblerARM32::bind(Label *L) { IOffsetT BoundPc = Buffer.size(); assert(!L->isBound()); // Labels can only be bound once. while (L->isLinked()) { IOffsetT Position = L->getLinkPosition(); IOffsetT Dest = BoundPc - Position; IValueT Inst = Buffer.load
(Position); Buffer.store
(Position, encodeBranchOffset(Dest, Inst)); L->setPosition(decodeBranchOffset(Inst)); } L->bindTo(BoundPc); } void AssemblerARM32::emitTextInst(const std::string &Text, SizeT InstSize) { AssemblerFixup *F = createTextFixup(Text, InstSize); emitFixup(F); for (SizeT I = 0; I < InstSize; ++I) { AssemblerBuffer::EnsureCapacity ensured(&Buffer); Buffer.emit
(0); } } void AssemblerARM32::emitType01(CondARM32::Cond Cond, IValueT InstType, IValueT Opcode, bool SetFlags, IValueT Rn, IValueT Rd, IValueT Imm12, EmitChecks RuleChecks, const char *InstName) { switch (RuleChecks) { case NoChecks: break; case RdIsPcAndSetFlags: verifyRegNotPcWhenSetFlags(Rd, SetFlags, InstName); break; } assert(Rd < RegARM32::getNumGPRegs()); assert(CondARM32::isDefined(Cond)); const IValueT Encoding = (encodeCondition(Cond) << kConditionShift) | (InstType << kTypeShift) | (Opcode << kOpcodeShift) | (encodeBool(SetFlags) << kSShift) | (Rn << kRnShift) | (Rd << kRdShift) | Imm12; emitInst(Encoding); } void AssemblerARM32::emitType01(CondARM32::Cond Cond, IValueT Opcode, const Operand *OpRd, const Operand *OpRn, const Operand *OpSrc1, bool SetFlags, EmitChecks RuleChecks, const char *InstName) { IValueT Rd = encodeGPRegister(OpRd, "Rd", InstName); IValueT Rn = encodeGPRegister(OpRn, "Rn", InstName); emitType01(Cond, Opcode, Rd, Rn, OpSrc1, SetFlags, RuleChecks, InstName); } void AssemblerARM32::emitType01(CondARM32::Cond Cond, IValueT Opcode, IValueT Rd, IValueT Rn, const Operand *OpSrc1, bool SetFlags, EmitChecks RuleChecks, const char *InstName) { IValueT Src1Value; // TODO(kschimpf) Other possible decodings of data operations. switch (encodeOperand(OpSrc1, Src1Value, WantGPRegs)) { default: llvm::report_fatal_error(std::string(InstName) + ": Can't encode instruction"); return; case EncodedAsRegister: { // XXX (register) // xxx{s}
,
,
{,
} // // cccc000xxxxsnnnnddddiiiiitt0mmmm where cccc=Cond, xxxx=Opcode, dddd=Rd, // nnnn=Rn, mmmm=Rm, iiiii=Shift, tt=ShiftKind, and s=SetFlags. constexpr IValueT Imm5 = 0; Src1Value = encodeShiftRotateImm5(Src1Value, OperandARM32::kNoShift, Imm5); emitType01(Cond, kInstTypeDataRegister, Opcode, SetFlags, Rn, Rd, Src1Value, RuleChecks, InstName); return; } case EncodedAsShiftedRegister: { // Form is defined in case EncodedAsRegister. (i.e. XXX (register)). emitType01(Cond, kInstTypeDataRegister, Opcode, SetFlags, Rn, Rd, Src1Value, RuleChecks, InstName); return; } case EncodedAsConstI32: { // See if we can convert this to an XXX (immediate). IValueT RotateAmt; IValueT Imm8; if (!OperandARM32FlexImm::canHoldImm(Src1Value, &RotateAmt, &Imm8)) llvm::report_fatal_error(std::string(InstName) + ": Immediate rotated constant not valid"); Src1Value = encodeRotatedImm8(RotateAmt, Imm8); // Intentionally fall to next case! } case EncodedAsRotatedImm8: { // XXX (Immediate) // xxx{s}
,
, #
// // cccc001xxxxsnnnnddddiiiiiiiiiiii where cccc=Cond, xxxx=Opcode, dddd=Rd, // nnnn=Rn, s=SetFlags and iiiiiiiiiiii=Src1Value defining RotatedImm8. emitType01(Cond, kInstTypeDataImmediate, Opcode, SetFlags, Rn, Rd, Src1Value, RuleChecks, InstName); return; } case EncodedAsRegShiftReg: { // XXX (register-shifted reg) // xxx{s}
,
,
,
// // cccc000xxxxfnnnnddddssss0tt1mmmm where cccc=Cond, xxxx=Opcode, dddd=Rd, // nnnn=Rn, ssss=Rs, f=SetFlags, tt is encoding of type, and // Src1Value=ssss01tt1mmmm. emitType01(Cond, kInstTypeDataRegShift, Opcode, SetFlags, Rn, Rd, Src1Value, RuleChecks, InstName); return; } } } void AssemblerARM32::emitType05(CondARM32::Cond Cond, IOffsetT Offset, bool Link) { // cccc101liiiiiiiiiiiiiiiiiiiiiiii where cccc=Cond, l=Link, and // iiiiiiiiiiiiiiiiiiiiiiii= // EncodedBranchOffset(cccc101l000000000000000000000000, Offset); assert(CondARM32::isDefined(Cond)); IValueT Encoding = static_cast
(Cond) << kConditionShift | 5 << kTypeShift | (Link ? 1 : 0) << kLinkShift; Encoding = encodeBranchOffset(Offset, Encoding); emitInst(Encoding); } void AssemblerARM32::emitBranch(Label *L, CondARM32::Cond Cond, bool Link) { // TODO(kschimpf): Handle far jumps. if (L->isBound()) { const int32_t Dest = L->getPosition() - Buffer.size(); emitType05(Cond, Dest, Link); return; } const IOffsetT Position = Buffer.size(); // Use the offset field of the branch instruction for linking the sites. emitType05(Cond, L->getEncodedPosition(), Link); L->linkTo(*this, Position); } void AssemblerARM32::emitCompareOp(CondARM32::Cond Cond, IValueT Opcode, const Operand *OpRn, const Operand *OpSrc1, const char *InstName) { // XXX (register) // XXX
,
{,
} // // ccccyyyxxxx1nnnn0000iiiiitt0mmmm where cccc=Cond, nnnn=Rn, mmmm=Rm, iiiii // defines shift constant, tt=ShiftKind, yyy=kInstTypeDataRegister, and // xxxx=Opcode. // // XXX (immediate) // XXX
, #
// // ccccyyyxxxx1nnnn0000iiiiiiiiiiii where cccc=Cond, dddd=Rd, nnnn=Rn, // yyy=kInstTypeDataImmdiate, xxxx=Opcode, and iiiiiiiiiiii=Src1Value // defining RotatedImm8. constexpr bool SetFlags = true; constexpr IValueT Rd = RegARM32::Encoded_Reg_r0; IValueT Rn = encodeGPRegister(OpRn, "Rn", InstName); emitType01(Cond, Opcode, Rd, Rn, OpSrc1, SetFlags, NoChecks, InstName); } void AssemblerARM32::emitMemOp(CondARM32::Cond Cond, IValueT InstType, bool IsLoad, bool IsByte, IValueT Rt, IValueT Address) { assert(Rt < RegARM32::getNumGPRegs()); assert(CondARM32::isDefined(Cond)); const IValueT Encoding = (encodeCondition(Cond) << kConditionShift) | (InstType << kTypeShift) | (IsLoad ? L : 0) | (IsByte ? B : 0) | (Rt << kRdShift) | Address; emitInst(Encoding); } void AssemblerARM32::emitMemOp(CondARM32::Cond Cond, bool IsLoad, bool IsByte, IValueT Rt, const Operand *OpAddress, const TargetInfo &TInfo, const char *InstName) { IValueT Address; switch (encodeAddress(OpAddress, Address, TInfo, Imm12Address)) { default: llvm::report_fatal_error(std::string(InstName) + ": Memory address not understood"); case EncodedAsImmRegOffset: { // XXX{B} (immediate): // xxx{b}
, [
{, #+/-
}] ; p=1, w=0 // xxx{b}
, [
], #+/-
; p=1, w=1 // xxx{b}
, [
, #+/-
]! ; p=0, w=1 // // cccc010pubwlnnnnttttiiiiiiiiiiii where cccc=Cond, tttt=Rt, nnnn=Rn, // iiiiiiiiiiii=imm12, b=IsByte, pu0w<<21 is a BlockAddr, l=IsLoad, and // pu0w0nnnn0000iiiiiiiiiiii=Address. RegARM32::GPRRegister Rn = getGPRReg(kRnShift, Address); // Check if conditions of rules violated. verifyRegNotPc(Rn, "Rn", InstName); verifyPOrNotW(Address, InstName); if (!IsByte && (Rn == RegARM32::Encoded_Reg_sp) && !isBitSet(P, Address) && isBitSet(U, Address) && !isBitSet(W, Address) && (mask(Address, kImm12Shift, kImmed12Bits) == 0x8 /* 000000000100 */)) llvm::report_fatal_error(std::string(InstName) + ": Use push/pop instead"); emitMemOp(Cond, kInstTypeMemImmediate, IsLoad, IsByte, Rt, Address); return; } case EncodedAsShiftRotateImm5: { // XXX{B} (register) // xxx{b}
, [
, +/-
{,
}]{!} // xxx{b}
, [
], +/-
{,
} // // cccc011pubwlnnnnttttiiiiiss0mmmm where cccc=Cond, tttt=Rt, // b=IsByte, U=1 if +, pu0b is a BlockAddr, l=IsLoad, and // pu0w0nnnn0000iiiiiss0mmmm=Address. RegARM32::GPRRegister Rn = getGPRReg(kRnShift, Address); RegARM32::GPRRegister Rm = getGPRReg(kRmShift, Address); // Check if conditions of rules violated. verifyPOrNotW(Address, InstName); verifyRegNotPc(Rm, "Rm", InstName); if (IsByte) verifyRegNotPc(Rt, "Rt", InstName); if (isBitSet(W, Address)) { verifyRegNotPc(Rn, "Rn", InstName); verifyRegsNotEq(Rn, "Rn", Rt, "Rt", InstName); } emitMemOp(Cond, kInstTypeRegisterShift, IsLoad, IsByte, Rt, Address); return; } } } void AssemblerARM32::emitMemOpEnc3(CondARM32::Cond Cond, IValueT Opcode, IValueT Rt, const Operand *OpAddress, const TargetInfo &TInfo, const char *InstName) { IValueT Address; switch (encodeAddress(OpAddress, Address, TInfo, RotatedImm8Enc3Address)) { default: llvm::report_fatal_error(std::string(InstName) + ": Memory address not understood"); case EncodedAsImmRegOffset: { // XXXH (immediate) // xxxh
, [
{, #+-
}] // xxxh
, [
, #+/-
] // xxxh
, [
, #+/-
]! // // cccc000pu0wxnnnnttttiiiiyyyyjjjj where cccc=Cond, nnnn=Rn, tttt=Rt, // iiiijjjj=Imm8, pu0w<<21 is a BlockAddr, x000000000000yyyy0000=Opcode, // and pu0w0nnnn0000iiii0000jjjj=Address. assert(Rt < RegARM32::getNumGPRegs()); assert(CondARM32::isDefined(Cond)); verifyPOrNotW(Address, InstName); verifyRegNotPc(Rt, "Rt", InstName); if (isBitSet(W, Address)) verifyRegsNotEq(getGPRReg(kRnShift, Address), "Rn", Rt, "Rt", InstName); const IValueT Encoding = (encodeCondition(Cond) << kConditionShift) | Opcode | (Rt << kRdShift) | Address; emitInst(Encoding); return; } case EncodedAsShiftRotateImm5: { // XXXH (register) // xxxh
, [
, +/-
]{!} // xxxh
, [
], +/-
// // cccc000pu0wxnnnntttt00001011mmmm where cccc=Cond, tttt=Rt, nnnn=Rn, // mmmm=Rm, pu0w<<21 is a BlockAddr, x000000000000yyyy0000=Opcode, and // pu0w0nnnn000000000000mmmm=Address. assert(Rt < RegARM32::getNumGPRegs()); assert(CondARM32::isDefined(Cond)); verifyPOrNotW(Address, InstName); verifyRegNotPc(Rt, "Rt", InstName); verifyAddrRegNotPc(kRmShift, Address, "Rm", InstName); const RegARM32::GPRRegister Rn = getGPRReg(kRnShift, Address); if (isBitSet(W, Address)) { verifyRegNotPc(Rn, "Rn", InstName); verifyRegsNotEq(Rn, "Rn", Rt, "Rt", InstName); } if (mask(Address, kShiftImmShift, 5) != 0) // For encoding 3, no shift is allowed. llvm::report_fatal_error(std::string(InstName) + ": Shift constant not allowed"); const IValueT Encoding = (encodeCondition(Cond) << kConditionShift) | Opcode | (Rt << kRdShift) | Address; emitInst(Encoding); return; } } } void AssemblerARM32::emitDivOp(CondARM32::Cond Cond, IValueT Opcode, IValueT Rd, IValueT Rn, IValueT Rm) { assert(Rd < RegARM32::getNumGPRegs()); assert(Rn < RegARM32::getNumGPRegs()); assert(Rm < RegARM32::getNumGPRegs()); assert(CondARM32::isDefined(Cond)); const IValueT Encoding = Opcode | (encodeCondition(Cond) << kConditionShift) | (Rn << kDivRnShift) | (Rd << kDivRdShift) | B26 | B25 | B24 | B20 | B15 | B14 | B13 | B12 | B4 | (Rm << kDivRmShift); emitInst(Encoding); } void AssemblerARM32::emitInsertExtractInt(CondARM32::Cond Cond, const Operand *OpQn, uint32_t Index, const Operand *OpRt, bool IsExtract, const char *InstName) { const IValueT Rt = encodeGPRegister(OpRt, "Rt", InstName); IValueT Dn = mapQRegToDReg(encodeQRegister(OpQn, "Qn", InstName)); assert(Rt != RegARM32::Encoded_Reg_pc); assert(Rt != RegARM32::Encoded_Reg_sp); assert(CondARM32::isDefined(Cond)); const uint32_t BitSize = typeWidthInBytes(OpRt->getType()) * CHAR_BIT; IValueT Opcode1 = 0; IValueT Opcode2 = 0; switch (BitSize) { default: llvm::report_fatal_error(std::string(InstName) + ": Unable to process type " + typeStdString(OpRt->getType())); case 8: assert(Index < 16); Dn = Dn | mask(Index, 3, 1); Opcode1 = B1 | mask(Index, 2, 1); Opcode2 = mask(Index, 0, 2); break; case 16: assert(Index < 8); Dn = Dn | mask(Index, 2, 1); Opcode1 = mask(Index, 1, 1); Opcode2 = (mask(Index, 0, 1) << 1) | B0; break; case 32: assert(Index < 4); Dn = Dn | mask(Index, 1, 1); Opcode1 = mask(Index, 0, 1); break; } const IValueT Encoding = B27 | B26 | B25 | B11 | B9 | B8 | B4 | (encodeCondition(Cond) << kConditionShift) | (Opcode1 << 21) | (getXXXXInRegYXXXX(Dn) << kRnShift) | (Rt << 12) | (encodeBool(IsExtract) << 20) | (getYInRegYXXXX(Dn) << 7) | (Opcode2 << 5); emitInst(Encoding); } void AssemblerARM32::emitMoveSS(CondARM32::Cond Cond, IValueT Sd, IValueT Sm) { // VMOV (register) - ARM section A8.8.340, encoding A2: // vmov
.f32
,
// // cccc11101D110000dddd101001M0mmmm where cccc=Cond, ddddD=Sd, and mmmmM=Sm. constexpr IValueT VmovssOpcode = B23 | B21 | B20 | B6; constexpr IValueT S0 = 0; emitVFPsss(Cond, VmovssOpcode, Sd, S0, Sm); } void AssemblerARM32::emitMulOp(CondARM32::Cond Cond, IValueT Opcode, IValueT Rd, IValueT Rn, IValueT Rm, IValueT Rs, bool SetFlags) { assert(Rd < RegARM32::getNumGPRegs()); assert(Rn < RegARM32::getNumGPRegs()); assert(Rm < RegARM32::getNumGPRegs()); assert(Rs < RegARM32::getNumGPRegs()); assert(CondARM32::isDefined(Cond)); IValueT Encoding = Opcode | (encodeCondition(Cond) << kConditionShift) | (encodeBool(SetFlags) << kSShift) | (Rn << kRnShift) | (Rd << kRdShift) | (Rs << kRsShift) | B7 | B4 | (Rm << kRmShift); emitInst(Encoding); } void AssemblerARM32::emitMultiMemOp(CondARM32::Cond Cond, BlockAddressMode AddressMode, bool IsLoad, IValueT BaseReg, IValueT Registers) { assert(CondARM32::isDefined(Cond)); assert(BaseReg < RegARM32::getNumGPRegs()); assert(Registers < (1 << RegARM32::getNumGPRegs())); IValueT Encoding = (encodeCondition(Cond) << kConditionShift) | B27 | AddressMode | (IsLoad ? L : 0) | (BaseReg << kRnShift) | Registers; emitInst(Encoding); } void AssemblerARM32::emitSignExtend(CondARM32::Cond Cond, IValueT Opcode, const Operand *OpRd, const Operand *OpSrc0, const char *InstName) { IValueT Rd = encodeGPRegister(OpRd, "Rd", InstName); IValueT Rm = encodeGPRegister(OpSrc0, "Rm", InstName); // Note: For the moment, we assume no rotation is specified. RotationValue Rotation = kRotateNone; constexpr IValueT Rn = RegARM32::Encoded_Reg_pc; const Type Ty = OpSrc0->getType(); switch (Ty) { default: llvm::report_fatal_error(std::string(InstName) + ": Type " + typeString(Ty) + " not allowed"); break; case IceType_i1: case IceType_i8: { // SXTB/UXTB - Arm sections A8.8.233 and A8.8.274, encoding A1: // sxtb
,
{,
} // uxtb
,
{,
} // // ccccxxxxxxxx1111ddddrr000111mmmm where cccc=Cond, xxxxxxxx<<20=Opcode, // dddd=Rd, mmmm=Rm, and rr defined (RotationValue) rotate. break; } case IceType_i16: { // SXTH/UXTH - ARM sections A8.8.235 and A8.8.276, encoding A1: // uxth
<
{,
} // // cccc01101111nnnnddddrr000111mmmm where cccc=Cond, dddd=Rd, mmmm=Rm, and // rr defined (RotationValue) rotate. Opcode |= B20; break; } } assert(CondARM32::isDefined(Cond)); IValueT Rot = encodeRotation(Rotation); if (!Utils::IsUint(2, Rot)) llvm::report_fatal_error(std::string(InstName) + ": Illegal rotation value"); IValueT Encoding = (encodeCondition(Cond) << kConditionShift) | Opcode | (Rn << kRnShift) | (Rd << kRdShift) | (Rot << kRotationShift) | B6 | B5 | B4 | (Rm << kRmShift); emitInst(Encoding); } void AssemblerARM32::emitSIMDBase(IValueT Opcode, IValueT Dd, IValueT Dn, IValueT Dm, bool UseQRegs, bool IsFloatTy) { const IValueT Encoding = Opcode | B25 | (encodeCondition(CondARM32::kNone) << kConditionShift) | (getYInRegYXXXX(Dd) << 22) | (getXXXXInRegYXXXX(Dn) << 16) | (getXXXXInRegYXXXX(Dd) << 12) | (IsFloatTy ? B10 : 0) | (getYInRegYXXXX(Dn) << 7) | (encodeBool(UseQRegs) << 6) | (getYInRegYXXXX(Dm) << 5) | getXXXXInRegYXXXX(Dm); emitInst(Encoding); } void AssemblerARM32::emitSIMD(IValueT Opcode, Type ElmtTy, IValueT Dd, IValueT Dn, IValueT Dm, bool UseQRegs) { constexpr IValueT ElmtShift = 20; const IValueT ElmtSize = encodeElmtType(ElmtTy); assert(Utils::IsUint(2, ElmtSize)); emitSIMDBase(Opcode | (ElmtSize << ElmtShift), Dd, Dn, Dm, UseQRegs, isFloatingType(ElmtTy)); } void AssemblerARM32::emitSIMDqqqBase(IValueT Opcode, const Operand *OpQd, const Operand *OpQn, const Operand *OpQm, bool IsFloatTy, const char *OpcodeName) { const IValueT Qd = encodeQRegister(OpQd, "Qd", OpcodeName); const IValueT Qn = encodeQRegister(OpQn, "Qn", OpcodeName); const IValueT Qm = encodeQRegister(OpQm, "Qm", OpcodeName); constexpr bool UseQRegs = true; emitSIMDBase(Opcode, mapQRegToDReg(Qd), mapQRegToDReg(Qn), mapQRegToDReg(Qm), UseQRegs, IsFloatTy); } void AssemblerARM32::emitSIMDqqq(IValueT Opcode, Type ElmtTy, const Operand *OpQd, const Operand *OpQn, const Operand *OpQm, const char *OpcodeName) { constexpr IValueT ElmtShift = 20; const IValueT ElmtSize = encodeElmtType(ElmtTy); assert(Utils::IsUint(2, ElmtSize)); emitSIMDqqqBase(Opcode | (ElmtSize << ElmtShift), OpQd, OpQn, OpQm, isFloatingType(ElmtTy), OpcodeName); } void AssemblerARM32::emitSIMDShiftqqc(IValueT Opcode, const Operand *OpQd, const Operand *OpQm, const IValueT Imm6, const char *OpcodeName) { const IValueT Qd = encodeQRegister(OpQd, "Qd", OpcodeName); const IValueT Qn = 0; const IValueT Qm = encodeQRegister(OpQm, "Qm", OpcodeName); constexpr bool UseQRegs = true; constexpr bool IsFloatTy = false; constexpr IValueT ElmtShift = 16; emitSIMDBase(Opcode | (Imm6 << ElmtShift), mapQRegToDReg(Qd), mapQRegToDReg(Qn), mapQRegToDReg(Qm), UseQRegs, IsFloatTy); } void AssemblerARM32::emitSIMDCvtqq(IValueT Opcode, const Operand *OpQd, const Operand *OpQm, const char *OpcodeName) { const IValueT SIMDOpcode = B24 | B23 | B21 | B20 | B19 | B17 | B16 | B10 | B9 | Opcode; constexpr bool UseQRegs = true; constexpr bool IsFloatTy = false; const IValueT Qd = encodeQRegister(OpQd, "Qd", OpcodeName); constexpr IValueT Qn = 0; const IValueT Qm = encodeQRegister(OpQm, "Qm", OpcodeName); emitSIMDBase(SIMDOpcode, mapQRegToDReg(Qd), mapQRegToDReg(Qn), mapQRegToDReg(Qm), UseQRegs, IsFloatTy); } void AssemblerARM32::emitVFPddd(CondARM32::Cond Cond, IValueT Opcode, IValueT Dd, IValueT Dn, IValueT Dm) { assert(Dd < RegARM32::getNumDRegs()); assert(Dn < RegARM32::getNumDRegs()); assert(Dm < RegARM32::getNumDRegs()); assert(CondARM32::isDefined(Cond)); constexpr IValueT VFPOpcode = B27 | B26 | B25 | B11 | B9 | B8; const IValueT Encoding = Opcode | VFPOpcode | (encodeCondition(Cond) << kConditionShift) | (getYInRegYXXXX(Dd) << 22) | (getXXXXInRegYXXXX(Dn) << 16) | (getXXXXInRegYXXXX(Dd) << 12) | (getYInRegYXXXX(Dn) << 7) | (getYInRegYXXXX(Dm) << 5) | getXXXXInRegYXXXX(Dm); emitInst(Encoding); } void AssemblerARM32::emitVFPddd(CondARM32::Cond Cond, IValueT Opcode, const Operand *OpDd, const Operand *OpDn, const Operand *OpDm, const char *InstName) { IValueT Dd = encodeDRegister(OpDd, "Dd", InstName); IValueT Dn = encodeDRegister(OpDn, "Dn", InstName); IValueT Dm = encodeDRegister(OpDm, "Dm", InstName); emitVFPddd(Cond, Opcode, Dd, Dn, Dm); } void AssemblerARM32::emitVFPsss(CondARM32::Cond Cond, IValueT Opcode, IValueT Sd, IValueT Sn, IValueT Sm) { assert(Sd < RegARM32::getNumSRegs()); assert(Sn < RegARM32::getNumSRegs()); assert(Sm < RegARM32::getNumSRegs()); assert(CondARM32::isDefined(Cond)); constexpr IValueT VFPOpcode = B27 | B26 | B25 | B11 | B9; const IValueT Encoding = Opcode | VFPOpcode | (encodeCondition(Cond) << kConditionShift) | (getYInRegXXXXY(Sd) << 22) | (getXXXXInRegXXXXY(Sn) << 16) | (getXXXXInRegXXXXY(Sd) << 12) | (getYInRegXXXXY(Sn) << 7) | (getYInRegXXXXY(Sm) << 5) | getXXXXInRegXXXXY(Sm); emitInst(Encoding); } void AssemblerARM32::emitVFPsss(CondARM32::Cond Cond, IValueT Opcode, const Operand *OpSd, const Operand *OpSn, const Operand *OpSm, const char *InstName) { const IValueT Sd = encodeSRegister(OpSd, "Sd", InstName); const IValueT Sn = encodeSRegister(OpSn, "Sn", InstName); const IValueT Sm = encodeSRegister(OpSm, "Sm", InstName); emitVFPsss(Cond, Opcode, Sd, Sn, Sm); } void AssemblerARM32::adc(const Operand *OpRd, const Operand *OpRn, const Operand *OpSrc1, bool SetFlags, CondARM32::Cond Cond) { // ADC (register) - ARM section 18.8.2, encoding A1: // adc{s}
,
,
{,
} // // cccc0000101snnnnddddiiiiitt0mmmm where cccc=Cond, dddd=Rd, nnnn=Rn, // mmmm=Rm, iiiii=Shift, tt=ShiftKind, and s=SetFlags. // // ADC (Immediate) - ARM section A8.8.1, encoding A1: // adc{s}
,
, #
// // cccc0010101snnnnddddiiiiiiiiiiii where cccc=Cond, dddd=Rd, nnnn=Rn, // s=SetFlags and iiiiiiiiiiii=Src1Value defining RotatedImm8. constexpr const char *AdcName = "adc"; constexpr IValueT AdcOpcode = B2 | B0; // 0101 emitType01(Cond, AdcOpcode, OpRd, OpRn, OpSrc1, SetFlags, RdIsPcAndSetFlags, AdcName); } void AssemblerARM32::add(const Operand *OpRd, const Operand *OpRn, const Operand *OpSrc1, bool SetFlags, CondARM32::Cond Cond) { // ADD (register) - ARM section A8.8.7, encoding A1: // add{s}
,
,
{,
} // ADD (Sp plus register) - ARM section A8.8.11, encoding A1: // add{s}
sp,
,
{,
} // // cccc0000100snnnnddddiiiiitt0mmmm where cccc=Cond, dddd=Rd, nnnn=Rn, // mmmm=Rm, iiiii=Shift, tt=ShiftKind, and s=SetFlags. // // ADD (Immediate) - ARM section A8.8.5, encoding A1: // add{s}
,
, #
// ADD (SP plus immediate) - ARM section A8.8.9, encoding A1. // add{s}
, sp, #
// // cccc0010100snnnnddddiiiiiiiiiiii where cccc=Cond, dddd=Rd, nnnn=Rn, // s=SetFlags and iiiiiiiiiiii=Src1Value defining RotatedImm8. constexpr const char *AddName = "add"; constexpr IValueT Add = B2; // 0100 emitType01(Cond, Add, OpRd, OpRn, OpSrc1, SetFlags, RdIsPcAndSetFlags, AddName); } void AssemblerARM32::and_(const Operand *OpRd, const Operand *OpRn, const Operand *OpSrc1, bool SetFlags, CondARM32::Cond Cond) { // AND (register) - ARM section A8.8.14, encoding A1: // and{s}
,
{,
} // // cccc0000000snnnnddddiiiiitt0mmmm where cccc=Cond, dddd=Rd, nnnn=Rn, // mmmm=Rm, iiiii=Shift, tt=ShiftKind, and s=SetFlags. // // AND (Immediate) - ARM section A8.8.13, encoding A1: // and{s}
,
, #
// // cccc0010100snnnnddddiiiiiiiiiiii where cccc=Cond, dddd=Rd, nnnn=Rn, // s=SetFlags and iiiiiiiiiiii=Src1Value defining RotatedImm8. constexpr const char *AndName = "and"; constexpr IValueT And = 0; // 0000 emitType01(Cond, And, OpRd, OpRn, OpSrc1, SetFlags, RdIsPcAndSetFlags, AndName); } void AssemblerARM32::b(Label *L, CondARM32::Cond Cond) { emitBranch(L, Cond, false); } void AssemblerARM32::bkpt(uint16_t Imm16) { // BKPT - ARM section A*.8.24 - encoding A1: // bkpt #
// // cccc00010010iiiiiiiiiiii0111iiii where cccc=AL and iiiiiiiiiiiiiiii=Imm16 const IValueT Encoding = (CondARM32::AL << kConditionShift) | B24 | B21 | ((Imm16 >> 4) << 8) | B6 | B5 | B4 | (Imm16 & 0xf); emitInst(Encoding); } void AssemblerARM32::bic(const Operand *OpRd, const Operand *OpRn, const Operand *OpSrc1, bool SetFlags, CondARM32::Cond Cond) { // BIC (register) - ARM section A8.8.22, encoding A1: // bic{s}
,
,
{,
} // // cccc0001110snnnnddddiiiiitt0mmmm where cccc=Cond, dddd=Rd, nnnn=Rn, // mmmm=Rm, iiiii=Shift, tt=ShiftKind, and s=SetFlags. // // BIC (immediate) - ARM section A8.8.21, encoding A1: // bic{s}
,
, #
// // cccc0011110snnnnddddiiiiiiiiiiii where cccc=Cond, dddd=Rn, nnnn=Rn, // s=SetFlags, and iiiiiiiiiiii=Src1Value defining RotatedImm8. constexpr const char *BicName = "bic"; constexpr IValueT BicOpcode = B3 | B2 | B1; // i.e. 1110 emitType01(Cond, BicOpcode, OpRd, OpRn, OpSrc1, SetFlags, RdIsPcAndSetFlags, BicName); } void AssemblerARM32::bl(const ConstantRelocatable *Target) { // BL (immediate) - ARM section A8.8.25, encoding A1: // bl
// // cccc1011iiiiiiiiiiiiiiiiiiiiiiii where cccc=Cond (not currently allowed) // and iiiiiiiiiiiiiiiiiiiiiiii is the (encoded) Target to branch to. emitFixup(createBlFixup(Target)); constexpr CondARM32::Cond Cond = CondARM32::AL; constexpr IValueT Immed = 0; constexpr bool Link = true; emitType05(Cond, Immed, Link); } void AssemblerARM32::blx(const Operand *Target) { // BLX (register) - ARM section A8.8.26, encoding A1: // blx
// // cccc000100101111111111110011mmmm where cccc=Cond (not currently allowed) // and mmmm=Rm. constexpr const char *BlxName = "Blx"; IValueT Rm = encodeGPRegister(Target, "Rm", BlxName); verifyRegNotPc(Rm, "Rm", BlxName); constexpr CondARM32::Cond Cond = CondARM32::AL; int32_t Encoding = (encodeCondition(Cond) << kConditionShift) | B24 | B21 | (0xfff << 8) | B5 | B4 | (Rm << kRmShift); emitInst(Encoding); } void AssemblerARM32::bx(RegARM32::GPRRegister Rm, CondARM32::Cond Cond) { // BX - ARM section A8.8.27, encoding A1: // bx
// // cccc000100101111111111110001mmmm where mmmm=rm and cccc=Cond. assert(CondARM32::isDefined(Cond)); const IValueT Encoding = (encodeCondition(Cond) << kConditionShift) | B24 | B21 | (0xfff << 8) | B4 | (encodeGPRRegister(Rm) << kRmShift); emitInst(Encoding); } void AssemblerARM32::clz(const Operand *OpRd, const Operand *OpSrc, CondARM32::Cond Cond) { // CLZ - ARM section A8.8.33, encoding A1: // clz
// // cccc000101101111dddd11110001mmmm where cccc=Cond, dddd=Rd, and mmmm=Rm. constexpr const char *ClzName = "clz"; constexpr const char *RdName = "Rd"; constexpr const char *RmName = "Rm"; IValueT Rd = encodeGPRegister(OpRd, RdName, ClzName); assert(Rd < RegARM32::getNumGPRegs()); verifyRegNotPc(Rd, RdName, ClzName); IValueT Rm = encodeGPRegister(OpSrc, RmName, ClzName); assert(Rm < RegARM32::getNumGPRegs()); verifyRegNotPc(Rm, RmName, ClzName); assert(CondARM32::isDefined(Cond)); constexpr IValueT PredefinedBits = B24 | B22 | B21 | (0xF << 16) | (0xf << 8) | B4; const IValueT Encoding = PredefinedBits | (Cond << kConditionShift) | (Rd << kRdShift) | (Rm << kRmShift); emitInst(Encoding); } void AssemblerARM32::cmn(const Operand *OpRn, const Operand *OpSrc1, CondARM32::Cond Cond) { // CMN (immediate) - ARM section A8.8.34, encoding A1: // cmn
, #
// // cccc00110111nnnn0000iiiiiiiiiiii where cccc=Cond, dddd=Rd, nnnn=Rn, // s=SetFlags and iiiiiiiiiiii=Src1Value defining RotatedImm8. // // CMN (register) - ARM section A8.8.35, encodeing A1: // cmn
,
{,
} // // cccc00010111nnnn0000iiiiitt0mmmm where cccc=Cond, nnnn=Rn, mmmm=Rm, // iiiii=Shift, and tt=ShiftKind. constexpr const char *CmnName = "cmn"; constexpr IValueT CmnOpcode = B3 | B1 | B0; // ie. 1011 emitCompareOp(Cond, CmnOpcode, OpRn, OpSrc1, CmnName); } void AssemblerARM32::cmp(const Operand *OpRn, const Operand *OpSrc1, CondARM32::Cond Cond) { // CMP (register) - ARM section A8.8.38, encoding A1: // cmp
,
{,
} // // cccc00010101nnnn0000iiiiitt0mmmm where cccc=Cond, nnnn=Rn, mmmm=Rm, // iiiii=Shift, and tt=ShiftKind. // // CMP (immediate) - ARM section A8.8.37 // cmp
, #
// // cccc00110101nnnn0000iiiiiiiiiiii where cccc=Cond, dddd=Rd, nnnn=Rn, // s=SetFlags and iiiiiiiiiiii=Src1Value defining RotatedImm8. constexpr const char *CmpName = "cmp"; constexpr IValueT CmpOpcode = B3 | B1; // ie. 1010 emitCompareOp(Cond, CmpOpcode, OpRn, OpSrc1, CmpName); } void AssemblerARM32::dmb(IValueT Option) { // DMB - ARM section A8.8.43, encoding A1: // dmb
// // 1111010101111111111100000101xxxx where xxxx=Option. assert(Utils::IsUint(4, Option) && "Bad dmb option"); const IValueT Encoding = (encodeCondition(CondARM32::kNone) << kConditionShift) | B26 | B24 | B22 | B21 | B20 | B19 | B18 | B17 | B16 | B15 | B14 | B13 | B12 | B6 | B4 | Option; emitInst(Encoding); } void AssemblerARM32::eor(const Operand *OpRd, const Operand *OpRn, const Operand *OpSrc1, bool SetFlags, CondARM32::Cond Cond) { // EOR (register) - ARM section A*.8.47, encoding A1: // eor{s}
,
,
{,
} // // cccc0000001snnnnddddiiiiitt0mmmm where cccc=Cond, dddd=Rd, nnnn=Rn, // mmmm=Rm, iiiii=Shift, tt=ShiftKind, and s=SetFlags. // // EOR (Immediate) - ARM section A8.*.46, encoding A1: // eor{s}
,
, #RotatedImm8 // // cccc0010001snnnnddddiiiiiiiiiiii where cccc=Cond, dddd=Rd, nnnn=Rn, // s=SetFlags and iiiiiiiiiiii=Src1Value defining RotatedImm8. constexpr const char *EorName = "eor"; constexpr IValueT EorOpcode = B0; // 0001 emitType01(Cond, EorOpcode, OpRd, OpRn, OpSrc1, SetFlags, RdIsPcAndSetFlags, EorName); } void AssemblerARM32::ldr(const Operand *OpRt, const Operand *OpAddress, CondARM32::Cond Cond, const TargetInfo &TInfo) { constexpr const char *LdrName = "ldr"; constexpr bool IsLoad = true; IValueT Rt = encodeGPRegister(OpRt, "Rt", LdrName); const Type Ty = OpRt->getType(); switch (Ty) { case IceType_i64: // LDRD is not implemented because target lowering handles i64 and double by // using two (32-bit) load instructions. Note: Intentionally drop to default // case. llvm::report_fatal_error(std::string("ldr : Type ") + typeString(Ty) + " not implemented"); default: llvm::report_fatal_error(std::string("ldr : Type ") + typeString(Ty) + " not allowed"); case IceType_i1: case IceType_i8: { // LDRB (immediate) - ARM section A8.8.68, encoding A1: // ldrb
, [
{, #+/-
}] ; p=1, w=0 // ldrb
, [
], #+/-
; p=1, w=1 // ldrb
, [
, #+/-
]! ; p=0, w=1 // // cccc010pu1w1nnnnttttiiiiiiiiiiii where cccc=Cond, tttt=Rt, nnnn=Rn, // iiiiiiiiiiii=imm12, u=1 if +, pu0w is a BlockAddr, and // pu0w0nnnn0000iiiiiiiiiiii=Address. // // LDRB (register) - ARM section A8.8.66, encoding A1: // ldrb
, [
, +/-
{,
}]{!} // ldrb
, [
], +/-
{,
} // // cccc011pu1w1nnnnttttiiiiiss0mmmm where cccc=Cond, tttt=Rt, U=1 if +, pu0b // is a BlockAddr, and pu0w0nnnn0000iiiiiss0mmmm=Address. constexpr bool IsByte = true; emitMemOp(Cond, IsLoad, IsByte, Rt, OpAddress, TInfo, LdrName); return; } case IceType_i16: { // LDRH (immediate) - ARM section A8.8.80, encoding A1: // ldrh
, [
{, #+/-
}] // ldrh
, [
], #+/-
// ldrh
, [
, #+/-
]! // // cccc000pu1w1nnnnttttiiii1011iiii where cccc=Cond, tttt=Rt, nnnn=Rn, // iiiiiiii=Imm8, u=1 if +, pu0w is a BlockAddr, and // pu0w0nnnn0000iiiiiiiiiiii=Address. constexpr const char *Ldrh = "ldrh"; emitMemOpEnc3(Cond, L | B7 | B5 | B4, Rt, OpAddress, TInfo, Ldrh); return; } case IceType_i32: { // LDR (immediate) - ARM section A8.8.63, encoding A1: // ldr
, [
{, #+/-
}] ; p=1, w=0 // ldr
, [
], #+/-
; p=1, w=1 // ldr
, [
, #+/-
]! ; p=0, w=1 // // cccc010pu0w1nnnnttttiiiiiiiiiiii where cccc=Cond, tttt=Rt, nnnn=Rn, // iiiiiiiiiiii=imm12, u=1 if +, pu0w is a BlockAddr, and // // LDR (register) - ARM section A8.8.70, encoding A1: // ldrb
, [
, +/-
{,
}]{!} // ldrb
, [
], +-
{,
} // // cccc011pu0w1nnnnttttiiiiiss0mmmm where cccc=Cond, tttt=Rt, U=1 if +, pu0b // is a BlockAddr, and pu0w0nnnn0000iiiiiss0mmmm=Address. constexpr bool IsByte = false; emitMemOp(Cond, IsLoad, IsByte, Rt, OpAddress, TInfo, LdrName); return; } } } void AssemblerARM32::emitMemExOp(CondARM32::Cond Cond, Type Ty, bool IsLoad, const Operand *OpRd, IValueT Rt, const Operand *OpAddress, const TargetInfo &TInfo, const char *InstName) { IValueT Rd = encodeGPRegister(OpRd, "Rd", InstName); IValueT MemExOpcode = IsLoad ? B0 : 0; switch (Ty) { default: llvm::report_fatal_error(std::string(InstName) + ": Type " + typeString(Ty) + " not allowed"); case IceType_i1: case IceType_i8: MemExOpcode |= B2; break; case IceType_i16: MemExOpcode |= B2 | B1; break; case IceType_i32: break; case IceType_i64: MemExOpcode |= B1; } IValueT AddressRn; if (encodeAddress(OpAddress, AddressRn, TInfo, NoImmOffsetAddress) != EncodedAsImmRegOffset) llvm::report_fatal_error(std::string(InstName) + ": Can't extract Rn from address"); assert(Utils::IsAbsoluteUint(3, MemExOpcode)); assert(Rd < RegARM32::getNumGPRegs()); assert(Rt < RegARM32::getNumGPRegs()); assert(CondARM32::isDefined(Cond)); IValueT Encoding = (Cond << kConditionShift) | B24 | B23 | B11 | B10 | B9 | B8 | B7 | B4 | (MemExOpcode << kMemExOpcodeShift) | AddressRn | (Rd << kRdShift) | (Rt << kRmShift); emitInst(Encoding); return; } void AssemblerARM32::ldrex(const Operand *OpRt, const Operand *OpAddress, CondARM32::Cond Cond, const TargetInfo &TInfo) { // LDREXB - ARM section A8.8.76, encoding A1: // ldrexb
, [
] // // cccc00011101nnnntttt111110011111 where cccc=Cond, tttt=Rt, and nnnn=Rn. // // LDREXH - ARM section A8.8.78, encoding A1: // ldrexh
, [
] // // cccc00011111nnnntttt111110011111 where cccc=Cond, tttt=Rt, and nnnn=Rn. // // LDREX - ARM section A8.8.75, encoding A1: // ldrex
, [
] // // cccc00011001nnnntttt111110011111 where cccc=Cond, tttt=Rt, and nnnn=Rn. // // LDREXD - ARM section A8. // ldrexd
, [
] // // cccc00011001nnnntttt111110011111 where cccc=Cond, tttt=Rt, and nnnn=Rn. constexpr const char *LdrexName = "ldrex"; const Type Ty = OpRt->getType(); constexpr bool IsLoad = true; constexpr IValueT Rm = RegARM32::Encoded_Reg_pc; emitMemExOp(Cond, Ty, IsLoad, OpRt, Rm, OpAddress, TInfo, LdrexName); } void AssemblerARM32::emitShift(const CondARM32::Cond Cond, const OperandARM32::ShiftKind Shift, const Operand *OpRd, const Operand *OpRm, const Operand *OpSrc1, const bool SetFlags, const char *InstName) { constexpr IValueT ShiftOpcode = B3 | B2 | B0; // 1101 IValueT Rd = encodeGPRegister(OpRd, "Rd", InstName); IValueT Rm = encodeGPRegister(OpRm, "Rm", InstName); IValueT Value; switch (encodeOperand(OpSrc1, Value, WantGPRegs)) { default: llvm::report_fatal_error(std::string(InstName) + ": Last operand not understood"); case EncodedAsShiftImm5: { // XXX (immediate) // xxx{s}
,
, #imm5 // // cccc0001101s0000ddddiiiii000mmmm where cccc=Cond, s=SetFlags, dddd=Rd, // iiiii=imm5, and mmmm=Rm. constexpr IValueT Rn = 0; // Rn field is not used. Value = Value | (Rm << kRmShift) | (Shift << kShiftShift); emitType01(Cond, kInstTypeDataRegShift, ShiftOpcode, SetFlags, Rn, Rd, Value, RdIsPcAndSetFlags, InstName); return; } case EncodedAsRegister: { // XXX (register) // xxx{S}