Skip to content

Commit

Permalink
TCling: Re-implement autoload via MaterializationUnits
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
hahnjo authored and vgvassilev committed Dec 9, 2022
1 parent d207867 commit 9b2041e
Show file tree
Hide file tree
Showing 9 changed files with 214 additions and 145 deletions.
18 changes: 18 additions & 0 deletions core/clingutils/res/TClingUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,24 @@ std::string GetModuleFileName(const char* moduleName);
// that can be used in C++ as a variable name.
void GetCppName(std::string &output, const char *input);

//______________________________________________________________________________
// Demangle the input symbol name for dlsym.
static inline std::string DemangleNameForDlsym(const std::string& name)
{
std::string nameForDlsym = name;

#ifdef R__MACOSX
// The JIT gives us a mangled name which has two leading underscores on
// osx, for instance __ZN8TRandom34RndmEv. However, on dlsym OSX requres
// single _ (eg. __ZN8TRandom34RndmEv, dropping the extra _).
// FIXME: get this information from the DataLayout via getGlobalPrefix()!
if (llvm::StringRef(nameForDlsym).startswith("__"))
nameForDlsym.erase(0, 1);
#endif //R__MACOSX

return nameForDlsym;
}

//______________________________________________________________________________
// Return the type with all parts fully qualified (most typedefs),
// including template arguments, appended to name.
Expand Down
29 changes: 10 additions & 19 deletions core/metacling/src/TCling.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,14 @@ void TCling__PrintStackTrace() {
gSystem->StackTrace();
}

////////////////////////////////////////////////////////////////////////////////
/// Load a library.

extern "C" int TCling__LoadLibrary(const char *library)
{
return gSystem->Load(library, "", false);
}

////////////////////////////////////////////////////////////////////////////////
/// Re-apply the lock count delta that TCling__ResetInterpreterMutex() caused.

Expand Down Expand Up @@ -681,14 +689,6 @@ static clang::ClassTemplateDecl* FindTemplateInNamespace(clang::Decl* decl)
return nullptr; // something went wrong.
}

////////////////////////////////////////////////////////////////////////////////
/// Autoload a library provided the mangled name of a missing symbol.

void* llvmLazyFunctionCreator(const std::string& mangled_name)
{
return ((TCling*)gCling)->LazyFunctionCreatorAutoload(mangled_name);
}

//______________________________________________________________________________
//
//
Expand Down Expand Up @@ -1639,9 +1639,8 @@ TCling::TCling(const char *name, const char *title, const char* const argv[])
llvm::StringRef stem = llvm::sys::path::stem(FileName);
return stem.startswith("libNew") || stem.startswith("libcppyy_backend");
};
// Initialize the dyld for the llvmLazyFunctionCreator.
// Initialize the dyld for AutoloadLibraryGenerator.
DLM.initializeDyld(ShouldPermanentlyIgnore);
fInterpreter->installLazyFunctionCreator(llvmLazyFunctionCreator);
}
}

