mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-12-16 11:30:51 +00:00
dc05f3a64b
Certain ARM instructions accept 32-bit immediate operands encoded as a 8-bit integer value (0-255) and a 4-bit rotation (0-30, even). Current ARM assembly syntax support in LLVM allows the decoded (32-bit) immediate to be specified as a single immediate operand for such instructions: mov r0, #4278190080 The ARMARM defines an extended assembly syntax allowing the encoding to be made more explicit, as in: mov r0, #255, #8 ; (same 32-bit value as above) The behaviour of the two instructions can be different w.r.t flags, which is documented under "Modified immediate constants" in ARMARM. This patch enables support for this extended syntax at the MC layer. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@223113 91177308-0d34-0410-b5e6-96231b3b80d8
1577 lines
49 KiB
C++
1577 lines
49 KiB
C++
//===-- 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 "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/Support/raw_ostream.h"
|
|
using namespace llvm;
|
|
|
|
#define DEBUG_TYPE "asm-printer"
|
|
|
|
#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,
|
|
const MCSubtargetInfo &STI) :
|
|
MCInstPrinter(MAI, MII, MRI) {
|
|
// Initialize the set of available features.
|
|
setAvailableFeatures(STI.getFeatureBits());
|
|
}
|
|
|
|
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) {
|
|
unsigned Opcode = MI->getOpcode();
|
|
|
|
switch(Opcode) {
|
|
|
|
// Check for HINT instructions w/ canonical names.
|
|
case ARM::HINT:
|
|
case ARM::tHINT:
|
|
case ARM::t2HINT:
|
|
switch (MI->getOperand(0).getImm()) {
|
|
case 0: O << "\tnop"; break;
|
|
case 1: O << "\tyield"; break;
|
|
case 2: O << "\twfe"; break;
|
|
case 3: O << "\twfi"; break;
|
|
case 4: O << "\tsev"; break;
|
|
case 5:
|
|
if ((getAvailableFeatures() & ARM::HasV8Ops)) {
|
|
O << "\tsevl";
|
|
break;
|
|
} // Fallthrough for non-v8
|
|
default:
|
|
// Anything else should just print normally.
|
|
printInstruction(MI, O);
|
|
printAnnotation(O, Annot);
|
|
return;
|
|
}
|
|
printPredicateOperand(MI, 1, O);
|
|
if (Opcode == ARM::t2HINT)
|
|
O << ".w";
|
|
printAnnotation(O, Annot);
|
|
return;
|
|
|
|
// 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, O);
|
|
printPredicateOperand(MI, 4, 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, O);
|
|
printPredicateOperand(MI, 3, 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, O);
|
|
if (Opcode == ARM::t2STMDB_UPD)
|
|
O << ".w";
|
|
O << '\t';
|
|
printRegisterList(MI, 4, 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, 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, O);
|
|
if (Opcode == ARM::t2LDMIA_UPD)
|
|
O << ".w";
|
|
O << '\t';
|
|
printRegisterList(MI, 4, 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, 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, O);
|
|
O << '\t';
|
|
printRegisterList(MI, 4, 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, O);
|
|
O << '\t';
|
|
printRegisterList(MI, 4, 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, O);
|
|
O << '\t';
|
|
printRegName(O, BaseReg);
|
|
if (Writeback) O << "!";
|
|
O << ", ";
|
|
printRegisterList(MI, 3, 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, O);
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
// B9.3.3 ERET (Thumb)
|
|
// For a target that has Virtualization Extensions, ERET is the preferred
|
|
// disassembly of SUBS PC, LR, #0
|
|
case ARM::t2SUBS_PC_LR: {
|
|
if (MI->getNumOperands() == 3 &&
|
|
MI->getOperand(0).isImm() &&
|
|
MI->getOperand(0).getImm() == 0 &&
|
|
(getAvailableFeatures() & ARM::FeatureVirtualization)) {
|
|
O << "\teret";
|
|
printPredicateOperand(MI, 1, O);
|
|
printAnnotation(O, Annot);
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
printInstruction(MI, O);
|
|
printAnnotation(O, Annot);
|
|
}
|
|
|
|
void ARMInstPrinter::printOperand(const MCInst *MI, unsigned OpNo,
|
|
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;
|
|
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;
|
|
} 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 '#'?
|
|
O << *Expr;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ARMInstPrinter::printThumbLdrLabelOperand(const MCInst *MI, unsigned OpNum,
|
|
raw_ostream &O) {
|
|
const MCOperand &MO1 = MI->getOperand(OpNum);
|
|
if (MO1.isExpr()) {
|
|
O << *MO1.getExpr();
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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, 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, O);
|
|
}
|
|
|
|
void ARMInstPrinter::printAddrMode2OffsetOperand(const MCInst *MI,
|
|
unsigned OpNum,
|
|
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,
|
|
raw_ostream &O) {
|
|
const MCOperand &MO1 = MI->getOperand(Op);
|
|
if (!MO1.isReg()) { // For label symbolic references.
|
|
printOperand(MI, Op, 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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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, O);
|
|
return;
|
|
}
|
|
|
|
O << markup("<mem:") << "[";
|
|
printRegName(O, MO1.getReg());
|
|
|
|
unsigned ImmOffs = ARM_AM::getAM5Offset(MO2.getImm());
|
|
unsigned Op = ARM_AM::getAM5Op(MO2.getImm());
|
|
if (AlwaysPrintImm0 || ImmOffs || Op == ARM_AM::sub) {
|
|
O << ", "
|
|
<< markup("<imm:")
|
|
<< "#"
|
|
<< ARM_AM::getAddrOpcStr(ARM_AM::getAM5Op(MO2.getImm()))
|
|
<< ImmOffs * 4
|
|
<< markup(">");
|
|
}
|
|
O << "]" << markup(">");
|
|
}
|
|
|
|
void ARMInstPrinter::printAddrMode6Operand(const MCInst *MI, unsigned OpNum,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
raw_ostream &O) {
|
|
unsigned val = MI->getOperand(OpNum).getImm();
|
|
O << ARM_MB::MemBOptToString(val, (getAvailableFeatures() & ARM::HasV8Ops));
|
|
}
|
|
|
|
void ARMInstPrinter::printInstSyncBOption(const MCInst *MI, unsigned OpNum,
|
|
raw_ostream &O) {
|
|
unsigned val = MI->getOperand(OpNum).getImm();
|
|
O << ARM_ISB::InstSyncBOptToString(val);
|
|
}
|
|
|
|
void ARMInstPrinter::printShiftImmOperand(const MCInst *MI, unsigned OpNum,
|
|
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,
|
|
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,
|
|
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,
|
|
raw_ostream &O) {
|
|
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,
|
|
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,
|
|
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,
|
|
raw_ostream &O) {
|
|
const MCOperand &Op = MI->getOperand(OpNum);
|
|
O << ARM_PROC::IModToString(Op.getImm());
|
|
}
|
|
|
|
void ARMInstPrinter::printCPSIFlag(const MCInst *MI, unsigned OpNum,
|
|
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,
|
|
raw_ostream &O) {
|
|
const MCOperand &Op = MI->getOperand(OpNum);
|
|
unsigned SpecRegRBit = Op.getImm() >> 4;
|
|
unsigned Mask = Op.getImm() & 0xf;
|
|
uint64_t FeatureBits = getAvailableFeatures();
|
|
|
|
if (FeatureBits & ARM::FeatureMClass) {
|
|
unsigned SYSm = Op.getImm();
|
|
unsigned Opcode = MI->getOpcode();
|
|
|
|
// For writes, handle extended mask bits if the DSP extension is present.
|
|
if (Opcode == ARM::t2MSR_M && (FeatureBits & ARM::FeatureDSPThumb2)) {
|
|
switch (SYSm) {
|
|
case 0x400: O << "apsr_g"; return;
|
|
case 0xc00: O << "apsr_nzcvqg"; return;
|
|
case 0x401: O << "iapsr_g"; return;
|
|
case 0xc01: O << "iapsr_nzcvqg"; return;
|
|
case 0x402: O << "eapsr_g"; return;
|
|
case 0xc02: O << "eapsr_nzcvqg"; return;
|
|
case 0x403: O << "xpsr_g"; return;
|
|
case 0xc03: O << "xpsr_nzcvqg"; 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.
|
|
switch (SYSm) {
|
|
case 0: O << "apsr_nzcvq"; return;
|
|
case 1: O << "iapsr_nzcvq"; return;
|
|
case 2: O << "eapsr_nzcvq"; return;
|
|
case 3: O << "xpsr_nzcvq"; return;
|
|
}
|
|
}
|
|
|
|
switch (SYSm) {
|
|
default: llvm_unreachable("Unexpected mask value!");
|
|
case 0: O << "apsr"; return;
|
|
case 1: O << "iapsr"; return;
|
|
case 2: O << "eapsr"; return;
|
|
case 3: O << "xpsr"; return;
|
|
case 5: O << "ipsr"; return;
|
|
case 6: O << "epsr"; return;
|
|
case 7: O << "iepsr"; return;
|
|
case 8: O << "msp"; return;
|
|
case 9: O << "psp"; return;
|
|
case 16: O << "primask"; return;
|
|
case 17: O << "basepri"; return;
|
|
case 18: O << "basepri_max"; return;
|
|
case 19: O << "faultmask"; return;
|
|
case 20: O << "control"; return;
|
|
}
|
|
}
|
|
|
|
// As special cases, CPSR_f, CPSR_s and CPSR_fs prefer printing as
|
|
// APSR_nzcvq, APSR_g and APSRnzcvqg, respectively.
|
|
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,
|
|
raw_ostream &O) {
|
|
uint32_t Banked = MI->getOperand(OpNum).getImm();
|
|
uint32_t R = (Banked & 0x20) >> 5;
|
|
uint32_t SysM = Banked & 0x1f;
|
|
|
|
// Nothing much we can do about this, the encodings are specified in B9.2.3 of
|
|
// the ARM ARM v7C, and are all over the shop.
|
|
if (R) {
|
|
O << "SPSR_";
|
|
|
|
switch(SysM) {
|
|
case 0x0e: O << "fiq"; return;
|
|
case 0x10: O << "irq"; return;
|
|
case 0x12: O << "svc"; return;
|
|
case 0x14: O << "abt"; return;
|
|
case 0x16: O << "und"; return;
|
|
case 0x1c: O << "mon"; return;
|
|
case 0x1e: O << "hyp"; return;
|
|
default: llvm_unreachable("Invalid banked SPSR register");
|
|
}
|
|
}
|
|
|
|
assert(!R && "should have dealt with SPSR regs");
|
|
const char *RegNames[] = {
|
|
"r8_usr", "r9_usr", "r10_usr", "r11_usr", "r12_usr", "sp_usr", "lr_usr", "",
|
|
"r8_fiq", "r9_fiq", "r10_fiq", "r11_fiq", "r12_fiq", "sp_fiq", "lr_fiq", "",
|
|
"lr_irq", "sp_irq", "lr_svc", "sp_svc", "lr_abt", "sp_abt", "lr_und", "sp_und",
|
|
"", "", "", "", "lr_mon", "sp_mon", "elr_hyp", "sp_hyp"
|
|
};
|
|
const char *Name = RegNames[SysM];
|
|
assert(Name[0] && "invalid banked register operand");
|
|
|
|
O << Name;
|
|
}
|
|
|
|
void ARMInstPrinter::printPredicateOperand(const MCInst *MI, unsigned OpNum,
|
|
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,
|
|
raw_ostream &O) {
|
|
ARMCC::CondCodes CC = (ARMCC::CondCodes)MI->getOperand(OpNum).getImm();
|
|
O << ARMCondCodeToString(CC);
|
|
}
|
|
|
|
void ARMInstPrinter::printSBitModifierOperand(const MCInst *MI, unsigned OpNum,
|
|
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,
|
|
raw_ostream &O) {
|
|
O << MI->getOperand(OpNum).getImm();
|
|
}
|
|
|
|
void ARMInstPrinter::printPImmediate(const MCInst *MI, unsigned OpNum,
|
|
raw_ostream &O) {
|
|
O << "p" << MI->getOperand(OpNum).getImm();
|
|
}
|
|
|
|
void ARMInstPrinter::printCImmediate(const MCInst *MI, unsigned OpNum,
|
|
raw_ostream &O) {
|
|
O << "c" << MI->getOperand(OpNum).getImm();
|
|
}
|
|
|
|
void ARMInstPrinter::printCoprocOptionImm(const MCInst *MI, unsigned OpNum,
|
|
raw_ostream &O) {
|
|
O << "{" << MI->getOperand(OpNum).getImm() << "}";
|
|
}
|
|
|
|
void ARMInstPrinter::printPCLabel(const MCInst *MI, unsigned OpNum,
|
|
raw_ostream &O) {
|
|
llvm_unreachable("Unhandled PC-relative pseudo-instruction!");
|
|
}
|
|
|
|
template<unsigned scale>
|
|
void ARMInstPrinter::printAdrLabelOperand(const MCInst *MI, unsigned OpNum,
|
|
raw_ostream &O) {
|
|
const MCOperand &MO = MI->getOperand(OpNum);
|
|
|
|
if (MO.isExpr()) {
|
|
O << *MO.getExpr();
|
|
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,
|
|
raw_ostream &O) {
|
|
O << markup("<imm:")
|
|
<< "#" << formatImm(MI->getOperand(OpNum).getImm() * 4)
|
|
<< markup(">");
|
|
}
|
|
|
|
void ARMInstPrinter::printThumbSRImm(const MCInst *MI, unsigned OpNum,
|
|
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,
|
|
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,
|
|
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, 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,
|
|
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, 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,
|
|
raw_ostream &O) {
|
|
printThumbAddrModeImm5SOperand(MI, Op, O, 1);
|
|
}
|
|
|
|
void ARMInstPrinter::printThumbAddrModeImm5S2Operand(const MCInst *MI,
|
|
unsigned Op,
|
|
raw_ostream &O) {
|
|
printThumbAddrModeImm5SOperand(MI, Op, O, 2);
|
|
}
|
|
|
|
void ARMInstPrinter::printThumbAddrModeImm5S4Operand(const MCInst *MI,
|
|
unsigned Op,
|
|
raw_ostream &O) {
|
|
printThumbAddrModeImm5SOperand(MI, Op, O, 4);
|
|
}
|
|
|
|
void ARMInstPrinter::printThumbAddrModeSPOperand(const MCInst *MI, unsigned Op,
|
|
raw_ostream &O) {
|
|
printThumbAddrModeImm5SOperand(MI, Op, 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,
|
|
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,
|
|
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, 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,
|
|
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,
|
|
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, 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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
raw_ostream &O) {
|
|
unsigned Imm = MI->getOperand(OpNum).getImm();
|
|
O << markup("<imm:")
|
|
<< "#" << formatImm(Imm + 1)
|
|
<< markup(">");
|
|
}
|
|
|
|
void ARMInstPrinter::printRotImmOperand(const MCInst *MI, unsigned OpNum,
|
|
raw_ostream &O) {
|
|
unsigned Imm = MI->getOperand(OpNum).getImm();
|
|
if (Imm == 0)
|
|
return;
|
|
O << ", ror "
|
|
<< markup("<imm:")
|
|
<< "#";
|
|
switch (Imm) {
|
|
default: assert (0 && "illegal ror immediate!");
|
|
case 1: O << "8"; break;
|
|
case 2: O << "16"; break;
|
|
case 3: O << "24"; break;
|
|
}
|
|
O << markup(">");
|
|
}
|
|
|
|
void ARMInstPrinter::printModImmOperand(const MCInst *MI, unsigned OpNum,
|
|
raw_ostream &O) {
|
|
MCOperand Op = MI->getOperand(OpNum);
|
|
|
|
// Support for fixups (MCFixup)
|
|
if (Op.isExpr())
|
|
return printOperand(MI, OpNum, 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,
|
|
raw_ostream &O) {
|
|
O << markup("<imm:")
|
|
<< "#" << 16 - MI->getOperand(OpNum).getImm()
|
|
<< markup(">");
|
|
}
|
|
|
|
void ARMInstPrinter::printFBits32(const MCInst *MI, unsigned OpNum,
|
|
raw_ostream &O) {
|
|
O << markup("<imm:")
|
|
<< "#" << 32 - MI->getOperand(OpNum).getImm()
|
|
<< markup(">");
|
|
}
|
|
|
|
void ARMInstPrinter::printVectorIndex(const MCInst *MI, unsigned OpNum,
|
|
raw_ostream &O) {
|
|
O << "[" << MI->getOperand(OpNum).getImm() << "]";
|
|
}
|
|
|
|
void ARMInstPrinter::printVectorListOne(const MCInst *MI, unsigned OpNum,
|
|
raw_ostream &O) {
|
|
O << "{";
|
|
printRegName(O, MI->getOperand(OpNum).getReg());
|
|
O << "}";
|
|
}
|
|
|
|
void ARMInstPrinter::printVectorListTwo(const MCInst *MI, unsigned OpNum,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
raw_ostream &O) {
|
|
O << "{";
|
|
printRegName(O, MI->getOperand(OpNum).getReg());
|
|
O << "[]}";
|
|
}
|
|
|
|
void ARMInstPrinter::printVectorListTwoAllLanes(const MCInst *MI,
|
|
unsigned OpNum,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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 << "}";
|
|
}
|