| //===----- CompileOnDemandLayer.cpp - Lazily emit IR on first call --------===// | |
| // | |
| // The LLVM Compiler Infrastructure | |
| // | |
| // This file is distributed under the University of Illinois Open Source | |
| // License. See LICENSE.TXT for details. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/ExecutionEngine/Orc/CompileOnDemandLayer.h" | |
| #include "llvm/Bitcode/BitcodeReader.h" | |
| #include "llvm/Bitcode/BitcodeWriter.h" | |
| #include "llvm/IR/Mangler.h" | |
| #include "llvm/IR/Module.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| #include "llvm/Transforms/Utils/Cloning.h" | |
| using namespace llvm; | |
| using namespace llvm::orc; | |
| namespace { | |
| template <typename MaterializerFtor> | |
| class LambdaValueMaterializer final : public ValueMaterializer { | |
| public: | |
| LambdaValueMaterializer(MaterializerFtor M) : M(std::move(M)) {} | |
| Value *materialize(Value *V) final { return M(V); } | |
| private: | |
| MaterializerFtor M; | |
| }; | |
| template <typename MaterializerFtor> | |
| LambdaValueMaterializer<MaterializerFtor> | |
| createLambdaValueMaterializer(MaterializerFtor M) { | |
| return LambdaValueMaterializer<MaterializerFtor>(std::move(M)); | |
| } | |
| } // namespace | |
| static void extractAliases(MaterializationResponsibility &R, Module &M, | |
| MangleAndInterner &Mangle) { | |
| SymbolAliasMap Aliases; | |
| std::vector<GlobalAlias *> ModAliases; | |
| for (auto &A : M.aliases()) | |
| ModAliases.push_back(&A); | |
| for (auto *A : ModAliases) { | |
| Constant *Aliasee = A->getAliasee(); | |
| assert(A->hasName() && "Anonymous alias?"); | |
| assert(Aliasee->hasName() && "Anonymous aliasee"); | |
| std::string AliasName = A->getName(); | |
| Aliases[Mangle(AliasName)] = SymbolAliasMapEntry( | |
| {Mangle(Aliasee->getName()), JITSymbolFlags::fromGlobalValue(*A)}); | |
| if (isa<Function>(Aliasee)) { | |
| auto *F = cloneFunctionDecl(M, *cast<Function>(Aliasee)); | |
| A->replaceAllUsesWith(F); | |
| A->eraseFromParent(); | |
| F->setName(AliasName); | |
| } else if (isa<GlobalValue>(Aliasee)) { | |
| auto *G = cloneGlobalVariableDecl(M, *cast<GlobalVariable>(Aliasee)); | |
| A->replaceAllUsesWith(G); | |
| A->eraseFromParent(); | |
| G->setName(AliasName); | |
| } | |
| } | |
| R.replace(symbolAliases(std::move(Aliases))); | |
| } | |
| static std::unique_ptr<Module> | |
| extractAndClone(Module &M, LLVMContext &NewContext, StringRef Suffix, | |
| function_ref<bool(const GlobalValue *)> ShouldCloneDefinition) { | |
| SmallVector<char, 1> ClonedModuleBuffer; | |
| { | |
| std::set<GlobalValue *> ClonedDefsInSrc; | |
| ValueToValueMapTy VMap; | |
| auto Tmp = CloneModule(M, VMap, [&](const GlobalValue *GV) { | |
| if (ShouldCloneDefinition(GV)) { | |
| ClonedDefsInSrc.insert(const_cast<GlobalValue *>(GV)); | |
| return true; | |
| } | |
| return false; | |
| }); | |
| for (auto *GV : ClonedDefsInSrc) { | |
| // Delete the definition and bump the linkage in the source module. | |
| if (isa<Function>(GV)) { | |
| auto &F = *cast<Function>(GV); | |
| F.deleteBody(); | |
| F.setPersonalityFn(nullptr); | |
| } else if (isa<GlobalVariable>(GV)) { | |
| cast<GlobalVariable>(GV)->setInitializer(nullptr); | |
| } else | |
| llvm_unreachable("Unsupported global type"); | |
| GV->setLinkage(GlobalValue::ExternalLinkage); | |
| } | |
| BitcodeWriter BCWriter(ClonedModuleBuffer); | |
| BCWriter.writeModule(*Tmp); | |
| BCWriter.writeSymtab(); | |
| BCWriter.writeStrtab(); | |
| } | |
| MemoryBufferRef ClonedModuleBufferRef( | |
| StringRef(ClonedModuleBuffer.data(), ClonedModuleBuffer.size()), | |
| "cloned module buffer"); | |
| auto ClonedModule = | |
| cantFail(parseBitcodeFile(ClonedModuleBufferRef, NewContext)); | |
| ClonedModule->setModuleIdentifier((M.getName() + Suffix).str()); | |
| return ClonedModule; | |
| } | |
| static std::unique_ptr<Module> extractGlobals(Module &M, | |
| LLVMContext &NewContext) { | |
| return extractAndClone(M, NewContext, ".globals", [](const GlobalValue *GV) { | |
| return isa<GlobalVariable>(GV); | |
| }); | |
| } | |
| namespace llvm { | |
| namespace orc { | |
| class ExtractingIRMaterializationUnit : public IRMaterializationUnit { | |
| public: | |
| ExtractingIRMaterializationUnit(ExecutionSession &ES, | |
| CompileOnDemandLayer2 &Parent, | |
| std::unique_ptr<Module> M) | |
| : IRMaterializationUnit(ES, std::move(M)), Parent(Parent) {} | |
| ExtractingIRMaterializationUnit(std::unique_ptr<Module> M, | |
| SymbolFlagsMap SymbolFlags, | |
| SymbolNameToDefinitionMap SymbolToDefinition, | |
| CompileOnDemandLayer2 &Parent) | |
| : IRMaterializationUnit(std::move(M), std::move(SymbolFlags), | |
| std::move(SymbolToDefinition)), | |
| Parent(Parent) {} | |
| private: | |
| void materialize(MaterializationResponsibility R) override { | |
| // FIXME: Need a 'notify lazy-extracting/emitting' callback to tie the | |
| // extracted module key, extracted module, and source module key | |
| // together. This could be used, for example, to provide a specific | |
| // memory manager instance to the linking layer. | |
| auto RequestedSymbols = R.getRequestedSymbols(); | |
| // Extract the requested functions into a new module. | |
| std::unique_ptr<Module> ExtractedFunctionsModule; | |
| if (!RequestedSymbols.empty()) { | |
| std::string Suffix; | |
| std::set<const GlobalValue *> FunctionsToClone; | |
| for (auto &Name : RequestedSymbols) { | |
| auto I = SymbolToDefinition.find(Name); | |
| assert(I != SymbolToDefinition.end() && I->second != nullptr && | |
| "Should have a non-null definition"); | |
| FunctionsToClone.insert(I->second); | |
| Suffix += "."; | |
| Suffix += *Name; | |
| } | |
| std::lock_guard<std::mutex> Lock(SourceModuleMutex); | |
| ExtractedFunctionsModule = | |
| extractAndClone(*M, Parent.GetAvailableContext(), Suffix, | |
| [&](const GlobalValue *GV) -> bool { | |
| return FunctionsToClone.count(GV); | |
| }); | |
| } | |
| // Build a new ExtractingIRMaterializationUnit to delegate the unrequested | |
| // symbols to. | |
| SymbolFlagsMap DelegatedSymbolFlags; | |
| IRMaterializationUnit::SymbolNameToDefinitionMap | |
| DelegatedSymbolToDefinition; | |
| for (auto &KV : SymbolToDefinition) { | |
| if (RequestedSymbols.count(KV.first)) | |
| continue; | |
| DelegatedSymbolFlags[KV.first] = | |
| JITSymbolFlags::fromGlobalValue(*KV.second); | |
| DelegatedSymbolToDefinition[KV.first] = KV.second; | |
| } | |
| if (!DelegatedSymbolFlags.empty()) { | |
| assert(DelegatedSymbolFlags.size() == | |
| DelegatedSymbolToDefinition.size() && | |
| "SymbolFlags and SymbolToDefinition should have the same number " | |
| "of entries"); | |
| R.replace(llvm::make_unique<ExtractingIRMaterializationUnit>( | |
| std::move(M), std::move(DelegatedSymbolFlags), | |
| std::move(DelegatedSymbolToDefinition), Parent)); | |
| } | |
| if (ExtractedFunctionsModule) | |
| Parent.emitExtractedFunctionsModule(std::move(R), | |
| std::move(ExtractedFunctionsModule)); | |
| } | |
| void discard(const VSO &V, SymbolStringPtr Name) override { | |
| // All original symbols were materialized by the CODLayer and should be | |
| // final. The function bodies provided by M should never be overridden. | |
| llvm_unreachable("Discard should never be called on an " | |
| "ExtractingIRMaterializationUnit"); | |
| } | |
| mutable std::mutex SourceModuleMutex; | |
| CompileOnDemandLayer2 &Parent; | |
| }; | |
| CompileOnDemandLayer2::CompileOnDemandLayer2( | |
| ExecutionSession &ES, IRLayer &BaseLayer, JITCompileCallbackManager &CCMgr, | |
| IndirectStubsManagerBuilder BuildIndirectStubsManager, | |
| GetAvailableContextFunction GetAvailableContext) | |
| : IRLayer(ES), BaseLayer(BaseLayer), CCMgr(CCMgr), | |
| BuildIndirectStubsManager(std::move(BuildIndirectStubsManager)), | |
| GetAvailableContext(std::move(GetAvailableContext)) {} | |
| Error CompileOnDemandLayer2::add(VSO &V, VModuleKey K, | |
| std::unique_ptr<Module> M) { | |
| return IRLayer::add(V, K, std::move(M)); | |
| } | |
| void CompileOnDemandLayer2::emit(MaterializationResponsibility R, VModuleKey K, | |
| std::unique_ptr<Module> M) { | |
| auto &ES = getExecutionSession(); | |
| assert(M && "M should not be null"); | |
| for (auto &GV : M->global_values()) | |
| if (GV.hasWeakLinkage()) | |
| GV.setLinkage(GlobalValue::ExternalLinkage); | |
| MangleAndInterner Mangle(ES, M->getDataLayout()); | |
| extractAliases(R, *M, Mangle); | |
| auto GlobalsModule = extractGlobals(*M, GetAvailableContext()); | |
| // Delete the bodies of any available externally functions, rename the | |
| // rest, and build the compile callbacks. | |
| std::map<SymbolStringPtr, std::pair<JITTargetAddress, JITSymbolFlags>> | |
| StubCallbacksAndLinkages; | |
| auto &TargetVSO = R.getTargetVSO(); | |
| for (auto &F : M->functions()) { | |
| if (F.isDeclaration()) | |
| continue; | |
| if (F.hasAvailableExternallyLinkage()) { | |
| F.deleteBody(); | |
| F.setPersonalityFn(nullptr); | |
| continue; | |
| } | |
| assert(F.hasName() && "Function should have a name"); | |
| std::string StubUnmangledName = F.getName(); | |
| F.setName(F.getName() + "$body"); | |
| auto StubDecl = cloneFunctionDecl(*M, F); | |
| StubDecl->setName(StubUnmangledName); | |
| StubDecl->setPersonalityFn(nullptr); | |
| StubDecl->setLinkage(GlobalValue::ExternalLinkage); | |
| F.replaceAllUsesWith(StubDecl); | |
| auto StubName = Mangle(StubUnmangledName); | |
| auto BodyName = Mangle(F.getName()); | |
| if (auto CallbackAddr = CCMgr.getCompileCallback( | |
| [BodyName, &TargetVSO, &ES]() -> JITTargetAddress { | |
| if (auto Sym = lookup({&TargetVSO}, BodyName)) | |
| return Sym->getAddress(); | |
| else { | |
| ES.reportError(Sym.takeError()); | |
| return 0; | |
| } | |
| })) { | |
| auto Flags = JITSymbolFlags::fromGlobalValue(F); | |
| Flags &= ~JITSymbolFlags::Weak; | |
| StubCallbacksAndLinkages[std::move(StubName)] = | |
| std::make_pair(*CallbackAddr, Flags); | |
| } else { | |
| ES.reportError(CallbackAddr.takeError()); | |
| R.failMaterialization(); | |
| return; | |
| } | |
| } | |
| // Build the stub inits map. | |
| IndirectStubsManager::StubInitsMap StubInits; | |
| for (auto &KV : StubCallbacksAndLinkages) | |
| StubInits[*KV.first] = KV.second; | |
| // Build the function-body-extracting materialization unit. | |
| if (auto Err = R.getTargetVSO().define( | |
| llvm::make_unique<ExtractingIRMaterializationUnit>(ES, *this, | |
| std::move(M)))) { | |
| ES.reportError(std::move(Err)); | |
| R.failMaterialization(); | |
| return; | |
| } | |
| // Build the stubs. | |
| // FIXME: Remove function bodies materialization unit if stub creation fails. | |
| auto &StubsMgr = getStubsManager(TargetVSO); | |
| if (auto Err = StubsMgr.createStubs(StubInits)) { | |
| ES.reportError(std::move(Err)); | |
| R.failMaterialization(); | |
| return; | |
| } | |
| // Resolve and finalize stubs. | |
| SymbolMap ResolvedStubs; | |
| for (auto &KV : StubCallbacksAndLinkages) { | |
| if (auto Sym = StubsMgr.findStub(*KV.first, false)) | |
| ResolvedStubs[KV.first] = Sym; | |
| else | |
| llvm_unreachable("Stub went missing"); | |
| } | |
| R.resolve(ResolvedStubs); | |
| BaseLayer.emit(std::move(R), std::move(K), std::move(GlobalsModule)); | |
| } | |
| IndirectStubsManager &CompileOnDemandLayer2::getStubsManager(const VSO &V) { | |
| std::lock_guard<std::mutex> Lock(CODLayerMutex); | |
| StubManagersMap::iterator I = StubsMgrs.find(&V); | |
| if (I == StubsMgrs.end()) | |
| I = StubsMgrs.insert(std::make_pair(&V, BuildIndirectStubsManager())).first; | |
| return *I->second; | |
| } | |
| void CompileOnDemandLayer2::emitExtractedFunctionsModule( | |
| MaterializationResponsibility R, std::unique_ptr<Module> M) { | |
| auto K = getExecutionSession().allocateVModule(); | |
| BaseLayer.emit(std::move(R), std::move(K), std::move(M)); | |
| } | |
| } // end namespace orc | |
| } // end namespace llvm |