AArch64AsmPrinter.cpp revision ebe69fe11e48d322045d5949c83283927a0d790b
1//===-- AArch64AsmPrinter.cpp - AArch64 LLVM assembly writer --------------===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file contains a printer that converts from our internal representation
11// of machine-dependent LLVM code to the AArch64 assembly language.
12//
13//===----------------------------------------------------------------------===//
14
15#include "AArch64.h"
16#include "AArch64MCInstLower.h"
17#include "AArch64MachineFunctionInfo.h"
18#include "AArch64RegisterInfo.h"
19#include "AArch64Subtarget.h"
20#include "InstPrinter/AArch64InstPrinter.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/MachineModuleInfoImpls.h"
27#include "llvm/CodeGen/StackMaps.h"
28#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h"
29#include "llvm/IR/DataLayout.h"
30#include "llvm/IR/DebugInfo.h"
31#include "llvm/MC/MCAsmInfo.h"
32#include "llvm/MC/MCContext.h"
33#include "llvm/MC/MCInst.h"
34#include "llvm/MC/MCInstBuilder.h"
35#include "llvm/MC/MCLinkerOptimizationHint.h"
36#include "llvm/MC/MCStreamer.h"
37#include "llvm/Support/Debug.h"
38#include "llvm/Support/TargetRegistry.h"
39using namespace llvm;
40
41#define DEBUG_TYPE "asm-printer"
42
43namespace {
44
45class AArch64AsmPrinter : public AsmPrinter {
46  AArch64MCInstLower MCInstLowering;
47  StackMaps SM;
48
49public:
50  AArch64AsmPrinter(TargetMachine &TM, std::unique_ptr<MCStreamer> Streamer)
51      : AsmPrinter(TM, std::move(Streamer)), MCInstLowering(OutContext, *this),
52        SM(*this), AArch64FI(nullptr), LOHLabelCounter(0) {}
53
54  const char *getPassName() const override {
55    return "AArch64 Assembly Printer";
56  }
57
58  /// \brief Wrapper for MCInstLowering.lowerOperand() for the
59  /// tblgen'erated pseudo lowering.
60  bool lowerOperand(const MachineOperand &MO, MCOperand &MCOp) const {
61    return MCInstLowering.lowerOperand(MO, MCOp);
62  }
63
64  void LowerSTACKMAP(MCStreamer &OutStreamer, StackMaps &SM,
65                     const MachineInstr &MI);
66  void LowerPATCHPOINT(MCStreamer &OutStreamer, StackMaps &SM,
67                       const MachineInstr &MI);
68  /// \brief tblgen'erated driver function for lowering simple MI->MC
69  /// pseudo instructions.
70  bool emitPseudoExpansionLowering(MCStreamer &OutStreamer,
71                                   const MachineInstr *MI);
72
73  void EmitInstruction(const MachineInstr *MI) override;
74
75  void getAnalysisUsage(AnalysisUsage &AU) const override {
76    AsmPrinter::getAnalysisUsage(AU);
77    AU.setPreservesAll();
78  }
79
80  bool runOnMachineFunction(MachineFunction &F) override {
81    AArch64FI = F.getInfo<AArch64FunctionInfo>();
82    return AsmPrinter::runOnMachineFunction(F);
83  }
84
85private:
86  MachineLocation getDebugValueLocation(const MachineInstr *MI) const;
87  void printOperand(const MachineInstr *MI, unsigned OpNum, raw_ostream &O);
88  bool printAsmMRegister(const MachineOperand &MO, char Mode, raw_ostream &O);
89  bool printAsmRegInClass(const MachineOperand &MO,
90                          const TargetRegisterClass *RC, bool isVector,
91                          raw_ostream &O);
92
93  bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNum,
94                       unsigned AsmVariant, const char *ExtraCode,
95                       raw_ostream &O) override;
96  bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNum,
97                             unsigned AsmVariant, const char *ExtraCode,
98                             raw_ostream &O) override;
99
100  void PrintDebugValueComment(const MachineInstr *MI, raw_ostream &OS);
101
102  void EmitFunctionBodyEnd() override;
103
104  MCSymbol *GetCPISymbol(unsigned CPID) const override;
105  void EmitEndOfAsmFile(Module &M) override;
106  AArch64FunctionInfo *AArch64FI;
107
108  /// \brief Emit the LOHs contained in AArch64FI.
109  void EmitLOHs();
110
111  typedef std::map<const MachineInstr *, MCSymbol *> MInstToMCSymbol;
112  MInstToMCSymbol LOHInstToLabel;
113  unsigned LOHLabelCounter;
114};
115
116} // end of anonymous namespace
117
118//===----------------------------------------------------------------------===//
119
120void AArch64AsmPrinter::EmitEndOfAsmFile(Module &M) {
121  Triple TT(TM.getTargetTriple());
122  if (TT.isOSBinFormatMachO()) {
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();
130  }
131
132  // Emit a .data.rel section containing any stubs that were created.
133  if (TT.isOSBinFormatELF()) {
134    const TargetLoweringObjectFileELF &TLOFELF =
135      static_cast<const TargetLoweringObjectFileELF &>(getObjFileLowering());
136
137    MachineModuleInfoELF &MMIELF = MMI->getObjFileInfo<MachineModuleInfoELF>();
138
139    // Output stubs for external and common global variables.
140    MachineModuleInfoELF::SymbolListTy Stubs = MMIELF.GetGVStubList();
141    if (!Stubs.empty()) {
142      OutStreamer.SwitchSection(TLOFELF.getDataRelSection());
143      const DataLayout *TD = TM.getDataLayout();
144
145      for (unsigned i = 0, e = Stubs.size(); i != e; ++i) {
146        OutStreamer.EmitLabel(Stubs[i].first);
147        OutStreamer.EmitSymbolValue(Stubs[i].second.getPointer(),
148                                    TD->getPointerSize(0));
149      }
150      Stubs.clear();
151    }
152  }
153
154}
155
156MachineLocation
157AArch64AsmPrinter::getDebugValueLocation(const MachineInstr *MI) const {
158  MachineLocation Location;
159  assert(MI->getNumOperands() == 4 && "Invalid no. of machine operands!");
160  // Frame address.  Currently handles register +- offset only.
161  if (MI->getOperand(0).isReg() && MI->getOperand(1).isImm())
162    Location.set(MI->getOperand(0).getReg(), MI->getOperand(1).getImm());
163  else {
164    DEBUG(dbgs() << "DBG_VALUE instruction ignored! " << *MI << "\n");
165  }
166  return Location;
167}
168
169void AArch64AsmPrinter::EmitLOHs() {
170  SmallVector<MCSymbol *, 3> MCArgs;
171
172  for (const auto &D : AArch64FI->getLOHContainer()) {
173    for (const MachineInstr *MI : D.getArgs()) {
174      MInstToMCSymbol::iterator LabelIt = LOHInstToLabel.find(MI);
175      assert(LabelIt != LOHInstToLabel.end() &&
176             "Label hasn't been inserted for LOH related instruction");
177      MCArgs.push_back(LabelIt->second);
178    }
179    OutStreamer.EmitLOHDirective(D.getKind(), MCArgs);
180    MCArgs.clear();
181  }
182}
183
184void AArch64AsmPrinter::EmitFunctionBodyEnd() {
185  if (!AArch64FI->getLOHRelated().empty())
186    EmitLOHs();
187}
188
189/// GetCPISymbol - Return the symbol for the specified constant pool entry.
190MCSymbol *AArch64AsmPrinter::GetCPISymbol(unsigned CPID) const {
191  // Darwin uses a linker-private symbol name for constant-pools (to
192  // avoid addends on the relocation?), ELF has no such concept and
193  // uses a normal private symbol.
194  if (getDataLayout().getLinkerPrivateGlobalPrefix()[0])
195    return OutContext.GetOrCreateSymbol(
196        Twine(getDataLayout().getLinkerPrivateGlobalPrefix()) + "CPI" +
197        Twine(getFunctionNumber()) + "_" + Twine(CPID));
198
199  return OutContext.GetOrCreateSymbol(
200      Twine(getDataLayout().getPrivateGlobalPrefix()) + "CPI" +
201      Twine(getFunctionNumber()) + "_" + Twine(CPID));
202}
203
204void AArch64AsmPrinter::printOperand(const MachineInstr *MI, unsigned OpNum,
205                                     raw_ostream &O) {
206  const MachineOperand &MO = MI->getOperand(OpNum);
207  switch (MO.getType()) {
208  default:
209    llvm_unreachable("<unknown operand type>");
210  case MachineOperand::MO_Register: {
211    unsigned Reg = MO.getReg();
212    assert(TargetRegisterInfo::isPhysicalRegister(Reg));
213    assert(!MO.getSubReg() && "Subregs should be eliminated!");
214    O << AArch64InstPrinter::getRegisterName(Reg);
215    break;
216  }
217  case MachineOperand::MO_Immediate: {
218    int64_t Imm = MO.getImm();
219    O << '#' << Imm;
220    break;
221  }
222  }
223}
224
225bool AArch64AsmPrinter::printAsmMRegister(const MachineOperand &MO, char Mode,
226                                          raw_ostream &O) {
227  unsigned Reg = MO.getReg();
228  switch (Mode) {
229  default:
230    return true; // Unknown mode.
231  case 'w':
232    Reg = getWRegFromXReg(Reg);
233    break;
234  case 'x':
235    Reg = getXRegFromWReg(Reg);
236    break;
237  }
238
239  O << AArch64InstPrinter::getRegisterName(Reg);
240  return false;
241}
242
243// Prints the register in MO using class RC using the offset in the
244// new register class. This should not be used for cross class
245// printing.
246bool AArch64AsmPrinter::printAsmRegInClass(const MachineOperand &MO,
247                                           const TargetRegisterClass *RC,
248                                           bool isVector, raw_ostream &O) {
249  assert(MO.isReg() && "Should only get here with a register!");
250  const AArch64RegisterInfo *RI =
251      MF->getSubtarget<AArch64Subtarget>().getRegisterInfo();
252  unsigned Reg = MO.getReg();
253  unsigned RegToPrint = RC->getRegister(RI->getEncodingValue(Reg));
254  assert(RI->regsOverlap(RegToPrint, Reg));
255  O << AArch64InstPrinter::getRegisterName(
256           RegToPrint, isVector ? AArch64::vreg : AArch64::NoRegAltName);
257  return false;
258}
259
260bool AArch64AsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNum,
261                                        unsigned AsmVariant,
262                                        const char *ExtraCode, raw_ostream &O) {
263  const MachineOperand &MO = MI->getOperand(OpNum);
264
265  // First try the generic code, which knows about modifiers like 'c' and 'n'.
266  if (!AsmPrinter::PrintAsmOperand(MI, OpNum, AsmVariant, ExtraCode, O))
267    return false;
268
269  // Does this asm operand have a single letter operand modifier?
270  if (ExtraCode && ExtraCode[0]) {
271    if (ExtraCode[1] != 0)
272      return true; // Unknown modifier.
273
274    switch (ExtraCode[0]) {
275    default:
276      return true; // Unknown modifier.
277    case 'w':      // Print W register
278    case 'x':      // Print X register
279      if (MO.isReg())
280        return printAsmMRegister(MO, ExtraCode[0], O);
281      if (MO.isImm() && MO.getImm() == 0) {
282        unsigned Reg = ExtraCode[0] == 'w' ? AArch64::WZR : AArch64::XZR;
283        O << AArch64InstPrinter::getRegisterName(Reg);
284        return false;
285      }
286      printOperand(MI, OpNum, O);
287      return false;
288    case 'b': // Print B register.
289    case 'h': // Print H register.
290    case 's': // Print S register.
291    case 'd': // Print D register.
292    case 'q': // Print Q register.
293      if (MO.isReg()) {
294        const TargetRegisterClass *RC;
295        switch (ExtraCode[0]) {
296        case 'b':
297          RC = &AArch64::FPR8RegClass;
298          break;
299        case 'h':
300          RC = &AArch64::FPR16RegClass;
301          break;
302        case 's':
303          RC = &AArch64::FPR32RegClass;
304          break;
305        case 'd':
306          RC = &AArch64::FPR64RegClass;
307          break;
308        case 'q':
309          RC = &AArch64::FPR128RegClass;
310          break;
311        default:
312          return true;
313        }
314        return printAsmRegInClass(MO, RC, false /* vector */, O);
315      }
316      printOperand(MI, OpNum, O);
317      return false;
318    }
319  }
320
321  // According to ARM, we should emit x and v registers unless we have a
322  // modifier.
323  if (MO.isReg()) {
324    unsigned Reg = MO.getReg();
325
326    // If this is a w or x register, print an x register.
327    if (AArch64::GPR32allRegClass.contains(Reg) ||
328        AArch64::GPR64allRegClass.contains(Reg))
329      return printAsmMRegister(MO, 'x', O);
330
331    // If this is a b, h, s, d, or q register, print it as a v register.
332    return printAsmRegInClass(MO, &AArch64::FPR128RegClass, true /* vector */,
333                              O);
334  }
335
336  printOperand(MI, OpNum, O);
337  return false;
338}
339
340bool AArch64AsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI,
341                                              unsigned OpNum,
342                                              unsigned AsmVariant,
343                                              const char *ExtraCode,
344                                              raw_ostream &O) {
345  if (ExtraCode && ExtraCode[0])
346    return true; // Unknown modifier.
347
348  const MachineOperand &MO = MI->getOperand(OpNum);
349  assert(MO.isReg() && "unexpected inline asm memory operand");
350  O << "[" << AArch64InstPrinter::getRegisterName(MO.getReg()) << "]";
351  return false;
352}
353
354void AArch64AsmPrinter::PrintDebugValueComment(const MachineInstr *MI,
355                                               raw_ostream &OS) {
356  unsigned NOps = MI->getNumOperands();
357  assert(NOps == 4);
358  OS << '\t' << MAI->getCommentString() << "DEBUG_VALUE: ";
359  // cast away const; DIetc do not take const operands for some reason.
360  DIVariable V(const_cast<MDNode *>(MI->getOperand(NOps - 1).getMetadata()));
361  OS << V.getName();
362  OS << " <- ";
363  // Frame address.  Currently handles register +- offset only.
364  assert(MI->getOperand(0).isReg() && MI->getOperand(1).isImm());
365  OS << '[';
366  printOperand(MI, 0, OS);
367  OS << '+';
368  printOperand(MI, 1, OS);
369  OS << ']';
370  OS << "+";
371  printOperand(MI, NOps - 2, OS);
372}
373
374void AArch64AsmPrinter::LowerSTACKMAP(MCStreamer &OutStreamer, StackMaps &SM,
375                                      const MachineInstr &MI) {
376  unsigned NumNOPBytes = MI.getOperand(1).getImm();
377
378  SM.recordStackMap(MI);
379  assert(NumNOPBytes % 4 == 0 && "Invalid number of NOP bytes requested!");
380
381  // Scan ahead to trim the shadow.
382  const MachineBasicBlock &MBB = *MI.getParent();
383  MachineBasicBlock::const_iterator MII(MI);
384  ++MII;
385  while (NumNOPBytes > 0) {
386    if (MII == MBB.end() || MII->isCall() ||
387        MII->getOpcode() == AArch64::DBG_VALUE ||
388        MII->getOpcode() == TargetOpcode::PATCHPOINT ||
389        MII->getOpcode() == TargetOpcode::STACKMAP)
390      break;
391    ++MII;
392    NumNOPBytes -= 4;
393  }
394
395  // Emit nops.
396  for (unsigned i = 0; i < NumNOPBytes; i += 4)
397    EmitToStreamer(OutStreamer, MCInstBuilder(AArch64::HINT).addImm(0));
398}
399
400// Lower a patchpoint of the form:
401// [<def>], <id>, <numBytes>, <target>, <numArgs>
402void AArch64AsmPrinter::LowerPATCHPOINT(MCStreamer &OutStreamer, StackMaps &SM,
403                                        const MachineInstr &MI) {
404  SM.recordPatchPoint(MI);
405
406  PatchPointOpers Opers(&MI);
407
408  int64_t CallTarget = Opers.getMetaOper(PatchPointOpers::TargetPos).getImm();
409  unsigned EncodedBytes = 0;
410  if (CallTarget) {
411    assert((CallTarget & 0xFFFFFFFFFFFF) == CallTarget &&
412           "High 16 bits of call target should be zero.");
413    unsigned ScratchReg = MI.getOperand(Opers.getNextScratchIdx()).getReg();
414    EncodedBytes = 16;
415    // Materialize the jump address:
416    EmitToStreamer(OutStreamer, MCInstBuilder(AArch64::MOVZWi)
417                                    .addReg(ScratchReg)
418                                    .addImm((CallTarget >> 32) & 0xFFFF)
419                                    .addImm(32));
420    EmitToStreamer(OutStreamer, MCInstBuilder(AArch64::MOVKWi)
421                                    .addReg(ScratchReg)
422                                    .addReg(ScratchReg)
423                                    .addImm((CallTarget >> 16) & 0xFFFF)
424                                    .addImm(16));
425    EmitToStreamer(OutStreamer, MCInstBuilder(AArch64::MOVKWi)
426                                    .addReg(ScratchReg)
427                                    .addReg(ScratchReg)
428                                    .addImm(CallTarget & 0xFFFF)
429                                    .addImm(0));
430    EmitToStreamer(OutStreamer, MCInstBuilder(AArch64::BLR).addReg(ScratchReg));
431  }
432  // Emit padding.
433  unsigned NumBytes = Opers.getMetaOper(PatchPointOpers::NBytesPos).getImm();
434  assert(NumBytes >= EncodedBytes &&
435         "Patchpoint can't request size less than the length of a call.");
436  assert((NumBytes - EncodedBytes) % 4 == 0 &&
437         "Invalid number of NOP bytes requested!");
438  for (unsigned i = EncodedBytes; i < NumBytes; i += 4)
439    EmitToStreamer(OutStreamer, MCInstBuilder(AArch64::HINT).addImm(0));
440}
441
442// Simple pseudo-instructions have their lowering (with expansion to real
443// instructions) auto-generated.
444#include "AArch64GenMCPseudoLowering.inc"
445
446void AArch64AsmPrinter::EmitInstruction(const MachineInstr *MI) {
447  // Do any auto-generated pseudo lowerings.
448  if (emitPseudoExpansionLowering(OutStreamer, MI))
449    return;
450
451  if (AArch64FI->getLOHRelated().count(MI)) {
452    // Generate a label for LOH related instruction
453    MCSymbol *LOHLabel = GetTempSymbol("loh", LOHLabelCounter++);
454    // Associate the instruction with the label
455    LOHInstToLabel[MI] = LOHLabel;
456    OutStreamer.EmitLabel(LOHLabel);
457  }
458
459  // Do any manual lowerings.
460  switch (MI->getOpcode()) {
461  default:
462    break;
463  case AArch64::DBG_VALUE: {
464    if (isVerbose() && OutStreamer.hasRawTextSupport()) {
465      SmallString<128> TmpStr;
466      raw_svector_ostream OS(TmpStr);
467      PrintDebugValueComment(MI, OS);
468      OutStreamer.EmitRawText(StringRef(OS.str()));
469    }
470    return;
471  }
472
473  // Tail calls use pseudo instructions so they have the proper code-gen
474  // attributes (isCall, isReturn, etc.). We lower them to the real
475  // instruction here.
476  case AArch64::TCRETURNri: {
477    MCInst TmpInst;
478    TmpInst.setOpcode(AArch64::BR);
479    TmpInst.addOperand(MCOperand::CreateReg(MI->getOperand(0).getReg()));
480    EmitToStreamer(OutStreamer, TmpInst);
481    return;
482  }
483  case AArch64::TCRETURNdi: {
484    MCOperand Dest;
485    MCInstLowering.lowerOperand(MI->getOperand(0), Dest);
486    MCInst TmpInst;
487    TmpInst.setOpcode(AArch64::B);
488    TmpInst.addOperand(Dest);
489    EmitToStreamer(OutStreamer, TmpInst);
490    return;
491  }
492  case AArch64::TLSDESC_BLR: {
493    MCOperand Callee, Sym;
494    MCInstLowering.lowerOperand(MI->getOperand(0), Callee);
495    MCInstLowering.lowerOperand(MI->getOperand(1), Sym);
496
497    // First emit a relocation-annotation. This expands to no code, but requests
498    // the following instruction gets an R_AARCH64_TLSDESC_CALL.
499    MCInst TLSDescCall;
500    TLSDescCall.setOpcode(AArch64::TLSDESCCALL);
501    TLSDescCall.addOperand(Sym);
502    EmitToStreamer(OutStreamer, TLSDescCall);
503
504    // Other than that it's just a normal indirect call to the function loaded
505    // from the descriptor.
506    MCInst BLR;
507    BLR.setOpcode(AArch64::BLR);
508    BLR.addOperand(Callee);
509    EmitToStreamer(OutStreamer, BLR);
510
511    return;
512  }
513
514  case TargetOpcode::STACKMAP:
515    return LowerSTACKMAP(OutStreamer, SM, *MI);
516
517  case TargetOpcode::PATCHPOINT:
518    return LowerPATCHPOINT(OutStreamer, SM, *MI);
519  }
520
521  // Finally, do the automated lowerings for everything else.
522  MCInst TmpInst;
523  MCInstLowering.Lower(MI, TmpInst);
524  EmitToStreamer(OutStreamer, TmpInst);
525}
526
527// Force static initialization.
528extern "C" void LLVMInitializeAArch64AsmPrinter() {
529  RegisterAsmPrinter<AArch64AsmPrinter> X(TheAArch64leTarget);
530  RegisterAsmPrinter<AArch64AsmPrinter> Y(TheAArch64beTarget);
531  RegisterAsmPrinter<AArch64AsmPrinter> Z(TheARM64Target);
532}
533