//===- MIRParser.cpp - MIR serialization format parser implementation -----===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file implements the class that parses the optional LLVM IR and machine // functions that are stored in MIR files. // //===----------------------------------------------------------------------===// #include "llvm/CodeGen/MIRParser/MIRParser.h" #include "MIParser.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/STLExtras.h" #include "llvm/AsmParser/Parser.h" #include "llvm/AsmParser/SlotMapping.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/CodeGen/MIRYamlMapping.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/IR/ValueSymbolTable.h" #include "llvm/Support/LineIterator.h" #include "llvm/Support/SMLoc.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/YAMLTraits.h" #include using namespace llvm; namespace llvm { /// This class implements the parsing of LLVM IR that's embedded inside a MIR /// file. class MIRParserImpl { SourceMgr SM; StringRef Filename; LLVMContext &Context; StringMap> Functions; SlotMapping IRSlots; public: MIRParserImpl(std::unique_ptr Contents, StringRef Filename, LLVMContext &Context); void reportDiagnostic(const SMDiagnostic &Diag); /// Report an error with the given message at unknown location. /// /// Always returns true. bool error(const Twine &Message); /// Report a given error with the location translated from the location in an /// embedded string literal to a location in the MIR file. /// /// Always returns true. bool error(const SMDiagnostic &Error, SMRange SourceRange); /// Try to parse the optional LLVM module and the machine functions in the MIR /// file. /// /// Return null if an error occurred. std::unique_ptr parse(); /// Parse the machine function in the current YAML document. /// /// \param NoLLVMIR - set to true when the MIR file doesn't have LLVM IR. /// A dummy IR function is created and inserted into the given module when /// this parameter is true. /// /// Return true if an error occurred. bool parseMachineFunction(yaml::Input &In, Module &M, bool NoLLVMIR); /// Initialize the machine function to the state that's described in the MIR /// file. /// /// Return true if error occurred. bool initializeMachineFunction(MachineFunction &MF); /// Initialize the machine basic block using it's YAML representation. /// /// Return true if an error occurred. bool initializeMachineBasicBlock( MachineFunction &MF, MachineBasicBlock &MBB, const yaml::MachineBasicBlock &YamlMBB, const DenseMap &MBBSlots); bool initializeRegisterInfo(MachineRegisterInfo &RegInfo, const yaml::MachineFunction &YamlMF); private: /// Return a MIR diagnostic converted from an MI string diagnostic. SMDiagnostic diagFromMIStringDiag(const SMDiagnostic &Error, SMRange SourceRange); /// Return a MIR diagnostic converted from an LLVM assembly diagnostic. SMDiagnostic diagFromLLVMAssemblyDiag(const SMDiagnostic &Error, SMRange SourceRange); /// Create an empty function with the given name. void createDummyFunction(StringRef Name, Module &M); }; } // end namespace llvm MIRParserImpl::MIRParserImpl(std::unique_ptr Contents, StringRef Filename, LLVMContext &Context) : SM(), Filename(Filename), Context(Context) { SM.AddNewSourceBuffer(std::move(Contents), SMLoc()); } bool MIRParserImpl::error(const Twine &Message) { Context.diagnose(DiagnosticInfoMIRParser( DS_Error, SMDiagnostic(Filename, SourceMgr::DK_Error, Message.str()))); return true; } bool MIRParserImpl::error(const SMDiagnostic &Error, SMRange SourceRange) { assert(Error.getKind() == SourceMgr::DK_Error && "Expected an error"); reportDiagnostic(diagFromMIStringDiag(Error, SourceRange)); return true; } void MIRParserImpl::reportDiagnostic(const SMDiagnostic &Diag) { DiagnosticSeverity Kind; switch (Diag.getKind()) { case SourceMgr::DK_Error: Kind = DS_Error; break; case SourceMgr::DK_Warning: Kind = DS_Warning; break; case SourceMgr::DK_Note: Kind = DS_Note; break; } Context.diagnose(DiagnosticInfoMIRParser(Kind, Diag)); } static void handleYAMLDiag(const SMDiagnostic &Diag, void *Context) { reinterpret_cast(Context)->reportDiagnostic(Diag); } std::unique_ptr MIRParserImpl::parse() { yaml::Input In(SM.getMemoryBuffer(SM.getMainFileID())->getBuffer(), /*Ctxt=*/nullptr, handleYAMLDiag, this); In.setContext(&In); if (!In.setCurrentDocument()) { if (In.error()) return nullptr; // Create an empty module when the MIR file is empty. return llvm::make_unique(Filename, Context); } std::unique_ptr M; bool NoLLVMIR = false; // Parse the block scalar manually so that we can return unique pointer // without having to go trough YAML traits. if (const auto *BSN = dyn_cast_or_null(In.getCurrentNode())) { SMDiagnostic Error; M = parseAssembly(MemoryBufferRef(BSN->getValue(), Filename), Error, Context, &IRSlots); if (!M) { reportDiagnostic(diagFromLLVMAssemblyDiag(Error, BSN->getSourceRange())); return M; } In.nextDocument(); if (!In.setCurrentDocument()) return M; } else { // Create an new, empty module. M = llvm::make_unique(Filename, Context); NoLLVMIR = true; } // Parse the machine functions. do { if (parseMachineFunction(In, *M, NoLLVMIR)) return nullptr; In.nextDocument(); } while (In.setCurrentDocument()); return M; } bool MIRParserImpl::parseMachineFunction(yaml::Input &In, Module &M, bool NoLLVMIR) { auto MF = llvm::make_unique(); yaml::yamlize(In, *MF, false); if (In.error()) return true; auto FunctionName = MF->Name; if (Functions.find(FunctionName) != Functions.end()) return error(Twine("redefinition of machine function '") + FunctionName + "'"); Functions.insert(std::make_pair(FunctionName, std::move(MF))); if (NoLLVMIR) createDummyFunction(FunctionName, M); else if (!M.getFunction(FunctionName)) return error(Twine("function '") + FunctionName + "' isn't defined in the provided LLVM IR"); return false; } void MIRParserImpl::createDummyFunction(StringRef Name, Module &M) { auto &Context = M.getContext(); Function *F = cast(M.getOrInsertFunction( Name, FunctionType::get(Type::getVoidTy(Context), false))); BasicBlock *BB = BasicBlock::Create(Context, "entry", F); new UnreachableInst(Context, BB); } bool MIRParserImpl::initializeMachineFunction(MachineFunction &MF) { auto It = Functions.find(MF.getName()); if (It == Functions.end()) return error(Twine("no machine function information for function '") + MF.getName() + "' in the MIR file"); // TODO: Recreate the machine function. const yaml::MachineFunction &YamlMF = *It->getValue(); if (YamlMF.Alignment) MF.setAlignment(YamlMF.Alignment); MF.setExposesReturnsTwice(YamlMF.ExposesReturnsTwice); MF.setHasInlineAsm(YamlMF.HasInlineAsm); if (initializeRegisterInfo(MF.getRegInfo(), YamlMF)) return true; const auto &F = *MF.getFunction(); DenseMap MBBSlots; for (const auto &YamlMBB : YamlMF.BasicBlocks) { const BasicBlock *BB = nullptr; if (!YamlMBB.Name.empty()) { BB = dyn_cast_or_null( F.getValueSymbolTable().lookup(YamlMBB.Name)); if (!BB) return error(Twine("basic block '") + YamlMBB.Name + "' is not defined in the function '" + MF.getName() + "'"); } auto *MBB = MF.CreateMachineBasicBlock(BB); MF.insert(MF.end(), MBB); bool WasInserted = MBBSlots.insert(std::make_pair(YamlMBB.ID, MBB)).second; if (!WasInserted) return error(Twine("redefinition of machine basic block with id #") + Twine(YamlMBB.ID)); } // Initialize the machine basic blocks after creating them all so that the // machine instructions parser can resolve the MBB references. unsigned I = 0; for (const auto &YamlMBB : YamlMF.BasicBlocks) { if (initializeMachineBasicBlock(MF, *MF.getBlockNumbered(I++), YamlMBB, MBBSlots)) return true; } return false; } bool MIRParserImpl::initializeMachineBasicBlock( MachineFunction &MF, MachineBasicBlock &MBB, const yaml::MachineBasicBlock &YamlMBB, const DenseMap &MBBSlots) { MBB.setAlignment(YamlMBB.Alignment); if (YamlMBB.AddressTaken) MBB.setHasAddressTaken(); MBB.setIsLandingPad(YamlMBB.IsLandingPad); SMDiagnostic Error; // Parse the successors. for (const auto &MBBSource : YamlMBB.Successors) { MachineBasicBlock *SuccMBB = nullptr; if (parseMBBReference(SuccMBB, SM, MF, MBBSource.Value, MBBSlots, IRSlots, Error)) return error(Error, MBBSource.SourceRange); // TODO: Report an error when adding the same successor more than once. MBB.addSuccessor(SuccMBB); } // Parse the instructions. for (const auto &MISource : YamlMBB.Instructions) { MachineInstr *MI = nullptr; if (parseMachineInstr(MI, SM, MF, MISource.Value, MBBSlots, IRSlots, Error)) return error(Error, MISource.SourceRange); MBB.insert(MBB.end(), MI); } return false; } bool MIRParserImpl::initializeRegisterInfo( MachineRegisterInfo &RegInfo, const yaml::MachineFunction &YamlMF) { assert(RegInfo.isSSA()); if (!YamlMF.IsSSA) RegInfo.leaveSSA(); assert(RegInfo.tracksLiveness()); if (!YamlMF.TracksRegLiveness) RegInfo.invalidateLiveness(); RegInfo.enableSubRegLiveness(YamlMF.TracksSubRegLiveness); return false; } SMDiagnostic MIRParserImpl::diagFromMIStringDiag(const SMDiagnostic &Error, SMRange SourceRange) { assert(SourceRange.isValid() && "Invalid source range"); SMLoc Loc = SourceRange.Start; bool HasQuote = Loc.getPointer() < SourceRange.End.getPointer() && *Loc.getPointer() == '\''; // Translate the location of the error from the location in the MI string to // the corresponding location in the MIR file. Loc = Loc.getFromPointer(Loc.getPointer() + Error.getColumnNo() + (HasQuote ? 1 : 0)); // TODO: Translate any source ranges as well. return SM.GetMessage(Loc, Error.getKind(), Error.getMessage(), None, Error.getFixIts()); } SMDiagnostic MIRParserImpl::diagFromLLVMAssemblyDiag(const SMDiagnostic &Error, SMRange SourceRange) { assert(SourceRange.isValid()); // Translate the location of the error from the location in the llvm IR string // to the corresponding location in the MIR file. auto LineAndColumn = SM.getLineAndColumn(SourceRange.Start); unsigned Line = LineAndColumn.first + Error.getLineNo() - 1; unsigned Column = Error.getColumnNo(); StringRef LineStr = Error.getLineContents(); SMLoc Loc = Error.getLoc(); // Get the full line and adjust the column number by taking the indentation of // LLVM IR into account. for (line_iterator L(*SM.getMemoryBuffer(SM.getMainFileID()), false), E; L != E; ++L) { if (L.line_number() == Line) { LineStr = *L; Loc = SMLoc::getFromPointer(LineStr.data()); auto Indent = LineStr.find(Error.getLineContents()); if (Indent != StringRef::npos) Column += Indent; break; } } return SMDiagnostic(SM, Loc, Filename, Line, Column, Error.getKind(), Error.getMessage(), LineStr, Error.getRanges(), Error.getFixIts()); } MIRParser::MIRParser(std::unique_ptr Impl) : Impl(std::move(Impl)) {} MIRParser::~MIRParser() {} std::unique_ptr MIRParser::parseLLVMModule() { return Impl->parse(); } bool MIRParser::initializeMachineFunction(MachineFunction &MF) { return Impl->initializeMachineFunction(MF); } std::unique_ptr llvm::createMIRParserFromFile(StringRef Filename, SMDiagnostic &Error, LLVMContext &Context) { auto FileOrErr = MemoryBuffer::getFile(Filename); if (std::error_code EC = FileOrErr.getError()) { Error = SMDiagnostic(Filename, SourceMgr::DK_Error, "Could not open input file: " + EC.message()); return nullptr; } return createMIRParser(std::move(FileOrErr.get()), Context); } std::unique_ptr llvm::createMIRParser(std::unique_ptr Contents, LLVMContext &Context) { auto Filename = Contents->getBufferIdentifier(); return llvm::make_unique( llvm::make_unique(std::move(Contents), Filename, Context)); }