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 SmallVector<MCSymbol *, 3> MCArgs;
140 for (const auto &D : ARM64FI->getLOHContainer()) {
141 for (const MachineInstr *MI : D.getArgs()) {
142 MInstToMCSymbol::iterator LabelIt = LOHInstToLabel.find(MI);
143 assert(LabelIt != LOHInstToLabel.end() &&
144 "Label hasn't been inserted for LOH related instruction");
145 MCArgs.push_back(LabelIt->second);
147 OutStreamer.EmitLOHDirective(D.getKind(), MCArgs);
152 void ARM64AsmPrinter::EmitFunctionBodyEnd() {
153 if (!ARM64FI->getLOHRelated().empty())
157 /// GetCPISymbol - Return the symbol for the specified constant pool entry.
158 MCSymbol *ARM64AsmPrinter::GetCPISymbol(unsigned CPID) const {
159 // Darwin uses a linker-private symbol name for constant-pools (to
160 // avoid addends on the relocation?), ELF has no such concept and
161 // uses a normal private symbol.
162 if (getDataLayout().getLinkerPrivateGlobalPrefix()[0])
163 return OutContext.GetOrCreateSymbol(
164 Twine(getDataLayout().getLinkerPrivateGlobalPrefix()) + "CPI" +
165 Twine(getFunctionNumber()) + "_" + Twine(CPID));
167 return OutContext.GetOrCreateSymbol(
168 Twine(getDataLayout().getPrivateGlobalPrefix()) + "CPI" +
169 Twine(getFunctionNumber()) + "_" + Twine(CPID));
172 void ARM64AsmPrinter::printOperand(const MachineInstr *MI, unsigned OpNum,
174 const MachineOperand &MO = MI->getOperand(OpNum);
175 switch (MO.getType()) {
177 assert(0 && "<unknown operand type>");
178 case MachineOperand::MO_Register: {
179 unsigned Reg = MO.getReg();
180 assert(TargetRegisterInfo::isPhysicalRegister(Reg));
181 assert(!MO.getSubReg() && "Subregs should be eliminated!");
182 O << ARM64InstPrinter::getRegisterName(Reg);
185 case MachineOperand::MO_Immediate: {
186 int64_t Imm = MO.getImm();
193 bool ARM64AsmPrinter::printAsmMRegister(const MachineOperand &MO, char Mode,
195 unsigned Reg = MO.getReg();
198 return true; // Unknown mode.
200 Reg = getWRegFromXReg(Reg);
203 Reg = getXRegFromWReg(Reg);
207 O << ARM64InstPrinter::getRegisterName(Reg);
211 // Prints the register in MO using class RC using the offset in the
212 // new register class. This should not be used for cross class
214 bool ARM64AsmPrinter::printAsmRegInClass(const MachineOperand &MO,
215 const TargetRegisterClass *RC,
216 bool isVector, raw_ostream &O) {
217 assert(MO.isReg() && "Should only get here with a register!");
218 const ARM64RegisterInfo *RI =
219 static_cast<const ARM64RegisterInfo *>(TM.getRegisterInfo());
220 unsigned Reg = MO.getReg();
221 unsigned RegToPrint = RC->getRegister(RI->getEncodingValue(Reg));
222 assert(RI->regsOverlap(RegToPrint, Reg));
223 O << ARM64InstPrinter::getRegisterName(
224 RegToPrint, isVector ? ARM64::vreg : ARM64::NoRegAltName);
228 bool ARM64AsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNum,
230 const char *ExtraCode, raw_ostream &O) {
231 const MachineOperand &MO = MI->getOperand(OpNum);
232 // Does this asm operand have a single letter operand modifier?
233 if (ExtraCode && ExtraCode[0]) {
234 if (ExtraCode[1] != 0)
235 return true; // Unknown modifier.
237 switch (ExtraCode[0]) {
239 return true; // Unknown modifier.
240 case 'w': // Print W register
241 case 'x': // Print X register
243 return printAsmMRegister(MO, ExtraCode[0], O);
244 if (MO.isImm() && MO.getImm() == 0) {
245 unsigned Reg = ExtraCode[0] == 'w' ? ARM64::WZR : ARM64::XZR;
246 O << ARM64InstPrinter::getRegisterName(Reg);
249 printOperand(MI, OpNum, O);
251 case 'b': // Print B register.
252 case 'h': // Print H register.
253 case 's': // Print S register.
254 case 'd': // Print D register.
255 case 'q': // Print Q register.
257 const TargetRegisterClass *RC;
258 switch (ExtraCode[0]) {
260 RC = &ARM64::FPR8RegClass;
263 RC = &ARM64::FPR16RegClass;
266 RC = &ARM64::FPR32RegClass;
269 RC = &ARM64::FPR64RegClass;
272 RC = &ARM64::FPR128RegClass;
277 return printAsmRegInClass(MO, RC, false /* vector */, O);
279 printOperand(MI, OpNum, O);
284 // According to ARM, we should emit x and v registers unless we have a
287 unsigned Reg = MO.getReg();
289 // If this is a w or x register, print an x register.
290 if (ARM64::GPR32allRegClass.contains(Reg) ||
291 ARM64::GPR64allRegClass.contains(Reg))
292 return printAsmMRegister(MO, 'x', O);
294 // If this is a b, h, s, d, or q register, print it as a v register.
295 return printAsmRegInClass(MO, &ARM64::FPR128RegClass, true /* vector */, O);
298 printOperand(MI, OpNum, O);
302 bool ARM64AsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI,
303 unsigned OpNum, unsigned AsmVariant,
304 const char *ExtraCode,
306 if (ExtraCode && ExtraCode[0])
307 return true; // Unknown modifier.
309 const MachineOperand &MO = MI->getOperand(OpNum);
310 assert(MO.isReg() && "unexpected inline asm memory operand");
311 O << "[" << ARM64InstPrinter::getRegisterName(MO.getReg()) << "]";
315 void ARM64AsmPrinter::PrintDebugValueComment(const MachineInstr *MI,
317 unsigned NOps = MI->getNumOperands();
319 OS << '\t' << MAI->getCommentString() << "DEBUG_VALUE: ";
320 // cast away const; DIetc do not take const operands for some reason.
321 DIVariable V(const_cast<MDNode *>(MI->getOperand(NOps - 1).getMetadata()));
324 // Frame address. Currently handles register +- offset only.
325 assert(MI->getOperand(0).isReg() && MI->getOperand(1).isImm());
327 printOperand(MI, 0, OS);
329 printOperand(MI, 1, OS);
332 printOperand(MI, NOps - 2, OS);
335 void ARM64AsmPrinter::LowerSTACKMAP(MCStreamer &OutStreamer, StackMaps &SM,
336 const MachineInstr &MI) {
337 unsigned NumNOPBytes = MI.getOperand(1).getImm();
339 SM.recordStackMap(MI);
341 assert(NumNOPBytes % 4 == 0 && "Invalid number of NOP bytes requested!");
342 for (unsigned i = 0; i < NumNOPBytes; i += 4)
343 EmitToStreamer(OutStreamer, MCInstBuilder(ARM64::HINT).addImm(0));
346 // Lower a patchpoint of the form:
347 // [<def>], <id>, <numBytes>, <target>, <numArgs>
348 void ARM64AsmPrinter::LowerPATCHPOINT(MCStreamer &OutStreamer, StackMaps &SM,
349 const MachineInstr &MI) {
350 SM.recordPatchPoint(MI);
352 PatchPointOpers Opers(&MI);
354 int64_t CallTarget = Opers.getMetaOper(PatchPointOpers::TargetPos).getImm();
355 unsigned EncodedBytes = 0;
357 assert((CallTarget & 0xFFFFFFFFFFFF) == CallTarget &&
358 "High 16 bits of call target should be zero.");
359 unsigned ScratchReg = MI.getOperand(Opers.getNextScratchIdx()).getReg();
361 // Materialize the jump address:
362 EmitToStreamer(OutStreamer, MCInstBuilder(ARM64::MOVZWi)
364 .addImm((CallTarget >> 32) & 0xFFFF)
366 EmitToStreamer(OutStreamer, MCInstBuilder(ARM64::MOVKWi)
369 .addImm((CallTarget >> 16) & 0xFFFF)
371 EmitToStreamer(OutStreamer, MCInstBuilder(ARM64::MOVKWi)
374 .addImm(CallTarget & 0xFFFF)
376 EmitToStreamer(OutStreamer, MCInstBuilder(ARM64::BLR).addReg(ScratchReg));
379 unsigned NumBytes = Opers.getMetaOper(PatchPointOpers::NBytesPos).getImm();
380 assert(NumBytes >= EncodedBytes &&
381 "Patchpoint can't request size less than the length of a call.");
382 assert((NumBytes - EncodedBytes) % 4 == 0 &&
383 "Invalid number of NOP bytes requested!");
384 for (unsigned i = EncodedBytes; i < NumBytes; i += 4)
385 EmitToStreamer(OutStreamer, MCInstBuilder(ARM64::HINT).addImm(0));
388 // Simple pseudo-instructions have their lowering (with expansion to real
389 // instructions) auto-generated.
390 #include "ARM64GenMCPseudoLowering.inc"
392 static unsigned getRealIndexedOpcode(unsigned Opc) {
394 case ARM64::LDRXpre_isel: return ARM64::LDRXpre;
395 case ARM64::LDRWpre_isel: return ARM64::LDRWpre;
396 case ARM64::LDRDpre_isel: return ARM64::LDRDpre;
397 case ARM64::LDRSpre_isel: return ARM64::LDRSpre;
398 case ARM64::LDRBBpre_isel: return ARM64::LDRBBpre;
399 case ARM64::LDRHHpre_isel: return ARM64::LDRHHpre;
400 case ARM64::LDRSBWpre_isel: return ARM64::LDRSBWpre;
401 case ARM64::LDRSBXpre_isel: return ARM64::LDRSBXpre;
402 case ARM64::LDRSHWpre_isel: return ARM64::LDRSHWpre;
403 case ARM64::LDRSHXpre_isel: return ARM64::LDRSHXpre;
404 case ARM64::LDRSWpre_isel: return ARM64::LDRSWpre;
406 case ARM64::LDRDpost_isel: return ARM64::LDRDpost;
407 case ARM64::LDRSpost_isel: return ARM64::LDRSpost;
408 case ARM64::LDRXpost_isel: return ARM64::LDRXpost;
409 case ARM64::LDRWpost_isel: return ARM64::LDRWpost;
410 case ARM64::LDRHHpost_isel: return ARM64::LDRHHpost;
411 case ARM64::LDRBBpost_isel: return ARM64::LDRBBpost;
412 case ARM64::LDRSWpost_isel: return ARM64::LDRSWpost;
413 case ARM64::LDRSHWpost_isel: return ARM64::LDRSHWpost;
414 case ARM64::LDRSHXpost_isel: return ARM64::LDRSHXpost;
415 case ARM64::LDRSBWpost_isel: return ARM64::LDRSBWpost;
416 case ARM64::LDRSBXpost_isel: return ARM64::LDRSBXpost;
418 case ARM64::STRXpre_isel: return ARM64::STRXpre;
419 case ARM64::STRWpre_isel: return ARM64::STRWpre;
420 case ARM64::STRHHpre_isel: return ARM64::STRHHpre;
421 case ARM64::STRBBpre_isel: return ARM64::STRBBpre;
422 case ARM64::STRDpre_isel: return ARM64::STRDpre;
423 case ARM64::STRSpre_isel: return ARM64::STRSpre;
425 llvm_unreachable("Unexpected pre-indexed opcode!");
428 void ARM64AsmPrinter::EmitInstruction(const MachineInstr *MI) {
429 // Do any auto-generated pseudo lowerings.
430 if (emitPseudoExpansionLowering(OutStreamer, MI))
433 if (ARM64FI->getLOHRelated().count(MI)) {
434 // Generate a label for LOH related instruction
435 MCSymbol *LOHLabel = GetTempSymbol("loh", LOHLabelCounter++);
436 // Associate the instruction with the label
437 LOHInstToLabel[MI] = LOHLabel;
438 OutStreamer.EmitLabel(LOHLabel);
441 // Do any manual lowerings.
442 switch (MI->getOpcode()) {
445 case ARM64::DBG_VALUE: {
446 if (isVerbose() && OutStreamer.hasRawTextSupport()) {
447 SmallString<128> TmpStr;
448 raw_svector_ostream OS(TmpStr);
449 PrintDebugValueComment(MI, OS);
450 OutStreamer.EmitRawText(StringRef(OS.str()));
454 // Indexed loads and stores use a pseudo to handle complex operand
455 // tricks and writeback to the base register. We strip off the writeback
456 // operand and switch the opcode here. Post-indexed stores were handled by the
457 // tablegen'erated pseudos above. (The complex operand <--> simple
458 // operand isel is beyond tablegen's ability, so we do these manually).
459 case ARM64::LDRHHpre_isel:
460 case ARM64::LDRBBpre_isel:
461 case ARM64::LDRXpre_isel:
462 case ARM64::LDRWpre_isel:
463 case ARM64::LDRDpre_isel:
464 case ARM64::LDRSpre_isel:
465 case ARM64::LDRSBWpre_isel:
466 case ARM64::LDRSBXpre_isel:
467 case ARM64::LDRSHWpre_isel:
468 case ARM64::LDRSHXpre_isel:
469 case ARM64::LDRSWpre_isel:
470 case ARM64::LDRDpost_isel:
471 case ARM64::LDRSpost_isel:
472 case ARM64::LDRXpost_isel:
473 case ARM64::LDRWpost_isel:
474 case ARM64::LDRHHpost_isel:
475 case ARM64::LDRBBpost_isel:
476 case ARM64::LDRSWpost_isel:
477 case ARM64::LDRSHWpost_isel:
478 case ARM64::LDRSHXpost_isel:
479 case ARM64::LDRSBWpost_isel:
480 case ARM64::LDRSBXpost_isel: {
482 // For loads, the writeback operand to be skipped is the second.
483 TmpInst.setOpcode(getRealIndexedOpcode(MI->getOpcode()));
484 TmpInst.addOperand(MCOperand::CreateReg(MI->getOperand(0).getReg()));
485 TmpInst.addOperand(MCOperand::CreateReg(MI->getOperand(2).getReg()));
486 TmpInst.addOperand(MCOperand::CreateImm(MI->getOperand(3).getImm()));
487 EmitToStreamer(OutStreamer, TmpInst);
490 case ARM64::STRXpre_isel:
491 case ARM64::STRWpre_isel:
492 case ARM64::STRHHpre_isel:
493 case ARM64::STRBBpre_isel:
494 case ARM64::STRDpre_isel:
495 case ARM64::STRSpre_isel: {
497 // For loads, the writeback operand to be skipped is the first.
498 TmpInst.setOpcode(getRealIndexedOpcode(MI->getOpcode()));
499 TmpInst.addOperand(MCOperand::CreateReg(MI->getOperand(1).getReg()));
500 TmpInst.addOperand(MCOperand::CreateReg(MI->getOperand(2).getReg()));
501 TmpInst.addOperand(MCOperand::CreateImm(MI->getOperand(3).getImm()));
502 EmitToStreamer(OutStreamer, TmpInst);
506 // Tail calls use pseudo instructions so they have the proper code-gen
507 // attributes (isCall, isReturn, etc.). We lower them to the real
509 case ARM64::TCRETURNri: {
511 TmpInst.setOpcode(ARM64::BR);
512 TmpInst.addOperand(MCOperand::CreateReg(MI->getOperand(0).getReg()));
513 EmitToStreamer(OutStreamer, TmpInst);
516 case ARM64::TCRETURNdi: {
518 MCInstLowering.lowerOperand(MI->getOperand(0), Dest);
520 TmpInst.setOpcode(ARM64::B);
521 TmpInst.addOperand(Dest);
522 EmitToStreamer(OutStreamer, TmpInst);
525 case ARM64::TLSDESC_BLR: {
526 MCOperand Callee, Sym;
527 MCInstLowering.lowerOperand(MI->getOperand(0), Callee);
528 MCInstLowering.lowerOperand(MI->getOperand(1), Sym);
530 // First emit a relocation-annotation. This expands to no code, but requests
531 // the following instruction gets an R_AARCH64_TLSDESC_CALL.
533 TLSDescCall.setOpcode(ARM64::TLSDESCCALL);
534 TLSDescCall.addOperand(Sym);
535 EmitToStreamer(OutStreamer, TLSDescCall);
537 // Other than that it's just a normal indirect call to the function loaded
538 // from the descriptor.
540 BLR.setOpcode(ARM64::BLR);
541 BLR.addOperand(Callee);
542 EmitToStreamer(OutStreamer, BLR);
547 case TargetOpcode::STACKMAP:
548 return LowerSTACKMAP(OutStreamer, SM, *MI);
550 case TargetOpcode::PATCHPOINT:
551 return LowerPATCHPOINT(OutStreamer, SM, *MI);
554 // Finally, do the automated lowerings for everything else.
556 MCInstLowering.Lower(MI, TmpInst);
557 EmitToStreamer(OutStreamer, TmpInst);
560 // Force static initialization.
561 extern "C" void LLVMInitializeARM64AsmPrinter() {
562 RegisterAsmPrinter<ARM64AsmPrinter> X(TheARM64Target);