| //===-- ARMInstPrinter.cpp - Convert ARM 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 ARM MCInst to a .s file. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "ARMInstPrinter.h" | |
| #include "Utils/ARMBaseInfo.h" | |
| #include "MCTargetDesc/ARMAddressingModes.h" | |
| #include "MCTargetDesc/ARMBaseInfo.h" | |
| #include "llvm/MC/MCAsmInfo.h" | |
| #include "llvm/MC/MCExpr.h" | |
| #include "llvm/MC/MCInst.h" | |
| #include "llvm/MC/MCInstrInfo.h" | |
| #include "llvm/MC/MCRegisterInfo.h" | |
| #include "llvm/MC/MCSubtargetInfo.h" | |
| #include "llvm/MC/SubtargetFeature.h" | |
| #include "llvm/Support/Casting.h" | |
| #include "llvm/Support/ErrorHandling.h" | |
| #include "llvm/Support/MathExtras.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| #include <algorithm> | |
| #include <cassert> | |
| #include <cstdint> | |
| using namespace llvm; | |
| #define DEBUG_TYPE "asm-printer" | |
| #define PRINT_ALIAS_INSTR | |
| #include "ARMGenAsmWriter.inc" | |
| /// translateShiftImm - Convert shift immediate from 0-31 to 1-32 for printing. | |
| /// | |
| /// getSORegOffset returns an integer from 0-31, representing '32' as 0. | |
| static unsigned translateShiftImm(unsigned imm) { | |
| // lsr #32 and asr #32 exist, but should be encoded as a 0. | |
| assert((imm & ~0x1f) == 0 && "Invalid shift encoding"); | |
| if (imm == 0) | |
| return 32; | |
| return imm; | |
| } | |
| /// Prints the shift value with an immediate value. | |
| static void printRegImmShift(raw_ostream &O, ARM_AM::ShiftOpc ShOpc, | |
| unsigned ShImm, bool UseMarkup) { | |
| if (ShOpc == ARM_AM::no_shift || (ShOpc == ARM_AM::lsl && !ShImm)) | |
| return; | |
| O << ", "; | |
| assert(!(ShOpc == ARM_AM::ror && !ShImm) && "Cannot have ror #0"); | |
| O << getShiftOpcStr(ShOpc); | |
| if (ShOpc != ARM_AM::rrx) { | |
| O << " "; | |
| if (UseMarkup) | |
| O << "<imm:"; | |
| O << "#" << translateShiftImm(ShImm); | |
| if (UseMarkup) | |
| O << ">"; | |
| } | |
| } | |
| ARMInstPrinter::ARMInstPrinter(const MCAsmInfo &MAI, const MCInstrInfo &MII, | |
| const MCRegisterInfo &MRI) | |
| : MCInstPrinter(MAI, MII, MRI) {} | |
| void ARMInstPrinter::printRegName(raw_ostream &OS, unsigned RegNo) const { | |
| OS << markup("<reg:") << getRegisterName(RegNo) << markup(">"); | |
| } | |
| void ARMInstPrinter::printInst(const MCInst *MI, raw_ostream &O, | |
| StringRef Annot, const MCSubtargetInfo &STI) { | |
| unsigned Opcode = MI->getOpcode(); | |
| switch (Opcode) { | |
| // Check for MOVs and print canonical forms, instead. | |
| case ARM::MOVsr: { | |
| // FIXME: Thumb variants? | |
| const MCOperand &Dst = MI->getOperand(0); | |
| const MCOperand &MO1 = MI->getOperand(1); | |
| const MCOperand &MO2 = MI->getOperand(2); | |
| const MCOperand &MO3 = MI->getOperand(3); | |
| O << '\t' << ARM_AM::getShiftOpcStr(ARM_AM::getSORegShOp(MO3.getImm())); | |
| printSBitModifierOperand(MI, 6, STI, O); | |
| printPredicateOperand(MI, 4, STI, O); | |
| O << '\t'; | |
| printRegName(O, Dst.getReg()); | |
| O << ", "; | |
| printRegName(O, MO1.getReg()); | |
| O << ", "; | |
| printRegName(O, MO2.getReg()); | |
| assert(ARM_AM::getSORegOffset(MO3.getImm()) == 0); | |
| printAnnotation(O, Annot); | |
| return; | |
| } | |
| case ARM::MOVsi: { | |
| // FIXME: Thumb variants? | |
| const MCOperand &Dst = MI->getOperand(0); | |
| const MCOperand &MO1 = MI->getOperand(1); | |
| const MCOperand &MO2 = MI->getOperand(2); | |
| O << '\t' << ARM_AM::getShiftOpcStr(ARM_AM::getSORegShOp(MO2.getImm())); | |
| printSBitModifierOperand(MI, 5, STI, O); | |
| printPredicateOperand(MI, 3, STI, O); | |
| O << '\t'; | |
| printRegName(O, Dst.getReg()); | |
| O << ", "; | |
| printRegName(O, MO1.getReg()); | |
| if (ARM_AM::getSORegShOp(MO2.getImm()) == ARM_AM::rrx) { | |
| printAnnotation(O, Annot); | |
| return; | |
| } | |
| O << ", " << markup("<imm:") << "#" | |
| << translateShiftImm(ARM_AM::getSORegOffset(MO2.getImm())) << markup(">"); | |
| printAnnotation(O, Annot); | |
| return; | |
| } | |
| // A8.6.123 PUSH | |
| case ARM::STMDB_UPD: | |
| case ARM::t2STMDB_UPD: | |
| if (MI->getOperand(0).getReg() == ARM::SP && MI->getNumOperands() > 5) { | |
| // Should only print PUSH if there are at least two registers in the list. | |
| O << '\t' << "push"; | |
| printPredicateOperand(MI, 2, STI, O); | |
| if (Opcode == ARM::t2STMDB_UPD) | |
| O << ".w"; | |
| O << '\t'; | |
| printRegisterList(MI, 4, STI, O); | |
| printAnnotation(O, Annot); | |
| return; | |
| } else | |
| break; | |
| case ARM::STR_PRE_IMM: | |
| if (MI->getOperand(2).getReg() == ARM::SP && | |
| MI->getOperand(3).getImm() == -4) { | |
| O << '\t' << "push"; | |
| printPredicateOperand(MI, 4, STI, O); | |
| O << "\t{"; | |
| printRegName(O, MI->getOperand(1).getReg()); | |
| O << "}"; | |
| printAnnotation(O, Annot); | |
| return; | |
| } else | |
| break; | |
| // A8.6.122 POP | |
| case ARM::LDMIA_UPD: | |
| case ARM::t2LDMIA_UPD: | |
| if (MI->getOperand(0).getReg() == ARM::SP && MI->getNumOperands() > 5) { | |
| // Should only print POP if there are at least two registers in the list. | |
| O << '\t' << "pop"; | |
| printPredicateOperand(MI, 2, STI, O); | |
| if (Opcode == ARM::t2LDMIA_UPD) | |
| O << ".w"; | |
| O << '\t'; | |
| printRegisterList(MI, 4, STI, O); | |
| printAnnotation(O, Annot); | |
| return; | |
| } else | |
| break; | |
| case ARM::LDR_POST_IMM: | |
| if (MI->getOperand(2).getReg() == ARM::SP && | |
| MI->getOperand(4).getImm() == 4) { | |
| O << '\t' << "pop"; | |
| printPredicateOperand(MI, 5, STI, O); | |
| O << "\t{"; | |
| printRegName(O, MI->getOperand(0).getReg()); | |
| O << "}"; | |
| printAnnotation(O, Annot); | |
| return; | |
| } else | |
| break; | |
| // A8.6.355 VPUSH | |
| case ARM::VSTMSDB_UPD: | |
| case ARM::VSTMDDB_UPD: | |
| if (MI->getOperand(0).getReg() == ARM::SP) { | |
| O << '\t' << "vpush"; | |
| printPredicateOperand(MI, 2, STI, O); | |
| O << '\t'; | |
| printRegisterList(MI, 4, STI, O); | |
| printAnnotation(O, Annot); | |
| return; | |
| } else | |
| break; | |
| // A8.6.354 VPOP | |
| case ARM::VLDMSIA_UPD: | |
| case ARM::VLDMDIA_UPD: | |
| if (MI->getOperand(0).getReg() == ARM::SP) { | |
| O << '\t' << "vpop"; | |
| printPredicateOperand(MI, 2, STI, O); | |
| O << '\t'; | |
| printRegisterList(MI, 4, STI, O); | |
| printAnnotation(O, Annot); | |
| return; | |
| } else | |
| break; | |
| case ARM::tLDMIA: { | |
| bool Writeback = true; | |
| unsigned BaseReg = MI->getOperand(0).getReg(); | |
| for (unsigned i = 3; i < MI->getNumOperands(); ++i) { | |
| if (MI->getOperand(i).getReg() == BaseReg) | |
| Writeback = false; | |
| } | |
| O << "\tldm"; | |
| printPredicateOperand(MI, 1, STI, O); | |
| O << '\t'; | |
| printRegName(O, BaseReg); | |
| if (Writeback) | |
| O << "!"; | |
| O << ", "; | |
| printRegisterList(MI, 3, STI, O); | |
| printAnnotation(O, Annot); | |
| return; | |
| } | |
| // Combine 2 GPRs from disassember into a GPRPair to match with instr def. | |
| // ldrexd/strexd require even/odd GPR pair. To enforce this constraint, | |
| // a single GPRPair reg operand is used in the .td file to replace the two | |
| // GPRs. However, when decoding them, the two GRPs cannot be automatically | |
| // expressed as a GPRPair, so we have to manually merge them. | |
| // FIXME: We would really like to be able to tablegen'erate this. | |
| case ARM::LDREXD: | |
| case ARM::STREXD: | |
| case ARM::LDAEXD: | |
| case ARM::STLEXD: { | |
| const MCRegisterClass &MRC = MRI.getRegClass(ARM::GPRRegClassID); | |
| bool isStore = Opcode == ARM::STREXD || Opcode == ARM::STLEXD; | |
| unsigned Reg = MI->getOperand(isStore ? 1 : 0).getReg(); | |
| if (MRC.contains(Reg)) { | |
| MCInst NewMI; | |
| MCOperand NewReg; | |
| NewMI.setOpcode(Opcode); | |
| if (isStore) | |
| NewMI.addOperand(MI->getOperand(0)); | |
| NewReg = MCOperand::createReg(MRI.getMatchingSuperReg( | |
| Reg, ARM::gsub_0, &MRI.getRegClass(ARM::GPRPairRegClassID))); | |
| NewMI.addOperand(NewReg); | |
| // Copy the rest operands into NewMI. | |
| for (unsigned i = isStore ? 3 : 2; i < MI->getNumOperands(); ++i) | |
| NewMI.addOperand(MI->getOperand(i)); | |
| printInstruction(&NewMI, STI, O); | |
| return; | |
| } | |
| break; | |
| } | |
| case ARM::TSB: | |
| case ARM::t2TSB: | |
| O << "\ttsb\tcsync"; | |
| return; | |
| } | |
| if (!printAliasInstr(MI, STI, O)) | |
| printInstruction(MI, STI, O); | |
| printAnnotation(O, Annot); | |
| } | |
| void ARMInstPrinter::printOperand(const MCInst *MI, unsigned OpNo, | |
| const MCSubtargetInfo &STI, raw_ostream &O) { | |
| const MCOperand &Op = MI->getOperand(OpNo); | |
| if (Op.isReg()) { | |
| unsigned Reg = Op.getReg(); | |
| printRegName(O, Reg); | |
| } else if (Op.isImm()) { | |
| O << markup("<imm:") << '#' << formatImm(Op.getImm()) << markup(">"); | |
| } else { | |
| assert(Op.isExpr() && "unknown operand kind in printOperand"); | |
| const MCExpr *Expr = Op.getExpr(); | |
| switch (Expr->getKind()) { | |
| case MCExpr::Binary: | |
| O << '#'; | |
| Expr->print(O, &MAI); | |
| break; | |
| case MCExpr::Constant: { | |
| // If a symbolic branch target was added as a constant expression then | |
| // print that address in hex. And only print 32 unsigned bits for the | |
| // address. | |
| const MCConstantExpr *Constant = cast<MCConstantExpr>(Expr); | |
| int64_t TargetAddress; | |
| if (!Constant->evaluateAsAbsolute(TargetAddress)) { | |
| O << '#'; | |
| Expr->print(O, &MAI); | |
| } else { | |
| O << "0x"; | |
| O.write_hex(static_cast<uint32_t>(TargetAddress)); | |
| } | |
| break; | |
| } | |
| default: | |
| // FIXME: Should we always treat this as if it is a constant literal and | |
| // prefix it with '#'? | |
| Expr->print(O, &MAI); | |
| break; | |
| } | |
| } | |
| } | |
| void ARMInstPrinter::printThumbLdrLabelOperand(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| const MCOperand &MO1 = MI->getOperand(OpNum); | |
| if (MO1.isExpr()) { | |
| MO1.getExpr()->print(O, &MAI); | |
| return; | |
| } | |
| O << markup("<mem:") << "[pc, "; | |
| int32_t OffImm = (int32_t)MO1.getImm(); | |
| bool isSub = OffImm < 0; | |
| // Special value for #-0. All others are normal. | |
| if (OffImm == INT32_MIN) | |
| OffImm = 0; | |
| if (isSub) { | |
| O << markup("<imm:") << "#-" << formatImm(-OffImm) << markup(">"); | |
| } else { | |
| O << markup("<imm:") << "#" << formatImm(OffImm) << markup(">"); | |
| } | |
| O << "]" << markup(">"); | |
| } | |
| // so_reg is a 4-operand unit corresponding to register forms of the A5.1 | |
| // "Addressing Mode 1 - Data-processing operands" forms. This includes: | |
| // REG 0 0 - e.g. R5 | |
| // REG REG 0,SH_OPC - e.g. R5, ROR R3 | |
| // REG 0 IMM,SH_OPC - e.g. R5, LSL #3 | |
| void ARMInstPrinter::printSORegRegOperand(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| const MCOperand &MO1 = MI->getOperand(OpNum); | |
| const MCOperand &MO2 = MI->getOperand(OpNum + 1); | |
| const MCOperand &MO3 = MI->getOperand(OpNum + 2); | |
| printRegName(O, MO1.getReg()); | |
| // Print the shift opc. | |
| ARM_AM::ShiftOpc ShOpc = ARM_AM::getSORegShOp(MO3.getImm()); | |
| O << ", " << ARM_AM::getShiftOpcStr(ShOpc); | |
| if (ShOpc == ARM_AM::rrx) | |
| return; | |
| O << ' '; | |
| printRegName(O, MO2.getReg()); | |
| assert(ARM_AM::getSORegOffset(MO3.getImm()) == 0); | |
| } | |
| void ARMInstPrinter::printSORegImmOperand(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| const MCOperand &MO1 = MI->getOperand(OpNum); | |
| const MCOperand &MO2 = MI->getOperand(OpNum + 1); | |
| printRegName(O, MO1.getReg()); | |
| // Print the shift opc. | |
| printRegImmShift(O, ARM_AM::getSORegShOp(MO2.getImm()), | |
| ARM_AM::getSORegOffset(MO2.getImm()), UseMarkup); | |
| } | |
| //===--------------------------------------------------------------------===// | |
| // Addressing Mode #2 | |
| //===--------------------------------------------------------------------===// | |
| void ARMInstPrinter::printAM2PreOrOffsetIndexOp(const MCInst *MI, unsigned Op, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| const MCOperand &MO1 = MI->getOperand(Op); | |
| const MCOperand &MO2 = MI->getOperand(Op + 1); | |
| const MCOperand &MO3 = MI->getOperand(Op + 2); | |
| O << markup("<mem:") << "["; | |
| printRegName(O, MO1.getReg()); | |
| if (!MO2.getReg()) { | |
| if (ARM_AM::getAM2Offset(MO3.getImm())) { // Don't print +0. | |
| O << ", " << markup("<imm:") << "#" | |
| << ARM_AM::getAddrOpcStr(ARM_AM::getAM2Op(MO3.getImm())) | |
| << ARM_AM::getAM2Offset(MO3.getImm()) << markup(">"); | |
| } | |
| O << "]" << markup(">"); | |
| return; | |
| } | |
| O << ", "; | |
| O << ARM_AM::getAddrOpcStr(ARM_AM::getAM2Op(MO3.getImm())); | |
| printRegName(O, MO2.getReg()); | |
| printRegImmShift(O, ARM_AM::getAM2ShiftOpc(MO3.getImm()), | |
| ARM_AM::getAM2Offset(MO3.getImm()), UseMarkup); | |
| O << "]" << markup(">"); | |
| } | |
| void ARMInstPrinter::printAddrModeTBB(const MCInst *MI, unsigned Op, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| const MCOperand &MO1 = MI->getOperand(Op); | |
| const MCOperand &MO2 = MI->getOperand(Op + 1); | |
| O << markup("<mem:") << "["; | |
| printRegName(O, MO1.getReg()); | |
| O << ", "; | |
| printRegName(O, MO2.getReg()); | |
| O << "]" << markup(">"); | |
| } | |
| void ARMInstPrinter::printAddrModeTBH(const MCInst *MI, unsigned Op, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| const MCOperand &MO1 = MI->getOperand(Op); | |
| const MCOperand &MO2 = MI->getOperand(Op + 1); | |
| O << markup("<mem:") << "["; | |
| printRegName(O, MO1.getReg()); | |
| O << ", "; | |
| printRegName(O, MO2.getReg()); | |
| O << ", lsl " << markup("<imm:") << "#1" << markup(">") << "]" << markup(">"); | |
| } | |
| void ARMInstPrinter::printAddrMode2Operand(const MCInst *MI, unsigned Op, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| const MCOperand &MO1 = MI->getOperand(Op); | |
| if (!MO1.isReg()) { // FIXME: This is for CP entries, but isn't right. | |
| printOperand(MI, Op, STI, O); | |
| return; | |
| } | |
| #ifndef NDEBUG | |
| const MCOperand &MO3 = MI->getOperand(Op + 2); | |
| unsigned IdxMode = ARM_AM::getAM2IdxMode(MO3.getImm()); | |
| assert(IdxMode != ARMII::IndexModePost && "Should be pre or offset index op"); | |
| #endif | |
| printAM2PreOrOffsetIndexOp(MI, Op, STI, O); | |
| } | |
| void ARMInstPrinter::printAddrMode2OffsetOperand(const MCInst *MI, | |
| unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| const MCOperand &MO1 = MI->getOperand(OpNum); | |
| const MCOperand &MO2 = MI->getOperand(OpNum + 1); | |
| if (!MO1.getReg()) { | |
| unsigned ImmOffs = ARM_AM::getAM2Offset(MO2.getImm()); | |
| O << markup("<imm:") << '#' | |
| << ARM_AM::getAddrOpcStr(ARM_AM::getAM2Op(MO2.getImm())) << ImmOffs | |
| << markup(">"); | |
| return; | |
| } | |
| O << ARM_AM::getAddrOpcStr(ARM_AM::getAM2Op(MO2.getImm())); | |
| printRegName(O, MO1.getReg()); | |
| printRegImmShift(O, ARM_AM::getAM2ShiftOpc(MO2.getImm()), | |
| ARM_AM::getAM2Offset(MO2.getImm()), UseMarkup); | |
| } | |
| //===--------------------------------------------------------------------===// | |
| // Addressing Mode #3 | |
| //===--------------------------------------------------------------------===// | |
| void ARMInstPrinter::printAM3PreOrOffsetIndexOp(const MCInst *MI, unsigned Op, | |
| raw_ostream &O, | |
| bool AlwaysPrintImm0) { | |
| const MCOperand &MO1 = MI->getOperand(Op); | |
| const MCOperand &MO2 = MI->getOperand(Op + 1); | |
| const MCOperand &MO3 = MI->getOperand(Op + 2); | |
| O << markup("<mem:") << '['; | |
| printRegName(O, MO1.getReg()); | |
| if (MO2.getReg()) { | |
| O << ", " << getAddrOpcStr(ARM_AM::getAM3Op(MO3.getImm())); | |
| printRegName(O, MO2.getReg()); | |
| O << ']' << markup(">"); | |
| return; | |
| } | |
| // If the op is sub we have to print the immediate even if it is 0 | |
| unsigned ImmOffs = ARM_AM::getAM3Offset(MO3.getImm()); | |
| ARM_AM::AddrOpc op = ARM_AM::getAM3Op(MO3.getImm()); | |
| if (AlwaysPrintImm0 || ImmOffs || (op == ARM_AM::sub)) { | |
| O << ", " << markup("<imm:") << "#" << ARM_AM::getAddrOpcStr(op) << ImmOffs | |
| << markup(">"); | |
| } | |
| O << ']' << markup(">"); | |
| } | |
| template <bool AlwaysPrintImm0> | |
| void ARMInstPrinter::printAddrMode3Operand(const MCInst *MI, unsigned Op, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| const MCOperand &MO1 = MI->getOperand(Op); | |
| if (!MO1.isReg()) { // For label symbolic references. | |
| printOperand(MI, Op, STI, O); | |
| return; | |
| } | |
| assert(ARM_AM::getAM3IdxMode(MI->getOperand(Op + 2).getImm()) != | |
| ARMII::IndexModePost && | |
| "unexpected idxmode"); | |
| printAM3PreOrOffsetIndexOp(MI, Op, O, AlwaysPrintImm0); | |
| } | |
| void ARMInstPrinter::printAddrMode3OffsetOperand(const MCInst *MI, | |
| unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| const MCOperand &MO1 = MI->getOperand(OpNum); | |
| const MCOperand &MO2 = MI->getOperand(OpNum + 1); | |
| if (MO1.getReg()) { | |
| O << getAddrOpcStr(ARM_AM::getAM3Op(MO2.getImm())); | |
| printRegName(O, MO1.getReg()); | |
| return; | |
| } | |
| unsigned ImmOffs = ARM_AM::getAM3Offset(MO2.getImm()); | |
| O << markup("<imm:") << '#' | |
| << ARM_AM::getAddrOpcStr(ARM_AM::getAM3Op(MO2.getImm())) << ImmOffs | |
| << markup(">"); | |
| } | |
| void ARMInstPrinter::printPostIdxImm8Operand(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| const MCOperand &MO = MI->getOperand(OpNum); | |
| unsigned Imm = MO.getImm(); | |
| O << markup("<imm:") << '#' << ((Imm & 256) ? "" : "-") << (Imm & 0xff) | |
| << markup(">"); | |
| } | |
| void ARMInstPrinter::printPostIdxRegOperand(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| const MCOperand &MO1 = MI->getOperand(OpNum); | |
| const MCOperand &MO2 = MI->getOperand(OpNum + 1); | |
| O << (MO2.getImm() ? "" : "-"); | |
| printRegName(O, MO1.getReg()); | |
| } | |
| void ARMInstPrinter::printPostIdxImm8s4Operand(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| const MCOperand &MO = MI->getOperand(OpNum); | |
| unsigned Imm = MO.getImm(); | |
| O << markup("<imm:") << '#' << ((Imm & 256) ? "" : "-") << ((Imm & 0xff) << 2) | |
| << markup(">"); | |
| } | |
| void ARMInstPrinter::printLdStmModeOperand(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| ARM_AM::AMSubMode Mode = | |
| ARM_AM::getAM4SubMode(MI->getOperand(OpNum).getImm()); | |
| O << ARM_AM::getAMSubModeStr(Mode); | |
| } | |
| template <bool AlwaysPrintImm0> | |
| void ARMInstPrinter::printAddrMode5Operand(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| const MCOperand &MO1 = MI->getOperand(OpNum); | |
| const MCOperand &MO2 = MI->getOperand(OpNum + 1); | |
| if (!MO1.isReg()) { // FIXME: This is for CP entries, but isn't right. | |
| printOperand(MI, OpNum, STI, O); | |
| return; | |
| } | |
| O << markup("<mem:") << "["; | |
| printRegName(O, MO1.getReg()); | |
| unsigned ImmOffs = ARM_AM::getAM5Offset(MO2.getImm()); | |
| ARM_AM::AddrOpc Op = ARM_AM::getAM5Op(MO2.getImm()); | |
| if (AlwaysPrintImm0 || ImmOffs || Op == ARM_AM::sub) { | |
| O << ", " << markup("<imm:") << "#" << ARM_AM::getAddrOpcStr(Op) | |
| << ImmOffs * 4 << markup(">"); | |
| } | |
| O << "]" << markup(">"); | |
| } | |
| template <bool AlwaysPrintImm0> | |
| void ARMInstPrinter::printAddrMode5FP16Operand(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| const MCOperand &MO1 = MI->getOperand(OpNum); | |
| const MCOperand &MO2 = MI->getOperand(OpNum+1); | |
| if (!MO1.isReg()) { // FIXME: This is for CP entries, but isn't right. | |
| printOperand(MI, OpNum, STI, O); | |
| return; | |
| } | |
| O << markup("<mem:") << "["; | |
| printRegName(O, MO1.getReg()); | |
| unsigned ImmOffs = ARM_AM::getAM5FP16Offset(MO2.getImm()); | |
| unsigned Op = ARM_AM::getAM5FP16Op(MO2.getImm()); | |
| if (AlwaysPrintImm0 || ImmOffs || Op == ARM_AM::sub) { | |
| O << ", " | |
| << markup("<imm:") | |
| << "#" | |
| << ARM_AM::getAddrOpcStr(ARM_AM::getAM5FP16Op(MO2.getImm())) | |
| << ImmOffs * 2 | |
| << markup(">"); | |
| } | |
| O << "]" << markup(">"); | |
| } | |
| void ARMInstPrinter::printAddrMode6Operand(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| const MCOperand &MO1 = MI->getOperand(OpNum); | |
| const MCOperand &MO2 = MI->getOperand(OpNum + 1); | |
| O << markup("<mem:") << "["; | |
| printRegName(O, MO1.getReg()); | |
| if (MO2.getImm()) { | |
| O << ":" << (MO2.getImm() << 3); | |
| } | |
| O << "]" << markup(">"); | |
| } | |
| void ARMInstPrinter::printAddrMode7Operand(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| const MCOperand &MO1 = MI->getOperand(OpNum); | |
| O << markup("<mem:") << "["; | |
| printRegName(O, MO1.getReg()); | |
| O << "]" << markup(">"); | |
| } | |
| void ARMInstPrinter::printAddrMode6OffsetOperand(const MCInst *MI, | |
| unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| const MCOperand &MO = MI->getOperand(OpNum); | |
| if (MO.getReg() == 0) | |
| O << "!"; | |
| else { | |
| O << ", "; | |
| printRegName(O, MO.getReg()); | |
| } | |
| } | |
| void ARMInstPrinter::printBitfieldInvMaskImmOperand(const MCInst *MI, | |
| unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| const MCOperand &MO = MI->getOperand(OpNum); | |
| uint32_t v = ~MO.getImm(); | |
| int32_t lsb = countTrailingZeros(v); | |
| int32_t width = (32 - countLeadingZeros(v)) - lsb; | |
| assert(MO.isImm() && "Not a valid bf_inv_mask_imm value!"); | |
| O << markup("<imm:") << '#' << lsb << markup(">") << ", " << markup("<imm:") | |
| << '#' << width << markup(">"); | |
| } | |
| void ARMInstPrinter::printMemBOption(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| unsigned val = MI->getOperand(OpNum).getImm(); | |
| O << ARM_MB::MemBOptToString(val, STI.getFeatureBits()[ARM::HasV8Ops]); | |
| } | |
| void ARMInstPrinter::printInstSyncBOption(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| unsigned val = MI->getOperand(OpNum).getImm(); | |
| O << ARM_ISB::InstSyncBOptToString(val); | |
| } | |
| void ARMInstPrinter::printTraceSyncBOption(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| unsigned val = MI->getOperand(OpNum).getImm(); | |
| O << ARM_TSB::TraceSyncBOptToString(val); | |
| } | |
| void ARMInstPrinter::printShiftImmOperand(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| unsigned ShiftOp = MI->getOperand(OpNum).getImm(); | |
| bool isASR = (ShiftOp & (1 << 5)) != 0; | |
| unsigned Amt = ShiftOp & 0x1f; | |
| if (isASR) { | |
| O << ", asr " << markup("<imm:") << "#" << (Amt == 0 ? 32 : Amt) | |
| << markup(">"); | |
| } else if (Amt) { | |
| O << ", lsl " << markup("<imm:") << "#" << Amt << markup(">"); | |
| } | |
| } | |
| void ARMInstPrinter::printPKHLSLShiftImm(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| unsigned Imm = MI->getOperand(OpNum).getImm(); | |
| if (Imm == 0) | |
| return; | |
| assert(Imm > 0 && Imm < 32 && "Invalid PKH shift immediate value!"); | |
| O << ", lsl " << markup("<imm:") << "#" << Imm << markup(">"); | |
| } | |
| void ARMInstPrinter::printPKHASRShiftImm(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| unsigned Imm = MI->getOperand(OpNum).getImm(); | |
| // A shift amount of 32 is encoded as 0. | |
| if (Imm == 0) | |
| Imm = 32; | |
| assert(Imm > 0 && Imm <= 32 && "Invalid PKH shift immediate value!"); | |
| O << ", asr " << markup("<imm:") << "#" << Imm << markup(">"); | |
| } | |
| void ARMInstPrinter::printRegisterList(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| assert(std::is_sorted(MI->begin() + OpNum, MI->end(), | |
| [&](const MCOperand &LHS, const MCOperand &RHS) { | |
| return MRI.getEncodingValue(LHS.getReg()) < | |
| MRI.getEncodingValue(RHS.getReg()); | |
| })); | |
| O << "{"; | |
| for (unsigned i = OpNum, e = MI->getNumOperands(); i != e; ++i) { | |
| if (i != OpNum) | |
| O << ", "; | |
| printRegName(O, MI->getOperand(i).getReg()); | |
| } | |
| O << "}"; | |
| } | |
| void ARMInstPrinter::printGPRPairOperand(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| unsigned Reg = MI->getOperand(OpNum).getReg(); | |
| printRegName(O, MRI.getSubReg(Reg, ARM::gsub_0)); | |
| O << ", "; | |
| printRegName(O, MRI.getSubReg(Reg, ARM::gsub_1)); | |
| } | |
| void ARMInstPrinter::printSetendOperand(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| const MCOperand &Op = MI->getOperand(OpNum); | |
| if (Op.getImm()) | |
| O << "be"; | |
| else | |
| O << "le"; | |
| } | |
| void ARMInstPrinter::printCPSIMod(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, raw_ostream &O) { | |
| const MCOperand &Op = MI->getOperand(OpNum); | |
| O << ARM_PROC::IModToString(Op.getImm()); | |
| } | |
| void ARMInstPrinter::printCPSIFlag(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, raw_ostream &O) { | |
| const MCOperand &Op = MI->getOperand(OpNum); | |
| unsigned IFlags = Op.getImm(); | |
| for (int i = 2; i >= 0; --i) | |
| if (IFlags & (1 << i)) | |
| O << ARM_PROC::IFlagsToString(1 << i); | |
| if (IFlags == 0) | |
| O << "none"; | |
| } | |
| void ARMInstPrinter::printMSRMaskOperand(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| const MCOperand &Op = MI->getOperand(OpNum); | |
| const FeatureBitset &FeatureBits = STI.getFeatureBits(); | |
| if (FeatureBits[ARM::FeatureMClass]) { | |
| unsigned SYSm = Op.getImm() & 0xFFF; // 12-bit SYSm | |
| unsigned Opcode = MI->getOpcode(); | |
| // For writes, handle extended mask bits if the DSP extension is present. | |
| if (Opcode == ARM::t2MSR_M && FeatureBits[ARM::FeatureDSP]) { | |
| auto TheReg =ARMSysReg::lookupMClassSysRegBy12bitSYSmValue(SYSm); | |
| if (TheReg && TheReg->isInRequiredFeatures({ARM::FeatureDSP})) { | |
| O << TheReg->Name; | |
| return; | |
| } | |
| } | |
| // Handle the basic 8-bit mask. | |
| SYSm &= 0xff; | |
| if (Opcode == ARM::t2MSR_M && FeatureBits [ARM::HasV7Ops]) { | |
| // ARMv7-M deprecates using MSR APSR without a _<bits> qualifier as an | |
| // alias for MSR APSR_nzcvq. | |
| auto TheReg = ARMSysReg::lookupMClassSysRegAPSRNonDeprecated(SYSm); | |
| if (TheReg) { | |
| O << TheReg->Name; | |
| return; | |
| } | |
| } | |
| auto TheReg = ARMSysReg::lookupMClassSysRegBy8bitSYSmValue(SYSm); | |
| if (TheReg) { | |
| O << TheReg->Name; | |
| return; | |
| } | |
| O << SYSm; | |
| return; | |
| } | |
| // As special cases, CPSR_f, CPSR_s and CPSR_fs prefer printing as | |
| // APSR_nzcvq, APSR_g and APSRnzcvqg, respectively. | |
| unsigned SpecRegRBit = Op.getImm() >> 4; | |
| unsigned Mask = Op.getImm() & 0xf; | |
| if (!SpecRegRBit && (Mask == 8 || Mask == 4 || Mask == 12)) { | |
| O << "APSR_"; | |
| switch (Mask) { | |
| default: | |
| llvm_unreachable("Unexpected mask value!"); | |
| case 4: | |
| O << "g"; | |
| return; | |
| case 8: | |
| O << "nzcvq"; | |
| return; | |
| case 12: | |
| O << "nzcvqg"; | |
| return; | |
| } | |
| } | |
| if (SpecRegRBit) | |
| O << "SPSR"; | |
| else | |
| O << "CPSR"; | |
| if (Mask) { | |
| O << '_'; | |
| if (Mask & 8) | |
| O << 'f'; | |
| if (Mask & 4) | |
| O << 's'; | |
| if (Mask & 2) | |
| O << 'x'; | |
| if (Mask & 1) | |
| O << 'c'; | |
| } | |
| } | |
| void ARMInstPrinter::printBankedRegOperand(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| uint32_t Banked = MI->getOperand(OpNum).getImm(); | |
| auto TheReg = ARMBankedReg::lookupBankedRegByEncoding(Banked); | |
| assert(TheReg && "invalid banked register operand"); | |
| std::string Name = TheReg->Name; | |
| uint32_t isSPSR = (Banked & 0x20) >> 5; | |
| if (isSPSR) | |
| Name.replace(0, 4, "SPSR"); // convert 'spsr_' to 'SPSR_' | |
| O << Name; | |
| } | |
| void ARMInstPrinter::printPredicateOperand(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| ARMCC::CondCodes CC = (ARMCC::CondCodes)MI->getOperand(OpNum).getImm(); | |
| // Handle the undefined 15 CC value here for printing so we don't abort(). | |
| if ((unsigned)CC == 15) | |
| O << "<und>"; | |
| else if (CC != ARMCC::AL) | |
| O << ARMCondCodeToString(CC); | |
| } | |
| void ARMInstPrinter::printMandatoryPredicateOperand(const MCInst *MI, | |
| unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| ARMCC::CondCodes CC = (ARMCC::CondCodes)MI->getOperand(OpNum).getImm(); | |
| O << ARMCondCodeToString(CC); | |
| } | |
| void ARMInstPrinter::printSBitModifierOperand(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| if (MI->getOperand(OpNum).getReg()) { | |
| assert(MI->getOperand(OpNum).getReg() == ARM::CPSR && | |
| "Expect ARM CPSR register!"); | |
| O << 's'; | |
| } | |
| } | |
| void ARMInstPrinter::printNoHashImmediate(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| O << MI->getOperand(OpNum).getImm(); | |
| } | |
| void ARMInstPrinter::printPImmediate(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| O << "p" << MI->getOperand(OpNum).getImm(); | |
| } | |
| void ARMInstPrinter::printCImmediate(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| O << "c" << MI->getOperand(OpNum).getImm(); | |
| } | |
| void ARMInstPrinter::printCoprocOptionImm(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| O << "{" << MI->getOperand(OpNum).getImm() << "}"; | |
| } | |
| void ARMInstPrinter::printPCLabel(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, raw_ostream &O) { | |
| llvm_unreachable("Unhandled PC-relative pseudo-instruction!"); | |
| } | |
| template <unsigned scale> | |
| void ARMInstPrinter::printAdrLabelOperand(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| const MCOperand &MO = MI->getOperand(OpNum); | |
| if (MO.isExpr()) { | |
| MO.getExpr()->print(O, &MAI); | |
| return; | |
| } | |
| int32_t OffImm = (int32_t)MO.getImm() << scale; | |
| O << markup("<imm:"); | |
| if (OffImm == INT32_MIN) | |
| O << "#-0"; | |
| else if (OffImm < 0) | |
| O << "#-" << -OffImm; | |
| else | |
| O << "#" << OffImm; | |
| O << markup(">"); | |
| } | |
| void ARMInstPrinter::printThumbS4ImmOperand(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| O << markup("<imm:") << "#" << formatImm(MI->getOperand(OpNum).getImm() * 4) | |
| << markup(">"); | |
| } | |
| void ARMInstPrinter::printThumbSRImm(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| unsigned Imm = MI->getOperand(OpNum).getImm(); | |
| O << markup("<imm:") << "#" << formatImm((Imm == 0 ? 32 : Imm)) | |
| << markup(">"); | |
| } | |
| void ARMInstPrinter::printThumbITMask(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| // (3 - the number of trailing zeros) is the number of then / else. | |
| unsigned Mask = MI->getOperand(OpNum).getImm(); | |
| unsigned Firstcond = MI->getOperand(OpNum - 1).getImm(); | |
| unsigned CondBit0 = Firstcond & 1; | |
| unsigned NumTZ = countTrailingZeros(Mask); | |
| assert(NumTZ <= 3 && "Invalid IT mask!"); | |
| for (unsigned Pos = 3, e = NumTZ; Pos > e; --Pos) { | |
| bool T = ((Mask >> Pos) & 1) == CondBit0; | |
| if (T) | |
| O << 't'; | |
| else | |
| O << 'e'; | |
| } | |
| } | |
| void ARMInstPrinter::printThumbAddrModeRROperand(const MCInst *MI, unsigned Op, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| const MCOperand &MO1 = MI->getOperand(Op); | |
| const MCOperand &MO2 = MI->getOperand(Op + 1); | |
| if (!MO1.isReg()) { // FIXME: This is for CP entries, but isn't right. | |
| printOperand(MI, Op, STI, O); | |
| return; | |
| } | |
| O << markup("<mem:") << "["; | |
| printRegName(O, MO1.getReg()); | |
| if (unsigned RegNum = MO2.getReg()) { | |
| O << ", "; | |
| printRegName(O, RegNum); | |
| } | |
| O << "]" << markup(">"); | |
| } | |
| void ARMInstPrinter::printThumbAddrModeImm5SOperand(const MCInst *MI, | |
| unsigned Op, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O, | |
| unsigned Scale) { | |
| const MCOperand &MO1 = MI->getOperand(Op); | |
| const MCOperand &MO2 = MI->getOperand(Op + 1); | |
| if (!MO1.isReg()) { // FIXME: This is for CP entries, but isn't right. | |
| printOperand(MI, Op, STI, O); | |
| return; | |
| } | |
| O << markup("<mem:") << "["; | |
| printRegName(O, MO1.getReg()); | |
| if (unsigned ImmOffs = MO2.getImm()) { | |
| O << ", " << markup("<imm:") << "#" << formatImm(ImmOffs * Scale) | |
| << markup(">"); | |
| } | |
| O << "]" << markup(">"); | |
| } | |
| void ARMInstPrinter::printThumbAddrModeImm5S1Operand(const MCInst *MI, | |
| unsigned Op, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| printThumbAddrModeImm5SOperand(MI, Op, STI, O, 1); | |
| } | |
| void ARMInstPrinter::printThumbAddrModeImm5S2Operand(const MCInst *MI, | |
| unsigned Op, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| printThumbAddrModeImm5SOperand(MI, Op, STI, O, 2); | |
| } | |
| void ARMInstPrinter::printThumbAddrModeImm5S4Operand(const MCInst *MI, | |
| unsigned Op, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| printThumbAddrModeImm5SOperand(MI, Op, STI, O, 4); | |
| } | |
| void ARMInstPrinter::printThumbAddrModeSPOperand(const MCInst *MI, unsigned Op, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| printThumbAddrModeImm5SOperand(MI, Op, STI, O, 4); | |
| } | |
| // Constant shifts t2_so_reg is a 2-operand unit corresponding to the Thumb2 | |
| // register with shift forms. | |
| // REG 0 0 - e.g. R5 | |
| // REG IMM, SH_OPC - e.g. R5, LSL #3 | |
| void ARMInstPrinter::printT2SOOperand(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| const MCOperand &MO1 = MI->getOperand(OpNum); | |
| const MCOperand &MO2 = MI->getOperand(OpNum + 1); | |
| unsigned Reg = MO1.getReg(); | |
| printRegName(O, Reg); | |
| // Print the shift opc. | |
| assert(MO2.isImm() && "Not a valid t2_so_reg value!"); | |
| printRegImmShift(O, ARM_AM::getSORegShOp(MO2.getImm()), | |
| ARM_AM::getSORegOffset(MO2.getImm()), UseMarkup); | |
| } | |
| template <bool AlwaysPrintImm0> | |
| void ARMInstPrinter::printAddrModeImm12Operand(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| const MCOperand &MO1 = MI->getOperand(OpNum); | |
| const MCOperand &MO2 = MI->getOperand(OpNum + 1); | |
| if (!MO1.isReg()) { // FIXME: This is for CP entries, but isn't right. | |
| printOperand(MI, OpNum, STI, O); | |
| return; | |
| } | |
| O << markup("<mem:") << "["; | |
| printRegName(O, MO1.getReg()); | |
| int32_t OffImm = (int32_t)MO2.getImm(); | |
| bool isSub = OffImm < 0; | |
| // Special value for #-0. All others are normal. | |
| if (OffImm == INT32_MIN) | |
| OffImm = 0; | |
| if (isSub) { | |
| O << ", " << markup("<imm:") << "#-" << formatImm(-OffImm) << markup(">"); | |
| } else if (AlwaysPrintImm0 || OffImm > 0) { | |
| O << ", " << markup("<imm:") << "#" << formatImm(OffImm) << markup(">"); | |
| } | |
| O << "]" << markup(">"); | |
| } | |
| template <bool AlwaysPrintImm0> | |
| void ARMInstPrinter::printT2AddrModeImm8Operand(const MCInst *MI, | |
| unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| const MCOperand &MO1 = MI->getOperand(OpNum); | |
| const MCOperand &MO2 = MI->getOperand(OpNum + 1); | |
| O << markup("<mem:") << "["; | |
| printRegName(O, MO1.getReg()); | |
| int32_t OffImm = (int32_t)MO2.getImm(); | |
| bool isSub = OffImm < 0; | |
| // Don't print +0. | |
| if (OffImm == INT32_MIN) | |
| OffImm = 0; | |
| if (isSub) { | |
| O << ", " << markup("<imm:") << "#-" << -OffImm << markup(">"); | |
| } else if (AlwaysPrintImm0 || OffImm > 0) { | |
| O << ", " << markup("<imm:") << "#" << OffImm << markup(">"); | |
| } | |
| O << "]" << markup(">"); | |
| } | |
| template <bool AlwaysPrintImm0> | |
| void ARMInstPrinter::printT2AddrModeImm8s4Operand(const MCInst *MI, | |
| unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| const MCOperand &MO1 = MI->getOperand(OpNum); | |
| const MCOperand &MO2 = MI->getOperand(OpNum + 1); | |
| if (!MO1.isReg()) { // For label symbolic references. | |
| printOperand(MI, OpNum, STI, O); | |
| return; | |
| } | |
| O << markup("<mem:") << "["; | |
| printRegName(O, MO1.getReg()); | |
| int32_t OffImm = (int32_t)MO2.getImm(); | |
| bool isSub = OffImm < 0; | |
| assert(((OffImm & 0x3) == 0) && "Not a valid immediate!"); | |
| // Don't print +0. | |
| if (OffImm == INT32_MIN) | |
| OffImm = 0; | |
| if (isSub) { | |
| O << ", " << markup("<imm:") << "#-" << -OffImm << markup(">"); | |
| } else if (AlwaysPrintImm0 || OffImm > 0) { | |
| O << ", " << markup("<imm:") << "#" << OffImm << markup(">"); | |
| } | |
| O << "]" << markup(">"); | |
| } | |
| void ARMInstPrinter::printT2AddrModeImm0_1020s4Operand( | |
| const MCInst *MI, unsigned OpNum, const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| const MCOperand &MO1 = MI->getOperand(OpNum); | |
| const MCOperand &MO2 = MI->getOperand(OpNum + 1); | |
| O << markup("<mem:") << "["; | |
| printRegName(O, MO1.getReg()); | |
| if (MO2.getImm()) { | |
| O << ", " << markup("<imm:") << "#" << formatImm(MO2.getImm() * 4) | |
| << markup(">"); | |
| } | |
| O << "]" << markup(">"); | |
| } | |
| void ARMInstPrinter::printT2AddrModeImm8OffsetOperand( | |
| const MCInst *MI, unsigned OpNum, const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| const MCOperand &MO1 = MI->getOperand(OpNum); | |
| int32_t OffImm = (int32_t)MO1.getImm(); | |
| O << ", " << markup("<imm:"); | |
| if (OffImm == INT32_MIN) | |
| O << "#-0"; | |
| else if (OffImm < 0) | |
| O << "#-" << -OffImm; | |
| else | |
| O << "#" << OffImm; | |
| O << markup(">"); | |
| } | |
| void ARMInstPrinter::printT2AddrModeImm8s4OffsetOperand( | |
| const MCInst *MI, unsigned OpNum, const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| const MCOperand &MO1 = MI->getOperand(OpNum); | |
| int32_t OffImm = (int32_t)MO1.getImm(); | |
| assert(((OffImm & 0x3) == 0) && "Not a valid immediate!"); | |
| O << ", " << markup("<imm:"); | |
| if (OffImm == INT32_MIN) | |
| O << "#-0"; | |
| else if (OffImm < 0) | |
| O << "#-" << -OffImm; | |
| else | |
| O << "#" << OffImm; | |
| O << markup(">"); | |
| } | |
| void ARMInstPrinter::printT2AddrModeSoRegOperand(const MCInst *MI, | |
| unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| const MCOperand &MO1 = MI->getOperand(OpNum); | |
| const MCOperand &MO2 = MI->getOperand(OpNum + 1); | |
| const MCOperand &MO3 = MI->getOperand(OpNum + 2); | |
| O << markup("<mem:") << "["; | |
| printRegName(O, MO1.getReg()); | |
| assert(MO2.getReg() && "Invalid so_reg load / store address!"); | |
| O << ", "; | |
| printRegName(O, MO2.getReg()); | |
| unsigned ShAmt = MO3.getImm(); | |
| if (ShAmt) { | |
| assert(ShAmt <= 3 && "Not a valid Thumb2 addressing mode!"); | |
| O << ", lsl " << markup("<imm:") << "#" << ShAmt << markup(">"); | |
| } | |
| O << "]" << markup(">"); | |
| } | |
| void ARMInstPrinter::printFPImmOperand(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| const MCOperand &MO = MI->getOperand(OpNum); | |
| O << markup("<imm:") << '#' << ARM_AM::getFPImmFloat(MO.getImm()) | |
| << markup(">"); | |
| } | |
| void ARMInstPrinter::printNEONModImmOperand(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| unsigned EncodedImm = MI->getOperand(OpNum).getImm(); | |
| unsigned EltBits; | |
| uint64_t Val = ARM_AM::decodeNEONModImm(EncodedImm, EltBits); | |
| O << markup("<imm:") << "#0x"; | |
| O.write_hex(Val); | |
| O << markup(">"); | |
| } | |
| void ARMInstPrinter::printImmPlusOneOperand(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| unsigned Imm = MI->getOperand(OpNum).getImm(); | |
| O << markup("<imm:") << "#" << formatImm(Imm + 1) << markup(">"); | |
| } | |
| void ARMInstPrinter::printRotImmOperand(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| unsigned Imm = MI->getOperand(OpNum).getImm(); | |
| if (Imm == 0) | |
| return; | |
| assert(Imm <= 3 && "illegal ror immediate!"); | |
| O << ", ror " << markup("<imm:") << "#" << 8 * Imm << markup(">"); | |
| } | |
| void ARMInstPrinter::printModImmOperand(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| MCOperand Op = MI->getOperand(OpNum); | |
| // Support for fixups (MCFixup) | |
| if (Op.isExpr()) | |
| return printOperand(MI, OpNum, STI, O); | |
| unsigned Bits = Op.getImm() & 0xFF; | |
| unsigned Rot = (Op.getImm() & 0xF00) >> 7; | |
| bool PrintUnsigned = false; | |
| switch (MI->getOpcode()) { | |
| case ARM::MOVi: | |
| // Movs to PC should be treated unsigned | |
| PrintUnsigned = (MI->getOperand(OpNum - 1).getReg() == ARM::PC); | |
| break; | |
| case ARM::MSRi: | |
| // Movs to special registers should be treated unsigned | |
| PrintUnsigned = true; | |
| break; | |
| } | |
| int32_t Rotated = ARM_AM::rotr32(Bits, Rot); | |
| if (ARM_AM::getSOImmVal(Rotated) == Op.getImm()) { | |
| // #rot has the least possible value | |
| O << "#" << markup("<imm:"); | |
| if (PrintUnsigned) | |
| O << static_cast<uint32_t>(Rotated); | |
| else | |
| O << Rotated; | |
| O << markup(">"); | |
| return; | |
| } | |
| // Explicit #bits, #rot implied | |
| O << "#" << markup("<imm:") << Bits << markup(">") << ", #" << markup("<imm:") | |
| << Rot << markup(">"); | |
| } | |
| void ARMInstPrinter::printFBits16(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, raw_ostream &O) { | |
| O << markup("<imm:") << "#" << 16 - MI->getOperand(OpNum).getImm() | |
| << markup(">"); | |
| } | |
| void ARMInstPrinter::printFBits32(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, raw_ostream &O) { | |
| O << markup("<imm:") << "#" << 32 - MI->getOperand(OpNum).getImm() | |
| << markup(">"); | |
| } | |
| void ARMInstPrinter::printVectorIndex(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| O << "[" << MI->getOperand(OpNum).getImm() << "]"; | |
| } | |
| void ARMInstPrinter::printVectorListOne(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| O << "{"; | |
| printRegName(O, MI->getOperand(OpNum).getReg()); | |
| O << "}"; | |
| } | |
| void ARMInstPrinter::printVectorListTwo(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| unsigned Reg = MI->getOperand(OpNum).getReg(); | |
| unsigned Reg0 = MRI.getSubReg(Reg, ARM::dsub_0); | |
| unsigned Reg1 = MRI.getSubReg(Reg, ARM::dsub_1); | |
| O << "{"; | |
| printRegName(O, Reg0); | |
| O << ", "; | |
| printRegName(O, Reg1); | |
| O << "}"; | |
| } | |
| void ARMInstPrinter::printVectorListTwoSpaced(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| unsigned Reg = MI->getOperand(OpNum).getReg(); | |
| unsigned Reg0 = MRI.getSubReg(Reg, ARM::dsub_0); | |
| unsigned Reg1 = MRI.getSubReg(Reg, ARM::dsub_2); | |
| O << "{"; | |
| printRegName(O, Reg0); | |
| O << ", "; | |
| printRegName(O, Reg1); | |
| O << "}"; | |
| } | |
| void ARMInstPrinter::printVectorListThree(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| // Normally, it's not safe to use register enum values directly with | |
| // addition to get the next register, but for VFP registers, the | |
| // sort order is guaranteed because they're all of the form D<n>. | |
| O << "{"; | |
| printRegName(O, MI->getOperand(OpNum).getReg()); | |
| O << ", "; | |
| printRegName(O, MI->getOperand(OpNum).getReg() + 1); | |
| O << ", "; | |
| printRegName(O, MI->getOperand(OpNum).getReg() + 2); | |
| O << "}"; | |
| } | |
| void ARMInstPrinter::printVectorListFour(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| // Normally, it's not safe to use register enum values directly with | |
| // addition to get the next register, but for VFP registers, the | |
| // sort order is guaranteed because they're all of the form D<n>. | |
| O << "{"; | |
| printRegName(O, MI->getOperand(OpNum).getReg()); | |
| O << ", "; | |
| printRegName(O, MI->getOperand(OpNum).getReg() + 1); | |
| O << ", "; | |
| printRegName(O, MI->getOperand(OpNum).getReg() + 2); | |
| O << ", "; | |
| printRegName(O, MI->getOperand(OpNum).getReg() + 3); | |
| O << "}"; | |
| } | |
| void ARMInstPrinter::printVectorListOneAllLanes(const MCInst *MI, | |
| unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| O << "{"; | |
| printRegName(O, MI->getOperand(OpNum).getReg()); | |
| O << "[]}"; | |
| } | |
| void ARMInstPrinter::printVectorListTwoAllLanes(const MCInst *MI, | |
| unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| unsigned Reg = MI->getOperand(OpNum).getReg(); | |
| unsigned Reg0 = MRI.getSubReg(Reg, ARM::dsub_0); | |
| unsigned Reg1 = MRI.getSubReg(Reg, ARM::dsub_1); | |
| O << "{"; | |
| printRegName(O, Reg0); | |
| O << "[], "; | |
| printRegName(O, Reg1); | |
| O << "[]}"; | |
| } | |
| void ARMInstPrinter::printVectorListThreeAllLanes(const MCInst *MI, | |
| unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| // Normally, it's not safe to use register enum values directly with | |
| // addition to get the next register, but for VFP registers, the | |
| // sort order is guaranteed because they're all of the form D<n>. | |
| O << "{"; | |
| printRegName(O, MI->getOperand(OpNum).getReg()); | |
| O << "[], "; | |
| printRegName(O, MI->getOperand(OpNum).getReg() + 1); | |
| O << "[], "; | |
| printRegName(O, MI->getOperand(OpNum).getReg() + 2); | |
| O << "[]}"; | |
| } | |
| void ARMInstPrinter::printVectorListFourAllLanes(const MCInst *MI, | |
| unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| // Normally, it's not safe to use register enum values directly with | |
| // addition to get the next register, but for VFP registers, the | |
| // sort order is guaranteed because they're all of the form D<n>. | |
| O << "{"; | |
| printRegName(O, MI->getOperand(OpNum).getReg()); | |
| O << "[], "; | |
| printRegName(O, MI->getOperand(OpNum).getReg() + 1); | |
| O << "[], "; | |
| printRegName(O, MI->getOperand(OpNum).getReg() + 2); | |
| O << "[], "; | |
| printRegName(O, MI->getOperand(OpNum).getReg() + 3); | |
| O << "[]}"; | |
| } | |
| void ARMInstPrinter::printVectorListTwoSpacedAllLanes( | |
| const MCInst *MI, unsigned OpNum, const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| unsigned Reg = MI->getOperand(OpNum).getReg(); | |
| unsigned Reg0 = MRI.getSubReg(Reg, ARM::dsub_0); | |
| unsigned Reg1 = MRI.getSubReg(Reg, ARM::dsub_2); | |
| O << "{"; | |
| printRegName(O, Reg0); | |
| O << "[], "; | |
| printRegName(O, Reg1); | |
| O << "[]}"; | |
| } | |
| void ARMInstPrinter::printVectorListThreeSpacedAllLanes( | |
| const MCInst *MI, unsigned OpNum, const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| // Normally, it's not safe to use register enum values directly with | |
| // addition to get the next register, but for VFP registers, the | |
| // sort order is guaranteed because they're all of the form D<n>. | |
| O << "{"; | |
| printRegName(O, MI->getOperand(OpNum).getReg()); | |
| O << "[], "; | |
| printRegName(O, MI->getOperand(OpNum).getReg() + 2); | |
| O << "[], "; | |
| printRegName(O, MI->getOperand(OpNum).getReg() + 4); | |
| O << "[]}"; | |
| } | |
| void ARMInstPrinter::printVectorListFourSpacedAllLanes( | |
| const MCInst *MI, unsigned OpNum, const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| // Normally, it's not safe to use register enum values directly with | |
| // addition to get the next register, but for VFP registers, the | |
| // sort order is guaranteed because they're all of the form D<n>. | |
| O << "{"; | |
| printRegName(O, MI->getOperand(OpNum).getReg()); | |
| O << "[], "; | |
| printRegName(O, MI->getOperand(OpNum).getReg() + 2); | |
| O << "[], "; | |
| printRegName(O, MI->getOperand(OpNum).getReg() + 4); | |
| O << "[], "; | |
| printRegName(O, MI->getOperand(OpNum).getReg() + 6); | |
| O << "[]}"; | |
| } | |
| void ARMInstPrinter::printVectorListThreeSpaced(const MCInst *MI, | |
| unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| // Normally, it's not safe to use register enum values directly with | |
| // addition to get the next register, but for VFP registers, the | |
| // sort order is guaranteed because they're all of the form D<n>. | |
| O << "{"; | |
| printRegName(O, MI->getOperand(OpNum).getReg()); | |
| O << ", "; | |
| printRegName(O, MI->getOperand(OpNum).getReg() + 2); | |
| O << ", "; | |
| printRegName(O, MI->getOperand(OpNum).getReg() + 4); | |
| O << "}"; | |
| } | |
| void ARMInstPrinter::printVectorListFourSpaced(const MCInst *MI, unsigned OpNum, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| // Normally, it's not safe to use register enum values directly with | |
| // addition to get the next register, but for VFP registers, the | |
| // sort order is guaranteed because they're all of the form D<n>. | |
| O << "{"; | |
| printRegName(O, MI->getOperand(OpNum).getReg()); | |
| O << ", "; | |
| printRegName(O, MI->getOperand(OpNum).getReg() + 2); | |
| O << ", "; | |
| printRegName(O, MI->getOperand(OpNum).getReg() + 4); | |
| O << ", "; | |
| printRegName(O, MI->getOperand(OpNum).getReg() + 6); | |
| O << "}"; | |
| } | |
| template<int64_t Angle, int64_t Remainder> | |
| void ARMInstPrinter::printComplexRotationOp(const MCInst *MI, unsigned OpNo, | |
| const MCSubtargetInfo &STI, | |
| raw_ostream &O) { | |
| unsigned Val = MI->getOperand(OpNo).getImm(); | |
| O << "#" << (Val * Angle) + Remainder; | |
| } | |