TCling: Re-implement autoload via MaterializationUnits

Replace the existing LazyFunctionCreator interface by a function
to add DefinitionGenerators to be passed via the chain Interpreter
-> IncrementalExecutor -> IncrementalJIT.
Implement a DefinitionGenerator in TCling(Callbacks) to define
MaterializationUnits for autoloading symbols. This also allows to
remove the double DynamicLibrarySearchGenerator now that the created
AutoloadLibraryMUs inject the addresses into the JIT after loading
the required library.
This commit is contained in:
Jonas Hahnfeld 2022-07-07 14:02:28 +02:00 committed by jenkins
parent 619f5e83a2
commit 927f761b8b
6 changed files with 55 additions and 123 deletions

View File

@ -29,6 +29,9 @@ namespace llvm {
class StringRef;
class Type;
template <typename T> class SmallVectorImpl;
namespace orc {
class DefinitionGenerator;
}
}
namespace clang {
@ -735,8 +738,9 @@ namespace cling {
///\brief Create suitable default compilation options.
CompilationOptions makeDefaultCompilationOpts() const;
//FIXME: This must be in InterpreterCallbacks.
void installLazyFunctionCreator(void* (*fp)(const std::string&));
/// Register a DefinitionGenerator to dynamically provide symbols for
/// generated code that are not already available within the process.
void addGenerator(std::unique_ptr<llvm::orc::DefinitionGenerator> G);
//FIXME: Lets the IncrementalParser run static inits on transaction
// completed. Find a better way.

View File

@ -190,21 +190,6 @@ IncrementalExecutor::HandleMissingFunction(const std::string& mangled_name) cons
return utils::FunctionToVoidPtr(&unresolvedSymbol);
}
void*
IncrementalExecutor::NotifyLazyFunctionCreators(const std::string& mangled_name) const {
for (auto it = m_lazyFuncCreator.begin(), et = m_lazyFuncCreator.end();
it != et; ++it) {
void* ret = (void*)((LazyFunctionCreatorFunc_t)*it)(mangled_name);
if (ret)
return ret;
}
void *address = nullptr;
if (m_externalIncrementalExecutor)
address = m_externalIncrementalExecutor->getAddressOfGlobal(mangled_name);
return (address ? address : HandleMissingFunction(mangled_name));
}
#if 0
// FIXME: employ to empty module dependencies *within* the *current* module.
static void
@ -315,10 +300,9 @@ void IncrementalExecutor::setCallbacks(InterpreterCallbacks* callbacks) {
m_DyLibManager.setCallbacks(callbacks);
}
void
IncrementalExecutor::installLazyFunctionCreator(LazyFunctionCreatorFunc_t fp)
{
m_lazyFuncCreator.push_back(fp);
void IncrementalExecutor::addGenerator(
std::unique_ptr<llvm::orc::DefinitionGenerator> G) {
m_JIT->addGenerator(std::move(G));
}
void IncrementalExecutor::replaceSymbol(const char* Name, void* Addr) const {

View File

@ -42,6 +42,9 @@ namespace llvm {
class GlobalValue;
class Module;
class TargetMachine;
namespace orc {
class DefinitionGenerator;
}
}
namespace cling {
@ -50,9 +53,6 @@ namespace cling {
class Value;
class IncrementalExecutor {
public:
typedef void* (*LazyFunctionCreatorFunc_t)(const std::string&);
private:
///\brief Our JIT interface.
///
@ -126,11 +126,6 @@ namespace cling {
///
std::vector<llvm::Module*> m_ModulesToJIT;
///\brief Lazy function creator, which is a final callback which the
/// JIT fires if there is unresolved symbol.
///
std::vector<LazyFunctionCreatorFunc_t> m_lazyFuncCreator;
///\brief Set of the symbols that the JIT couldn't resolve.
///
mutable std::unordered_set<std::string> m_unresolvedSymbols;
@ -175,7 +170,9 @@ namespace cling {
return m_DyLibManager;
}
void installLazyFunctionCreator(LazyFunctionCreatorFunc_t fp);
/// Register a DefinitionGenerator to dynamically provide symbols for
/// generated code that are not already available within the process.
void addGenerator(std::unique_ptr<llvm::orc::DefinitionGenerator> G);
///\brief Unload a set of JIT symbols.
llvm::Error unloadModule(const Transaction& T) const {
@ -240,10 +237,6 @@ namespace cling {
///
void AddAtExitFunc(void (*func)(void*), void* arg, const Transaction* T);
///\brief Try to resolve a symbol through our LazyFunctionCreators;
/// print an error message if that fails.
void* NotifyLazyFunctionCreators(const std::string&) const;
private:
///\brief Emit a llvm::Module to the JIT.
///
@ -262,9 +255,11 @@ namespace cling {
bool diagnoseUnresolvedSymbols(llvm::StringRef trigger,
llvm::StringRef title = llvm::StringRef()) const;
public:
///\brief Remember that the symbol could not be resolved by the JIT.
void* HandleMissingFunction(const std::string& symbol) const;
private:
///\brief Runs an initializer function.
ExecutionResult executeInit(llvm::StringRef function) const {
typedef void (*InitFun_t)();

View File

@ -24,77 +24,6 @@ using namespace llvm::orc;
namespace cling {
/// This class is a combination of the logic in DynamicLibrarySearchGenerator,
/// falling back to our symbol resolution logic.
class HostLookupLazyFallbackGenerator : public DefinitionGenerator {
const IncrementalExecutor & m_IncrExecutor;
// char m_GlobalPrefix;
public:
HostLookupLazyFallbackGenerator(const IncrementalExecutor &Exe,
char GlobalPrefix)
: m_IncrExecutor(Exe)/*, m_GlobalPrefix(GlobalPrefix)*/ { }
Error tryToGenerate(LookupState& LS, LookupKind K, JITDylib& JD,
JITDylibLookupFlags JDLookupFlags,
const SymbolLookupSet& Symbols) override {
// FIXME: Uncomment when we figure out how to not load weak symbols from
// m_IncrExecutor.NotifyLazyFunctionCreators
// orc::SymbolMap NewSymbols;
// bool HasGlobalPrefix = (m_GlobalPrefix != '\0');
// for (auto &KV : Symbols) {
// auto &Name = KV.first;
// if ((*Name).empty())
// continue;
// if (HasGlobalPrefix && (*Name).front() != m_GlobalPrefix)
// continue;
// std::string Tmp((*Name).data() + HasGlobalPrefix,
// (*Name).size() - HasGlobalPrefix);
// void *Addr = sys::DynamicLibrary::SearchForAddressOfSymbol(Tmp.c_str());
// // FIXME: Here we will load random libraries due to weak symbols which is
// // suboptimal. We should let the JIT create them.
// if (!Addr)
// Addr = m_IncrExecutor.NotifyLazyFunctionCreators(Tmp.c_str());
// if (Addr) {
// NewSymbols[Name] = JITEvaluatedSymbol(
// static_cast<JITTargetAddress>(reinterpret_cast<uintptr_t>(Addr)),
// JITSymbolFlags::Exported);
// }
// }
// if (NewSymbols.empty())
// return Error::success();
// return JD.define(absoluteSymbols(std::move(NewSymbols)));
SymbolNameSet Missing;
for (llvm::orc::SymbolStringPtr Name : Symbols.getSymbolNames())
if (!m_IncrExecutor.NotifyLazyFunctionCreators((*Name).str()))
Missing.insert(Name);
if (!Missing.empty())
return make_error<SymbolsNotFound>(std::move(Missing));
return llvm::Error::success();
}
};
namespace {
// This replaces llvm::orc::ExecutionSession::logErrorsToStdErr:
// IncrementalExecutor has its own diagnostics (for now); these
// diagnostics here might be superior as they show *all* unresolved
// symbols, so show them in case of "verbose" nonetheless.
static void silenceJITErrors(Error Err) {
if (!Err || Err.isA<SymbolsNotFound>())
return;
logAllUnhandledErrors(std::move(Err), errs(), "cling JIT session error: ");
}
}
IncrementalJIT::IncrementalJIT(
IncrementalExecutor& Executor, std::unique_ptr<TargetMachine> TM,
std::unique_ptr<llvm::orc::ExecutorProcessControl> EPC, Error& Err,
@ -152,24 +81,36 @@ IncrementalJIT::IncrementalJIT(
}
Jit->getMainJITDylib().addGenerator(std::move(*HostProcessLookup));
// Lazy symbol generation callback
auto Notifier =
std::make_unique<HostLookupLazyFallbackGenerator>(Executor, LinkerPrefix);
Jit->getMainJITDylib().addGenerator(std::move(Notifier));
// This replaces llvm::orc::ExecutionSession::logErrorsToStdErr:
auto&& ErrorReporter = [&Executor, LinkerPrefix, Verbose](Error Err) {
Err = handleErrors(std::move(Err),
[&](std::unique_ptr<SymbolsNotFound> Err) -> Error {
// IncrementalExecutor has its own diagnostics (for
// now) that tries to guess which library needs to be
// loaded.
for (auto&& symbol : Err->getSymbols()) {
std::string symbolStr = (*symbol).str();
if (LinkerPrefix != '\0' &&
symbolStr[0] == LinkerPrefix) {
symbolStr.erase(0, 1);
}
Executor.HandleMissingFunction(symbolStr);
}
// Process symbol resolution after the callback.
// FIXME: if we resolve the FIXME in HostLookupLazyFallbackGenerator, we will
// need just one generator.
HostProcessLookup =
DynamicLibrarySearchGenerator::GetForCurrentProcess(LinkerPrefix);
if (!HostProcessLookup) {
Err = HostProcessLookup.takeError();
return;
}
Jit->getMainJITDylib().addGenerator(std::move(*HostProcessLookup));
// However, the diagnstic here might be superior as
// they show *all* unresolved symbols, so show them in
// case of "verbose" nonetheless.
if (Verbose)
return Error(std::move(Err));
return Error::success();
});
if (!Verbose)
Jit->getExecutionSession().setErrorReporter(silenceJITErrors);
if (!Err)
return;
logAllUnhandledErrors(std::move(Err), errs(), "cling JIT session error: ");
};
Jit->getExecutionSession().setErrorReporter(ErrorReporter);
}
void IncrementalJIT::addModule(Transaction& T) {

View File

@ -13,6 +13,7 @@
#include "llvm/ADT/FunctionExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/IR/Module.h"
#include "llvm/ExecutionEngine/Orc/Core.h"
#include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h"
#include "llvm/ExecutionEngine/Orc/LLJIT.h"
#include "llvm/ExecutionEngine/Orc/ThreadSafeModule.h"
@ -54,6 +55,12 @@ public:
std::unique_ptr<llvm::orc::ExecutorProcessControl> EPC,
llvm::Error &Err, bool Verbose = false);
/// Register a DefinitionGenerator to dynamically provide symbols for
/// generated code that are not already available within the process.
void addGenerator(std::unique_ptr<llvm::orc::DefinitionGenerator> G) {
Jit->getMainJITDylib().addGenerator(std::move(G));
}
// FIXME: Accept a LLVMContext as well, e.g. the one that was used for the
// particular module in Interpreter, CIFactory or BackendPasses (would be
// more efficient)

View File

@ -1616,9 +1616,10 @@ namespace cling {
transactions.size(), 0);
}
void Interpreter::installLazyFunctionCreator(void* (*fp)(const std::string&)) {
void
Interpreter::addGenerator(std::unique_ptr<llvm::orc::DefinitionGenerator> G) {
if (m_Executor)
m_Executor->installLazyFunctionCreator(fp);
m_Executor->addGenerator(std::move(G));
}
Value Interpreter::Evaluate(const char* expr, DeclContext* DC,