llvm-project/llvm/lib/ExecutionEngine/Orc/CompileOnDemandLayer.cpp
Lang Hames d5f56c5979 [ORC] Rename VSO to JITDylib.
VSO was a little close to VDSO (an acronym on Linux for Virtual Dynamic Shared
Object) for comfort. It also risks giving the impression that instances of this
class could be shared between ExecutionSessions, which they can not.

JITDylib seems moderately less confusing, while still hinting at how this
class is intended to be used, i.e. as a JIT-compiled stand-in for a dynamic
library (code that would have been a dynamic library if you had wanted to
compile it ahead of time).

llvm-svn: 340084
2018-08-17 21:18:18 +00:00

345 lines
12 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/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 JITDylib &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(JITDylib &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 &TargetJD = R.getTargetJITDylib();
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, &TargetJD, &ES]() -> JITTargetAddress {
if (auto Sym = lookup({&TargetJD}, 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.getTargetJITDylib().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(TargetJD);
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 JITDylib &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