//===-- Assembler.cpp -------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "Assembler.h" #include "Target.h" #include "llvm/CodeGen/GlobalISel/CallLowering.h" #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" #include "llvm/CodeGen/MachineInstrBuilder.h" #include "llvm/CodeGen/MachineModuleInfo.h" #include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/CodeGen/TargetInstrInfo.h" #include "llvm/CodeGen/TargetPassConfig.h" #include "llvm/CodeGen/TargetSubtargetInfo.h" #include "llvm/ExecutionEngine/SectionMemoryManager.h" #include "llvm/IR/LegacyPassManager.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/Support/MemoryBuffer.h" namespace exegesis { static constexpr const char ModuleID[] = "ExegesisInfoTest"; static constexpr const char FunctionID[] = "foo"; static std::vector generateSnippetSetupCode(const llvm::ArrayRef RegsToDef, const ExegesisTarget &ET, const llvm::LLVMTargetMachine &TM, bool &IsComplete) { IsComplete = true; std::vector Result; for (const unsigned Reg : RegsToDef) { // Load a constant in the register. const auto Code = ET.setRegToConstant(*TM.getMCSubtargetInfo(), Reg); if (Code.empty()) IsComplete = false; Result.insert(Result.end(), Code.begin(), Code.end()); } return Result; } // Small utility function to add named passes. static bool addPass(llvm::PassManagerBase &PM, llvm::StringRef PassName, llvm::TargetPassConfig &TPC) { const llvm::PassRegistry *PR = llvm::PassRegistry::getPassRegistry(); const llvm::PassInfo *PI = PR->getPassInfo(PassName); if (!PI) { llvm::errs() << " run-pass " << PassName << " is not registered.\n"; return true; } if (!PI->getNormalCtor()) { llvm::errs() << " cannot create pass: " << PI->getPassName() << "\n"; return true; } llvm::Pass *P = PI->getNormalCtor()(); std::string Banner = std::string("After ") + std::string(P->getPassName()); PM.add(P); TPC.printAndVerify(Banner); return false; } // Creates a void MachineFunction with no argument. static llvm::MachineFunction & createVoidVoidMachineFunction(llvm::StringRef FunctionID, llvm::Module *Module, llvm::MachineModuleInfo *MMI) { llvm::Type *const ReturnType = llvm::Type::getInt32Ty(Module->getContext()); llvm::FunctionType *FunctionType = llvm::FunctionType::get(ReturnType, false); llvm::Function *const F = llvm::Function::Create( FunctionType, llvm::GlobalValue::InternalLinkage, FunctionID, Module); // Making sure we can create a MachineFunction out of this Function even if it // contains no IR. F->setIsMaterializable(true); return MMI->getOrCreateMachineFunction(*F); } static void fillMachineFunction(llvm::MachineFunction &MF, llvm::ArrayRef Instructions) { llvm::MachineBasicBlock *MBB = MF.CreateMachineBasicBlock(); MF.push_back(MBB); const llvm::MCInstrInfo *MCII = MF.getTarget().getMCInstrInfo(); llvm::DebugLoc DL; for (const llvm::MCInst &Inst : Instructions) { const unsigned Opcode = Inst.getOpcode(); const llvm::MCInstrDesc &MCID = MCII->get(Opcode); llvm::MachineInstrBuilder Builder = llvm::BuildMI(MBB, DL, MCID); for (unsigned OpIndex = 0, E = Inst.getNumOperands(); OpIndex < E; ++OpIndex) { const llvm::MCOperand &Op = Inst.getOperand(OpIndex); if (Op.isReg()) { const bool IsDef = OpIndex < MCID.getNumDefs(); unsigned Flags = 0; const llvm::MCOperandInfo &OpInfo = MCID.operands().begin()[OpIndex]; if (IsDef && !OpInfo.isOptionalDef()) Flags |= llvm::RegState::Define; Builder.addReg(Op.getReg(), Flags); } else if (Op.isImm()) { Builder.addImm(Op.getImm()); } else { llvm_unreachable("Not yet implemented"); } } } // Insert the return code. const llvm::TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo(); if (TII->getReturnOpcode() < TII->getNumOpcodes()) { llvm::BuildMI(MBB, DL, TII->get(TII->getReturnOpcode())); } else { llvm::MachineIRBuilder MIB(MF); MIB.setMBB(*MBB); MF.getSubtarget().getCallLowering()->lowerReturn(MIB, nullptr, 0); } } static std::unique_ptr createModule(const std::unique_ptr &Context, const llvm::DataLayout DL) { auto Module = llvm::make_unique(ModuleID, *Context); Module->setDataLayout(DL); return Module; } llvm::BitVector getFunctionReservedRegs(const llvm::TargetMachine &TM) { std::unique_ptr Context = llvm::make_unique(); std::unique_ptr Module = createModule(Context, TM.createDataLayout()); std::unique_ptr MMI = llvm::make_unique(&TM); llvm::MachineFunction &MF = createVoidVoidMachineFunction(FunctionID, Module.get(), MMI.get()); // Saving reserved registers for client. return MF.getSubtarget().getRegisterInfo()->getReservedRegs(MF); } void assembleToStream(const ExegesisTarget &ET, std::unique_ptr TM, llvm::ArrayRef RegsToDef, llvm::ArrayRef Instructions, llvm::raw_pwrite_stream &AsmStream) { std::unique_ptr Context = llvm::make_unique(); std::unique_ptr Module = createModule(Context, TM->createDataLayout()); std::unique_ptr MMI = llvm::make_unique(TM.get()); llvm::MachineFunction &MF = createVoidVoidMachineFunction(FunctionID, Module.get(), MMI.get()); // We need to instruct the passes that we're done with SSA and virtual // registers. auto &Properties = MF.getProperties(); Properties.set(llvm::MachineFunctionProperties::Property::NoVRegs); Properties.reset(llvm::MachineFunctionProperties::Property::IsSSA); bool IsSnippetSetupComplete = false; std::vector SnippetWithSetup = generateSnippetSetupCode(RegsToDef, ET, *TM, IsSnippetSetupComplete); if (!SnippetWithSetup.empty()) { SnippetWithSetup.insert(SnippetWithSetup.end(), Instructions.begin(), Instructions.end()); Instructions = SnippetWithSetup; } // If the snippet setup is not complete, we disable liveliness tracking. This // means that we won't know what values are in the registers. if (!IsSnippetSetupComplete) Properties.reset(llvm::MachineFunctionProperties::Property::TracksLiveness); // prologue/epilogue pass needs the reserved registers to be frozen, this // is usually done by the SelectionDAGISel pass. MF.getRegInfo().freezeReservedRegs(MF); // Fill the MachineFunction from the instructions. fillMachineFunction(MF, Instructions); // We create the pass manager, run the passes to populate AsmBuffer. llvm::MCContext &MCContext = MMI->getContext(); llvm::legacy::PassManager PM; llvm::TargetLibraryInfoImpl TLII(llvm::Triple(Module->getTargetTriple())); PM.add(new llvm::TargetLibraryInfoWrapperPass(TLII)); llvm::TargetPassConfig *TPC = TM->createPassConfig(PM); PM.add(TPC); PM.add(MMI.release()); TPC->printAndVerify("MachineFunctionGenerator::assemble"); // Add target-specific passes. ET.addTargetSpecificPasses(PM); TPC->printAndVerify("After ExegesisTarget::addTargetSpecificPasses"); // Adding the following passes: // - machineverifier: checks that the MachineFunction is well formed. // - prologepilog: saves and restore callee saved registers. for (const char *PassName : {"machineverifier", "prologepilog"}) if (addPass(PM, PassName, *TPC)) llvm::report_fatal_error("Unable to add a mandatory pass"); TPC->setInitialized(); // AsmPrinter is responsible for generating the assembly into AsmBuffer. if (TM->addAsmPrinter(PM, AsmStream, nullptr, llvm::TargetMachine::CGFT_ObjectFile, MCContext)) llvm::report_fatal_error("Cannot add AsmPrinter passes"); PM.run(*Module); // Run all the passes } llvm::object::OwningBinary getObjectFromBuffer(llvm::StringRef InputData) { // Storing the generated assembly into a MemoryBuffer that owns the memory. std::unique_ptr Buffer = llvm::MemoryBuffer::getMemBufferCopy(InputData); // Create the ObjectFile from the MemoryBuffer. std::unique_ptr Obj = llvm::cantFail( llvm::object::ObjectFile::createObjectFile(Buffer->getMemBufferRef())); // Returning both the MemoryBuffer and the ObjectFile. return llvm::object::OwningBinary( std::move(Obj), std::move(Buffer)); } llvm::object::OwningBinary getObjectFromFile(llvm::StringRef Filename) { return llvm::cantFail(llvm::object::ObjectFile::createObjectFile(Filename)); } namespace { // Implementation of this class relies on the fact that a single object with a // single function will be loaded into memory. class TrackingSectionMemoryManager : public llvm::SectionMemoryManager { public: explicit TrackingSectionMemoryManager(uintptr_t *CodeSize) : CodeSize(CodeSize) {} uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment, unsigned SectionID, llvm::StringRef SectionName) override { *CodeSize = Size; return llvm::SectionMemoryManager::allocateCodeSection( Size, Alignment, SectionID, SectionName); } private: uintptr_t *const CodeSize = nullptr; }; } // namespace ExecutableFunction::ExecutableFunction( std::unique_ptr TM, llvm::object::OwningBinary &&ObjectFileHolder) : Context(llvm::make_unique()) { assert(ObjectFileHolder.getBinary() && "cannot create object file"); // Initializing the execution engine. // We need to use the JIT EngineKind to be able to add an object file. LLVMLinkInMCJIT(); uintptr_t CodeSize = 0; std::string Error; ExecEngine.reset( llvm::EngineBuilder(createModule(Context, TM->createDataLayout())) .setErrorStr(&Error) .setMCPU(TM->getTargetCPU()) .setEngineKind(llvm::EngineKind::JIT) .setMCJITMemoryManager( llvm::make_unique(&CodeSize)) .create(TM.release())); if (!ExecEngine) llvm::report_fatal_error(Error); // Adding the generated object file containing the assembled function. // The ExecutionEngine makes sure the object file is copied into an // executable page. ExecEngine->addObjectFile(std::move(ObjectFileHolder)); // Fetching function bytes. FunctionBytes = llvm::StringRef(reinterpret_cast( ExecEngine->getFunctionAddress(FunctionID)), CodeSize); } } // namespace exegesis