brintos

brintos / llvm-project-archived public Read only

0
0
Text · 19.3 KiB · 8dd8b9d Raw
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