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(InputData: AsmStr, BufferName: "<inline asm>" ); |
56 | |
57 | // Tell SrcMgr about this buffer, it takes ownership of the buffer. |
58 | unsigned BufNum = SrcMgr.AddNewSourceBuffer(F: std::move(Buffer), IncludeLoc: SMLoc()); |
59 | |
60 | // Store LocMDNode in DiagInfo, using BufNum as an identifier. |
61 | if (LocMDNode) { |
62 | LocInfos.resize(new_size: 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(Start: 0, N: 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(String: Str); |
94 | emitInlineAsmEnd(StartInfo: STI, EndInfo: nullptr); |
95 | return; |
96 | } |
97 | |
98 | unsigned BufNum = addInlineAsmDiagBuffer(AsmStr: 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, CB: 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&: *Parser, MII: *MII, Options: MCOptions)); |
113 | if (!TAP) |
114 | report_fatal_error(reason: "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(StartInfo: STI, EndInfo: &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(Ptr: LastEmitted, Size: 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(reason: "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(s: StrStart, c: '}'); |
226 | if (!StrEnd) |
227 | report_fatal_error(reason: "Unterminated ${:foo} operand in inline asm" |
228 | " string: '" + Twine(AsmStr) + "'" ); |
229 | if (CurVariant == -1 || CurVariant == AsmPrinterVariant) |
230 | AP->PrintSpecial(MI, OS, Code: 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(C: *IDEnd)) |
238 | ++IDEnd; |
239 | |
240 | unsigned Val; |
241 | if (StringRef(IDStart, IDEnd-IDStart).getAsInteger(Radix: 10, Result&: Val)) |
242 | report_fatal_error(reason: "Bad $ operand number in inline asm string: '" + |
243 | Twine(AsmStr) + "'" ); |
244 | LastEmitted = IDEnd; |
245 | |
246 | if (Val >= NumOperands - 1) |
247 | report_fatal_error(reason: "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(reason: "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(reason: "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(i: 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(i: OpNo).isMetadata()) { |
290 | Error = true; |
291 | } else { |
292 | const InlineAsm::Flag F(MI->getOperand(i: 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(i: OpNo).isBlockAddress()) { |
299 | const BlockAddress *BA = MI->getOperand(i: OpNo).getBlockAddress(); |
300 | MCSymbol *Sym = AP->GetBlockAddressSymbol(BA); |
301 | Sym->print(OS, MAI: AP->MAI); |
302 | MMI->getContext().registerInlineAsmLabel(Sym); |
303 | } else if (MI->getOperand(i: OpNo).isMBB()) { |
304 | const MCSymbol *Sym = MI->getOperand(i: OpNo).getMBB()->getSymbol(); |
305 | Sym->print(OS, MAI: AP->MAI); |
306 | } else if (F.isMemKind()) { |
307 | Error = AP->PrintAsmMemoryOperand( |
308 | MI, OpNo, ExtraCode: Modifier[0] ? Modifier : nullptr, OS); |
309 | } else { |
310 | Error = AP->PrintAsmOperand(MI, OpNo, |
311 | ExtraCode: 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, ErrorStr: 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(i: 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(T: MAI->getInlineAsmStart()); |
342 | OutStreamer->emitRawComment(T: 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(T: 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(C: MI->operands())) { |
355 | if (MO.isMetadata() && (LocMD = MO.getMetadata()) && |
356 | LocMD->getNumOperands() != 0) { |
357 | if (const ConstantInt *CI = |
358 | mdconst::dyn_extract<ConstantInt>(MD: LocMD->getOperand(I: 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: I); |
381 | if (!MO.isImm()) |
382 | continue; |
383 | const InlineAsm::Flag F(MO.getImm()); |
384 | if (F.isClobberKind()) { |
385 | Register Reg = MI->getOperand(i: I + 1).getReg(); |
386 | if (!TRI->isAsmClobberable(MF: *MF, PhysReg: Reg)) |
387 | RestrRegs.push_back(Elt: 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(Reg: 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(DI: DiagnosticInfoInlineAsm( |
405 | LocCookie, Msg, DiagnosticSeverity::DS_Warning)); |
406 | MMI->getModule()->getContext().diagnose( |
407 | DI: DiagnosticInfoInlineAsm(LocCookie, Note, DiagnosticSeverity::DS_Note)); |
408 | |
409 | for (const Register RR : RestrRegs) { |
410 | if (std::optional<std::string> reason = |
411 | TRI->explainReservedReg(MF: *MF, PhysReg: RR)) { |
412 | MMI->getModule()->getContext().diagnose(DI: DiagnosticInfoInlineAsm( |
413 | LocCookie, *reason, DiagnosticSeverity::DS_Note)); |
414 | } |
415 | } |
416 | } |
417 | |
418 | emitInlineAsm(Str: StringData, STI: getSubtargetInfo(), MCOptions: TM.Options.MCOptions, LocMDNode: LocMD, |
419 | Dialect: 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(T: 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(reason: 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(GV: *MO.getGlobal())->print(OS, MAI); |
462 | printOffset(Offset: 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 *, 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(i: 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, ExtraCode: nullptr, OS&: 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, OS&: 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 *, 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 | |