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