mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-12-25 14:32:53 +00:00
1a913ed178
the machine instruction representation of the immediate value to be encoded into an integer with similar fields as the actual VMOV instruction. This makes things easier for the disassembler, since it can just stuff the bits into the immediate operand, but harder for the asm printer since it has to decode the value to be printed. Testcase for the encoding will follow later when MC has more support for ARM. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@105836 91177308-0d34-0410-b5e6-96231b3b80d8
1465 lines
54 KiB
C++
1465 lines
54 KiB
C++
//===-- ARMAsmPrinter.cpp - Print machine code to an ARM .s file ----------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file contains a printer that converts from our internal representation
|
|
// of machine-dependent LLVM code to GAS-format ARM assembly language.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#define DEBUG_TYPE "asm-printer"
|
|
#include "ARM.h"
|
|
#include "ARMBuildAttrs.h"
|
|
#include "ARMAddressingModes.h"
|
|
#include "ARMConstantPoolValue.h"
|
|
#include "ARMInstPrinter.h"
|
|
#include "ARMMachineFunctionInfo.h"
|
|
#include "ARMMCInstLower.h"
|
|
#include "ARMTargetMachine.h"
|
|
#include "llvm/Analysis/DebugInfo.h"
|
|
#include "llvm/Constants.h"
|
|
#include "llvm/Module.h"
|
|
#include "llvm/Type.h"
|
|
#include "llvm/Assembly/Writer.h"
|
|
#include "llvm/CodeGen/AsmPrinter.h"
|
|
#include "llvm/CodeGen/MachineModuleInfoImpls.h"
|
|
#include "llvm/CodeGen/MachineFunctionPass.h"
|
|
#include "llvm/CodeGen/MachineJumpTableInfo.h"
|
|
#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h"
|
|
#include "llvm/MC/MCAsmInfo.h"
|
|
#include "llvm/MC/MCContext.h"
|
|
#include "llvm/MC/MCExpr.h"
|
|
#include "llvm/MC/MCInst.h"
|
|
#include "llvm/MC/MCSectionMachO.h"
|
|
#include "llvm/MC/MCStreamer.h"
|
|
#include "llvm/MC/MCSymbol.h"
|
|
#include "llvm/Target/Mangler.h"
|
|
#include "llvm/Target/TargetData.h"
|
|
#include "llvm/Target/TargetMachine.h"
|
|
#include "llvm/Target/TargetOptions.h"
|
|
#include "llvm/Target/TargetRegistry.h"
|
|
#include "llvm/ADT/SmallPtrSet.h"
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/Support/CommandLine.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include <cctype>
|
|
using namespace llvm;
|
|
|
|
static cl::opt<bool>
|
|
EnableMCInst("enable-arm-mcinst-printer", cl::Hidden,
|
|
cl::desc("enable experimental asmprinter gunk in the arm backend"));
|
|
|
|
namespace {
|
|
class ARMAsmPrinter : public AsmPrinter {
|
|
|
|
/// Subtarget - Keep a pointer to the ARMSubtarget around so that we can
|
|
/// make the right decision when printing asm code for different targets.
|
|
const ARMSubtarget *Subtarget;
|
|
|
|
/// AFI - Keep a pointer to ARMFunctionInfo for the current
|
|
/// MachineFunction.
|
|
ARMFunctionInfo *AFI;
|
|
|
|
/// MCP - Keep a pointer to constantpool entries of the current
|
|
/// MachineFunction.
|
|
const MachineConstantPool *MCP;
|
|
|
|
public:
|
|
explicit ARMAsmPrinter(TargetMachine &TM, MCStreamer &Streamer)
|
|
: AsmPrinter(TM, Streamer), AFI(NULL), MCP(NULL) {
|
|
Subtarget = &TM.getSubtarget<ARMSubtarget>();
|
|
}
|
|
|
|
virtual const char *getPassName() const {
|
|
return "ARM Assembly Printer";
|
|
}
|
|
|
|
void printInstructionThroughMCStreamer(const MachineInstr *MI);
|
|
|
|
|
|
void printOperand(const MachineInstr *MI, int OpNum, raw_ostream &O,
|
|
const char *Modifier = 0);
|
|
void printSOImmOperand(const MachineInstr *MI, int OpNum, raw_ostream &O);
|
|
void printSOImm2PartOperand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O);
|
|
void printSORegOperand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O);
|
|
void printAddrMode2Operand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O);
|
|
void printAddrMode2OffsetOperand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O);
|
|
void printAddrMode3Operand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O);
|
|
void printAddrMode3OffsetOperand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O);
|
|
void printAddrMode4Operand(const MachineInstr *MI, int OpNum,raw_ostream &O,
|
|
const char *Modifier = 0);
|
|
void printAddrMode5Operand(const MachineInstr *MI, int OpNum,raw_ostream &O,
|
|
const char *Modifier = 0);
|
|
void printAddrMode6Operand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O);
|
|
void printAddrMode6OffsetOperand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O);
|
|
void printAddrModePCOperand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O,
|
|
const char *Modifier = 0);
|
|
void printBitfieldInvMaskImmOperand (const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O);
|
|
|
|
void printThumbS4ImmOperand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O);
|
|
void printThumbITMask(const MachineInstr *MI, int OpNum, raw_ostream &O);
|
|
void printThumbAddrModeRROperand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O);
|
|
void printThumbAddrModeRI5Operand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O,
|
|
unsigned Scale);
|
|
void printThumbAddrModeS1Operand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O);
|
|
void printThumbAddrModeS2Operand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O);
|
|
void printThumbAddrModeS4Operand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O);
|
|
void printThumbAddrModeSPOperand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O);
|
|
|
|
void printT2SOOperand(const MachineInstr *MI, int OpNum, raw_ostream &O);
|
|
void printT2AddrModeImm12Operand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O);
|
|
void printT2AddrModeImm8Operand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O);
|
|
void printT2AddrModeImm8s4Operand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O);
|
|
void printT2AddrModeImm8OffsetOperand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O);
|
|
void printT2AddrModeImm8s4OffsetOperand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O) {}
|
|
void printT2AddrModeSoRegOperand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O);
|
|
|
|
void printCPSOptionOperand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O) {}
|
|
void printMSRMaskOperand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O) {}
|
|
void printNegZeroOperand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O) {}
|
|
void printPredicateOperand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O);
|
|
void printMandatoryPredicateOperand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O);
|
|
void printSBitModifierOperand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O);
|
|
void printPCLabel(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O);
|
|
void printRegisterList(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O);
|
|
void printCPInstOperand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O,
|
|
const char *Modifier);
|
|
void printJTBlockOperand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O);
|
|
void printJT2BlockOperand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O);
|
|
void printTBAddrMode(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O);
|
|
void printNoHashImmediate(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O);
|
|
void printVFPf32ImmOperand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O);
|
|
void printVFPf64ImmOperand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O);
|
|
void printNEONModImmOperand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O);
|
|
|
|
virtual bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNum,
|
|
unsigned AsmVariant, const char *ExtraCode,
|
|
raw_ostream &O);
|
|
virtual bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNum,
|
|
unsigned AsmVariant,
|
|
const char *ExtraCode, raw_ostream &O);
|
|
|
|
void printInstruction(const MachineInstr *MI, raw_ostream &O); // autogen
|
|
static const char *getRegisterName(unsigned RegNo);
|
|
|
|
virtual void EmitInstruction(const MachineInstr *MI);
|
|
bool runOnMachineFunction(MachineFunction &F);
|
|
|
|
virtual void EmitConstantPool() {} // we emit constant pools customly!
|
|
virtual void EmitFunctionEntryLabel();
|
|
void EmitStartOfAsmFile(Module &M);
|
|
void EmitEndOfAsmFile(Module &M);
|
|
|
|
MCSymbol *GetARMSetPICJumpTableLabel2(unsigned uid, unsigned uid2,
|
|
const MachineBasicBlock *MBB) const;
|
|
MCSymbol *GetARMJTIPICJumpTableLabel2(unsigned uid, unsigned uid2) const;
|
|
|
|
/// EmitMachineConstantPoolValue - Print a machine constantpool value to
|
|
/// the .s file.
|
|
virtual void EmitMachineConstantPoolValue(MachineConstantPoolValue *MCPV) {
|
|
SmallString<128> Str;
|
|
raw_svector_ostream OS(Str);
|
|
EmitMachineConstantPoolValue(MCPV, OS);
|
|
OutStreamer.EmitRawText(OS.str());
|
|
}
|
|
|
|
void EmitMachineConstantPoolValue(MachineConstantPoolValue *MCPV,
|
|
raw_ostream &O) {
|
|
switch (TM.getTargetData()->getTypeAllocSize(MCPV->getType())) {
|
|
case 1: O << MAI->getData8bitsDirective(0); break;
|
|
case 2: O << MAI->getData16bitsDirective(0); break;
|
|
case 4: O << MAI->getData32bitsDirective(0); break;
|
|
default: assert(0 && "Unknown CPV size");
|
|
}
|
|
|
|
ARMConstantPoolValue *ACPV = static_cast<ARMConstantPoolValue*>(MCPV);
|
|
|
|
if (ACPV->isLSDA()) {
|
|
O << MAI->getPrivateGlobalPrefix() << "_LSDA_" << getFunctionNumber();
|
|
} else if (ACPV->isBlockAddress()) {
|
|
O << *GetBlockAddressSymbol(ACPV->getBlockAddress());
|
|
} else if (ACPV->isGlobalValue()) {
|
|
const GlobalValue *GV = ACPV->getGV();
|
|
bool isIndirect = Subtarget->isTargetDarwin() &&
|
|
Subtarget->GVIsIndirectSymbol(GV, TM.getRelocationModel());
|
|
if (!isIndirect)
|
|
O << *Mang->getSymbol(GV);
|
|
else {
|
|
// FIXME: Remove this when Darwin transition to @GOT like syntax.
|
|
MCSymbol *Sym = GetSymbolWithGlobalValueBase(GV, "$non_lazy_ptr");
|
|
O << *Sym;
|
|
|
|
MachineModuleInfoMachO &MMIMachO =
|
|
MMI->getObjFileInfo<MachineModuleInfoMachO>();
|
|
MachineModuleInfoImpl::StubValueTy &StubSym =
|
|
GV->hasHiddenVisibility() ? MMIMachO.getHiddenGVStubEntry(Sym) :
|
|
MMIMachO.getGVStubEntry(Sym);
|
|
if (StubSym.getPointer() == 0)
|
|
StubSym = MachineModuleInfoImpl::
|
|
StubValueTy(Mang->getSymbol(GV), !GV->hasInternalLinkage());
|
|
}
|
|
} else {
|
|
assert(ACPV->isExtSymbol() && "unrecognized constant pool value");
|
|
O << *GetExternalSymbolSymbol(ACPV->getSymbol());
|
|
}
|
|
|
|
if (ACPV->hasModifier()) O << "(" << ACPV->getModifier() << ")";
|
|
if (ACPV->getPCAdjustment() != 0) {
|
|
O << "-(" << MAI->getPrivateGlobalPrefix() << "PC"
|
|
<< getFunctionNumber() << "_" << ACPV->getLabelId()
|
|
<< "+" << (unsigned)ACPV->getPCAdjustment();
|
|
if (ACPV->mustAddCurrentAddress())
|
|
O << "-.";
|
|
O << ')';
|
|
}
|
|
}
|
|
};
|
|
} // end of anonymous namespace
|
|
|
|
#include "ARMGenAsmWriter.inc"
|
|
|
|
void ARMAsmPrinter::EmitFunctionEntryLabel() {
|
|
if (AFI->isThumbFunction()) {
|
|
OutStreamer.EmitRawText(StringRef("\t.code\t16"));
|
|
if (!Subtarget->isTargetDarwin())
|
|
OutStreamer.EmitRawText(StringRef("\t.thumb_func"));
|
|
else {
|
|
// This needs to emit to a temporary string to get properly quoted
|
|
// MCSymbols when they have spaces in them.
|
|
SmallString<128> Tmp;
|
|
raw_svector_ostream OS(Tmp);
|
|
OS << "\t.thumb_func\t" << *CurrentFnSym;
|
|
OutStreamer.EmitRawText(OS.str());
|
|
}
|
|
}
|
|
|
|
OutStreamer.EmitLabel(CurrentFnSym);
|
|
}
|
|
|
|
/// runOnMachineFunction - This uses the printInstruction()
|
|
/// method to print assembly for each instruction.
|
|
///
|
|
bool ARMAsmPrinter::runOnMachineFunction(MachineFunction &MF) {
|
|
AFI = MF.getInfo<ARMFunctionInfo>();
|
|
MCP = MF.getConstantPool();
|
|
|
|
return AsmPrinter::runOnMachineFunction(MF);
|
|
}
|
|
|
|
void ARMAsmPrinter::printOperand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O, const char *Modifier) {
|
|
const MachineOperand &MO = MI->getOperand(OpNum);
|
|
unsigned TF = MO.getTargetFlags();
|
|
|
|
switch (MO.getType()) {
|
|
default:
|
|
assert(0 && "<unknown operand type>");
|
|
case MachineOperand::MO_Register: {
|
|
unsigned Reg = MO.getReg();
|
|
assert(TargetRegisterInfo::isPhysicalRegister(Reg));
|
|
if (Modifier && strcmp(Modifier, "dregpair") == 0) {
|
|
unsigned DRegLo = TM.getRegisterInfo()->getSubReg(Reg, ARM::dsub_0);
|
|
unsigned DRegHi = TM.getRegisterInfo()->getSubReg(Reg, ARM::dsub_1);
|
|
O << '{'
|
|
<< getRegisterName(DRegLo) << ',' << getRegisterName(DRegHi)
|
|
<< '}';
|
|
} else if (Modifier && strcmp(Modifier, "lane") == 0) {
|
|
unsigned RegNum = ARMRegisterInfo::getRegisterNumbering(Reg);
|
|
unsigned DReg =
|
|
TM.getRegisterInfo()->getMatchingSuperReg(Reg,
|
|
RegNum & 1 ? ARM::ssub_1 : ARM::ssub_0, &ARM::DPR_VFP2RegClass);
|
|
O << getRegisterName(DReg) << '[' << (RegNum & 1) << ']';
|
|
} else {
|
|
assert(!MO.getSubReg() && "Subregs should be eliminated!");
|
|
O << getRegisterName(Reg);
|
|
}
|
|
break;
|
|
}
|
|
case MachineOperand::MO_Immediate: {
|
|
int64_t Imm = MO.getImm();
|
|
O << '#';
|
|
if ((Modifier && strcmp(Modifier, "lo16") == 0) ||
|
|
(TF & ARMII::MO_LO16))
|
|
O << ":lower16:";
|
|
else if ((Modifier && strcmp(Modifier, "hi16") == 0) ||
|
|
(TF & ARMII::MO_HI16))
|
|
O << ":upper16:";
|
|
O << Imm;
|
|
break;
|
|
}
|
|
case MachineOperand::MO_MachineBasicBlock:
|
|
O << *MO.getMBB()->getSymbol();
|
|
return;
|
|
case MachineOperand::MO_GlobalAddress: {
|
|
bool isCallOp = Modifier && !strcmp(Modifier, "call");
|
|
const GlobalValue *GV = MO.getGlobal();
|
|
|
|
if ((Modifier && strcmp(Modifier, "lo16") == 0) ||
|
|
(TF & ARMII::MO_LO16))
|
|
O << ":lower16:";
|
|
else if ((Modifier && strcmp(Modifier, "hi16") == 0) ||
|
|
(TF & ARMII::MO_HI16))
|
|
O << ":upper16:";
|
|
O << *Mang->getSymbol(GV);
|
|
|
|
printOffset(MO.getOffset(), O);
|
|
|
|
if (isCallOp && Subtarget->isTargetELF() &&
|
|
TM.getRelocationModel() == Reloc::PIC_)
|
|
O << "(PLT)";
|
|
break;
|
|
}
|
|
case MachineOperand::MO_ExternalSymbol: {
|
|
bool isCallOp = Modifier && !strcmp(Modifier, "call");
|
|
O << *GetExternalSymbolSymbol(MO.getSymbolName());
|
|
|
|
if (isCallOp && Subtarget->isTargetELF() &&
|
|
TM.getRelocationModel() == Reloc::PIC_)
|
|
O << "(PLT)";
|
|
break;
|
|
}
|
|
case MachineOperand::MO_ConstantPoolIndex:
|
|
O << *GetCPISymbol(MO.getIndex());
|
|
break;
|
|
case MachineOperand::MO_JumpTableIndex:
|
|
O << *GetJTISymbol(MO.getIndex());
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void printSOImm(raw_ostream &O, int64_t V, bool VerboseAsm,
|
|
const MCAsmInfo *MAI) {
|
|
// Break it up into two parts that make up a shifter immediate.
|
|
V = ARM_AM::getSOImmVal(V);
|
|
assert(V != -1 && "Not a valid so_imm value!");
|
|
|
|
unsigned Imm = ARM_AM::getSOImmValImm(V);
|
|
unsigned Rot = ARM_AM::getSOImmValRot(V);
|
|
|
|
// Print low-level immediate formation info, per
|
|
// A5.1.3: "Data-processing operands - Immediate".
|
|
if (Rot) {
|
|
O << "#" << Imm << ", " << Rot;
|
|
// Pretty printed version.
|
|
if (VerboseAsm) {
|
|
O << "\t" << MAI->getCommentString() << ' ';
|
|
O << (int)ARM_AM::rotr32(Imm, Rot);
|
|
}
|
|
} else {
|
|
O << "#" << Imm;
|
|
}
|
|
}
|
|
|
|
/// printSOImmOperand - SOImm is 4-bit rotate amount in bits 8-11 with 8-bit
|
|
/// immediate in bits 0-7.
|
|
void ARMAsmPrinter::printSOImmOperand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O) {
|
|
const MachineOperand &MO = MI->getOperand(OpNum);
|
|
assert(MO.isImm() && "Not a valid so_imm value!");
|
|
printSOImm(O, MO.getImm(), isVerbose(), MAI);
|
|
}
|
|
|
|
/// printSOImm2PartOperand - SOImm is broken into two pieces using a 'mov'
|
|
/// followed by an 'orr' to materialize.
|
|
void ARMAsmPrinter::printSOImm2PartOperand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O) {
|
|
const MachineOperand &MO = MI->getOperand(OpNum);
|
|
assert(MO.isImm() && "Not a valid so_imm value!");
|
|
unsigned V1 = ARM_AM::getSOImmTwoPartFirst(MO.getImm());
|
|
unsigned V2 = ARM_AM::getSOImmTwoPartSecond(MO.getImm());
|
|
printSOImm(O, V1, isVerbose(), MAI);
|
|
O << "\n\torr";
|
|
printPredicateOperand(MI, 2, O);
|
|
O << "\t";
|
|
printOperand(MI, 0, O);
|
|
O << ", ";
|
|
printOperand(MI, 0, O);
|
|
O << ", ";
|
|
printSOImm(O, V2, isVerbose(), MAI);
|
|
}
|
|
|
|
// 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 ARMAsmPrinter::printSORegOperand(const MachineInstr *MI, int Op,
|
|
raw_ostream &O) {
|
|
const MachineOperand &MO1 = MI->getOperand(Op);
|
|
const MachineOperand &MO2 = MI->getOperand(Op+1);
|
|
const MachineOperand &MO3 = MI->getOperand(Op+2);
|
|
|
|
O << getRegisterName(MO1.getReg());
|
|
|
|
// Print the shift opc.
|
|
O << ", "
|
|
<< ARM_AM::getShiftOpcStr(ARM_AM::getSORegShOp(MO3.getImm()))
|
|
<< " ";
|
|
|
|
if (MO2.getReg()) {
|
|
O << getRegisterName(MO2.getReg());
|
|
assert(ARM_AM::getSORegOffset(MO3.getImm()) == 0);
|
|
} else {
|
|
O << "#" << ARM_AM::getSORegOffset(MO3.getImm());
|
|
}
|
|
}
|
|
|
|
void ARMAsmPrinter::printAddrMode2Operand(const MachineInstr *MI, int Op,
|
|
raw_ostream &O) {
|
|
const MachineOperand &MO1 = MI->getOperand(Op);
|
|
const MachineOperand &MO2 = MI->getOperand(Op+1);
|
|
const MachineOperand &MO3 = MI->getOperand(Op+2);
|
|
|
|
if (!MO1.isReg()) { // FIXME: This is for CP entries, but isn't right.
|
|
printOperand(MI, Op, O);
|
|
return;
|
|
}
|
|
|
|
O << "[" << getRegisterName(MO1.getReg());
|
|
|
|
if (!MO2.getReg()) {
|
|
if (ARM_AM::getAM2Offset(MO3.getImm())) // Don't print +0.
|
|
O << ", #"
|
|
<< ARM_AM::getAddrOpcStr(ARM_AM::getAM2Op(MO3.getImm()))
|
|
<< ARM_AM::getAM2Offset(MO3.getImm());
|
|
O << "]";
|
|
return;
|
|
}
|
|
|
|
O << ", "
|
|
<< ARM_AM::getAddrOpcStr(ARM_AM::getAM2Op(MO3.getImm()))
|
|
<< getRegisterName(MO2.getReg());
|
|
|
|
if (unsigned ShImm = ARM_AM::getAM2Offset(MO3.getImm()))
|
|
O << ", "
|
|
<< ARM_AM::getShiftOpcStr(ARM_AM::getAM2ShiftOpc(MO3.getImm()))
|
|
<< " #" << ShImm;
|
|
O << "]";
|
|
}
|
|
|
|
void ARMAsmPrinter::printAddrMode2OffsetOperand(const MachineInstr *MI, int Op,
|
|
raw_ostream &O) {
|
|
const MachineOperand &MO1 = MI->getOperand(Op);
|
|
const MachineOperand &MO2 = MI->getOperand(Op+1);
|
|
|
|
if (!MO1.getReg()) {
|
|
unsigned ImmOffs = ARM_AM::getAM2Offset(MO2.getImm());
|
|
O << "#"
|
|
<< ARM_AM::getAddrOpcStr(ARM_AM::getAM2Op(MO2.getImm()))
|
|
<< ImmOffs;
|
|
return;
|
|
}
|
|
|
|
O << ARM_AM::getAddrOpcStr(ARM_AM::getAM2Op(MO2.getImm()))
|
|
<< getRegisterName(MO1.getReg());
|
|
|
|
if (unsigned ShImm = ARM_AM::getAM2Offset(MO2.getImm()))
|
|
O << ", "
|
|
<< ARM_AM::getShiftOpcStr(ARM_AM::getAM2ShiftOpc(MO2.getImm()))
|
|
<< " #" << ShImm;
|
|
}
|
|
|
|
void ARMAsmPrinter::printAddrMode3Operand(const MachineInstr *MI, int Op,
|
|
raw_ostream &O) {
|
|
const MachineOperand &MO1 = MI->getOperand(Op);
|
|
const MachineOperand &MO2 = MI->getOperand(Op+1);
|
|
const MachineOperand &MO3 = MI->getOperand(Op+2);
|
|
|
|
assert(TargetRegisterInfo::isPhysicalRegister(MO1.getReg()));
|
|
O << "[" << getRegisterName(MO1.getReg());
|
|
|
|
if (MO2.getReg()) {
|
|
O << ", "
|
|
<< (char)ARM_AM::getAM3Op(MO3.getImm())
|
|
<< getRegisterName(MO2.getReg())
|
|
<< "]";
|
|
return;
|
|
}
|
|
|
|
if (unsigned ImmOffs = ARM_AM::getAM3Offset(MO3.getImm()))
|
|
O << ", #"
|
|
<< ARM_AM::getAddrOpcStr(ARM_AM::getAM3Op(MO3.getImm()))
|
|
<< ImmOffs;
|
|
O << "]";
|
|
}
|
|
|
|
void ARMAsmPrinter::printAddrMode3OffsetOperand(const MachineInstr *MI, int Op,
|
|
raw_ostream &O){
|
|
const MachineOperand &MO1 = MI->getOperand(Op);
|
|
const MachineOperand &MO2 = MI->getOperand(Op+1);
|
|
|
|
if (MO1.getReg()) {
|
|
O << (char)ARM_AM::getAM3Op(MO2.getImm())
|
|
<< getRegisterName(MO1.getReg());
|
|
return;
|
|
}
|
|
|
|
unsigned ImmOffs = ARM_AM::getAM3Offset(MO2.getImm());
|
|
O << "#"
|
|
<< ARM_AM::getAddrOpcStr(ARM_AM::getAM3Op(MO2.getImm()))
|
|
<< ImmOffs;
|
|
}
|
|
|
|
void ARMAsmPrinter::printAddrMode4Operand(const MachineInstr *MI, int Op,
|
|
raw_ostream &O,
|
|
const char *Modifier) {
|
|
const MachineOperand &MO2 = MI->getOperand(Op+1);
|
|
ARM_AM::AMSubMode Mode = ARM_AM::getAM4SubMode(MO2.getImm());
|
|
if (Modifier && strcmp(Modifier, "submode") == 0) {
|
|
O << ARM_AM::getAMSubModeStr(Mode);
|
|
} else if (Modifier && strcmp(Modifier, "wide") == 0) {
|
|
ARM_AM::AMSubMode Mode = ARM_AM::getAM4SubMode(MO2.getImm());
|
|
if (Mode == ARM_AM::ia)
|
|
O << ".w";
|
|
} else {
|
|
printOperand(MI, Op, O);
|
|
}
|
|
}
|
|
|
|
void ARMAsmPrinter::printAddrMode5Operand(const MachineInstr *MI, int Op,
|
|
raw_ostream &O,
|
|
const char *Modifier) {
|
|
const MachineOperand &MO1 = MI->getOperand(Op);
|
|
const MachineOperand &MO2 = MI->getOperand(Op+1);
|
|
|
|
if (!MO1.isReg()) { // FIXME: This is for CP entries, but isn't right.
|
|
printOperand(MI, Op, O);
|
|
return;
|
|
}
|
|
|
|
assert(TargetRegisterInfo::isPhysicalRegister(MO1.getReg()));
|
|
|
|
if (Modifier && strcmp(Modifier, "submode") == 0) {
|
|
ARM_AM::AMSubMode Mode = ARM_AM::getAM5SubMode(MO2.getImm());
|
|
O << ARM_AM::getAMSubModeStr(Mode);
|
|
return;
|
|
} else if (Modifier && strcmp(Modifier, "base") == 0) {
|
|
// Used for FSTM{D|S} and LSTM{D|S} operations.
|
|
O << getRegisterName(MO1.getReg());
|
|
return;
|
|
}
|
|
|
|
O << "[" << getRegisterName(MO1.getReg());
|
|
|
|
if (unsigned ImmOffs = ARM_AM::getAM5Offset(MO2.getImm())) {
|
|
O << ", #"
|
|
<< ARM_AM::getAddrOpcStr(ARM_AM::getAM5Op(MO2.getImm()))
|
|
<< ImmOffs*4;
|
|
}
|
|
O << "]";
|
|
}
|
|
|
|
void ARMAsmPrinter::printAddrMode6Operand(const MachineInstr *MI, int Op,
|
|
raw_ostream &O) {
|
|
const MachineOperand &MO1 = MI->getOperand(Op);
|
|
const MachineOperand &MO2 = MI->getOperand(Op+1);
|
|
|
|
O << "[" << getRegisterName(MO1.getReg());
|
|
if (MO2.getImm()) {
|
|
// FIXME: Both darwin as and GNU as violate ARM docs here.
|
|
O << ", :" << MO2.getImm();
|
|
}
|
|
O << "]";
|
|
}
|
|
|
|
void ARMAsmPrinter::printAddrMode6OffsetOperand(const MachineInstr *MI, int Op,
|
|
raw_ostream &O){
|
|
const MachineOperand &MO = MI->getOperand(Op);
|
|
if (MO.getReg() == 0)
|
|
O << "!";
|
|
else
|
|
O << ", " << getRegisterName(MO.getReg());
|
|
}
|
|
|
|
void ARMAsmPrinter::printAddrModePCOperand(const MachineInstr *MI, int Op,
|
|
raw_ostream &O,
|
|
const char *Modifier) {
|
|
if (Modifier && strcmp(Modifier, "label") == 0) {
|
|
printPCLabel(MI, Op+1, O);
|
|
return;
|
|
}
|
|
|
|
const MachineOperand &MO1 = MI->getOperand(Op);
|
|
assert(TargetRegisterInfo::isPhysicalRegister(MO1.getReg()));
|
|
O << "[pc, " << getRegisterName(MO1.getReg()) << "]";
|
|
}
|
|
|
|
void
|
|
ARMAsmPrinter::printBitfieldInvMaskImmOperand(const MachineInstr *MI, int Op,
|
|
raw_ostream &O) {
|
|
const MachineOperand &MO = MI->getOperand(Op);
|
|
uint32_t v = ~MO.getImm();
|
|
int32_t lsb = CountTrailingZeros_32(v);
|
|
int32_t width = (32 - CountLeadingZeros_32 (v)) - lsb;
|
|
assert(MO.isImm() && "Not a valid bf_inv_mask_imm value!");
|
|
O << "#" << lsb << ", #" << width;
|
|
}
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
void ARMAsmPrinter::printThumbS4ImmOperand(const MachineInstr *MI, int Op,
|
|
raw_ostream &O) {
|
|
O << "#" << MI->getOperand(Op).getImm() * 4;
|
|
}
|
|
|
|
void
|
|
ARMAsmPrinter::printThumbITMask(const MachineInstr *MI, int Op,
|
|
raw_ostream &O) {
|
|
// (3 - the number of trailing zeros) is the number of then / else.
|
|
unsigned Mask = MI->getOperand(Op).getImm();
|
|
unsigned CondBit0 = Mask >> 4 & 1;
|
|
unsigned NumTZ = CountTrailingZeros_32(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
|
|
ARMAsmPrinter::printThumbAddrModeRROperand(const MachineInstr *MI, int Op,
|
|
raw_ostream &O) {
|
|
const MachineOperand &MO1 = MI->getOperand(Op);
|
|
const MachineOperand &MO2 = MI->getOperand(Op+1);
|
|
O << "[" << getRegisterName(MO1.getReg());
|
|
O << ", " << getRegisterName(MO2.getReg()) << "]";
|
|
}
|
|
|
|
void
|
|
ARMAsmPrinter::printThumbAddrModeRI5Operand(const MachineInstr *MI, int Op,
|
|
raw_ostream &O,
|
|
unsigned Scale) {
|
|
const MachineOperand &MO1 = MI->getOperand(Op);
|
|
const MachineOperand &MO2 = MI->getOperand(Op+1);
|
|
const MachineOperand &MO3 = MI->getOperand(Op+2);
|
|
|
|
if (!MO1.isReg()) { // FIXME: This is for CP entries, but isn't right.
|
|
printOperand(MI, Op, O);
|
|
return;
|
|
}
|
|
|
|
O << "[" << getRegisterName(MO1.getReg());
|
|
if (MO3.getReg())
|
|
O << ", " << getRegisterName(MO3.getReg());
|
|
else if (unsigned ImmOffs = MO2.getImm())
|
|
O << ", #" << ImmOffs * Scale;
|
|
O << "]";
|
|
}
|
|
|
|
void
|
|
ARMAsmPrinter::printThumbAddrModeS1Operand(const MachineInstr *MI, int Op,
|
|
raw_ostream &O) {
|
|
printThumbAddrModeRI5Operand(MI, Op, O, 1);
|
|
}
|
|
void
|
|
ARMAsmPrinter::printThumbAddrModeS2Operand(const MachineInstr *MI, int Op,
|
|
raw_ostream &O) {
|
|
printThumbAddrModeRI5Operand(MI, Op, O, 2);
|
|
}
|
|
void
|
|
ARMAsmPrinter::printThumbAddrModeS4Operand(const MachineInstr *MI, int Op,
|
|
raw_ostream &O) {
|
|
printThumbAddrModeRI5Operand(MI, Op, O, 4);
|
|
}
|
|
|
|
void ARMAsmPrinter::printThumbAddrModeSPOperand(const MachineInstr *MI,int Op,
|
|
raw_ostream &O) {
|
|
const MachineOperand &MO1 = MI->getOperand(Op);
|
|
const MachineOperand &MO2 = MI->getOperand(Op+1);
|
|
O << "[" << getRegisterName(MO1.getReg());
|
|
if (unsigned ImmOffs = MO2.getImm())
|
|
O << ", #" << ImmOffs*4;
|
|
O << "]";
|
|
}
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
// 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 ARMAsmPrinter::printT2SOOperand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O) {
|
|
const MachineOperand &MO1 = MI->getOperand(OpNum);
|
|
const MachineOperand &MO2 = MI->getOperand(OpNum+1);
|
|
|
|
unsigned Reg = MO1.getReg();
|
|
assert(TargetRegisterInfo::isPhysicalRegister(Reg));
|
|
O << getRegisterName(Reg);
|
|
|
|
// Print the shift opc.
|
|
O << ", "
|
|
<< ARM_AM::getShiftOpcStr(ARM_AM::getSORegShOp(MO2.getImm()))
|
|
<< " ";
|
|
|
|
assert(MO2.isImm() && "Not a valid t2_so_reg value!");
|
|
O << "#" << ARM_AM::getSORegOffset(MO2.getImm());
|
|
}
|
|
|
|
void ARMAsmPrinter::printT2AddrModeImm12Operand(const MachineInstr *MI,
|
|
int OpNum,
|
|
raw_ostream &O) {
|
|
const MachineOperand &MO1 = MI->getOperand(OpNum);
|
|
const MachineOperand &MO2 = MI->getOperand(OpNum+1);
|
|
|
|
O << "[" << getRegisterName(MO1.getReg());
|
|
|
|
unsigned OffImm = MO2.getImm();
|
|
if (OffImm) // Don't print +0.
|
|
O << ", #" << OffImm;
|
|
O << "]";
|
|
}
|
|
|
|
void ARMAsmPrinter::printT2AddrModeImm8Operand(const MachineInstr *MI,
|
|
int OpNum,
|
|
raw_ostream &O) {
|
|
const MachineOperand &MO1 = MI->getOperand(OpNum);
|
|
const MachineOperand &MO2 = MI->getOperand(OpNum+1);
|
|
|
|
O << "[" << getRegisterName(MO1.getReg());
|
|
|
|
int32_t OffImm = (int32_t)MO2.getImm();
|
|
// Don't print +0.
|
|
if (OffImm < 0)
|
|
O << ", #-" << -OffImm;
|
|
else if (OffImm > 0)
|
|
O << ", #" << OffImm;
|
|
O << "]";
|
|
}
|
|
|
|
void ARMAsmPrinter::printT2AddrModeImm8s4Operand(const MachineInstr *MI,
|
|
int OpNum,
|
|
raw_ostream &O) {
|
|
const MachineOperand &MO1 = MI->getOperand(OpNum);
|
|
const MachineOperand &MO2 = MI->getOperand(OpNum+1);
|
|
|
|
O << "[" << getRegisterName(MO1.getReg());
|
|
|
|
int32_t OffImm = (int32_t)MO2.getImm() / 4;
|
|
// Don't print +0.
|
|
if (OffImm < 0)
|
|
O << ", #-" << -OffImm * 4;
|
|
else if (OffImm > 0)
|
|
O << ", #" << OffImm * 4;
|
|
O << "]";
|
|
}
|
|
|
|
void ARMAsmPrinter::printT2AddrModeImm8OffsetOperand(const MachineInstr *MI,
|
|
int OpNum,
|
|
raw_ostream &O) {
|
|
const MachineOperand &MO1 = MI->getOperand(OpNum);
|
|
int32_t OffImm = (int32_t)MO1.getImm();
|
|
// Don't print +0.
|
|
if (OffImm < 0)
|
|
O << "#-" << -OffImm;
|
|
else if (OffImm > 0)
|
|
O << "#" << OffImm;
|
|
}
|
|
|
|
void ARMAsmPrinter::printT2AddrModeSoRegOperand(const MachineInstr *MI,
|
|
int OpNum,
|
|
raw_ostream &O) {
|
|
const MachineOperand &MO1 = MI->getOperand(OpNum);
|
|
const MachineOperand &MO2 = MI->getOperand(OpNum+1);
|
|
const MachineOperand &MO3 = MI->getOperand(OpNum+2);
|
|
|
|
O << "[" << getRegisterName(MO1.getReg());
|
|
|
|
assert(MO2.getReg() && "Invalid so_reg load / store address!");
|
|
O << ", " << getRegisterName(MO2.getReg());
|
|
|
|
unsigned ShAmt = MO3.getImm();
|
|
if (ShAmt) {
|
|
assert(ShAmt <= 3 && "Not a valid Thumb2 addressing mode!");
|
|
O << ", lsl #" << ShAmt;
|
|
}
|
|
O << "]";
|
|
}
|
|
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
void ARMAsmPrinter::printPredicateOperand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O) {
|
|
ARMCC::CondCodes CC = (ARMCC::CondCodes)MI->getOperand(OpNum).getImm();
|
|
if (CC != ARMCC::AL)
|
|
O << ARMCondCodeToString(CC);
|
|
}
|
|
|
|
void ARMAsmPrinter::printMandatoryPredicateOperand(const MachineInstr *MI,
|
|
int OpNum,
|
|
raw_ostream &O) {
|
|
ARMCC::CondCodes CC = (ARMCC::CondCodes)MI->getOperand(OpNum).getImm();
|
|
O << ARMCondCodeToString(CC);
|
|
}
|
|
|
|
void ARMAsmPrinter::printSBitModifierOperand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O){
|
|
unsigned Reg = MI->getOperand(OpNum).getReg();
|
|
if (Reg) {
|
|
assert(Reg == ARM::CPSR && "Expect ARM CPSR register!");
|
|
O << 's';
|
|
}
|
|
}
|
|
|
|
void ARMAsmPrinter::printPCLabel(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O) {
|
|
int Id = (int)MI->getOperand(OpNum).getImm();
|
|
O << MAI->getPrivateGlobalPrefix()
|
|
<< "PC" << getFunctionNumber() << "_" << Id;
|
|
}
|
|
|
|
void ARMAsmPrinter::printRegisterList(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O) {
|
|
O << "{";
|
|
for (unsigned i = OpNum, e = MI->getNumOperands(); i != e; ++i) {
|
|
if (MI->getOperand(i).isImplicit())
|
|
continue;
|
|
if ((int)i != OpNum) O << ", ";
|
|
printOperand(MI, i, O);
|
|
}
|
|
O << "}";
|
|
}
|
|
|
|
void ARMAsmPrinter::printCPInstOperand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O, const char *Modifier) {
|
|
assert(Modifier && "This operand only works with a modifier!");
|
|
// There are two aspects to a CONSTANTPOOL_ENTRY operand, the label and the
|
|
// data itself.
|
|
if (!strcmp(Modifier, "label")) {
|
|
unsigned ID = MI->getOperand(OpNum).getImm();
|
|
OutStreamer.EmitLabel(GetCPISymbol(ID));
|
|
} else {
|
|
assert(!strcmp(Modifier, "cpentry") && "Unknown modifier for CPE");
|
|
unsigned CPI = MI->getOperand(OpNum).getIndex();
|
|
|
|
const MachineConstantPoolEntry &MCPE = MCP->getConstants()[CPI];
|
|
|
|
if (MCPE.isMachineConstantPoolEntry()) {
|
|
EmitMachineConstantPoolValue(MCPE.Val.MachineCPVal);
|
|
} else {
|
|
EmitGlobalConstant(MCPE.Val.ConstVal);
|
|
}
|
|
}
|
|
}
|
|
|
|
MCSymbol *ARMAsmPrinter::
|
|
GetARMSetPICJumpTableLabel2(unsigned uid, unsigned uid2,
|
|
const MachineBasicBlock *MBB) const {
|
|
SmallString<60> Name;
|
|
raw_svector_ostream(Name) << MAI->getPrivateGlobalPrefix()
|
|
<< getFunctionNumber() << '_' << uid << '_' << uid2
|
|
<< "_set_" << MBB->getNumber();
|
|
return OutContext.GetOrCreateSymbol(Name.str());
|
|
}
|
|
|
|
MCSymbol *ARMAsmPrinter::
|
|
GetARMJTIPICJumpTableLabel2(unsigned uid, unsigned uid2) const {
|
|
SmallString<60> Name;
|
|
raw_svector_ostream(Name) << MAI->getPrivateGlobalPrefix() << "JTI"
|
|
<< getFunctionNumber() << '_' << uid << '_' << uid2;
|
|
return OutContext.GetOrCreateSymbol(Name.str());
|
|
}
|
|
|
|
void ARMAsmPrinter::printJTBlockOperand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O) {
|
|
assert(!Subtarget->isThumb2() && "Thumb2 should use double-jump jumptables!");
|
|
|
|
const MachineOperand &MO1 = MI->getOperand(OpNum);
|
|
const MachineOperand &MO2 = MI->getOperand(OpNum+1); // Unique Id
|
|
|
|
unsigned JTI = MO1.getIndex();
|
|
MCSymbol *JTISymbol = GetARMJTIPICJumpTableLabel2(JTI, MO2.getImm());
|
|
// Can't use EmitLabel until instprinter happens, label comes out in the wrong
|
|
// order.
|
|
O << *JTISymbol << ":\n";
|
|
|
|
const char *JTEntryDirective = MAI->getData32bitsDirective();
|
|
|
|
const MachineJumpTableInfo *MJTI = MF->getJumpTableInfo();
|
|
const std::vector<MachineJumpTableEntry> &JT = MJTI->getJumpTables();
|
|
const std::vector<MachineBasicBlock*> &JTBBs = JT[JTI].MBBs;
|
|
bool UseSet= MAI->hasSetDirective() && TM.getRelocationModel() == Reloc::PIC_;
|
|
SmallPtrSet<MachineBasicBlock*, 8> JTSets;
|
|
for (unsigned i = 0, e = JTBBs.size(); i != e; ++i) {
|
|
MachineBasicBlock *MBB = JTBBs[i];
|
|
bool isNew = JTSets.insert(MBB);
|
|
|
|
if (UseSet && isNew) {
|
|
O << "\t.set\t"
|
|
<< *GetARMSetPICJumpTableLabel2(JTI, MO2.getImm(), MBB) << ','
|
|
<< *MBB->getSymbol() << '-' << *JTISymbol << '\n';
|
|
}
|
|
|
|
O << JTEntryDirective << ' ';
|
|
if (UseSet)
|
|
O << *GetARMSetPICJumpTableLabel2(JTI, MO2.getImm(), MBB);
|
|
else if (TM.getRelocationModel() == Reloc::PIC_)
|
|
O << *MBB->getSymbol() << '-' << *JTISymbol;
|
|
else
|
|
O << *MBB->getSymbol();
|
|
|
|
if (i != e-1)
|
|
O << '\n';
|
|
}
|
|
}
|
|
|
|
void ARMAsmPrinter::printJT2BlockOperand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O) {
|
|
const MachineOperand &MO1 = MI->getOperand(OpNum);
|
|
const MachineOperand &MO2 = MI->getOperand(OpNum+1); // Unique Id
|
|
unsigned JTI = MO1.getIndex();
|
|
|
|
MCSymbol *JTISymbol = GetARMJTIPICJumpTableLabel2(JTI, MO2.getImm());
|
|
|
|
// Can't use EmitLabel until instprinter happens, label comes out in the wrong
|
|
// order.
|
|
O << *JTISymbol << ":\n";
|
|
|
|
const MachineJumpTableInfo *MJTI = MF->getJumpTableInfo();
|
|
const std::vector<MachineJumpTableEntry> &JT = MJTI->getJumpTables();
|
|
const std::vector<MachineBasicBlock*> &JTBBs = JT[JTI].MBBs;
|
|
bool ByteOffset = false, HalfWordOffset = false;
|
|
if (MI->getOpcode() == ARM::t2TBB)
|
|
ByteOffset = true;
|
|
else if (MI->getOpcode() == ARM::t2TBH)
|
|
HalfWordOffset = true;
|
|
|
|
for (unsigned i = 0, e = JTBBs.size(); i != e; ++i) {
|
|
MachineBasicBlock *MBB = JTBBs[i];
|
|
if (ByteOffset)
|
|
O << MAI->getData8bitsDirective();
|
|
else if (HalfWordOffset)
|
|
O << MAI->getData16bitsDirective();
|
|
|
|
if (ByteOffset || HalfWordOffset)
|
|
O << '(' << *MBB->getSymbol() << "-" << *JTISymbol << ")/2";
|
|
else
|
|
O << "\tb.w " << *MBB->getSymbol();
|
|
|
|
if (i != e-1)
|
|
O << '\n';
|
|
}
|
|
}
|
|
|
|
void ARMAsmPrinter::printTBAddrMode(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O) {
|
|
O << "[pc, " << getRegisterName(MI->getOperand(OpNum).getReg());
|
|
if (MI->getOpcode() == ARM::t2TBH)
|
|
O << ", lsl #1";
|
|
O << ']';
|
|
}
|
|
|
|
void ARMAsmPrinter::printNoHashImmediate(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O) {
|
|
O << MI->getOperand(OpNum).getImm();
|
|
}
|
|
|
|
void ARMAsmPrinter::printVFPf32ImmOperand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O) {
|
|
const ConstantFP *FP = MI->getOperand(OpNum).getFPImm();
|
|
O << '#' << FP->getValueAPF().convertToFloat();
|
|
if (isVerbose()) {
|
|
O << "\t\t" << MAI->getCommentString() << ' ';
|
|
WriteAsOperand(O, FP, /*PrintType=*/false);
|
|
}
|
|
}
|
|
|
|
void ARMAsmPrinter::printVFPf64ImmOperand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O) {
|
|
const ConstantFP *FP = MI->getOperand(OpNum).getFPImm();
|
|
O << '#' << FP->getValueAPF().convertToDouble();
|
|
if (isVerbose()) {
|
|
O << "\t\t" << MAI->getCommentString() << ' ';
|
|
WriteAsOperand(O, FP, /*PrintType=*/false);
|
|
}
|
|
}
|
|
|
|
void ARMAsmPrinter::printNEONModImmOperand(const MachineInstr *MI, int OpNum,
|
|
raw_ostream &O) {
|
|
unsigned Imm = MI->getOperand(OpNum).getImm();
|
|
unsigned OpCmode = (Imm >> 8) & 0x1f;
|
|
unsigned Imm8 = Imm & 0xff;
|
|
uint64_t Val = 0;
|
|
|
|
if (OpCmode == 0xe) {
|
|
// 8-bit vector elements
|
|
Val = Imm8;
|
|
} else if ((OpCmode & 0xc) == 0x8) {
|
|
// 16-bit vector elements
|
|
unsigned ByteNum = (OpCmode & 0x6) >> 1;
|
|
Val = Imm8 << (8 * ByteNum);
|
|
} else if ((OpCmode & 0x8) == 0) {
|
|
// 32-bit vector elements, zero with one byte set
|
|
unsigned ByteNum = (OpCmode & 0x6) >> 1;
|
|
Val = Imm8 << (8 * ByteNum);
|
|
} else if ((OpCmode & 0xe) == 0xc) {
|
|
// 32-bit vector elements, one byte with low bits set
|
|
unsigned ByteNum = (OpCmode & 0x1);
|
|
Val = (Imm8 << (8 * ByteNum)) | (0xffff >> (8 * (1 - ByteNum)));
|
|
} else if (OpCmode == 0x1e) {
|
|
// 64-bit vector elements
|
|
for (unsigned ByteNum = 0; ByteNum < 8; ++ByteNum) {
|
|
if ((Imm >> ByteNum) & 1)
|
|
Val |= (uint64_t)0xff << (8 * ByteNum);
|
|
}
|
|
} else {
|
|
assert(false && "Unsupported NEON immediate");
|
|
}
|
|
O << "#0x" << utohexstr(Val);
|
|
}
|
|
|
|
bool ARMAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNum,
|
|
unsigned AsmVariant, const char *ExtraCode,
|
|
raw_ostream &O) {
|
|
// Does this asm operand have a single letter operand modifier?
|
|
if (ExtraCode && ExtraCode[0]) {
|
|
if (ExtraCode[1] != 0) return true; // Unknown modifier.
|
|
|
|
switch (ExtraCode[0]) {
|
|
default: return true; // Unknown modifier.
|
|
case 'a': // Print as a memory address.
|
|
if (MI->getOperand(OpNum).isReg()) {
|
|
O << "[" << getRegisterName(MI->getOperand(OpNum).getReg()) << "]";
|
|
return false;
|
|
}
|
|
// Fallthrough
|
|
case 'c': // Don't print "#" before an immediate operand.
|
|
if (!MI->getOperand(OpNum).isImm())
|
|
return true;
|
|
printNoHashImmediate(MI, OpNum, O);
|
|
return false;
|
|
case 'P': // Print a VFP double precision register.
|
|
case 'q': // Print a NEON quad precision register.
|
|
printOperand(MI, OpNum, O);
|
|
return false;
|
|
case 'Q':
|
|
case 'R':
|
|
case 'H':
|
|
report_fatal_error("llvm does not support 'Q', 'R', and 'H' modifiers!");
|
|
return true;
|
|
}
|
|
}
|
|
|
|
printOperand(MI, OpNum, O);
|
|
return false;
|
|
}
|
|
|
|
bool ARMAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI,
|
|
unsigned OpNum, unsigned AsmVariant,
|
|
const char *ExtraCode,
|
|
raw_ostream &O) {
|
|
if (ExtraCode && ExtraCode[0])
|
|
return true; // Unknown modifier.
|
|
|
|
const MachineOperand &MO = MI->getOperand(OpNum);
|
|
assert(MO.isReg() && "unexpected inline asm memory operand");
|
|
O << "[" << getRegisterName(MO.getReg()) << "]";
|
|
return false;
|
|
}
|
|
|
|
void ARMAsmPrinter::EmitInstruction(const MachineInstr *MI) {
|
|
if (EnableMCInst) {
|
|
printInstructionThroughMCStreamer(MI);
|
|
return;
|
|
}
|
|
|
|
if (MI->getOpcode() == ARM::CONSTPOOL_ENTRY)
|
|
EmitAlignment(2);
|
|
|
|
SmallString<128> Str;
|
|
raw_svector_ostream OS(Str);
|
|
if (MI->getOpcode() == ARM::DBG_VALUE) {
|
|
unsigned NOps = MI->getNumOperands();
|
|
assert(NOps==4);
|
|
OS << '\t' << MAI->getCommentString() << "DEBUG_VALUE: ";
|
|
// cast away const; DIetc do not take const operands for some reason.
|
|
DIVariable V(const_cast<MDNode *>(MI->getOperand(NOps-1).getMetadata()));
|
|
OS << V.getName();
|
|
OS << " <- ";
|
|
// Frame address. Currently handles register +- offset only.
|
|
assert(MI->getOperand(0).isReg() && MI->getOperand(1).isImm());
|
|
OS << '['; printOperand(MI, 0, OS); OS << '+'; printOperand(MI, 1, OS);
|
|
OS << ']';
|
|
OS << "+";
|
|
printOperand(MI, NOps-2, OS);
|
|
OutStreamer.EmitRawText(OS.str());
|
|
return;
|
|
}
|
|
|
|
printInstruction(MI, OS);
|
|
OutStreamer.EmitRawText(OS.str());
|
|
|
|
// Make sure the instruction that follows TBB is 2-byte aligned.
|
|
// FIXME: Constant island pass should insert an "ALIGN" instruction instead.
|
|
if (MI->getOpcode() == ARM::t2TBB)
|
|
EmitAlignment(1);
|
|
}
|
|
|
|
void ARMAsmPrinter::EmitStartOfAsmFile(Module &M) {
|
|
if (Subtarget->isTargetDarwin()) {
|
|
Reloc::Model RelocM = TM.getRelocationModel();
|
|
if (RelocM == Reloc::PIC_ || RelocM == Reloc::DynamicNoPIC) {
|
|
// Declare all the text sections up front (before the DWARF sections
|
|
// emitted by AsmPrinter::doInitialization) so the assembler will keep
|
|
// them together at the beginning of the object file. This helps
|
|
// avoid out-of-range branches that are due a fundamental limitation of
|
|
// the way symbol offsets are encoded with the current Darwin ARM
|
|
// relocations.
|
|
const TargetLoweringObjectFileMachO &TLOFMacho =
|
|
static_cast<const TargetLoweringObjectFileMachO &>(
|
|
getObjFileLowering());
|
|
OutStreamer.SwitchSection(TLOFMacho.getTextSection());
|
|
OutStreamer.SwitchSection(TLOFMacho.getTextCoalSection());
|
|
OutStreamer.SwitchSection(TLOFMacho.getConstTextCoalSection());
|
|
if (RelocM == Reloc::DynamicNoPIC) {
|
|
const MCSection *sect =
|
|
OutContext.getMachOSection("__TEXT", "__symbol_stub4",
|
|
MCSectionMachO::S_SYMBOL_STUBS,
|
|
12, SectionKind::getText());
|
|
OutStreamer.SwitchSection(sect);
|
|
} else {
|
|
const MCSection *sect =
|
|
OutContext.getMachOSection("__TEXT", "__picsymbolstub4",
|
|
MCSectionMachO::S_SYMBOL_STUBS,
|
|
16, SectionKind::getText());
|
|
OutStreamer.SwitchSection(sect);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Use unified assembler syntax.
|
|
OutStreamer.EmitRawText(StringRef("\t.syntax unified"));
|
|
|
|
// Emit ARM Build Attributes
|
|
if (Subtarget->isTargetELF()) {
|
|
// CPU Type
|
|
std::string CPUString = Subtarget->getCPUString();
|
|
if (CPUString != "generic")
|
|
OutStreamer.EmitRawText("\t.cpu " + Twine(CPUString));
|
|
|
|
// FIXME: Emit FPU type
|
|
if (Subtarget->hasVFP2())
|
|
OutStreamer.EmitRawText("\t.eabi_attribute " +
|
|
Twine(ARMBuildAttrs::VFP_arch) + ", 2");
|
|
|
|
// Signal various FP modes.
|
|
if (!UnsafeFPMath) {
|
|
OutStreamer.EmitRawText("\t.eabi_attribute " +
|
|
Twine(ARMBuildAttrs::ABI_FP_denormal) + ", 1");
|
|
OutStreamer.EmitRawText("\t.eabi_attribute " +
|
|
Twine(ARMBuildAttrs::ABI_FP_exceptions) + ", 1");
|
|
}
|
|
|
|
if (FiniteOnlyFPMath())
|
|
OutStreamer.EmitRawText("\t.eabi_attribute " +
|
|
Twine(ARMBuildAttrs::ABI_FP_number_model)+ ", 1");
|
|
else
|
|
OutStreamer.EmitRawText("\t.eabi_attribute " +
|
|
Twine(ARMBuildAttrs::ABI_FP_number_model)+ ", 3");
|
|
|
|
// 8-bytes alignment stuff.
|
|
OutStreamer.EmitRawText("\t.eabi_attribute " +
|
|
Twine(ARMBuildAttrs::ABI_align8_needed) + ", 1");
|
|
OutStreamer.EmitRawText("\t.eabi_attribute " +
|
|
Twine(ARMBuildAttrs::ABI_align8_preserved) + ", 1");
|
|
|
|
// Hard float. Use both S and D registers and conform to AAPCS-VFP.
|
|
if (Subtarget->isAAPCS_ABI() && FloatABIType == FloatABI::Hard) {
|
|
OutStreamer.EmitRawText("\t.eabi_attribute " +
|
|
Twine(ARMBuildAttrs::ABI_HardFP_use) + ", 3");
|
|
OutStreamer.EmitRawText("\t.eabi_attribute " +
|
|
Twine(ARMBuildAttrs::ABI_VFP_args) + ", 1");
|
|
}
|
|
// FIXME: Should we signal R9 usage?
|
|
}
|
|
}
|
|
|
|
|
|
void ARMAsmPrinter::EmitEndOfAsmFile(Module &M) {
|
|
if (Subtarget->isTargetDarwin()) {
|
|
// All darwin targets use mach-o.
|
|
const TargetLoweringObjectFileMachO &TLOFMacho =
|
|
static_cast<const TargetLoweringObjectFileMachO &>(getObjFileLowering());
|
|
MachineModuleInfoMachO &MMIMacho =
|
|
MMI->getObjFileInfo<MachineModuleInfoMachO>();
|
|
|
|
// Output non-lazy-pointers for external and common global variables.
|
|
MachineModuleInfoMachO::SymbolListTy Stubs = MMIMacho.GetGVStubList();
|
|
|
|
if (!Stubs.empty()) {
|
|
// Switch with ".non_lazy_symbol_pointer" directive.
|
|
OutStreamer.SwitchSection(TLOFMacho.getNonLazySymbolPointerSection());
|
|
EmitAlignment(2);
|
|
for (unsigned i = 0, e = Stubs.size(); i != e; ++i) {
|
|
// L_foo$stub:
|
|
OutStreamer.EmitLabel(Stubs[i].first);
|
|
// .indirect_symbol _foo
|
|
MachineModuleInfoImpl::StubValueTy &MCSym = Stubs[i].second;
|
|
OutStreamer.EmitSymbolAttribute(MCSym.getPointer(),MCSA_IndirectSymbol);
|
|
|
|
if (MCSym.getInt())
|
|
// External to current translation unit.
|
|
OutStreamer.EmitIntValue(0, 4/*size*/, 0/*addrspace*/);
|
|
else
|
|
// Internal to current translation unit.
|
|
//
|
|
// When we place the LSDA into the TEXT section, the type info pointers
|
|
// need to be indirect and pc-rel. We accomplish this by using NLPs.
|
|
// However, sometimes the types are local to the file. So we need to
|
|
// fill in the value for the NLP in those cases.
|
|
OutStreamer.EmitValue(MCSymbolRefExpr::Create(MCSym.getPointer(),
|
|
OutContext),
|
|
4/*size*/, 0/*addrspace*/);
|
|
}
|
|
|
|
Stubs.clear();
|
|
OutStreamer.AddBlankLine();
|
|
}
|
|
|
|
Stubs = MMIMacho.GetHiddenGVStubList();
|
|
if (!Stubs.empty()) {
|
|
OutStreamer.SwitchSection(getObjFileLowering().getDataSection());
|
|
EmitAlignment(2);
|
|
for (unsigned i = 0, e = Stubs.size(); i != e; ++i) {
|
|
// L_foo$stub:
|
|
OutStreamer.EmitLabel(Stubs[i].first);
|
|
// .long _foo
|
|
OutStreamer.EmitValue(MCSymbolRefExpr::
|
|
Create(Stubs[i].second.getPointer(),
|
|
OutContext),
|
|
4/*size*/, 0/*addrspace*/);
|
|
}
|
|
|
|
Stubs.clear();
|
|
OutStreamer.AddBlankLine();
|
|
}
|
|
|
|
// Funny Darwin hack: This flag tells the linker that no global symbols
|
|
// contain code that falls through to other global symbols (e.g. the obvious
|
|
// implementation of multiple entry points). If this doesn't occur, the
|
|
// linker can safely perform dead code stripping. Since LLVM never
|
|
// generates code that does this, it is always safe to set.
|
|
OutStreamer.EmitAssemblerFlag(MCAF_SubsectionsViaSymbols);
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
void ARMAsmPrinter::printInstructionThroughMCStreamer(const MachineInstr *MI) {
|
|
ARMMCInstLower MCInstLowering(OutContext, *Mang, *this);
|
|
switch (MI->getOpcode()) {
|
|
case ARM::t2MOVi32imm:
|
|
assert(0 && "Should be lowered by thumb2it pass");
|
|
default: break;
|
|
case ARM::PICADD: { // FIXME: Remove asm string from td file.
|
|
// This is a pseudo op for a label + instruction sequence, which looks like:
|
|
// LPC0:
|
|
// add r0, pc, r0
|
|
// This adds the address of LPC0 to r0.
|
|
|
|
// Emit the label.
|
|
// FIXME: MOVE TO SHARED PLACE.
|
|
unsigned Id = (unsigned)MI->getOperand(2).getImm();
|
|
const char *Prefix = MAI->getPrivateGlobalPrefix();
|
|
MCSymbol *Label =OutContext.GetOrCreateSymbol(Twine(Prefix)
|
|
+ "PC" + Twine(getFunctionNumber()) + "_" + Twine(Id));
|
|
OutStreamer.EmitLabel(Label);
|
|
|
|
|
|
// Form and emit tha dd.
|
|
MCInst AddInst;
|
|
AddInst.setOpcode(ARM::ADDrr);
|
|
AddInst.addOperand(MCOperand::CreateReg(MI->getOperand(0).getReg()));
|
|
AddInst.addOperand(MCOperand::CreateReg(ARM::PC));
|
|
AddInst.addOperand(MCOperand::CreateReg(MI->getOperand(1).getReg()));
|
|
OutStreamer.EmitInstruction(AddInst);
|
|
return;
|
|
}
|
|
case ARM::CONSTPOOL_ENTRY: { // FIXME: Remove asm string from td file.
|
|
/// CONSTPOOL_ENTRY - This instruction represents a floating constant pool
|
|
/// in the function. The first operand is the ID# for this instruction, the
|
|
/// second is the index into the MachineConstantPool that this is, the third
|
|
/// is the size in bytes of this constant pool entry.
|
|
unsigned LabelId = (unsigned)MI->getOperand(0).getImm();
|
|
unsigned CPIdx = (unsigned)MI->getOperand(1).getIndex();
|
|
|
|
EmitAlignment(2);
|
|
OutStreamer.EmitLabel(GetCPISymbol(LabelId));
|
|
|
|
const MachineConstantPoolEntry &MCPE = MCP->getConstants()[CPIdx];
|
|
if (MCPE.isMachineConstantPoolEntry())
|
|
EmitMachineConstantPoolValue(MCPE.Val.MachineCPVal);
|
|
else
|
|
EmitGlobalConstant(MCPE.Val.ConstVal);
|
|
|
|
return;
|
|
}
|
|
case ARM::MOVi2pieces: { // FIXME: Remove asmstring from td file.
|
|
// This is a hack that lowers as a two instruction sequence.
|
|
unsigned DstReg = MI->getOperand(0).getReg();
|
|
unsigned ImmVal = (unsigned)MI->getOperand(1).getImm();
|
|
|
|
unsigned SOImmValV1 = ARM_AM::getSOImmTwoPartFirst(ImmVal);
|
|
unsigned SOImmValV2 = ARM_AM::getSOImmTwoPartSecond(ImmVal);
|
|
|
|
{
|
|
MCInst TmpInst;
|
|
TmpInst.setOpcode(ARM::MOVi);
|
|
TmpInst.addOperand(MCOperand::CreateReg(DstReg));
|
|
TmpInst.addOperand(MCOperand::CreateImm(SOImmValV1));
|
|
|
|
// Predicate.
|
|
TmpInst.addOperand(MCOperand::CreateImm(MI->getOperand(2).getImm()));
|
|
TmpInst.addOperand(MCOperand::CreateReg(MI->getOperand(3).getReg()));
|
|
|
|
TmpInst.addOperand(MCOperand::CreateReg(0)); // cc_out
|
|
OutStreamer.EmitInstruction(TmpInst);
|
|
}
|
|
|
|
{
|
|
MCInst TmpInst;
|
|
TmpInst.setOpcode(ARM::ORRri);
|
|
TmpInst.addOperand(MCOperand::CreateReg(DstReg)); // dstreg
|
|
TmpInst.addOperand(MCOperand::CreateReg(DstReg)); // inreg
|
|
TmpInst.addOperand(MCOperand::CreateImm(SOImmValV2)); // so_imm
|
|
// Predicate.
|
|
TmpInst.addOperand(MCOperand::CreateImm(MI->getOperand(2).getImm()));
|
|
TmpInst.addOperand(MCOperand::CreateReg(MI->getOperand(3).getReg()));
|
|
|
|
TmpInst.addOperand(MCOperand::CreateReg(0)); // cc_out
|
|
OutStreamer.EmitInstruction(TmpInst);
|
|
}
|
|
return;
|
|
}
|
|
case ARM::MOVi32imm: { // FIXME: Remove asmstring from td file.
|
|
// This is a hack that lowers as a two instruction sequence.
|
|
unsigned DstReg = MI->getOperand(0).getReg();
|
|
const MachineOperand &MO = MI->getOperand(1);
|
|
MCOperand V1, V2;
|
|
if (MO.isImm()) {
|
|
unsigned ImmVal = (unsigned)MI->getOperand(1).getImm();
|
|
V1 = MCOperand::CreateImm(ImmVal & 65535);
|
|
V2 = MCOperand::CreateImm(ImmVal >> 16);
|
|
} else if (MO.isGlobal()) {
|
|
MCSymbol *Symbol = MCInstLowering.GetGlobalAddressSymbol(MO);
|
|
const MCSymbolRefExpr *SymRef1 =
|
|
MCSymbolRefExpr::Create(Symbol,
|
|
MCSymbolRefExpr::VK_ARM_LO16, OutContext);
|
|
const MCSymbolRefExpr *SymRef2 =
|
|
MCSymbolRefExpr::Create(Symbol,
|
|
MCSymbolRefExpr::VK_ARM_HI16, OutContext);
|
|
V1 = MCOperand::CreateExpr(SymRef1);
|
|
V2 = MCOperand::CreateExpr(SymRef2);
|
|
} else {
|
|
MI->dump();
|
|
llvm_unreachable("cannot handle this operand");
|
|
}
|
|
|
|
{
|
|
MCInst TmpInst;
|
|
TmpInst.setOpcode(ARM::MOVi16);
|
|
TmpInst.addOperand(MCOperand::CreateReg(DstReg)); // dstreg
|
|
TmpInst.addOperand(V1); // lower16(imm)
|
|
|
|
// Predicate.
|
|
TmpInst.addOperand(MCOperand::CreateImm(MI->getOperand(2).getImm()));
|
|
TmpInst.addOperand(MCOperand::CreateReg(MI->getOperand(3).getReg()));
|
|
|
|
OutStreamer.EmitInstruction(TmpInst);
|
|
}
|
|
|
|
{
|
|
MCInst TmpInst;
|
|
TmpInst.setOpcode(ARM::MOVTi16);
|
|
TmpInst.addOperand(MCOperand::CreateReg(DstReg)); // dstreg
|
|
TmpInst.addOperand(MCOperand::CreateReg(DstReg)); // srcreg
|
|
TmpInst.addOperand(V2); // upper16(imm)
|
|
|
|
// Predicate.
|
|
TmpInst.addOperand(MCOperand::CreateImm(MI->getOperand(2).getImm()));
|
|
TmpInst.addOperand(MCOperand::CreateReg(MI->getOperand(3).getReg()));
|
|
|
|
OutStreamer.EmitInstruction(TmpInst);
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
MCInst TmpInst;
|
|
MCInstLowering.Lower(MI, TmpInst);
|
|
OutStreamer.EmitInstruction(TmpInst);
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Target Registry Stuff
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
static MCInstPrinter *createARMMCInstPrinter(const Target &T,
|
|
unsigned SyntaxVariant,
|
|
const MCAsmInfo &MAI) {
|
|
if (SyntaxVariant == 0)
|
|
return new ARMInstPrinter(MAI, false);
|
|
return 0;
|
|
}
|
|
|
|
// Force static initialization.
|
|
extern "C" void LLVMInitializeARMAsmPrinter() {
|
|
RegisterAsmPrinter<ARMAsmPrinter> X(TheARMTarget);
|
|
RegisterAsmPrinter<ARMAsmPrinter> Y(TheThumbTarget);
|
|
|
|
TargetRegistry::RegisterMCInstPrinter(TheARMTarget, createARMMCInstPrinter);
|
|
TargetRegistry::RegisterMCInstPrinter(TheThumbTarget, createARMMCInstPrinter);
|
|
}
|
|
|