| //==-- AArch64InstPrinter.cpp - Convert AArch64 MCInst to assembly syntax --==// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This class prints an AArch64 MCInst to a .s file. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #define DEBUG_TYPE "asm-printer" |
| #include "AArch64InstPrinter.h" |
| #include "MCTargetDesc/AArch64MCTargetDesc.h" |
| #include "Utils/AArch64BaseInfo.h" |
| #include "llvm/MC/MCInst.h" |
| #include "llvm/MC/MCExpr.h" |
| #include "llvm/MC/MCRegisterInfo.h" |
| #include "llvm/Support/ErrorHandling.h" |
| #include "llvm/Support/Format.h" |
| #include "llvm/Support/raw_ostream.h" |
| |
| using namespace llvm; |
| |
| #define GET_INSTRUCTION_NAME |
| #define PRINT_ALIAS_INSTR |
| #include "AArch64GenAsmWriter.inc" |
| |
| static int64_t unpackSignedImm(int BitWidth, uint64_t Value) { |
| assert(!(Value & ~((1ULL << BitWidth)-1)) && "immediate not n-bit"); |
| if (Value & (1ULL << (BitWidth - 1))) |
| return static_cast<int64_t>(Value) - (1LL << BitWidth); |
| else |
| return Value; |
| } |
| |
| AArch64InstPrinter::AArch64InstPrinter(const MCAsmInfo &MAI, |
| const MCInstrInfo &MII, |
| const MCRegisterInfo &MRI, |
| const MCSubtargetInfo &STI) : |
| MCInstPrinter(MAI, MII, MRI) { |
| // Initialize the set of available features. |
| setAvailableFeatures(STI.getFeatureBits()); |
| } |
| |
| void AArch64InstPrinter::printRegName(raw_ostream &OS, unsigned RegNo) const { |
| OS << getRegisterName(RegNo); |
| } |
| |
| void |
| AArch64InstPrinter::printOffsetSImm9Operand(const MCInst *MI, |
| unsigned OpNum, raw_ostream &O) { |
| const MCOperand &MOImm = MI->getOperand(OpNum); |
| int32_t Imm = unpackSignedImm(9, MOImm.getImm()); |
| |
| O << '#' << Imm; |
| } |
| |
| void |
| AArch64InstPrinter::printAddrRegExtendOperand(const MCInst *MI, unsigned OpNum, |
| raw_ostream &O, unsigned MemSize, |
| unsigned RmSize) { |
| unsigned ExtImm = MI->getOperand(OpNum).getImm(); |
| unsigned OptionHi = ExtImm >> 1; |
| unsigned S = ExtImm & 1; |
| bool IsLSL = OptionHi == 1 && RmSize == 64; |
| |
| const char *Ext; |
| switch (OptionHi) { |
| case 1: |
| Ext = (RmSize == 32) ? "uxtw" : "lsl"; |
| break; |
| case 3: |
| Ext = (RmSize == 32) ? "sxtw" : "sxtx"; |
| break; |
| default: |
| llvm_unreachable("Incorrect Option on load/store (reg offset)"); |
| } |
| O << Ext; |
| |
| if (S) { |
| unsigned ShiftAmt = Log2_32(MemSize); |
| O << " #" << ShiftAmt; |
| } else if (IsLSL) { |
| O << " #0"; |
| } |
| } |
| |
| void |
| AArch64InstPrinter::printAddSubImmLSL0Operand(const MCInst *MI, |
| unsigned OpNum, raw_ostream &O) { |
| const MCOperand &Imm12Op = MI->getOperand(OpNum); |
| |
| if (Imm12Op.isImm()) { |
| int64_t Imm12 = Imm12Op.getImm(); |
| assert(Imm12 >= 0 && "Invalid immediate for add/sub imm"); |
| O << "#" << Imm12; |
| } else { |
| assert(Imm12Op.isExpr() && "Unexpected shift operand type"); |
| O << "#" << *Imm12Op.getExpr(); |
| } |
| } |
| |
| void |
| AArch64InstPrinter::printAddSubImmLSL12Operand(const MCInst *MI, unsigned OpNum, |
| raw_ostream &O) { |
| |
| printAddSubImmLSL0Operand(MI, OpNum, O); |
| |
| O << ", lsl #12"; |
| } |
| |
| void |
| AArch64InstPrinter::printBareImmOperand(const MCInst *MI, unsigned OpNum, |
| raw_ostream &O) { |
| const MCOperand &MO = MI->getOperand(OpNum); |
| O << MO.getImm(); |
| } |
| |
| template<unsigned RegWidth> void |
| AArch64InstPrinter::printBFILSBOperand(const MCInst *MI, unsigned OpNum, |
| raw_ostream &O) { |
| const MCOperand &ImmROp = MI->getOperand(OpNum); |
| unsigned LSB = ImmROp.getImm() == 0 ? 0 : RegWidth - ImmROp.getImm(); |
| |
| O << '#' << LSB; |
| } |
| |
| void AArch64InstPrinter::printBFIWidthOperand(const MCInst *MI, unsigned OpNum, |
| raw_ostream &O) { |
| const MCOperand &ImmSOp = MI->getOperand(OpNum); |
| unsigned Width = ImmSOp.getImm() + 1; |
| |
| O << '#' << Width; |
| } |
| |
| void |
| AArch64InstPrinter::printBFXWidthOperand(const MCInst *MI, unsigned OpNum, |
| raw_ostream &O) { |
| const MCOperand &ImmSOp = MI->getOperand(OpNum); |
| const MCOperand &ImmROp = MI->getOperand(OpNum - 1); |
| |
| unsigned ImmR = ImmROp.getImm(); |
| unsigned ImmS = ImmSOp.getImm(); |
| |
| assert(ImmS >= ImmR && "Invalid ImmR, ImmS combination for bitfield extract"); |
| |
| O << '#' << (ImmS - ImmR + 1); |
| } |
| |
| void |
| AArch64InstPrinter::printCRxOperand(const MCInst *MI, unsigned OpNum, |
| raw_ostream &O) { |
| const MCOperand &CRx = MI->getOperand(OpNum); |
| |
| O << 'c' << CRx.getImm(); |
| } |
| |
| |
| void |
| AArch64InstPrinter::printCVTFixedPosOperand(const MCInst *MI, unsigned OpNum, |
| raw_ostream &O) { |
| const MCOperand &ScaleOp = MI->getOperand(OpNum); |
| |
| O << '#' << (64 - ScaleOp.getImm()); |
| } |
| |
| |
| void AArch64InstPrinter::printFPImmOperand(const MCInst *MI, unsigned OpNum, |
| raw_ostream &o) { |
| const MCOperand &MOImm8 = MI->getOperand(OpNum); |
| |
| assert(MOImm8.isImm() |
| && "Immediate operand required for floating-point immediate inst"); |
| |
| uint32_t Imm8 = MOImm8.getImm(); |
| uint32_t Fraction = Imm8 & 0xf; |
| uint32_t Exponent = (Imm8 >> 4) & 0x7; |
| uint32_t Negative = (Imm8 >> 7) & 0x1; |
| |
| float Val = 1.0f + Fraction / 16.0f; |
| |
| // That is: |
| // 000 -> 2^1, 001 -> 2^2, 010 -> 2^3, 011 -> 2^4, |
| // 100 -> 2^-3, 101 -> 2^-2, 110 -> 2^-1, 111 -> 2^0 |
| if (Exponent & 0x4) { |
| Val /= 1 << (7 - Exponent); |
| } else { |
| Val *= 1 << (Exponent + 1); |
| } |
| |
| Val = Negative ? -Val : Val; |
| |
| o << '#' << format("%.8f", Val); |
| } |
| |
| void AArch64InstPrinter::printFPZeroOperand(const MCInst *MI, unsigned OpNum, |
| raw_ostream &o) { |
| o << "#0.0"; |
| } |
| |
| void |
| AArch64InstPrinter::printCondCodeOperand(const MCInst *MI, unsigned OpNum, |
| raw_ostream &O) { |
| const MCOperand &MO = MI->getOperand(OpNum); |
| |
| O << A64CondCodeToString(static_cast<A64CC::CondCodes>(MO.getImm())); |
| } |
| |
| template <unsigned field_width, unsigned scale> void |
| AArch64InstPrinter::printLabelOperand(const MCInst *MI, unsigned OpNum, |
| raw_ostream &O) { |
| const MCOperand &MO = MI->getOperand(OpNum); |
| |
| if (!MO.isImm()) { |
| printOperand(MI, OpNum, O); |
| return; |
| } |
| |
| // The immediate of LDR (lit) instructions is a signed 19-bit immediate, which |
| // is multiplied by 4 (because all A64 instructions are 32-bits wide). |
| uint64_t UImm = MO.getImm(); |
| uint64_t Sign = UImm & (1LL << (field_width - 1)); |
| int64_t SImm = scale * ((UImm & ~Sign) - Sign); |
| |
| O << "#" << SImm; |
| } |
| |
| template<unsigned RegWidth> void |
| AArch64InstPrinter::printLogicalImmOperand(const MCInst *MI, unsigned OpNum, |
| raw_ostream &O) { |
| const MCOperand &MO = MI->getOperand(OpNum); |
| uint64_t Val; |
| A64Imms::isLogicalImmBits(RegWidth, MO.getImm(), Val); |
| O << "#0x"; |
| O.write_hex(Val); |
| } |
| |
| void |
| AArch64InstPrinter::printOffsetUImm12Operand(const MCInst *MI, unsigned OpNum, |
| raw_ostream &O, int MemSize) { |
| const MCOperand &MOImm = MI->getOperand(OpNum); |
| |
| if (MOImm.isImm()) { |
| uint32_t Imm = MOImm.getImm() * MemSize; |
| |
| O << "#" << Imm; |
| } else { |
| O << "#" << *MOImm.getExpr(); |
| } |
| } |
| |
| void |
| AArch64InstPrinter::printShiftOperand(const MCInst *MI, unsigned OpNum, |
| raw_ostream &O, |
| A64SE::ShiftExtSpecifiers Shift) { |
| const MCOperand &MO = MI->getOperand(OpNum); |
| |
| // LSL #0 is not printed |
| if (Shift == A64SE::LSL && MO.isImm() && MO.getImm() == 0) |
| return; |
| |
| switch (Shift) { |
| case A64SE::LSL: O << "lsl"; break; |
| case A64SE::LSR: O << "lsr"; break; |
| case A64SE::ASR: O << "asr"; break; |
| case A64SE::ROR: O << "ror"; break; |
| default: llvm_unreachable("Invalid shift specifier in logical instruction"); |
| } |
| |
| O << " #" << MO.getImm(); |
| } |
| |
| void |
| AArch64InstPrinter::printMoveWideImmOperand(const MCInst *MI, unsigned OpNum, |
| raw_ostream &O) { |
| const MCOperand &UImm16MO = MI->getOperand(OpNum); |
| const MCOperand &ShiftMO = MI->getOperand(OpNum + 1); |
| |
| if (UImm16MO.isImm()) { |
| O << '#' << UImm16MO.getImm(); |
| |
| if (ShiftMO.getImm() != 0) |
| O << ", lsl #" << (ShiftMO.getImm() * 16); |
| |
| return; |
| } |
| |
| O << "#" << *UImm16MO.getExpr(); |
| } |
| |
| void AArch64InstPrinter::printNamedImmOperand(const NamedImmMapper &Mapper, |
| const MCInst *MI, unsigned OpNum, |
| raw_ostream &O) { |
| bool ValidName; |
| const MCOperand &MO = MI->getOperand(OpNum); |
| StringRef Name = Mapper.toString(MO.getImm(), ValidName); |
| |
| if (ValidName) |
| O << Name; |
| else |
| O << '#' << MO.getImm(); |
| } |
| |
| void |
| AArch64InstPrinter::printSysRegOperand(const A64SysReg::SysRegMapper &Mapper, |
| const MCInst *MI, unsigned OpNum, |
| raw_ostream &O) { |
| const MCOperand &MO = MI->getOperand(OpNum); |
| |
| bool ValidName; |
| std::string Name = Mapper.toString(MO.getImm(), ValidName); |
| if (ValidName) { |
| O << Name; |
| return; |
| } |
| } |
| |
| |
| void AArch64InstPrinter::printRegExtendOperand(const MCInst *MI, |
| unsigned OpNum, |
| raw_ostream &O, |
| A64SE::ShiftExtSpecifiers Ext) { |
| // FIXME: In principle TableGen should be able to detect this itself far more |
| // easily. We will only accumulate more of these hacks. |
| unsigned Reg0 = MI->getOperand(0).getReg(); |
| unsigned Reg1 = MI->getOperand(1).getReg(); |
| |
| if (isStackReg(Reg0) || isStackReg(Reg1)) { |
| A64SE::ShiftExtSpecifiers LSLEquiv; |
| |
| if (Reg0 == AArch64::XSP || Reg1 == AArch64::XSP) |
| LSLEquiv = A64SE::UXTX; |
| else |
| LSLEquiv = A64SE::UXTW; |
| |
| if (Ext == LSLEquiv) { |
| O << "lsl #" << MI->getOperand(OpNum).getImm(); |
| return; |
| } |
| } |
| |
| switch (Ext) { |
| case A64SE::UXTB: O << "uxtb"; break; |
| case A64SE::UXTH: O << "uxth"; break; |
| case A64SE::UXTW: O << "uxtw"; break; |
| case A64SE::UXTX: O << "uxtx"; break; |
| case A64SE::SXTB: O << "sxtb"; break; |
| case A64SE::SXTH: O << "sxth"; break; |
| case A64SE::SXTW: O << "sxtw"; break; |
| case A64SE::SXTX: O << "sxtx"; break; |
| default: llvm_unreachable("Unexpected shift type for printing"); |
| } |
| |
| const MCOperand &MO = MI->getOperand(OpNum); |
| if (MO.getImm() != 0) |
| O << " #" << MO.getImm(); |
| } |
| |
| template<int MemScale> void |
| AArch64InstPrinter::printSImm7ScaledOperand(const MCInst *MI, unsigned OpNum, |
| raw_ostream &O) { |
| const MCOperand &MOImm = MI->getOperand(OpNum); |
| int32_t Imm = unpackSignedImm(7, MOImm.getImm()); |
| |
| O << "#" << (Imm * MemScale); |
| } |
| |
| void AArch64InstPrinter::printOperand(const MCInst *MI, unsigned OpNo, |
| raw_ostream &O) { |
| const MCOperand &Op = MI->getOperand(OpNo); |
| if (Op.isReg()) { |
| unsigned Reg = Op.getReg(); |
| O << getRegisterName(Reg); |
| } else if (Op.isImm()) { |
| O << '#' << Op.getImm(); |
| } else { |
| assert(Op.isExpr() && "unknown operand kind in printOperand"); |
| // If a symbolic branch target was added as a constant expression then print |
| // that address in hex. |
| const MCConstantExpr *BranchTarget = dyn_cast<MCConstantExpr>(Op.getExpr()); |
| int64_t Address; |
| if (BranchTarget && BranchTarget->EvaluateAsAbsolute(Address)) { |
| O << "0x"; |
| O.write_hex(Address); |
| } |
| else { |
| // Otherwise, just print the expression. |
| O << *Op.getExpr(); |
| } |
| } |
| } |
| |
| |
| void AArch64InstPrinter::printInst(const MCInst *MI, raw_ostream &O, |
| StringRef Annot) { |
| if (MI->getOpcode() == AArch64::TLSDESCCALL) { |
| // This is a special assembler directive which applies an |
| // R_AARCH64_TLSDESC_CALL to the following (BLR) instruction. It has a fixed |
| // form outside the normal TableGenerated scheme. |
| O << "\t.tlsdesccall " << *MI->getOperand(0).getExpr(); |
| } else if (!printAliasInstr(MI, O)) |
| printInstruction(MI, O); |
| |
| printAnnotation(O, Annot); |
| } |