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