xref: /freebsd/contrib/llvm-project/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp (revision 723da5d92f40a413585107f8455280ea575fe410)
1  //===-- AsmPrinterInlineAsm.cpp - AsmPrinter Inline Asm Handling ----------===//
2  //
3  // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4  // See https://llvm.org/LICENSE.txt for license information.
5  // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6  //
7  //===----------------------------------------------------------------------===//
8  //
9  // This file implements the inline assembler pieces of the AsmPrinter class.
10  //
11  //===----------------------------------------------------------------------===//
12  
13  #include "llvm/ADT/SmallString.h"
14  #include "llvm/ADT/SmallVector.h"
15  #include "llvm/ADT/StringExtras.h"
16  #include "llvm/ADT/Twine.h"
17  #include "llvm/CodeGen/AsmPrinter.h"
18  #include "llvm/CodeGen/MachineBasicBlock.h"
19  #include "llvm/CodeGen/MachineFunction.h"
20  #include "llvm/CodeGen/MachineModuleInfo.h"
21  #include "llvm/CodeGen/TargetRegisterInfo.h"
22  #include "llvm/CodeGen/TargetSubtargetInfo.h"
23  #include "llvm/IR/Constants.h"
24  #include "llvm/IR/DataLayout.h"
25  #include "llvm/IR/DiagnosticInfo.h"
26  #include "llvm/IR/InlineAsm.h"
27  #include "llvm/IR/LLVMContext.h"
28  #include "llvm/IR/Module.h"
29  #include "llvm/MC/MCAsmInfo.h"
30  #include "llvm/MC/MCInstrInfo.h"
31  #include "llvm/MC/MCParser/MCAsmLexer.h"
32  #include "llvm/MC/MCParser/MCTargetAsmParser.h"
33  #include "llvm/MC/MCStreamer.h"
34  #include "llvm/MC/MCSymbol.h"
35  #include "llvm/MC/TargetRegistry.h"
36  #include "llvm/Support/ErrorHandling.h"
37  #include "llvm/Support/MemoryBuffer.h"
38  #include "llvm/Support/SourceMgr.h"
39  #include "llvm/Support/raw_ostream.h"
40  #include "llvm/Target/TargetMachine.h"
41  using namespace llvm;
42  
43  #define DEBUG_TYPE "asm-printer"
44  
45  unsigned AsmPrinter::addInlineAsmDiagBuffer(StringRef AsmStr,
46                                              const MDNode *LocMDNode) const {
47    MCContext &Context = MMI->getContext();
48    Context.initInlineSourceManager();
49    SourceMgr &SrcMgr = *Context.getInlineSourceManager();
50    std::vector<const MDNode *> &LocInfos = Context.getLocInfos();
51  
52    std::unique_ptr<MemoryBuffer> Buffer;
53    // The inline asm source manager will outlive AsmStr, so make a copy of the
54    // string for SourceMgr to own.
55    Buffer = MemoryBuffer::getMemBufferCopy(AsmStr, "<inline asm>");
56  
57    // Tell SrcMgr about this buffer, it takes ownership of the buffer.
58    unsigned BufNum = SrcMgr.AddNewSourceBuffer(std::move(Buffer), SMLoc());
59  
60    // Store LocMDNode in DiagInfo, using BufNum as an identifier.
61    if (LocMDNode) {
62      LocInfos.resize(BufNum);
63      LocInfos[BufNum - 1] = LocMDNode;
64    }
65  
66    return BufNum;
67  }
68  
69  
70  /// EmitInlineAsm - Emit a blob of inline asm to the output streamer.
71  void AsmPrinter::emitInlineAsm(StringRef Str, const MCSubtargetInfo &STI,
72                                 const MCTargetOptions &MCOptions,
73                                 const MDNode *LocMDNode,
74                                 InlineAsm::AsmDialect Dialect) const {
75    assert(!Str.empty() && "Can't emit empty inline asm block");
76  
77    // Remember if the buffer is nul terminated or not so we can avoid a copy.
78    bool isNullTerminated = Str.back() == 0;
79    if (isNullTerminated)
80      Str = Str.substr(0, Str.size()-1);
81  
82    // If the output streamer does not have mature MC support or the integrated
83    // assembler has been disabled or not required, just emit the blob textually.
84    // Otherwise parse the asm and emit it via MC support.
85    // This is useful in case the asm parser doesn't handle something but the
86    // system assembler does.
87    const MCAsmInfo *MCAI = TM.getMCAsmInfo();
88    assert(MCAI && "No MCAsmInfo");
89    if (!MCAI->useIntegratedAssembler() &&
90        !MCAI->parseInlineAsmUsingAsmParser() &&
91        !OutStreamer->isIntegratedAssemblerRequired()) {
92      emitInlineAsmStart();
93      OutStreamer->emitRawText(Str);
94      emitInlineAsmEnd(STI, nullptr);
95      return;
96    }
97  
98    unsigned BufNum = addInlineAsmDiagBuffer(Str, LocMDNode);
99    SourceMgr &SrcMgr = *MMI->getContext().getInlineSourceManager();
100    SrcMgr.setIncludeDirs(MCOptions.IASSearchPaths);
101  
102    std::unique_ptr<MCAsmParser> Parser(
103        createMCAsmParser(SrcMgr, OutContext, *OutStreamer, *MAI, BufNum));
104  
105    // We create a new MCInstrInfo here since we might be at the module level
106    // and not have a MachineFunction to initialize the TargetInstrInfo from and
107    // we only need MCInstrInfo for asm parsing. We create one unconditionally
108    // because it's not subtarget dependent.
109    std::unique_ptr<MCInstrInfo> MII(TM.getTarget().createMCInstrInfo());
110    assert(MII && "Failed to create instruction info");
111    std::unique_ptr<MCTargetAsmParser> TAP(TM.getTarget().createMCAsmParser(
112        STI, *Parser, *MII, MCOptions));
113    if (!TAP)
114      report_fatal_error("Inline asm not supported by this streamer because"
115                         " we don't have an asm parser for this target\n");
116  
117    // Respect inlineasm dialect on X86 targets only
118    if (TM.getTargetTriple().isX86()) {
119      Parser->setAssemblerDialect(Dialect);
120      // Enable lexing Masm binary and hex integer literals in intel inline
121      // assembly.
122      if (Dialect == InlineAsm::AD_Intel)
123        Parser->getLexer().setLexMasmIntegers(true);
124    }
125    Parser->setTargetParser(*TAP);
126  
127    emitInlineAsmStart();
128    // Don't implicitly switch to the text section before the asm.
129    (void)Parser->Run(/*NoInitialTextSection*/ true,
130                      /*NoFinalize*/ true);
131    emitInlineAsmEnd(STI, &TAP->getSTI());
132  }
133  
134  static void EmitInlineAsmStr(const char *AsmStr, const MachineInstr *MI,
135                               MachineModuleInfo *MMI, const MCAsmInfo *MAI,
136                               AsmPrinter *AP, uint64_t LocCookie,
137                               raw_ostream &OS) {
138    bool InputIsIntelDialect = MI->getInlineAsmDialect() == InlineAsm::AD_Intel;
139  
140    if (InputIsIntelDialect) {
141      // Switch to the inline assembly variant.
142      OS << "\t.intel_syntax\n\t";
143    }
144  
145    int CurVariant = -1; // The number of the {.|.|.} region we are in.
146    const char *LastEmitted = AsmStr; // One past the last character emitted.
147    unsigned NumOperands = MI->getNumOperands();
148  
149    int AsmPrinterVariant;
150    if (InputIsIntelDialect)
151      AsmPrinterVariant = 1; // X86MCAsmInfo.cpp's AsmWriterFlavorTy::Intel.
152    else
153      AsmPrinterVariant = MMI->getTarget().unqualifiedInlineAsmVariant();
154  
155    // FIXME: Should this happen for `asm inteldialect` as well?
156    if (!InputIsIntelDialect && MAI->getEmitGNUAsmStartIndentationMarker())
157      OS << '\t';
158  
159    while (*LastEmitted) {
160      switch (*LastEmitted) {
161      default: {
162        // Not a special case, emit the string section literally.
163        const char *LiteralEnd = LastEmitted+1;
164        while (*LiteralEnd && *LiteralEnd != '{' && *LiteralEnd != '|' &&
165               *LiteralEnd != '}' && *LiteralEnd != '$' && *LiteralEnd != '\n')
166          ++LiteralEnd;
167        if (CurVariant == -1 || CurVariant == AsmPrinterVariant)
168          OS.write(LastEmitted, LiteralEnd - LastEmitted);
169        LastEmitted = LiteralEnd;
170        break;
171      }
172      case '\n':
173        ++LastEmitted;   // Consume newline character.
174        OS << '\n';      // Indent code with newline.
175        break;
176      case '$': {
177        ++LastEmitted;   // Consume '$' character.
178        bool Done = true;
179  
180        // Handle escapes.
181        switch (*LastEmitted) {
182        default: Done = false; break;
183        case '$':     // $$ -> $
184          if (!InputIsIntelDialect)
185            if (CurVariant == -1 || CurVariant == AsmPrinterVariant)
186              OS << '$';
187          ++LastEmitted;  // Consume second '$' character.
188          break;
189        case '(':        // $( -> same as GCC's { character.
190          ++LastEmitted; // Consume '(' character.
191          if (CurVariant != -1)
192            report_fatal_error("Nested variants found in inline asm string: '" +
193                               Twine(AsmStr) + "'");
194          CurVariant = 0; // We're in the first variant now.
195          break;
196        case '|':
197          ++LastEmitted; // Consume '|' character.
198          if (CurVariant == -1)
199            OS << '|'; // This is gcc's behavior for | outside a variant.
200          else
201            ++CurVariant; // We're in the next variant.
202          break;
203        case ')':        // $) -> same as GCC's } char.
204          ++LastEmitted; // Consume ')' character.
205          if (CurVariant == -1)
206            OS << '}'; // This is gcc's behavior for } outside a variant.
207          else
208            CurVariant = -1;
209          break;
210        }
211        if (Done) break;
212  
213        bool HasCurlyBraces = false;
214        if (*LastEmitted == '{') {     // ${variable}
215          ++LastEmitted;               // Consume '{' character.
216          HasCurlyBraces = true;
217        }
218  
219        // If we have ${:foo}, then this is not a real operand reference, it is a
220        // "magic" string reference, just like in .td files.  Arrange to call
221        // PrintSpecial.
222        if (HasCurlyBraces && *LastEmitted == ':') {
223          ++LastEmitted;
224          const char *StrStart = LastEmitted;
225          const char *StrEnd = strchr(StrStart, '}');
226          if (!StrEnd)
227            report_fatal_error("Unterminated ${:foo} operand in inline asm"
228                               " string: '" + Twine(AsmStr) + "'");
229          if (CurVariant == -1 || CurVariant == AsmPrinterVariant)
230            AP->PrintSpecial(MI, OS, StringRef(StrStart, StrEnd - StrStart));
231          LastEmitted = StrEnd+1;
232          break;
233        }
234  
235        const char *IDStart = LastEmitted;
236        const char *IDEnd = IDStart;
237        while (isDigit(*IDEnd))
238          ++IDEnd;
239  
240        unsigned Val;
241        if (StringRef(IDStart, IDEnd-IDStart).getAsInteger(10, Val))
242          report_fatal_error("Bad $ operand number in inline asm string: '" +
243                             Twine(AsmStr) + "'");
244        LastEmitted = IDEnd;
245  
246        if (Val >= NumOperands - 1)
247          report_fatal_error("Invalid $ operand number in inline asm string: '" +
248                             Twine(AsmStr) + "'");
249  
250        char Modifier[2] = { 0, 0 };
251  
252        if (HasCurlyBraces) {
253          // If we have curly braces, check for a modifier character.  This
254          // supports syntax like ${0:u}, which correspond to "%u0" in GCC asm.
255          if (*LastEmitted == ':') {
256            ++LastEmitted;    // Consume ':' character.
257            if (*LastEmitted == 0)
258              report_fatal_error("Bad ${:} expression in inline asm string: '" +
259                                 Twine(AsmStr) + "'");
260  
261            Modifier[0] = *LastEmitted;
262            ++LastEmitted;    // Consume modifier character.
263          }
264  
265          if (*LastEmitted != '}')
266            report_fatal_error("Bad ${} expression in inline asm string: '" +
267                               Twine(AsmStr) + "'");
268          ++LastEmitted;    // Consume '}' character.
269        }
270  
271        // Okay, we finally have a value number.  Ask the target to print this
272        // operand!
273        if (CurVariant == -1 || CurVariant == AsmPrinterVariant) {
274          unsigned OpNo = InlineAsm::MIOp_FirstOperand;
275  
276          bool Error = false;
277  
278          // Scan to find the machine operand number for the operand.
279          for (; Val; --Val) {
280            if (OpNo >= MI->getNumOperands())
281              break;
282            const InlineAsm::Flag F(MI->getOperand(OpNo).getImm());
283            OpNo += F.getNumOperandRegisters() + 1;
284          }
285  
286          // We may have a location metadata attached to the end of the
287          // instruction, and at no point should see metadata at any
288          // other point while processing. It's an error if so.
289          if (OpNo >= MI->getNumOperands() || MI->getOperand(OpNo).isMetadata()) {
290            Error = true;
291          } else {
292            const InlineAsm::Flag F(MI->getOperand(OpNo).getImm());
293            ++OpNo; // Skip over the ID number.
294  
295            // FIXME: Shouldn't arch-independent output template handling go into
296            // PrintAsmOperand?
297            // Labels are target independent.
298            if (MI->getOperand(OpNo).isBlockAddress()) {
299              const BlockAddress *BA = MI->getOperand(OpNo).getBlockAddress();
300              MCSymbol *Sym = AP->GetBlockAddressSymbol(BA);
301              Sym->print(OS, AP->MAI);
302              MMI->getContext().registerInlineAsmLabel(Sym);
303            } else if (MI->getOperand(OpNo).isMBB()) {
304              const MCSymbol *Sym = MI->getOperand(OpNo).getMBB()->getSymbol();
305              Sym->print(OS, AP->MAI);
306            } else if (F.isMemKind()) {
307              Error = AP->PrintAsmMemoryOperand(
308                  MI, OpNo, Modifier[0] ? Modifier : nullptr, OS);
309            } else {
310              Error = AP->PrintAsmOperand(MI, OpNo,
311                                          Modifier[0] ? Modifier : nullptr, OS);
312            }
313          }
314          if (Error) {
315            std::string msg;
316            raw_string_ostream Msg(msg);
317            Msg << "invalid operand in inline asm: '" << AsmStr << "'";
318            MMI->getModule()->getContext().emitError(LocCookie, msg);
319          }
320        }
321        break;
322      }
323      }
324    }
325    if (InputIsIntelDialect)
326      OS << "\n\t.att_syntax";
327    OS << '\n' << (char)0;  // null terminate string.
328  }
329  
330  /// This method formats and emits the specified machine instruction that is an
331  /// inline asm.
332  void AsmPrinter::emitInlineAsm(const MachineInstr *MI) const {
333    assert(MI->isInlineAsm() && "printInlineAsm only works on inline asms");
334  
335    // Disassemble the AsmStr, printing out the literal pieces, the operands, etc.
336    const char *AsmStr = MI->getOperand(0).getSymbolName();
337  
338    // If this asmstr is empty, just print the #APP/#NOAPP markers.
339    // These are useful to see where empty asm's wound up.
340    if (AsmStr[0] == 0) {
341      OutStreamer->emitRawComment(MAI->getInlineAsmStart());
342      OutStreamer->emitRawComment(MAI->getInlineAsmEnd());
343      return;
344    }
345  
346    // Emit the #APP start marker.  This has to happen even if verbose-asm isn't
347    // enabled, so we use emitRawComment.
348    OutStreamer->emitRawComment(MAI->getInlineAsmStart());
349  
350    // Get the !srcloc metadata node if we have it, and decode the loc cookie from
351    // it.
352    uint64_t LocCookie = 0;
353    const MDNode *LocMD = nullptr;
354    for (const MachineOperand &MO : llvm::reverse(MI->operands())) {
355      if (MO.isMetadata() && (LocMD = MO.getMetadata()) &&
356          LocMD->getNumOperands() != 0) {
357        if (const ConstantInt *CI =
358                mdconst::dyn_extract<ConstantInt>(LocMD->getOperand(0))) {
359          LocCookie = CI->getZExtValue();
360          break;
361        }
362      }
363    }
364  
365    // Emit the inline asm to a temporary string so we can emit it through
366    // EmitInlineAsm.
367    SmallString<256> StringData;
368    raw_svector_ostream OS(StringData);
369  
370    AsmPrinter *AP = const_cast<AsmPrinter*>(this);
371    EmitInlineAsmStr(AsmStr, MI, MMI, MAI, AP, LocCookie, OS);
372  
373    // Emit warnings if we use reserved registers on the clobber list, as
374    // that might lead to undefined behaviour.
375    SmallVector<Register, 8> RestrRegs;
376    const TargetRegisterInfo *TRI = MF->getSubtarget().getRegisterInfo();
377    // Start with the first operand descriptor, and iterate over them.
378    for (unsigned I = InlineAsm::MIOp_FirstOperand, NumOps = MI->getNumOperands();
379         I < NumOps; ++I) {
380      const MachineOperand &MO = MI->getOperand(I);
381      if (!MO.isImm())
382        continue;
383      const InlineAsm::Flag F(MO.getImm());
384      if (F.isClobberKind()) {
385        Register Reg = MI->getOperand(I + 1).getReg();
386        if (!TRI->isAsmClobberable(*MF, Reg))
387          RestrRegs.push_back(Reg);
388      }
389      // Skip to one before the next operand descriptor, if it exists.
390      I += F.getNumOperandRegisters();
391    }
392  
393    if (!RestrRegs.empty()) {
394      std::string Msg = "inline asm clobber list contains reserved registers: ";
395      ListSeparator LS;
396      for (const Register RR : RestrRegs) {
397        Msg += LS;
398        Msg += TRI->getRegAsmName(RR);
399      }
400      const char *Note =
401          "Reserved registers on the clobber list may not be "
402          "preserved across the asm statement, and clobbering them may "
403          "lead to undefined behaviour.";
404      MMI->getModule()->getContext().diagnose(DiagnosticInfoInlineAsm(
405          LocCookie, Msg, DiagnosticSeverity::DS_Warning));
406      MMI->getModule()->getContext().diagnose(
407          DiagnosticInfoInlineAsm(LocCookie, Note, DiagnosticSeverity::DS_Note));
408  
409      for (const Register RR : RestrRegs) {
410        if (std::optional<std::string> reason =
411                TRI->explainReservedReg(*MF, RR)) {
412          MMI->getModule()->getContext().diagnose(DiagnosticInfoInlineAsm(
413              LocCookie, *reason, DiagnosticSeverity::DS_Note));
414        }
415      }
416    }
417  
418    emitInlineAsm(StringData, getSubtargetInfo(), TM.Options.MCOptions, LocMD,
419                  MI->getInlineAsmDialect());
420  
421    // Emit the #NOAPP end marker.  This has to happen even if verbose-asm isn't
422    // enabled, so we use emitRawComment.
423    OutStreamer->emitRawComment(MAI->getInlineAsmEnd());
424  }
425  
426  /// PrintSpecial - Print information related to the specified machine instr
427  /// that is independent of the operand, and may be independent of the instr
428  /// itself.  This can be useful for portably encoding the comment character
429  /// or other bits of target-specific knowledge into the asmstrings.  The
430  /// syntax used is ${:comment}.  Targets can override this to add support
431  /// for their own strange codes.
432  void AsmPrinter::PrintSpecial(const MachineInstr *MI, raw_ostream &OS,
433                                StringRef Code) const {
434    if (Code == "private") {
435      const DataLayout &DL = MF->getDataLayout();
436      OS << DL.getPrivateGlobalPrefix();
437    } else if (Code == "comment") {
438      OS << MAI->getCommentString();
439    } else if (Code == "uid") {
440      // Comparing the address of MI isn't sufficient, because machineinstrs may
441      // be allocated to the same address across functions.
442  
443      // If this is a new LastFn instruction, bump the counter.
444      if (LastMI != MI || LastFn != getFunctionNumber()) {
445        ++Counter;
446        LastMI = MI;
447        LastFn = getFunctionNumber();
448      }
449      OS << Counter;
450    } else {
451      std::string msg;
452      raw_string_ostream Msg(msg);
453      Msg << "Unknown special formatter '" << Code
454           << "' for machine instr: " << *MI;
455      report_fatal_error(Twine(Msg.str()));
456    }
457  }
458  
459  void AsmPrinter::PrintSymbolOperand(const MachineOperand &MO, raw_ostream &OS) {
460    assert(MO.isGlobal() && "caller should check MO.isGlobal");
461    getSymbolPreferLocal(*MO.getGlobal())->print(OS, MAI);
462    printOffset(MO.getOffset(), OS);
463  }
464  
465  /// PrintAsmOperand - Print the specified operand of MI, an INLINEASM
466  /// instruction, using the specified assembler variant.  Targets should
467  /// override this to format as appropriate for machine specific ExtraCodes
468  /// or when the arch-independent handling would be too complex otherwise.
469  bool AsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
470                                   const char *ExtraCode, raw_ostream &O) {
471    // Does this asm operand have a single letter operand modifier?
472    if (ExtraCode && ExtraCode[0]) {
473      if (ExtraCode[1] != 0) return true; // Unknown modifier.
474  
475      // https://gcc.gnu.org/onlinedocs/gccint/Output-Template.html
476      const MachineOperand &MO = MI->getOperand(OpNo);
477      switch (ExtraCode[0]) {
478      default:
479        return true;  // Unknown modifier.
480      case 'a': // Print as memory address.
481        if (MO.isReg()) {
482          PrintAsmMemoryOperand(MI, OpNo, nullptr, O);
483          return false;
484        }
485        [[fallthrough]]; // GCC allows '%a' to behave like '%c' with immediates.
486      case 'c': // Substitute immediate value without immediate syntax
487        if (MO.isImm()) {
488          O << MO.getImm();
489          return false;
490        }
491        if (MO.isGlobal()) {
492          PrintSymbolOperand(MO, O);
493          return false;
494        }
495        return true;
496      case 'n':  // Negate the immediate constant.
497        if (!MO.isImm())
498          return true;
499        O << -MO.getImm();
500        return false;
501      case 's':  // The GCC deprecated s modifier
502        if (!MO.isImm())
503          return true;
504        O << ((32 - MO.getImm()) & 31);
505        return false;
506      }
507    }
508    return true;
509  }
510  
511  bool AsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo,
512                                         const char *ExtraCode, raw_ostream &O) {
513    // Target doesn't support this yet!
514    return true;
515  }
516  
517  void AsmPrinter::emitInlineAsmStart() const {}
518  
519  void AsmPrinter::emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,
520                                    const MCSubtargetInfo *EndInfo) const {}
521