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