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