Path: blob/main/contrib/llvm-project/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp
35271 views
//===-- AsmPrinterInlineAsm.cpp - AsmPrinter Inline Asm Handling ----------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file implements the inline assembler pieces of the AsmPrinter class.9//10//===----------------------------------------------------------------------===//1112#include "llvm/ADT/SmallString.h"13#include "llvm/ADT/SmallVector.h"14#include "llvm/ADT/StringExtras.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"40using namespace llvm;4142#define DEBUG_TYPE "asm-printer"4344unsigned AsmPrinter::addInlineAsmDiagBuffer(StringRef AsmStr,45const MDNode *LocMDNode) const {46MCContext &Context = MMI->getContext();47Context.initInlineSourceManager();48SourceMgr &SrcMgr = *Context.getInlineSourceManager();49std::vector<const MDNode *> &LocInfos = Context.getLocInfos();5051std::unique_ptr<MemoryBuffer> Buffer;52// The inline asm source manager will outlive AsmStr, so make a copy of the53// string for SourceMgr to own.54Buffer = MemoryBuffer::getMemBufferCopy(AsmStr, "<inline asm>");5556// Tell SrcMgr about this buffer, it takes ownership of the buffer.57unsigned BufNum = SrcMgr.AddNewSourceBuffer(std::move(Buffer), SMLoc());5859// Store LocMDNode in DiagInfo, using BufNum as an identifier.60if (LocMDNode) {61LocInfos.resize(BufNum);62LocInfos[BufNum - 1] = LocMDNode;63}6465return BufNum;66}676869/// EmitInlineAsm - Emit a blob of inline asm to the output streamer.70void AsmPrinter::emitInlineAsm(StringRef Str, const MCSubtargetInfo &STI,71const MCTargetOptions &MCOptions,72const MDNode *LocMDNode,73InlineAsm::AsmDialect Dialect) const {74assert(!Str.empty() && "Can't emit empty inline asm block");7576// Remember if the buffer is nul terminated or not so we can avoid a copy.77bool isNullTerminated = Str.back() == 0;78if (isNullTerminated)79Str = Str.substr(0, Str.size()-1);8081// If the output streamer does not have mature MC support or the integrated82// 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 the85// system assembler does.86const MCAsmInfo *MCAI = TM.getMCAsmInfo();87assert(MCAI && "No MCAsmInfo");88if (!MCAI->useIntegratedAssembler() &&89!MCAI->parseInlineAsmUsingAsmParser() &&90!OutStreamer->isIntegratedAssemblerRequired()) {91emitInlineAsmStart();92OutStreamer->emitRawText(Str);93emitInlineAsmEnd(STI, nullptr);94return;95}9697unsigned BufNum = addInlineAsmDiagBuffer(Str, LocMDNode);98SourceMgr &SrcMgr = *MMI->getContext().getInlineSourceManager();99SrcMgr.setIncludeDirs(MCOptions.IASSearchPaths);100101std::unique_ptr<MCAsmParser> Parser(102createMCAsmParser(SrcMgr, OutContext, *OutStreamer, *MAI, BufNum));103104// We create a new MCInstrInfo here since we might be at the module level105// and not have a MachineFunction to initialize the TargetInstrInfo from and106// we only need MCInstrInfo for asm parsing. We create one unconditionally107// because it's not subtarget dependent.108std::unique_ptr<MCInstrInfo> MII(TM.getTarget().createMCInstrInfo());109assert(MII && "Failed to create instruction info");110std::unique_ptr<MCTargetAsmParser> TAP(TM.getTarget().createMCAsmParser(111STI, *Parser, *MII, MCOptions));112if (!TAP)113report_fatal_error("Inline asm not supported by this streamer because"114" we don't have an asm parser for this target\n");115116// Respect inlineasm dialect on X86 targets only117if (TM.getTargetTriple().isX86()) {118Parser->setAssemblerDialect(Dialect);119// Enable lexing Masm binary and hex integer literals in intel inline120// assembly.121if (Dialect == InlineAsm::AD_Intel)122Parser->getLexer().setLexMasmIntegers(true);123}124Parser->setTargetParser(*TAP);125126emitInlineAsmStart();127// Don't implicitly switch to the text section before the asm.128(void)Parser->Run(/*NoInitialTextSection*/ true,129/*NoFinalize*/ true);130emitInlineAsmEnd(STI, &TAP->getSTI());131}132133static void EmitInlineAsmStr(const char *AsmStr, const MachineInstr *MI,134MachineModuleInfo *MMI, const MCAsmInfo *MAI,135AsmPrinter *AP, uint64_t LocCookie,136raw_ostream &OS) {137bool InputIsIntelDialect = MI->getInlineAsmDialect() == InlineAsm::AD_Intel;138139if (InputIsIntelDialect) {140// Switch to the inline assembly variant.141OS << "\t.intel_syntax\n\t";142}143144int CurVariant = -1; // The number of the {.|.|.} region we are in.145const char *LastEmitted = AsmStr; // One past the last character emitted.146unsigned NumOperands = MI->getNumOperands();147148int AsmPrinterVariant;149if (InputIsIntelDialect)150AsmPrinterVariant = 1; // X86MCAsmInfo.cpp's AsmWriterFlavorTy::Intel.151else152AsmPrinterVariant = MMI->getTarget().unqualifiedInlineAsmVariant();153154// FIXME: Should this happen for `asm inteldialect` as well?155if (!InputIsIntelDialect && MAI->getEmitGNUAsmStartIndentationMarker())156OS << '\t';157158while (*LastEmitted) {159switch (*LastEmitted) {160default: {161// Not a special case, emit the string section literally.162const char *LiteralEnd = LastEmitted+1;163while (*LiteralEnd && *LiteralEnd != '{' && *LiteralEnd != '|' &&164*LiteralEnd != '}' && *LiteralEnd != '$' && *LiteralEnd != '\n')165++LiteralEnd;166if (CurVariant == -1 || CurVariant == AsmPrinterVariant)167OS.write(LastEmitted, LiteralEnd - LastEmitted);168LastEmitted = LiteralEnd;169break;170}171case '\n':172++LastEmitted; // Consume newline character.173OS << '\n'; // Indent code with newline.174break;175case '$': {176++LastEmitted; // Consume '$' character.177bool Done = true;178179// Handle escapes.180switch (*LastEmitted) {181default: Done = false; break;182case '$': // $$ -> $183if (!InputIsIntelDialect)184if (CurVariant == -1 || CurVariant == AsmPrinterVariant)185OS << '$';186++LastEmitted; // Consume second '$' character.187break;188case '(': // $( -> same as GCC's { character.189++LastEmitted; // Consume '(' character.190if (CurVariant != -1)191report_fatal_error("Nested variants found in inline asm string: '" +192Twine(AsmStr) + "'");193CurVariant = 0; // We're in the first variant now.194break;195case '|':196++LastEmitted; // Consume '|' character.197if (CurVariant == -1)198OS << '|'; // This is gcc's behavior for | outside a variant.199else200++CurVariant; // We're in the next variant.201break;202case ')': // $) -> same as GCC's } char.203++LastEmitted; // Consume ')' character.204if (CurVariant == -1)205OS << '}'; // This is gcc's behavior for } outside a variant.206else207CurVariant = -1;208break;209}210if (Done) break;211212bool HasCurlyBraces = false;213if (*LastEmitted == '{') { // ${variable}214++LastEmitted; // Consume '{' character.215HasCurlyBraces = true;216}217218// If we have ${:foo}, then this is not a real operand reference, it is a219// "magic" string reference, just like in .td files. Arrange to call220// PrintSpecial.221if (HasCurlyBraces && *LastEmitted == ':') {222++LastEmitted;223const char *StrStart = LastEmitted;224const char *StrEnd = strchr(StrStart, '}');225if (!StrEnd)226report_fatal_error("Unterminated ${:foo} operand in inline asm"227" string: '" + Twine(AsmStr) + "'");228if (CurVariant == -1 || CurVariant == AsmPrinterVariant)229AP->PrintSpecial(MI, OS, StringRef(StrStart, StrEnd - StrStart));230LastEmitted = StrEnd+1;231break;232}233234const char *IDStart = LastEmitted;235const char *IDEnd = IDStart;236while (isDigit(*IDEnd))237++IDEnd;238239unsigned Val;240if (StringRef(IDStart, IDEnd-IDStart).getAsInteger(10, Val))241report_fatal_error("Bad $ operand number in inline asm string: '" +242Twine(AsmStr) + "'");243LastEmitted = IDEnd;244245if (Val >= NumOperands - 1)246report_fatal_error("Invalid $ operand number in inline asm string: '" +247Twine(AsmStr) + "'");248249char Modifier[2] = { 0, 0 };250251if (HasCurlyBraces) {252// If we have curly braces, check for a modifier character. This253// supports syntax like ${0:u}, which correspond to "%u0" in GCC asm.254if (*LastEmitted == ':') {255++LastEmitted; // Consume ':' character.256if (*LastEmitted == 0)257report_fatal_error("Bad ${:} expression in inline asm string: '" +258Twine(AsmStr) + "'");259260Modifier[0] = *LastEmitted;261++LastEmitted; // Consume modifier character.262}263264if (*LastEmitted != '}')265report_fatal_error("Bad ${} expression in inline asm string: '" +266Twine(AsmStr) + "'");267++LastEmitted; // Consume '}' character.268}269270// Okay, we finally have a value number. Ask the target to print this271// operand!272if (CurVariant == -1 || CurVariant == AsmPrinterVariant) {273unsigned OpNo = InlineAsm::MIOp_FirstOperand;274275bool Error = false;276277// Scan to find the machine operand number for the operand.278for (; Val; --Val) {279if (OpNo >= MI->getNumOperands())280break;281const InlineAsm::Flag F(MI->getOperand(OpNo).getImm());282OpNo += F.getNumOperandRegisters() + 1;283}284285// We may have a location metadata attached to the end of the286// instruction, and at no point should see metadata at any287// other point while processing. It's an error if so.288if (OpNo >= MI->getNumOperands() || MI->getOperand(OpNo).isMetadata()) {289Error = true;290} else {291const InlineAsm::Flag F(MI->getOperand(OpNo).getImm());292++OpNo; // Skip over the ID number.293294// FIXME: Shouldn't arch-independent output template handling go into295// PrintAsmOperand?296// Labels are target independent.297if (MI->getOperand(OpNo).isBlockAddress()) {298const BlockAddress *BA = MI->getOperand(OpNo).getBlockAddress();299MCSymbol *Sym = AP->GetBlockAddressSymbol(BA);300Sym->print(OS, AP->MAI);301MMI->getContext().registerInlineAsmLabel(Sym);302} else if (MI->getOperand(OpNo).isMBB()) {303const MCSymbol *Sym = MI->getOperand(OpNo).getMBB()->getSymbol();304Sym->print(OS, AP->MAI);305} else if (F.isMemKind()) {306Error = AP->PrintAsmMemoryOperand(307MI, OpNo, Modifier[0] ? Modifier : nullptr, OS);308} else {309Error = AP->PrintAsmOperand(MI, OpNo,310Modifier[0] ? Modifier : nullptr, OS);311}312}313if (Error) {314std::string msg;315raw_string_ostream Msg(msg);316Msg << "invalid operand in inline asm: '" << AsmStr << "'";317MMI->getModule()->getContext().emitError(LocCookie, msg);318}319}320break;321}322}323}324if (InputIsIntelDialect)325OS << "\n\t.att_syntax";326OS << '\n' << (char)0; // null terminate string.327}328329/// This method formats and emits the specified machine instruction that is an330/// inline asm.331void AsmPrinter::emitInlineAsm(const MachineInstr *MI) const {332assert(MI->isInlineAsm() && "printInlineAsm only works on inline asms");333334// Disassemble the AsmStr, printing out the literal pieces, the operands, etc.335const char *AsmStr = MI->getOperand(0).getSymbolName();336337// If this asmstr is empty, just print the #APP/#NOAPP markers.338// These are useful to see where empty asm's wound up.339if (AsmStr[0] == 0) {340OutStreamer->emitRawComment(MAI->getInlineAsmStart());341OutStreamer->emitRawComment(MAI->getInlineAsmEnd());342return;343}344345// Emit the #APP start marker. This has to happen even if verbose-asm isn't346// enabled, so we use emitRawComment.347OutStreamer->emitRawComment(MAI->getInlineAsmStart());348349// Get the !srcloc metadata node if we have it, and decode the loc cookie from350// it.351uint64_t LocCookie = 0;352const MDNode *LocMD = nullptr;353for (const MachineOperand &MO : llvm::reverse(MI->operands())) {354if (MO.isMetadata() && (LocMD = MO.getMetadata()) &&355LocMD->getNumOperands() != 0) {356if (const ConstantInt *CI =357mdconst::dyn_extract<ConstantInt>(LocMD->getOperand(0))) {358LocCookie = CI->getZExtValue();359break;360}361}362}363364// Emit the inline asm to a temporary string so we can emit it through365// EmitInlineAsm.366SmallString<256> StringData;367raw_svector_ostream OS(StringData);368369AsmPrinter *AP = const_cast<AsmPrinter*>(this);370EmitInlineAsmStr(AsmStr, MI, MMI, MAI, AP, LocCookie, OS);371372// Emit warnings if we use reserved registers on the clobber list, as373// that might lead to undefined behaviour.374SmallVector<Register, 8> RestrRegs;375const TargetRegisterInfo *TRI = MF->getSubtarget().getRegisterInfo();376// Start with the first operand descriptor, and iterate over them.377for (unsigned I = InlineAsm::MIOp_FirstOperand, NumOps = MI->getNumOperands();378I < NumOps; ++I) {379const MachineOperand &MO = MI->getOperand(I);380if (!MO.isImm())381continue;382const InlineAsm::Flag F(MO.getImm());383if (F.isClobberKind()) {384Register Reg = MI->getOperand(I + 1).getReg();385if (!TRI->isAsmClobberable(*MF, Reg))386RestrRegs.push_back(Reg);387}388// Skip to one before the next operand descriptor, if it exists.389I += F.getNumOperandRegisters();390}391392if (!RestrRegs.empty()) {393std::string Msg = "inline asm clobber list contains reserved registers: ";394ListSeparator LS;395for (const Register RR : RestrRegs) {396Msg += LS;397Msg += TRI->getRegAsmName(RR);398}399const char *Note =400"Reserved registers on the clobber list may not be "401"preserved across the asm statement, and clobbering them may "402"lead to undefined behaviour.";403MMI->getModule()->getContext().diagnose(DiagnosticInfoInlineAsm(404LocCookie, Msg, DiagnosticSeverity::DS_Warning));405MMI->getModule()->getContext().diagnose(406DiagnosticInfoInlineAsm(LocCookie, Note, DiagnosticSeverity::DS_Note));407408for (const Register RR : RestrRegs) {409if (std::optional<std::string> reason =410TRI->explainReservedReg(*MF, RR)) {411MMI->getModule()->getContext().diagnose(DiagnosticInfoInlineAsm(412LocCookie, *reason, DiagnosticSeverity::DS_Note));413}414}415}416417emitInlineAsm(StringData, getSubtargetInfo(), TM.Options.MCOptions, LocMD,418MI->getInlineAsmDialect());419420// Emit the #NOAPP end marker. This has to happen even if verbose-asm isn't421// enabled, so we use emitRawComment.422OutStreamer->emitRawComment(MAI->getInlineAsmEnd());423}424425/// PrintSpecial - Print information related to the specified machine instr426/// that is independent of the operand, and may be independent of the instr427/// itself. This can be useful for portably encoding the comment character428/// or other bits of target-specific knowledge into the asmstrings. The429/// syntax used is ${:comment}. Targets can override this to add support430/// for their own strange codes.431void AsmPrinter::PrintSpecial(const MachineInstr *MI, raw_ostream &OS,432StringRef Code) const {433if (Code == "private") {434const DataLayout &DL = MF->getDataLayout();435OS << DL.getPrivateGlobalPrefix();436} else if (Code == "comment") {437OS << MAI->getCommentString();438} else if (Code == "uid") {439// Comparing the address of MI isn't sufficient, because machineinstrs may440// be allocated to the same address across functions.441442// If this is a new LastFn instruction, bump the counter.443if (LastMI != MI || LastFn != getFunctionNumber()) {444++Counter;445LastMI = MI;446LastFn = getFunctionNumber();447}448OS << Counter;449} else {450std::string msg;451raw_string_ostream Msg(msg);452Msg << "Unknown special formatter '" << Code453<< "' for machine instr: " << *MI;454report_fatal_error(Twine(Msg.str()));455}456}457458void AsmPrinter::PrintSymbolOperand(const MachineOperand &MO, raw_ostream &OS) {459assert(MO.isGlobal() && "caller should check MO.isGlobal");460getSymbolPreferLocal(*MO.getGlobal())->print(OS, MAI);461printOffset(MO.getOffset(), OS);462}463464/// PrintAsmOperand - Print the specified operand of MI, an INLINEASM465/// instruction, using the specified assembler variant. Targets should466/// override this to format as appropriate for machine specific ExtraCodes467/// or when the arch-independent handling would be too complex otherwise.468bool AsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,469const char *ExtraCode, raw_ostream &O) {470// Does this asm operand have a single letter operand modifier?471if (ExtraCode && ExtraCode[0]) {472if (ExtraCode[1] != 0) return true; // Unknown modifier.473474// https://gcc.gnu.org/onlinedocs/gccint/Output-Template.html475const MachineOperand &MO = MI->getOperand(OpNo);476switch (ExtraCode[0]) {477default:478return true; // Unknown modifier.479case 'a': // Print as memory address.480if (MO.isReg()) {481PrintAsmMemoryOperand(MI, OpNo, nullptr, O);482return false;483}484[[fallthrough]]; // GCC allows '%a' to behave like '%c' with immediates.485case 'c': // Substitute immediate value without immediate syntax486if (MO.isImm()) {487O << MO.getImm();488return false;489}490if (MO.isGlobal()) {491PrintSymbolOperand(MO, O);492return false;493}494return true;495case 'n': // Negate the immediate constant.496if (!MO.isImm())497return true;498O << -MO.getImm();499return false;500case 's': // The GCC deprecated s modifier501if (!MO.isImm())502return true;503O << ((32 - MO.getImm()) & 31);504return false;505}506}507return true;508}509510bool AsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo,511const char *ExtraCode, raw_ostream &O) {512// Target doesn't support this yet!513return true;514}515516void AsmPrinter::emitInlineAsmStart() const {}517518void AsmPrinter::emitInlineAsmEnd(const MCSubtargetInfo &StartInfo,519const MCSubtargetInfo *EndInfo) const {}520521522