LLJIT is a prefabricated ORC based JIT class that is meant to be the go-to replacement for MCJIT. Unlike OrcMCJITReplacement (which will continue to be supported) it is not API or bug-for-bug compatible, but targets the same use cases: Simple, non-lazy compilation and execution of LLVM IR. LLLazyJIT extends LLJIT with support for function-at-a-time lazy compilation, similar to what was provided by LLVM's original (now long deprecated) JIT APIs. This commit also contains some simple utility classes (CtorDtorRunner2, LocalCXXRuntimeOverrides2, JITTargetMachineBuilder) to support LLJIT and LLLazyJIT. Both of these classes are works in progress. Feedback from JIT clients is very welcome! llvm-svn: 335670
369 lines
13 KiB
C++
369 lines
13 KiB
C++
//===----- 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/IR/Mangler.h"
|
|
#include "llvm/IR/Module.h"
|
|
#include "llvm/Support/raw_ostream.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.delegate(symbolAliases(std::move(Aliases)));
|
|
}
|
|
|
|
static std::unique_ptr<Module> extractGlobals(Module &M) {
|
|
// FIXME: Add alias support.
|
|
|
|
auto GlobalsModule = llvm::make_unique<Module>(
|
|
(M.getName() + ".globals").str(), M.getContext());
|
|
GlobalsModule->setDataLayout(M.getDataLayout());
|
|
|
|
ValueToValueMapTy VMap;
|
|
|
|
for (auto &GV : M.globals())
|
|
if (!GV.isDeclaration() && !VMap.count(&GV))
|
|
cloneGlobalVariableDecl(*GlobalsModule, GV, &VMap);
|
|
|
|
// Clone the module flags.
|
|
cloneModuleFlagsMetadata(*GlobalsModule, M, VMap);
|
|
|
|
auto Materializer = createLambdaValueMaterializer([&](Value *V) -> Value * {
|
|
if (auto *F = dyn_cast<Function>(V))
|
|
return cloneFunctionDecl(*GlobalsModule, *F);
|
|
return nullptr;
|
|
});
|
|
|
|
// Move the global variable initializers.
|
|
for (auto &GV : M.globals()) {
|
|
if (!GV.isDeclaration())
|
|
moveGlobalVariableInitializer(GV, VMap, &Materializer);
|
|
GV.setInitializer(nullptr);
|
|
}
|
|
|
|
return GlobalsModule;
|
|
}
|
|
|
|
namespace llvm {
|
|
namespace orc {
|
|
|
|
class ExtractingIRMaterializationUnit : public IRMaterializationUnit {
|
|
public:
|
|
ExtractingIRMaterializationUnit(
|
|
ExecutionSession &ES, CompileOnDemandLayer2 &Parent,
|
|
std::unique_ptr<Module> M,
|
|
std::shared_ptr<SymbolResolver> BackingResolver)
|
|
: IRMaterializationUnit(ES, std::move(M)), Parent(Parent),
|
|
BackingResolver(std::move(BackingResolver)) {}
|
|
|
|
ExtractingIRMaterializationUnit(
|
|
std::unique_ptr<Module> M, SymbolFlagsMap SymbolFlags,
|
|
SymbolNameToDefinitionMap SymbolToDefinition,
|
|
CompileOnDemandLayer2 &Parent,
|
|
std::shared_ptr<SymbolResolver> BackingResolver)
|
|
: IRMaterializationUnit(std::move(M), std::move(SymbolFlags),
|
|
std::move(SymbolToDefinition)),
|
|
Parent(Parent), BackingResolver(std::move(BackingResolver)) {}
|
|
|
|
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.
|
|
|
|
// FIXME: The derived constructor should *only* look for the names of
|
|
// original function definitions in the target VSO. All other
|
|
// symbols should be looked up in the backing resolver.
|
|
|
|
// Find the functions that have been requested.
|
|
auto RequestedSymbols = R.getRequestedSymbols();
|
|
|
|
// Extract them into a new module.
|
|
auto ExtractedFunctionsModule =
|
|
Parent.extractFunctions(*M, RequestedSymbols, SymbolToDefinition);
|
|
|
|
// 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.delegate(llvm::make_unique<ExtractingIRMaterializationUnit>(
|
|
std::move(M), std::move(DelegatedSymbolFlags),
|
|
std::move(DelegatedSymbolToDefinition), Parent, BackingResolver));
|
|
}
|
|
|
|
Parent.emitExtractedFunctionsModule(
|
|
std::move(R), std::move(ExtractedFunctionsModule), BackingResolver);
|
|
}
|
|
|
|
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");
|
|
}
|
|
|
|
CompileOnDemandLayer2 &Parent;
|
|
std::shared_ptr<SymbolResolver> BackingResolver;
|
|
};
|
|
|
|
CompileOnDemandLayer2::CompileOnDemandLayer2(
|
|
ExecutionSession &ES, IRLayer &BaseLayer, JITCompileCallbackManager &CCMgr,
|
|
IndirectStubsManagerBuilder BuildIndirectStubsManager,
|
|
GetSymbolResolverFunction GetSymbolResolver,
|
|
SetSymbolResolverFunction SetSymbolResolver,
|
|
GetAvailableContextFunction GetAvailableContext)
|
|
: IRLayer(ES), BaseLayer(BaseLayer), CCMgr(CCMgr),
|
|
BuildIndirectStubsManager(std::move(BuildIndirectStubsManager)),
|
|
GetSymbolResolver(std::move(GetSymbolResolver)),
|
|
SetSymbolResolver(std::move(SetSymbolResolver)),
|
|
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);
|
|
|
|
// 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();
|
|
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);
|
|
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.
|
|
auto SR = GetSymbolResolver(K);
|
|
if (auto Err = R.getTargetVSO().define(
|
|
llvm::make_unique<ExtractingIRMaterializationUnit>(
|
|
ES, *this, std::move(M), SR))) {
|
|
ES.reportError(std::move(Err));
|
|
R.failMaterialization();
|
|
return;
|
|
}
|
|
|
|
// Replace the fallback symbol resolver: We will re-use M's VModuleKey for
|
|
// the GlobalsModule.
|
|
SetSymbolResolver(K, SR);
|
|
|
|
// 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;
|
|
}
|
|
|
|
std::unique_ptr<Module> CompileOnDemandLayer2::extractFunctions(
|
|
Module &M, const SymbolNameSet &SymbolNames,
|
|
const SymbolNameToDefinitionMap &SymbolToDefinition) {
|
|
assert(!SymbolNames.empty() && "Can not extract an empty function set");
|
|
|
|
std::string ExtractedModName;
|
|
{
|
|
raw_string_ostream ExtractedModNameStream(ExtractedModName);
|
|
ExtractedModNameStream << M.getName();
|
|
for (auto &Name : SymbolNames)
|
|
ExtractedModNameStream << "." << *Name;
|
|
}
|
|
|
|
auto ExtractedFunctionsModule =
|
|
llvm::make_unique<Module>(ExtractedModName, GetAvailableContext());
|
|
ExtractedFunctionsModule->setDataLayout(M.getDataLayout());
|
|
|
|
ValueToValueMapTy VMap;
|
|
|
|
auto Materializer = createLambdaValueMaterializer([&](Value *V) -> Value * {
|
|
if (auto *F = dyn_cast<Function>(V))
|
|
return cloneFunctionDecl(*ExtractedFunctionsModule, *F);
|
|
else if (auto *GV = dyn_cast<GlobalVariable>(V))
|
|
return cloneGlobalVariableDecl(*ExtractedFunctionsModule, *GV);
|
|
return nullptr;
|
|
});
|
|
|
|
std::vector<std::pair<Function *, Function *>> OrigToNew;
|
|
for (auto &FunctionName : SymbolNames) {
|
|
assert(SymbolToDefinition.count(FunctionName) &&
|
|
"No definition for symbol");
|
|
auto *OrigF = cast<Function>(SymbolToDefinition.find(FunctionName)->second);
|
|
auto *NewF = cloneFunctionDecl(*ExtractedFunctionsModule, *OrigF, &VMap);
|
|
OrigToNew.push_back(std::make_pair(OrigF, NewF));
|
|
}
|
|
|
|
for (auto &KV : OrigToNew)
|
|
moveFunctionBody(*KV.first, VMap, &Materializer, KV.second);
|
|
|
|
return ExtractedFunctionsModule;
|
|
}
|
|
|
|
void CompileOnDemandLayer2::emitExtractedFunctionsModule(
|
|
MaterializationResponsibility R, std::unique_ptr<Module> M,
|
|
std::shared_ptr<SymbolResolver> Resolver) {
|
|
auto &TargetVSO = R.getTargetVSO();
|
|
auto K = getExecutionSession().allocateVModule();
|
|
|
|
auto ExtractedFunctionsResolver = createSymbolResolver(
|
|
[=](SymbolFlagsMap &Flags, const SymbolNameSet &Symbols) {
|
|
return Resolver->lookupFlags(Flags, Symbols);
|
|
},
|
|
[=, &TargetVSO](std::shared_ptr<AsynchronousSymbolQuery> Query,
|
|
SymbolNameSet Symbols) {
|
|
auto RemainingSymbols = TargetVSO.lookup(Query, std::move(Symbols));
|
|
return Resolver->lookup(std::move(Query), std::move(RemainingSymbols));
|
|
});
|
|
|
|
SetSymbolResolver(K, std::move(ExtractedFunctionsResolver));
|
|
BaseLayer.emit(std::move(R), std::move(K), std::move(M));
|
|
}
|
|
|
|
} // end namespace orc
|
|
} // end namespace llvm
|