Expand Down Expand Up @@ -6526,15 +6525,7 @@ bool TCling::LibraryLoadingFailed(const std::string& errmessage, const std::stri
/// Autoload a library based on a missing symbol.

void* TCling::LazyFunctionCreatorAutoload(const std::string& mangled_name) {
std::string dlsym_mangled_name = mangled_name;

#ifdef R__MACOSX
// The JIT gives us a mangled name which has only two leading underscores on
// osx, for instance __ZN8TRandom34RndmEv. However, on dlsym OSX requres
// single _ (eg. __ZN8TRandom34RndmEv, dropping the extra _).
if (llvm::StringRef(mangled_name).startswith("__"))
dlsym_mangled_name.erase(0, 1);
#endif //R__MACOSX
std::string dlsym_mangled_name = ROOT::TMetaUtils::DemangleNameForDlsym(mangled_name);

// We have already loaded the library.
if (void* Addr = llvm::sys::DynamicLibrary::SearchForAddressOfSymbol(dlsym_mangled_name))
Expand Down
132 changes: 130 additions & 2 deletions core/metacling/src/TClingCallbacks.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@
#include "clang/Serialization/ASTReader.h"
#include "clang/Serialization/GlobalModuleIndex.h"

#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/ExecutionEngine/Orc/Core.h"

#include "llvm/Support/Error.h"
#include "llvm/Support/FileSystem.h"
Expand Down Expand Up @@ -69,6 +68,7 @@ extern "C" {
int TCling__CompileMacro(const char *fileName, const char *options);
void TCling__SplitAclicMode(const char* fileName, std::string &mode,
std::string &args, std::string &io, std::string &fname);
int TCling__LoadLibrary(const char *library);
bool TCling__LibraryLoadingFailed(const std::string&, const std::string&, bool, bool);
void TCling__LibraryLoadedRTTI(const void* dyLibHandle,
llvm::StringRef canonicalName);
Expand All @@ -81,12 +81,140 @@ extern "C" {
void TCling__UnlockCompilationDuringUserCodeExecution(void *state);
}

class AutoloadLibraryMU : public llvm::orc::MaterializationUnit {
public:
AutoloadLibraryMU(const std::string &Library, const llvm::orc::SymbolNameVector &Symbols)
: MaterializationUnit(getSymbolFlagsMap(Symbols), nullptr), fLibrary(Library), fSymbols(Symbols)
{
}

StringRef getName() const override { return "<Symbols from Autoloaded Library>"; }

void materialize(std::unique_ptr<llvm::orc::MaterializationResponsibility> R) override
{
llvm::orc::SymbolMap loadedSymbols;
llvm::orc::SymbolNameSet failedSymbols;
bool loadedLibrary = false;

for (auto symbol : fSymbols) {
std::string symbolStr = (*symbol).str();
std::string nameForDlsym = ROOT::TMetaUtils::DemangleNameForDlsym(symbolStr);

// Check if the symbol is available without loading the library.
void *addr = llvm::sys::DynamicLibrary::SearchForAddressOfSymbol(nameForDlsym);

if (!addr && !loadedLibrary) {
// Try to load the library which should provide the symbol definition.
// TODO: Should this interface with the DynamicLibraryManager directly?
if (TCling__LoadLibrary(fLibrary.c_str()) < 0) {
ROOT::TMetaUtils::Error("AutoloadLibraryMU", "Failed to load library %s", fLibrary.c_str());
}

// Only try loading the library once.
loadedLibrary = true;

addr = llvm::sys::DynamicLibrary::SearchForAddressOfSymbol(nameForDlsym);
}

if (addr) {
loadedSymbols[symbol] =
llvm::JITEvaluatedSymbol(llvm::pointerToJITTargetAddress(addr), llvm::JITSymbolFlags::Exported);
} else {
// Collect all failing symbols, delegate their responsibility and then
// fail their materialization. R->defineNonExistent() sounds like it
// should do that, but it's not implemented?!
failedSymbols.insert(symbol);
}
}

if (!failedSymbols.empty()) {
auto failingMR = R->delegate(failedSymbols);
if (failingMR) {
(*failingMR)->failMaterialization();
}
}

if (!loadedSymbols.empty()) {
llvm::cantFail(R->notifyResolved(loadedSymbols));
llvm::cantFail(R->notifyEmitted());
}
}

void discard(const llvm::orc::JITDylib &JD, const llvm::orc::SymbolStringPtr &Name) override {}

private:
static llvm::orc::SymbolFlagsMap getSymbolFlagsMap(const llvm::orc::SymbolNameVector &Symbols)
{
llvm::orc::SymbolFlagsMap map;
for (auto symbolName : Symbols)
map[symbolName] = llvm::JITSymbolFlags::Exported;
return map;
}

std::string fLibrary;
llvm::orc::SymbolNameVector fSymbols;
};

class AutoloadLibraryGenerator : public llvm::orc::DefinitionGenerator {
public:
AutoloadLibraryGenerator(cling::Interpreter *interp) : fInterpreter(interp) {}

llvm::Error tryToGenerate(llvm::orc::LookupState &LS, llvm::orc::LookupKind K, llvm::orc::JITDylib &JD,
llvm::orc::JITDylibLookupFlags JDLookupFlags,
const llvm::orc::SymbolLookupSet &Symbols) override
{
// If we get here, the symbols have not been found in the current process,
// so no need to check that again. Instead search for the library that
// provides the symbol and create one MaterializationUnit per library to
// actually load it if needed.
std::unordered_map<std::string, llvm::orc::SymbolNameVector> found;
llvm::orc::SymbolNameSet missing;

// TODO: Do we need to take gInterpreterMutex?
// R__LOCKGUARD(gInterpreterMutex);

for (auto &&KV : Symbols) {
llvm::orc::SymbolStringPtr name = KV.first;

const cling::DynamicLibraryManager &DLM = *fInterpreter->getDynamicLibraryManager();

std::string libName = DLM.searchLibrariesForSymbol((*name).str(),
/*searchSystem=*/true);

assert(!llvm::StringRef(libName).startswith("libNew") && "We must not resolve symbols from libNew!");

if (libName.empty()) {
missing.insert(name);
continue;
}

found[libName].push_back(name);
}

for (auto &&KV : found) {
auto MU = std::make_unique<AutoloadLibraryMU>(KV.first, std::move(KV.second));
if (auto Err = JD.define(MU))
return Err;
}

if (!missing.empty())
return llvm::make_error<llvm::orc::SymbolsNotFound>(std::move(missing));

return llvm::Error::success();
}

private:
cling::Interpreter *fInterpreter;
};

TClingCallbacks::TClingCallbacks(cling::Interpreter *interp, bool hasCodeGen) : InterpreterCallbacks(interp)
{
if (hasCodeGen) {
Transaction* T = 0;
m_Interpreter->declare("namespace __ROOT_SpecialObjects{}", &T);
fROOTSpecialNamespace = dyn_cast<NamespaceDecl>(T->getFirstDecl().getSingleDecl());

interp->addGenerator(std::make_unique<AutoloadLibraryGenerator>(interp));
}
}

Expand Down
8 changes: 6 additions & 2 deletions interpreter/cling/include/cling/Interpreter/Interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ namespace llvm {
class StringRef;
class Type;
template <typename T> class SmallVectorImpl;
namespace orc {
class DefinitionGenerator;
}
}

namespace clang {
Expand Down Expand Up @@ -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.
Expand Down
22 changes: 3 additions & 19 deletions interpreter/cling/lib/Interpreter/IncrementalExecutor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
21 changes: 8 additions & 13 deletions interpreter/cling/lib/Interpreter/IncrementalExecutor.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ namespace llvm {
class GlobalValue;
class Module;
class TargetMachine;
namespace orc {
class DefinitionGenerator;
}
}

namespace cling {
Expand All @@ -50,9 +53,6 @@ namespace cling {
class Value;

class IncrementalExecutor {
public:
typedef void* (*LazyFunctionCreatorFunc_t)(const std::string&);

private:
///\brief Our JIT interface.
///
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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.
///
Expand All @@ -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)();
Expand Down
Loading

0 comments on commit 9b2041e

Please sign in to comment.