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 "ARM64Subtarget.h"
21 #include "InstPrinter/ARM64InstPrinter.h"
22 #include "llvm/ADT/SmallString.h"
23 #include "llvm/ADT/StringSwitch.h"
24 #include "llvm/ADT/Twine.h"
25 #include "llvm/CodeGen/AsmPrinter.h"
26 #include "llvm/CodeGen/MachineInstr.h"
27 #include "llvm/CodeGen/StackMaps.h"
28 #include "llvm/CodeGen/MachineModuleInfoImpls.h"
29 #include "llvm/CodeGen/TargetLoweringObjectFileImpl.h"
30 #include "llvm/IR/DataLayout.h"
31 #include "llvm/IR/DebugInfo.h"
32 #include "llvm/MC/MCAsmInfo.h"
33 #include "llvm/MC/MCContext.h"
34 #include "llvm/MC/MCInst.h"
35 #include "llvm/MC/MCInstBuilder.h"
36 #include "llvm/MC/MCLinkerOptimizationHint.h"
37 #include "llvm/MC/MCStreamer.h"
38 #include "llvm/Support/Debug.h"
39 #include "llvm/Support/TargetRegistry.h"
44 class ARM64AsmPrinter : public AsmPrinter {
45 /// Subtarget - Keep a pointer to the ARM64Subtarget around so that we can
46 /// make the right decision when printing asm code for different targets.
47 const ARM64Subtarget *Subtarget;
49 ARM64MCInstLower MCInstLowering;
53 ARM64AsmPrinter(TargetMachine &TM, MCStreamer &Streamer)
54 : AsmPrinter(TM, Streamer), Subtarget(&TM.getSubtarget<ARM64Subtarget>()),
55 MCInstLowering(OutContext, *Mang, *this), SM(*this), ARM64FI(NULL),
58 virtual const char *getPassName() const { return "ARM64 Assembly Printer"; }
60 /// \brief Wrapper for MCInstLowering.lowerOperand() for the
61 /// tblgen'erated pseudo lowering.
62 bool lowerOperand(const MachineOperand &MO, MCOperand &MCOp) const {
63 return MCInstLowering.lowerOperand(MO, MCOp);
66 void LowerSTACKMAP(MCStreamer &OutStreamer, StackMaps &SM,
67 const MachineInstr &MI);
68 void LowerPATCHPOINT(MCStreamer &OutStreamer, StackMaps &SM,
69 const MachineInstr &MI);
70 /// \brief tblgen'erated driver function for lowering simple MI->MC
71 /// pseudo instructions.
72 bool emitPseudoExpansionLowering(MCStreamer &OutStreamer,
73 const MachineInstr *MI);
75 void EmitInstruction(const MachineInstr *MI);
77 void getAnalysisUsage(AnalysisUsage &AU) const {
78 AsmPrinter::getAnalysisUsage(AU);
82 bool runOnMachineFunction(MachineFunction &F) {
83 ARM64FI = F.getInfo<ARM64FunctionInfo>();
84 return AsmPrinter::runOnMachineFunction(F);
88 MachineLocation getDebugValueLocation(const MachineInstr *MI) const;
89 void printOperand(const MachineInstr *MI, unsigned OpNum, raw_ostream &O);
90 bool printAsmMRegister(const MachineOperand &MO, char Mode, raw_ostream &O);
91 bool printAsmRegInClass(const MachineOperand &MO,
92 const TargetRegisterClass *RC, bool isVector,
95 bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNum,
96 unsigned AsmVariant, const char *ExtraCode,
98 bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNum,
99 unsigned AsmVariant, const char *ExtraCode,
102 void PrintDebugValueComment(const MachineInstr *MI, raw_ostream &OS);
104 void EmitFunctionBodyEnd();
106 MCSymbol *GetCPISymbol(unsigned CPID) const;
107 void EmitEndOfAsmFile(Module &M);
108 ARM64FunctionInfo *ARM64FI;
110 /// \brief Emit the LOHs contained in ARM64FI.
113 typedef std::map<const MachineInstr *, MCSymbol *> MInstToMCSymbol;
114 MInstToMCSymbol LOHInstToLabel;
115 unsigned LOHLabelCounter;
118 } // end of anonymous namespace
120 //===----------------------------------------------------------------------===//
122 void ARM64AsmPrinter::EmitEndOfAsmFile(Module &M) {
123 // Funny Darwin hack: This flag tells the linker that no global symbols
124 // contain code that falls through to other global symbols (e.g. the obvious
125 // implementation of multiple entry points). If this doesn't occur, the
126 // linker can safely perform dead code stripping. Since LLVM never
127 // generates code that does this, it is always safe to set.
128 OutStreamer.EmitAssemblerFlag(MCAF_SubsectionsViaSymbols);
129 SM.serializeToStackMapSection();
131 // Emit a .data.rel section containing any stubs that were created.
132 if (Subtarget->isTargetELF()) {
133 const TargetLoweringObjectFileELF &TLOFELF =
134 static_cast<const TargetLoweringObjectFileELF &>(getObjFileLowering());
136 MachineModuleInfoELF &MMIELF = MMI->getObjFileInfo<MachineModuleInfoELF>();
138 // Output stubs for external and common global variables.
139 MachineModuleInfoELF::SymbolListTy Stubs = MMIELF.GetGVStubList();
140 if (!Stubs.empty()) {
141 OutStreamer.SwitchSection(TLOFELF.getDataRelSection());
142 const DataLayout *TD = TM.getDataLayout();
144 for (unsigned i = 0, e = Stubs.size(); i != e; ++i) {
145 OutStreamer.EmitLabel(Stubs[i].first);
146 OutStreamer.EmitSymbolValue(Stubs[i].second.getPointer(),
147 TD->getPointerSize(0));
156 ARM64AsmPrinter::getDebugValueLocation(const MachineInstr *MI) const {
157 MachineLocation Location;
158 assert(MI->getNumOperands() == 4 && "Invalid no. of machine operands!");
159 // Frame address. Currently handles register +- offset only.
160 if (MI->getOperand(0).isReg() && MI->getOperand(1).isImm())
161 Location.set(MI->getOperand(0).getReg(), MI->getOperand(1).getImm());
163 DEBUG(dbgs() << "DBG_VALUE instruction ignored! " << *MI << "\n");
168 void ARM64AsmPrinter::EmitLOHs() {
169 SmallVector<MCSymbol *, 3> MCArgs;
171 for (const auto &D : ARM64FI->getLOHContainer()) {
172 for (const MachineInstr *MI : D.getArgs()) {
173 MInstToMCSymbol::iterator LabelIt = LOHInstToLabel.find(MI);
174 assert(LabelIt != LOHInstToLabel.end() &&
175 "Label hasn't been inserted for LOH related instruction");
176 MCArgs.push_back(LabelIt->second);
178 OutStreamer.EmitLOHDirective(D.getKind(), MCArgs);
183 void ARM64AsmPrinter::EmitFunctionBodyEnd() {
184 if (!ARM64FI->getLOHRelated().empty())
188 /// GetCPISymbol - Return the symbol for the specified constant pool entry.
189 MCSymbol *ARM64AsmPrinter::GetCPISymbol(unsigned CPID) const {
190 // Darwin uses a linker-private symbol name for constant-pools (to
191 // avoid addends on the relocation?), ELF has no such concept and
192 // uses a normal private symbol.
193 if (getDataLayout().getLinkerPrivateGlobalPrefix()[0])
194 return OutContext.GetOrCreateSymbol(
195 Twine(getDataLayout().getLinkerPrivateGlobalPrefix()) + "CPI" +
196 Twine(getFunctionNumber()) + "_" + Twine(CPID));
198 return OutContext.GetOrCreateSymbol(
199 Twine(getDataLayout().getPrivateGlobalPrefix()) + "CPI" +
200 Twine(getFunctionNumber()) + "_" + Twine(CPID));
203 void ARM64AsmPrinter::printOperand(const MachineInstr *MI, unsigned OpNum,
205 const MachineOperand &MO = MI->getOperand(OpNum);
206 switch (MO.getType()) {
208 assert(0 && "<unknown operand type>");
209 case MachineOperand::MO_Register: {
210 unsigned Reg = MO.getReg();
211 assert(TargetRegisterInfo::isPhysicalRegister(Reg));
212 assert(!MO.getSubReg() && "Subregs should be eliminated!");
213 O << ARM64InstPrinter::getRegisterName(Reg);
216 case MachineOperand::MO_Immediate: {
217 int64_t Imm = MO.getImm();
224 bool ARM64AsmPrinter::printAsmMRegister(const MachineOperand &MO, char Mode,
226 unsigned Reg = MO.getReg();
229 return true; // Unknown mode.
231 Reg = getWRegFromXReg(Reg);
234 Reg = getXRegFromWReg(Reg);
238 O << ARM64InstPrinter::getRegisterName(Reg);
242 // Prints the register in MO using class RC using the offset in the
243 // new register class. This should not be used for cross class
245 bool ARM64AsmPrinter::printAsmRegInClass(const MachineOperand &MO,
246 const TargetRegisterClass *RC,
247 bool isVector, raw_ostream &O) {
248 assert(MO.isReg() && "Should only get here with a register!");
249 const ARM64RegisterInfo *RI =
250 static_cast<const ARM64RegisterInfo *>(TM.getRegisterInfo());
251 unsigned Reg = MO.getReg();
252 unsigned RegToPrint = RC->getRegister(RI->getEncodingValue(Reg));
253 assert(RI->regsOverlap(RegToPrint, Reg));
254 O << ARM64InstPrinter::getRegisterName(
255 RegToPrint, isVector ? ARM64::vreg : ARM64::NoRegAltName);
259 bool ARM64AsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNum,
261 const char *ExtraCode, raw_ostream &O) {
262 const MachineOperand &MO = MI->getOperand(OpNum);
263 // Does this asm operand have a single letter operand modifier?
264 if (ExtraCode && ExtraCode[0]) {
265 if (ExtraCode[1] != 0)
266 return true; // Unknown modifier.
268 switch (ExtraCode[0]) {
270 return true; // Unknown modifier.
271 case 'w': // Print W register
272 case 'x': // Print X register
274 return printAsmMRegister(MO, ExtraCode[0], O);
275 if (MO.isImm() && MO.getImm() == 0) {
276 unsigned Reg = ExtraCode[0] == 'w' ? ARM64::WZR : ARM64::XZR;
277 O << ARM64InstPrinter::getRegisterName(Reg);
280 printOperand(MI, OpNum, O);
282 case 'b': // Print B register.
283 case 'h': // Print H register.
284 case 's': // Print S register.
285 case 'd': // Print D register.
286 case 'q': // Print Q register.
288 const TargetRegisterClass *RC;
289 switch (ExtraCode[0]) {
291 RC = &ARM64::FPR8RegClass;
294 RC = &ARM64::FPR16RegClass;
297 RC = &ARM64::FPR32RegClass;
300 RC = &ARM64::FPR64RegClass;
303 RC = &ARM64::FPR128RegClass;
308 return printAsmRegInClass(MO, RC, false /* vector */, O);
310 printOperand(MI, OpNum, O);
315 // According to ARM, we should emit x and v registers unless we have a
318 unsigned Reg = MO.getReg();
320 // If this is a w or x register, print an x register.
321 if (ARM64::GPR32allRegClass.contains(Reg) ||
322 ARM64::GPR64allRegClass.contains(Reg))
323 return printAsmMRegister(MO, 'x', O);
325 // If this is a b, h, s, d, or q register, print it as a v register.
326 return printAsmRegInClass(MO, &ARM64::FPR128RegClass, true /* vector */, O);
329 printOperand(MI, OpNum, O);
333 bool ARM64AsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI,
334 unsigned OpNum, unsigned AsmVariant,
335 const char *ExtraCode,
337 if (ExtraCode && ExtraCode[0])
338 return true; // Unknown modifier.
340 const MachineOperand &MO = MI->getOperand(OpNum);
341 assert(MO.isReg() && "unexpected inline asm memory operand");
342 O << "[" << ARM64InstPrinter::getRegisterName(MO.getReg()) << "]";
346 void ARM64AsmPrinter::PrintDebugValueComment(const MachineInstr *MI,
348 unsigned NOps = MI->getNumOperands();
350 OS << '\t' << MAI->getCommentString() << "DEBUG_VALUE: ";
351 // cast away const; DIetc do not take const operands for some reason.
352 DIVariable V(const_cast<MDNode *>(MI->getOperand(NOps - 1).getMetadata()));
355 // Frame address. Currently handles register +- offset only.
356 assert(MI->getOperand(0).isReg() && MI->getOperand(1).isImm());
358 printOperand(MI, 0, OS);
360 printOperand(MI, 1, OS);
363 printOperand(MI, NOps - 2, OS);
366 void ARM64AsmPrinter::LowerSTACKMAP(MCStreamer &OutStreamer, StackMaps &SM,
367 const MachineInstr &MI) {
368 unsigned NumNOPBytes = MI.getOperand(1).getImm();
370 SM.recordStackMap(MI);
372 assert(NumNOPBytes % 4 == 0 && "Invalid number of NOP bytes requested!");
373 for (unsigned i = 0; i < NumNOPBytes; i += 4)
374 EmitToStreamer(OutStreamer, MCInstBuilder(ARM64::HINT).addImm(0));
377 // Lower a patchpoint of the form:
378 // [<def>], <id>, <numBytes>, <target>, <numArgs>
379 void ARM64AsmPrinter::LowerPATCHPOINT(MCStreamer &OutStreamer, StackMaps &SM,
380 const MachineInstr &MI) {
381 SM.recordPatchPoint(MI);
383 PatchPointOpers Opers(&MI);
385 int64_t CallTarget = Opers.getMetaOper(PatchPointOpers::TargetPos).getImm();
386 unsigned EncodedBytes = 0;
388 assert((CallTarget & 0xFFFFFFFFFFFF) == CallTarget &&
389 "High 16 bits of call target should be zero.");
390 unsigned ScratchReg = MI.getOperand(Opers.getNextScratchIdx()).getReg();
392 // Materialize the jump address:
393 EmitToStreamer(OutStreamer, MCInstBuilder(ARM64::MOVZWi)
395 .addImm((CallTarget >> 32) & 0xFFFF)
397 EmitToStreamer(OutStreamer, MCInstBuilder(ARM64::MOVKWi)
400 .addImm((CallTarget >> 16) & 0xFFFF)
402 EmitToStreamer(OutStreamer, MCInstBuilder(ARM64::MOVKWi)
405 .addImm(CallTarget & 0xFFFF)
407 EmitToStreamer(OutStreamer, MCInstBuilder(ARM64::BLR).addReg(ScratchReg));
410 unsigned NumBytes = Opers.getMetaOper(PatchPointOpers::NBytesPos).getImm();
411 assert(NumBytes >= EncodedBytes &&
412 "Patchpoint can't request size less than the length of a call.");
413 assert((NumBytes - EncodedBytes) % 4 == 0 &&
414 "Invalid number of NOP bytes requested!");
415 for (unsigned i = EncodedBytes; i < NumBytes; i += 4)
416 EmitToStreamer(OutStreamer, MCInstBuilder(ARM64::HINT).addImm(0));
419 // Simple pseudo-instructions have their lowering (with expansion to real
420 // instructions) auto-generated.
421 #include "ARM64GenMCPseudoLowering.inc"
423 static unsigned getRealIndexedOpcode(unsigned Opc) {
425 case ARM64::LDRXpre_isel: return ARM64::LDRXpre;
426 case ARM64::LDRWpre_isel: return ARM64::LDRWpre;
427 case ARM64::LDRDpre_isel: return ARM64::LDRDpre;
428 case ARM64::LDRSpre_isel: return ARM64::LDRSpre;
429 case ARM64::LDRBBpre_isel: return ARM64::LDRBBpre;
430 case ARM64::LDRHHpre_isel: return ARM64::LDRHHpre;
431 case ARM64::LDRSBWpre_isel: return ARM64::LDRSBWpre;
432 case ARM64::LDRSBXpre_isel: return ARM64::LDRSBXpre;
433 case ARM64::LDRSHWpre_isel: return ARM64::LDRSHWpre;
434 case ARM64::LDRSHXpre_isel: return ARM64::LDRSHXpre;
435 case ARM64::LDRSWpre_isel: return ARM64::LDRSWpre;
437 case ARM64::LDRDpost_isel: return ARM64::LDRDpost;
438 case ARM64::LDRSpost_isel: return ARM64::LDRSpost;
439 case ARM64::LDRXpost_isel: return ARM64::LDRXpost;
440 case ARM64::LDRWpost_isel: return ARM64::LDRWpost;
441 case ARM64::LDRHHpost_isel: return ARM64::LDRHHpost;
442 case ARM64::LDRBBpost_isel: return ARM64::LDRBBpost;
443 case ARM64::LDRSWpost_isel: return ARM64::LDRSWpost;
444 case ARM64::LDRSHWpost_isel: return ARM64::LDRSHWpost;
445 case ARM64::LDRSHXpost_isel: return ARM64::LDRSHXpost;
446 case ARM64::LDRSBWpost_isel: return ARM64::LDRSBWpost;
447 case ARM64::LDRSBXpost_isel: return ARM64::LDRSBXpost;
449 case ARM64::STRXpre_isel: return ARM64::STRXpre;
450 case ARM64::STRWpre_isel: return ARM64::STRWpre;
451 case ARM64::STRHHpre_isel: return ARM64::STRHHpre;
452 case ARM64::STRBBpre_isel: return ARM64::STRBBpre;
453 case ARM64::STRDpre_isel: return ARM64::STRDpre;
454 case ARM64::STRSpre_isel: return ARM64::STRSpre;
456 llvm_unreachable("Unexpected pre-indexed opcode!");
459 void ARM64AsmPrinter::EmitInstruction(const MachineInstr *MI) {
460 // Do any auto-generated pseudo lowerings.
461 if (emitPseudoExpansionLowering(OutStreamer, MI))
464 if (ARM64FI->getLOHRelated().count(MI)) {
465 // Generate a label for LOH related instruction
466 MCSymbol *LOHLabel = GetTempSymbol("loh", LOHLabelCounter++);
467 // Associate the instruction with the label
468 LOHInstToLabel[MI] = LOHLabel;
469 OutStreamer.EmitLabel(LOHLabel);
472 // Do any manual lowerings.
473 switch (MI->getOpcode()) {
476 case ARM64::DBG_VALUE: {
477 if (isVerbose() && OutStreamer.hasRawTextSupport()) {
478 SmallString<128> TmpStr;
479 raw_svector_ostream OS(TmpStr);
480 PrintDebugValueComment(MI, OS);
481 OutStreamer.EmitRawText(StringRef(OS.str()));
485 // Indexed loads and stores use a pseudo to handle complex operand
486 // tricks and writeback to the base register. We strip off the writeback
487 // operand and switch the opcode here. Post-indexed stores were handled by the
488 // tablegen'erated pseudos above. (The complex operand <--> simple
489 // operand isel is beyond tablegen's ability, so we do these manually).
490 case ARM64::LDRHHpre_isel:
491 case ARM64::LDRBBpre_isel:
492 case ARM64::LDRXpre_isel:
493 case ARM64::LDRWpre_isel:
494 case ARM64::LDRDpre_isel:
495 case ARM64::LDRSpre_isel:
496 case ARM64::LDRSBWpre_isel:
497 case ARM64::LDRSBXpre_isel:
498 case ARM64::LDRSHWpre_isel:
499 case ARM64::LDRSHXpre_isel:
500 case ARM64::LDRSWpre_isel:
501 case ARM64::LDRDpost_isel:
502 case ARM64::LDRSpost_isel:
503 case ARM64::LDRXpost_isel:
504 case ARM64::LDRWpost_isel:
505 case ARM64::LDRHHpost_isel:
506 case ARM64::LDRBBpost_isel:
507 case ARM64::LDRSWpost_isel:
508 case ARM64::LDRSHWpost_isel:
509 case ARM64::LDRSHXpost_isel:
510 case ARM64::LDRSBWpost_isel:
511 case ARM64::LDRSBXpost_isel: {
513 // For loads, the writeback operand to be skipped is the second.
514 TmpInst.setOpcode(getRealIndexedOpcode(MI->getOpcode()));
515 TmpInst.addOperand(MCOperand::CreateReg(MI->getOperand(0).getReg()));
516 TmpInst.addOperand(MCOperand::CreateReg(MI->getOperand(2).getReg()));
517 TmpInst.addOperand(MCOperand::CreateImm(MI->getOperand(3).getImm()));
518 EmitToStreamer(OutStreamer, TmpInst);
521 case ARM64::STRXpre_isel:
522 case ARM64::STRWpre_isel:
523 case ARM64::STRHHpre_isel:
524 case ARM64::STRBBpre_isel:
525 case ARM64::STRDpre_isel:
526 case ARM64::STRSpre_isel: {
528 // For loads, the writeback operand to be skipped is the first.
529 TmpInst.setOpcode(getRealIndexedOpcode(MI->getOpcode()));
530 TmpInst.addOperand(MCOperand::CreateReg(MI->getOperand(1).getReg()));
531 TmpInst.addOperand(MCOperand::CreateReg(MI->getOperand(2).getReg()));
532 TmpInst.addOperand(MCOperand::CreateImm(MI->getOperand(3).getImm()));
533 EmitToStreamer(OutStreamer, TmpInst);
537 // Tail calls use pseudo instructions so they have the proper code-gen
538 // attributes (isCall, isReturn, etc.). We lower them to the real
540 case ARM64::TCRETURNri: {
542 TmpInst.setOpcode(ARM64::BR);
543 TmpInst.addOperand(MCOperand::CreateReg(MI->getOperand(0).getReg()));
544 EmitToStreamer(OutStreamer, TmpInst);
547 case ARM64::TCRETURNdi: {
549 MCInstLowering.lowerOperand(MI->getOperand(0), Dest);
551 TmpInst.setOpcode(ARM64::B);
552 TmpInst.addOperand(Dest);
553 EmitToStreamer(OutStreamer, TmpInst);
556 case ARM64::TLSDESC_BLR: {
557 MCOperand Callee, Sym;
558 MCInstLowering.lowerOperand(MI->getOperand(0), Callee);
559 MCInstLowering.lowerOperand(MI->getOperand(1), Sym);
561 // First emit a relocation-annotation. This expands to no code, but requests
562 // the following instruction gets an R_AARCH64_TLSDESC_CALL.
564 TLSDescCall.setOpcode(ARM64::TLSDESCCALL);
565 TLSDescCall.addOperand(Sym);
566 EmitToStreamer(OutStreamer, TLSDescCall);
568 // Other than that it's just a normal indirect call to the function loaded
569 // from the descriptor.
571 BLR.setOpcode(ARM64::BLR);
572 BLR.addOperand(Callee);
573 EmitToStreamer(OutStreamer, BLR);
578 case TargetOpcode::STACKMAP:
579 return LowerSTACKMAP(OutStreamer, SM, *MI);
581 case TargetOpcode::PATCHPOINT:
582 return LowerPATCHPOINT(OutStreamer, SM, *MI);
585 // Finally, do the automated lowerings for everything else.
587 MCInstLowering.Lower(MI, TmpInst);
588 EmitToStreamer(OutStreamer, TmpInst);
591 // Force static initialization.
592 extern "C" void LLVMInitializeARM64AsmPrinter() {
593 RegisterAsmPrinter<ARM64AsmPrinter> X(TheARM64Target);