1 //===-- ARM64AsmPrinter.cpp - ARM64 LLVM assembly writer ------------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This file contains a printer that converts from our internal representation
11 // of machine-dependent LLVM code to the ARM64 assembly language.
13 //===----------------------------------------------------------------------===//
15 #define DEBUG_TYPE "asm-printer"
17 #include "ARM64MachineFunctionInfo.h"
18 #include "ARM64MCInstLower.h"
19 #include "ARM64RegisterInfo.h"
20 #include "InstPrinter/ARM64InstPrinter.h"
21 #include "llvm/ADT/SmallString.h"
22 #include "llvm/ADT/StringSwitch.h"
23 #include "llvm/ADT/Twine.h"
24 #include "llvm/CodeGen/AsmPrinter.h"
25 #include "llvm/CodeGen/MachineInstr.h"
26 #include "llvm/CodeGen/StackMaps.h"
27 #include "llvm/IR/DataLayout.h"
28 #include "llvm/IR/DebugInfo.h"
29 #include "llvm/MC/MCAsmInfo.h"
30 #include "llvm/MC/MCContext.h"
31 #include "llvm/MC/MCInst.h"
32 #include "llvm/MC/MCInstBuilder.h"
33 #include "llvm/MC/MCLinkerOptimizationHint.h"
34 #include "llvm/MC/MCStreamer.h"
35 #include "llvm/Support/Debug.h"
36 #include "llvm/Support/TargetRegistry.h"
41 class ARM64AsmPrinter : public AsmPrinter {
42 ARM64MCInstLower MCInstLowering;
46 ARM64AsmPrinter(TargetMachine &TM, MCStreamer &Streamer)
47 : AsmPrinter(TM, Streamer), MCInstLowering(OutContext, *Mang, *this),
48 SM(*this), ARM64FI(NULL), LOHLabelCounter(0) {}
50 virtual const char *getPassName() const { return "ARM64 Assembly Printer"; }
52 /// \brief Wrapper for MCInstLowering.lowerOperand() for the
53 /// tblgen'erated pseudo lowering.
54 bool lowerOperand(const MachineOperand &MO, MCOperand &MCOp) const {
55 return MCInstLowering.lowerOperand(MO, MCOp);
58 void LowerSTACKMAP(MCStreamer &OutStreamer, StackMaps &SM,
59 const MachineInstr &MI);
60 void LowerPATCHPOINT(MCStreamer &OutStreamer, StackMaps &SM,
61 const MachineInstr &MI);
62 /// \brief tblgen'erated driver function for lowering simple MI->MC
63 /// pseudo instructions.
64 bool emitPseudoExpansionLowering(MCStreamer &OutStreamer,
65 const MachineInstr *MI);
67 void EmitInstruction(const MachineInstr *MI);
69 void getAnalysisUsage(AnalysisUsage &AU) const {
70 AsmPrinter::getAnalysisUsage(AU);
74 bool runOnMachineFunction(MachineFunction &F) {
75 ARM64FI = F.getInfo<ARM64FunctionInfo>();
76 return AsmPrinter::runOnMachineFunction(F);
80 MachineLocation getDebugValueLocation(const MachineInstr *MI) const;
81 void printOperand(const MachineInstr *MI, unsigned OpNum, raw_ostream &O);
82 bool printAsmMRegister(const MachineOperand &MO, char Mode, raw_ostream &O);
83 bool printAsmRegInClass(const MachineOperand &MO,
84 const TargetRegisterClass *RC, bool isVector,
87 bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNum,
88 unsigned AsmVariant, const char *ExtraCode,
90 bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNum,
91 unsigned AsmVariant, const char *ExtraCode,
94 void PrintDebugValueComment(const MachineInstr *MI, raw_ostream &OS);
96 void EmitFunctionBodyEnd();
98 MCSymbol *GetCPISymbol(unsigned CPID) const;
99 void EmitEndOfAsmFile(Module &M);
100 ARM64FunctionInfo *ARM64FI;
102 /// \brief Emit the LOHs contained in ARM64FI.
105 typedef std::map<const MachineInstr *, MCSymbol *> MInstToMCSymbol;
106 MInstToMCSymbol LOHInstToLabel;
107 unsigned LOHLabelCounter;
110 } // end of anonymous namespace
112 //===----------------------------------------------------------------------===//
114 void ARM64AsmPrinter::EmitEndOfAsmFile(Module &M) {
115 // Funny Darwin hack: This flag tells the linker that no global symbols
116 // contain code that falls through to other global symbols (e.g. the obvious
117 // implementation of multiple entry points). If this doesn't occur, the
118 // linker can safely perform dead code stripping. Since LLVM never
119 // generates code that does this, it is always safe to set.
120 OutStreamer.EmitAssemblerFlag(MCAF_SubsectionsViaSymbols);
121 SM.serializeToStackMapSection();
125 ARM64AsmPrinter::getDebugValueLocation(const MachineInstr *MI) const {
126 MachineLocation Location;
127 assert(MI->getNumOperands() == 4 && "Invalid no. of machine operands!");
128 // Frame address. Currently handles register +- offset only.
129 if (MI->getOperand(0).isReg() && MI->getOperand(1).isImm())
130 Location.set(MI->getOperand(0).getReg(), MI->getOperand(1).getImm());
132 DEBUG(dbgs() << "DBG_VALUE instruction ignored! " << *MI << "\n");
137 void ARM64AsmPrinter::EmitLOHs() {
138 const ARM64FunctionInfo::MILOHDirectives &LOHs =
139 const_cast<const ARM64FunctionInfo *>(ARM64FI)
142 SmallVector<MCSymbol *, 3> MCArgs;
144 for (ARM64FunctionInfo::MILOHDirectives::const_iterator It = LOHs.begin(),
147 const ARM64FunctionInfo::MILOHArgs &MIArgs = It->getArgs();
148 for (ARM64FunctionInfo::MILOHArgs::const_iterator
149 MIArgsIt = MIArgs.begin(),
150 EndMIArgsIt = MIArgs.end();
151 MIArgsIt != EndMIArgsIt; ++MIArgsIt) {
152 MInstToMCSymbol::iterator LabelIt = LOHInstToLabel.find(*MIArgsIt);
153 assert(LabelIt != LOHInstToLabel.end() &&
154 "Label hasn't been inserted for LOH related instruction");
155 MCArgs.push_back(LabelIt->second);
157 OutStreamer.EmitLOHDirective(It->getKind(), MCArgs);
162 void ARM64AsmPrinter::EmitFunctionBodyEnd() {
163 if (!ARM64FI->getLOHRelated().empty())
167 /// GetCPISymbol - Return the symbol for the specified constant pool entry.
168 MCSymbol *ARM64AsmPrinter::GetCPISymbol(unsigned CPID) const {
169 // Darwin uses a linker-private symbol name for constant-pools (to
170 // avoid addends on the relocation?), ELF has no such concept and
171 // uses a normal private symbol.
172 if (getDataLayout().getLinkerPrivateGlobalPrefix()[0])
173 return OutContext.GetOrCreateSymbol(
174 Twine(getDataLayout().getLinkerPrivateGlobalPrefix()) + "CPI" +
175 Twine(getFunctionNumber()) + "_" + Twine(CPID));
177 return OutContext.GetOrCreateSymbol(
178 Twine(getDataLayout().getPrivateGlobalPrefix()) + "CPI" +
179 Twine(getFunctionNumber()) + "_" + Twine(CPID));
182 void ARM64AsmPrinter::printOperand(const MachineInstr *MI, unsigned OpNum,
184 const MachineOperand &MO = MI->getOperand(OpNum);
185 switch (MO.getType()) {
187 assert(0 && "<unknown operand type>");
188 case MachineOperand::MO_Register: {
189 unsigned Reg = MO.getReg();
190 assert(TargetRegisterInfo::isPhysicalRegister(Reg));
191 assert(!MO.getSubReg() && "Subregs should be eliminated!");
192 O << ARM64InstPrinter::getRegisterName(Reg);
195 case MachineOperand::MO_Immediate: {
196 int64_t Imm = MO.getImm();
203 bool ARM64AsmPrinter::printAsmMRegister(const MachineOperand &MO, char Mode,
205 unsigned Reg = MO.getReg();
208 return true; // Unknown mode.
210 Reg = getWRegFromXReg(Reg);
213 Reg = getXRegFromWReg(Reg);
217 O << ARM64InstPrinter::getRegisterName(Reg);
221 // Prints the register in MO using class RC using the offset in the
222 // new register class. This should not be used for cross class
224 bool ARM64AsmPrinter::printAsmRegInClass(const MachineOperand &MO,
225 const TargetRegisterClass *RC,
226 bool isVector, raw_ostream &O) {
227 assert(MO.isReg() && "Should only get here with a register!");
228 const ARM64RegisterInfo *RI =
229 static_cast<const ARM64RegisterInfo *>(TM.getRegisterInfo());
230 unsigned Reg = MO.getReg();
231 unsigned RegToPrint = RC->getRegister(RI->getEncodingValue(Reg));
232 assert(RI->regsOverlap(RegToPrint, Reg));
233 O << ARM64InstPrinter::getRegisterName(
234 RegToPrint, isVector ? ARM64::vreg : ARM64::NoRegAltName);
238 bool ARM64AsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNum,
240 const char *ExtraCode, raw_ostream &O) {
241 const MachineOperand &MO = MI->getOperand(OpNum);
242 // Does this asm operand have a single letter operand modifier?
243 if (ExtraCode && ExtraCode[0]) {
244 if (ExtraCode[1] != 0)
245 return true; // Unknown modifier.
247 switch (ExtraCode[0]) {
249 return true; // Unknown modifier.
250 case 'w': // Print W register
251 case 'x': // Print X register
253 return printAsmMRegister(MO, ExtraCode[0], O);
254 if (MO.isImm() && MO.getImm() == 0) {
255 unsigned Reg = ExtraCode[0] == 'w' ? ARM64::WZR : ARM64::XZR;
256 O << ARM64InstPrinter::getRegisterName(Reg);
259 printOperand(MI, OpNum, O);
261 case 'b': // Print B register.
262 case 'h': // Print H register.
263 case 's': // Print S register.
264 case 'd': // Print D register.
265 case 'q': // Print Q register.
267 const TargetRegisterClass *RC;
268 switch (ExtraCode[0]) {
270 RC = &ARM64::FPR8RegClass;
273 RC = &ARM64::FPR16RegClass;
276 RC = &ARM64::FPR32RegClass;
279 RC = &ARM64::FPR64RegClass;
282 RC = &ARM64::FPR128RegClass;
287 return printAsmRegInClass(MO, RC, false /* vector */, O);
289 printOperand(MI, OpNum, O);
294 // According to ARM, we should emit x and v registers unless we have a
297 unsigned Reg = MO.getReg();
299 // If this is a w or x register, print an x register.
300 if (ARM64::GPR32allRegClass.contains(Reg) ||
301 ARM64::GPR64allRegClass.contains(Reg))
302 return printAsmMRegister(MO, 'x', O);
304 // If this is a b, h, s, d, or q register, print it as a v register.
305 return printAsmRegInClass(MO, &ARM64::FPR128RegClass, true /* vector */, O);
308 printOperand(MI, OpNum, O);
312 bool ARM64AsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI,
313 unsigned OpNum, unsigned AsmVariant,
314 const char *ExtraCode,
316 if (ExtraCode && ExtraCode[0])
317 return true; // Unknown modifier.
319 const MachineOperand &MO = MI->getOperand(OpNum);
320 assert(MO.isReg() && "unexpected inline asm memory operand");
321 O << "[" << ARM64InstPrinter::getRegisterName(MO.getReg()) << "]";
325 void ARM64AsmPrinter::PrintDebugValueComment(const MachineInstr *MI,
327 unsigned NOps = MI->getNumOperands();
329 OS << '\t' << MAI->getCommentString() << "DEBUG_VALUE: ";
330 // cast away const; DIetc do not take const operands for some reason.
331 DIVariable V(const_cast<MDNode *>(MI->getOperand(NOps - 1).getMetadata()));
334 // Frame address. Currently handles register +- offset only.
335 assert(MI->getOperand(0).isReg() && MI->getOperand(1).isImm());
337 printOperand(MI, 0, OS);
339 printOperand(MI, 1, OS);
342 printOperand(MI, NOps - 2, OS);
345 void ARM64AsmPrinter::LowerSTACKMAP(MCStreamer &OutStreamer, StackMaps &SM,
346 const MachineInstr &MI) {
347 unsigned NumNOPBytes = MI.getOperand(1).getImm();
349 SM.recordStackMap(MI);
351 assert(NumNOPBytes % 4 == 0 && "Invalid number of NOP bytes requested!");
352 for (unsigned i = 0; i < NumNOPBytes; i += 4)
353 EmitToStreamer(OutStreamer, MCInstBuilder(ARM64::HINT).addImm(0));
356 // Lower a patchpoint of the form:
357 // [<def>], <id>, <numBytes>, <target>, <numArgs>
358 void ARM64AsmPrinter::LowerPATCHPOINT(MCStreamer &OutStreamer, StackMaps &SM,
359 const MachineInstr &MI) {
360 SM.recordPatchPoint(MI);
362 PatchPointOpers Opers(&MI);
364 int64_t CallTarget = Opers.getMetaOper(PatchPointOpers::TargetPos).getImm();
365 unsigned EncodedBytes = 0;
367 assert((CallTarget & 0xFFFFFFFFFFFF) == CallTarget &&
368 "High 16 bits of call target should be zero.");
369 unsigned ScratchReg = MI.getOperand(Opers.getNextScratchIdx()).getReg();
371 // Materialize the jump address:
372 EmitToStreamer(OutStreamer, MCInstBuilder(ARM64::MOVZWi)
374 .addImm((CallTarget >> 32) & 0xFFFF)
376 EmitToStreamer(OutStreamer, MCInstBuilder(ARM64::MOVKWi)
379 .addImm((CallTarget >> 16) & 0xFFFF)
381 EmitToStreamer(OutStreamer, MCInstBuilder(ARM64::MOVKWi)
384 .addImm(CallTarget & 0xFFFF)
386 EmitToStreamer(OutStreamer, MCInstBuilder(ARM64::BLR).addReg(ScratchReg));
389 unsigned NumBytes = Opers.getMetaOper(PatchPointOpers::NBytesPos).getImm();
390 assert(NumBytes >= EncodedBytes &&
391 "Patchpoint can't request size less than the length of a call.");
392 assert((NumBytes - EncodedBytes) % 4 == 0 &&
393 "Invalid number of NOP bytes requested!");
394 for (unsigned i = EncodedBytes; i < NumBytes; i += 4)
395 EmitToStreamer(OutStreamer, MCInstBuilder(ARM64::HINT).addImm(0));
398 // Simple pseudo-instructions have their lowering (with expansion to real
399 // instructions) auto-generated.
400 #include "ARM64GenMCPseudoLowering.inc"
402 static unsigned getRealIndexedOpcode(unsigned Opc) {
404 case ARM64::LDRXpre_isel: return ARM64::LDRXpre;
405 case ARM64::LDRWpre_isel: return ARM64::LDRWpre;
406 case ARM64::LDRDpre_isel: return ARM64::LDRDpre;
407 case ARM64::LDRSpre_isel: return ARM64::LDRSpre;
408 case ARM64::LDRBBpre_isel: return ARM64::LDRBBpre;
409 case ARM64::LDRHHpre_isel: return ARM64::LDRHHpre;
410 case ARM64::LDRSBWpre_isel: return ARM64::LDRSBWpre;
411 case ARM64::LDRSBXpre_isel: return ARM64::LDRSBXpre;
412 case ARM64::LDRSHWpre_isel: return ARM64::LDRSHWpre;
413 case ARM64::LDRSHXpre_isel: return ARM64::LDRSHXpre;
414 case ARM64::LDRSWpre_isel: return ARM64::LDRSWpre;
416 case ARM64::LDRDpost_isel: return ARM64::LDRDpost;
417 case ARM64::LDRSpost_isel: return ARM64::LDRSpost;
418 case ARM64::LDRXpost_isel: return ARM64::LDRXpost;
419 case ARM64::LDRWpost_isel: return ARM64::LDRWpost;
420 case ARM64::LDRHHpost_isel: return ARM64::LDRHHpost;
421 case ARM64::LDRBBpost_isel: return ARM64::LDRBBpost;
422 case ARM64::LDRSWpost_isel: return ARM64::LDRSWpost;
423 case ARM64::LDRSHWpost_isel: return ARM64::LDRSHWpost;
424 case ARM64::LDRSHXpost_isel: return ARM64::LDRSHXpost;
425 case ARM64::LDRSBWpost_isel: return ARM64::LDRSBWpost;
426 case ARM64::LDRSBXpost_isel: return ARM64::LDRSBXpost;
428 case ARM64::STRXpre_isel: return ARM64::STRXpre;
429 case ARM64::STRWpre_isel: return ARM64::STRWpre;
430 case ARM64::STRHHpre_isel: return ARM64::STRHHpre;
431 case ARM64::STRBBpre_isel: return ARM64::STRBBpre;
432 case ARM64::STRDpre_isel: return ARM64::STRDpre;
433 case ARM64::STRSpre_isel: return ARM64::STRSpre;
435 llvm_unreachable("Unexpected pre-indexed opcode!");
438 void ARM64AsmPrinter::EmitInstruction(const MachineInstr *MI) {
439 // Do any auto-generated pseudo lowerings.
440 if (emitPseudoExpansionLowering(OutStreamer, MI))
443 if (ARM64FI->getLOHRelated().count(MI)) {
444 // Generate a label for LOH related instruction
445 MCSymbol *LOHLabel = GetTempSymbol("loh", LOHLabelCounter++);
446 // Associate the instruction with the label
447 LOHInstToLabel[MI] = LOHLabel;
448 OutStreamer.EmitLabel(LOHLabel);
451 // Do any manual lowerings.
452 switch (MI->getOpcode()) {
455 case ARM64::DBG_VALUE: {
456 if (isVerbose() && OutStreamer.hasRawTextSupport()) {
457 SmallString<128> TmpStr;
458 raw_svector_ostream OS(TmpStr);
459 PrintDebugValueComment(MI, OS);
460 OutStreamer.EmitRawText(StringRef(OS.str()));
464 // Indexed loads and stores use a pseudo to handle complex operand
465 // tricks and writeback to the base register. We strip off the writeback
466 // operand and switch the opcode here. Post-indexed stores were handled by the
467 // tablegen'erated pseudos above. (The complex operand <--> simple
468 // operand isel is beyond tablegen's ability, so we do these manually).
469 case ARM64::LDRHHpre_isel:
470 case ARM64::LDRBBpre_isel:
471 case ARM64::LDRXpre_isel:
472 case ARM64::LDRWpre_isel:
473 case ARM64::LDRDpre_isel:
474 case ARM64::LDRSpre_isel:
475 case ARM64::LDRSBWpre_isel:
476 case ARM64::LDRSBXpre_isel:
477 case ARM64::LDRSHWpre_isel:
478 case ARM64::LDRSHXpre_isel:
479 case ARM64::LDRSWpre_isel:
480 case ARM64::LDRDpost_isel:
481 case ARM64::LDRSpost_isel:
482 case ARM64::LDRXpost_isel:
483 case ARM64::LDRWpost_isel:
484 case ARM64::LDRHHpost_isel:
485 case ARM64::LDRBBpost_isel:
486 case ARM64::LDRSWpost_isel:
487 case ARM64::LDRSHWpost_isel:
488 case ARM64::LDRSHXpost_isel:
489 case ARM64::LDRSBWpost_isel:
490 case ARM64::LDRSBXpost_isel: {
492 // For loads, the writeback operand to be skipped is the second.
493 TmpInst.setOpcode(getRealIndexedOpcode(MI->getOpcode()));
494 TmpInst.addOperand(MCOperand::CreateReg(MI->getOperand(0).getReg()));
495 TmpInst.addOperand(MCOperand::CreateReg(MI->getOperand(2).getReg()));
496 TmpInst.addOperand(MCOperand::CreateImm(MI->getOperand(3).getImm()));
497 EmitToStreamer(OutStreamer, TmpInst);
500 case ARM64::STRXpre_isel:
501 case ARM64::STRWpre_isel:
502 case ARM64::STRHHpre_isel:
503 case ARM64::STRBBpre_isel:
504 case ARM64::STRDpre_isel:
505 case ARM64::STRSpre_isel: {
507 // For loads, the writeback operand to be skipped is the first.
508 TmpInst.setOpcode(getRealIndexedOpcode(MI->getOpcode()));
509 TmpInst.addOperand(MCOperand::CreateReg(MI->getOperand(1).getReg()));
510 TmpInst.addOperand(MCOperand::CreateReg(MI->getOperand(2).getReg()));
511 TmpInst.addOperand(MCOperand::CreateImm(MI->getOperand(3).getImm()));
512 EmitToStreamer(OutStreamer, TmpInst);
516 // Tail calls use pseudo instructions so they have the proper code-gen
517 // attributes (isCall, isReturn, etc.). We lower them to the real
519 case ARM64::TCRETURNri: {
521 TmpInst.setOpcode(ARM64::BR);
522 TmpInst.addOperand(MCOperand::CreateReg(MI->getOperand(0).getReg()));
523 EmitToStreamer(OutStreamer, TmpInst);
526 case ARM64::TCRETURNdi: {
528 MCInstLowering.lowerOperand(MI->getOperand(0), Dest);
530 TmpInst.setOpcode(ARM64::B);
531 TmpInst.addOperand(Dest);
532 EmitToStreamer(OutStreamer, TmpInst);
535 case ARM64::TLSDESC_BLR: {
536 MCOperand Callee, Sym;
537 MCInstLowering.lowerOperand(MI->getOperand(0), Callee);
538 MCInstLowering.lowerOperand(MI->getOperand(1), Sym);
540 // First emit a relocation-annotation. This expands to no code, but requests
541 // the following instruction gets an R_AARCH64_TLSDESC_CALL.
543 TLSDescCall.setOpcode(ARM64::TLSDESCCALL);
544 TLSDescCall.addOperand(Sym);
545 EmitToStreamer(OutStreamer, TLSDescCall);
547 // Other than that it's just a normal indirect call to the function loaded
548 // from the descriptor.
550 BLR.setOpcode(ARM64::BLR);
551 BLR.addOperand(Callee);
552 EmitToStreamer(OutStreamer, BLR);
557 case TargetOpcode::STACKMAP:
558 return LowerSTACKMAP(OutStreamer, SM, *MI);
560 case TargetOpcode::PATCHPOINT:
561 return LowerPATCHPOINT(OutStreamer, SM, *MI);
564 // Finally, do the automated lowerings for everything else.
566 MCInstLowering.Lower(MI, TmpInst);
567 EmitToStreamer(OutStreamer, TmpInst);
570 // Force static initialization.
571 extern "C" void LLVMInitializeARM64AsmPrinter() {
572 RegisterAsmPrinter<ARM64AsmPrinter> X(TheARM64Target);