Skip to content

Commit 9b2041e

Browse files
hahnjovgvassilev
authored andcommitted
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.
1 parent d207867 commit 9b2041e

File tree

9 files changed

+214
-145
lines changed

9 files changed

+214
-145
lines changed

core/clingutils/res/TClingUtils.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,24 @@ std::string GetModuleFileName(const char* moduleName);
569569
// that can be used in C++ as a variable name.
570570
void GetCppName(std::string &output, const char *input);
571571

572+
//______________________________________________________________________________
573+
// Demangle the input symbol name for dlsym.
574+
static inline std::string DemangleNameForDlsym(const std::string& name)
575+
{
576+
std::string nameForDlsym = name;
577+
578+
#ifdef R__MACOSX
579+
// The JIT gives us a mangled name which has two leading underscores on
580+
// osx, for instance __ZN8TRandom34RndmEv. However, on dlsym OSX requres
581+
// single _ (eg. __ZN8TRandom34RndmEv, dropping the extra _).
582+
// FIXME: get this information from the DataLayout via getGlobalPrefix()!
583+
if (llvm::StringRef(nameForDlsym).startswith("__"))
584+
nameForDlsym.erase(0, 1);
585+
#endif //R__MACOSX
586+
587+
return nameForDlsym;
588+
}
589+
572590
//______________________________________________________________________________
573591
// Return the type with all parts fully qualified (most typedefs),
574592
// including template arguments, appended to name.

core/metacling/src/TCling.cxx

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,14 @@ void TCling__PrintStackTrace() {
325325
gSystem->StackTrace();
326326
}
327327

328+
////////////////////////////////////////////////////////////////////////////////
329+
/// Load a library.
330+
331+
extern "C" int TCling__LoadLibrary(const char *library)
332+
{
333+
return gSystem->Load(library, "", false);
334+
}
335+
328336
////////////////////////////////////////////////////////////////////////////////
329337
/// Re-apply the lock count delta that TCling__ResetInterpreterMutex() caused.
330338

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

684-
////////////////////////////////////////////////////////////////////////////////
685-
/// Autoload a library provided the mangled name of a missing symbol.
686-
687-
void* llvmLazyFunctionCreator(const std::string& mangled_name)
688-
{
689-
return ((TCling*)gCling)->LazyFunctionCreatorAutoload(mangled_name);
690-
}
691-
692692
//______________________________________________________________________________
693693
//
694694
//
@@ -1639,9 +1639,8 @@ TCling::TCling(const char *name, const char *title, const char* const argv[])
16391639
llvm::StringRef stem = llvm::sys::path::stem(FileName);
16401640
return stem.startswith("libNew") || stem.startswith("libcppyy_backend");
16411641
};
1642-
// Initialize the dyld for the llvmLazyFunctionCreator.
1642+
// Initialize the dyld for AutoloadLibraryGenerator.
16431643
DLM.initializeDyld(ShouldPermanentlyIgnore);
1644-
fInterpreter->installLazyFunctionCreator(llvmLazyFunctionCreator);
16451644
}
16461645
}
16471646

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

65286527
void* TCling::LazyFunctionCreatorAutoload(const std::string& mangled_name) {
6529-
std::string dlsym_mangled_name = mangled_name;
6530-
6531-
#ifdef R__MACOSX
6532-
// The JIT gives us a mangled name which has only two leading underscores on
6533-
// osx, for instance __ZN8TRandom34RndmEv. However, on dlsym OSX requres
6534-
// single _ (eg. __ZN8TRandom34RndmEv, dropping the extra _).
6535-
if (llvm::StringRef(mangled_name).startswith("__"))
6536-
dlsym_mangled_name.erase(0, 1);
6537-
#endif //R__MACOSX
6528+
std::string dlsym_mangled_name = ROOT::TMetaUtils::DemangleNameForDlsym(mangled_name);
65386529

65396530
// We have already loaded the library.
65406531
if (void* Addr = llvm::sys::DynamicLibrary::SearchForAddressOfSymbol(dlsym_mangled_name))

core/metacling/src/TClingCallbacks.cxx

Lines changed: 130 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,7 @@
3434
#include "clang/Serialization/ASTReader.h"
3535
#include "clang/Serialization/GlobalModuleIndex.h"
3636

37-
#include "llvm/Object/ELFObjectFile.h"
38-
#include "llvm/Object/ObjectFile.h"
37+
#include "llvm/ExecutionEngine/Orc/Core.h"
3938

4039
#include "llvm/Support/Error.h"
4140
#include "llvm/Support/FileSystem.h"
@@ -69,6 +68,7 @@ extern "C" {
6968
int TCling__CompileMacro(const char *fileName, const char *options);
7069
void TCling__SplitAclicMode(const char* fileName, std::string &mode,
7170
std::string &args, std::string &io, std::string &fname);
71+
int TCling__LoadLibrary(const char *library);
7272
bool TCling__LibraryLoadingFailed(const std::string&, const std::string&, bool, bool);
7373
void TCling__LibraryLoadedRTTI(const void* dyLibHandle,
7474
llvm::StringRef canonicalName);
@@ -81,12 +81,140 @@ extern "C" {
8181
void TCling__UnlockCompilationDuringUserCodeExecution(void *state);
8282
}
8383

84+
class AutoloadLibraryMU : public llvm::orc::MaterializationUnit {
85+
public:
86+
AutoloadLibraryMU(const std::string &Library, const llvm::orc::SymbolNameVector &Symbols)
87+
: MaterializationUnit(getSymbolFlagsMap(Symbols), nullptr), fLibrary(Library), fSymbols(Symbols)
88+
{
89+
}
90+
91+
StringRef getName() const override { return "<Symbols from Autoloaded Library>"; }
92+
93+
void materialize(std::unique_ptr<llvm::orc::MaterializationResponsibility> R) override
94+
{
95+
llvm::orc::SymbolMap loadedSymbols;
96+
llvm::orc::SymbolNameSet failedSymbols;
97+
bool loadedLibrary = false;
98+
99+
for (auto symbol : fSymbols) {
100+
std::string symbolStr = (*symbol).str();
101+
std::string nameForDlsym = ROOT::TMetaUtils::DemangleNameForDlsym(symbolStr);
102+
103+
// Check if the symbol is available without loading the library.
104+
void *addr = llvm::sys::DynamicLibrary::SearchForAddressOfSymbol(nameForDlsym);
105+
106+
if (!addr && !loadedLibrary) {
107+
// Try to load the library which should provide the symbol definition.
108+
// TODO: Should this interface with the DynamicLibraryManager directly?
109+
if (TCling__LoadLibrary(fLibrary.c_str()) < 0) {
110+
ROOT::TMetaUtils::Error("AutoloadLibraryMU", "Failed to load library %s", fLibrary.c_str());
111+
}
112+
113+
// Only try loading the library once.
114+
loadedLibrary = true;
115+
116+
addr = llvm::sys::DynamicLibrary::SearchForAddressOfSymbol(nameForDlsym);
117+
}
118+
119+
if (addr) {
120+
loadedSymbols[symbol] =
121+
llvm::JITEvaluatedSymbol(llvm::pointerToJITTargetAddress(addr), llvm::JITSymbolFlags::Exported);
122+
} else {
123+
// Collect all failing symbols, delegate their responsibility and then
124+
// fail their materialization. R->defineNonExistent() sounds like it
125+
// should do that, but it's not implemented?!
126+
failedSymbols.insert(symbol);
127+
}
128+
}
129+
130+
if (!failedSymbols.empty()) {
131+
auto failingMR = R->delegate(failedSymbols);
132+
if (failingMR) {
133+
(*failingMR)->failMaterialization();
134+
}
135+
}
136+
137+
if (!loadedSymbols.empty()) {
138+
llvm::cantFail(R->notifyResolved(loadedSymbols));
139+
llvm::cantFail(R->notifyEmitted());
140+
}
141+
}
142+
143+
void discard(const llvm::orc::JITDylib &JD, const llvm::orc::SymbolStringPtr &Name) override {}
144+
145+
private:
146+
static llvm::orc::SymbolFlagsMap getSymbolFlagsMap(const llvm::orc::SymbolNameVector &Symbols)
147+
{
148+
llvm::orc::SymbolFlagsMap map;
149+
for (auto symbolName : Symbols)
150+
map[symbolName] = llvm::JITSymbolFlags::Exported;
151+
return map;
152+
}
153+
154+
std::string fLibrary;
155+
llvm::orc::SymbolNameVector fSymbols;
156+
};
157+
158+
class AutoloadLibraryGenerator : public llvm::orc::DefinitionGenerator {
159+
public:
160+
AutoloadLibraryGenerator(cling::Interpreter *interp) : fInterpreter(interp) {}
161+
162+
llvm::Error tryToGenerate(llvm::orc::LookupState &LS, llvm::orc::LookupKind K, llvm::orc::JITDylib &JD,
163+
llvm::orc::JITDylibLookupFlags JDLookupFlags,
164+
const llvm::orc::SymbolLookupSet &Symbols) override
165+
{
166+
// If we get here, the symbols have not been found in the current process,
167+
// so no need to check that again. Instead search for the library that
168+
// provides the symbol and create one MaterializationUnit per library to
169+
// actually load it if needed.
170+
std::unordered_map<std::string, llvm::orc::SymbolNameVector> found;
171+
llvm::orc::SymbolNameSet missing;
172+
173+
// TODO: Do we need to take gInterpreterMutex?
174+
// R__LOCKGUARD(gInterpreterMutex);
175+
176+
for (auto &&KV : Symbols) {
177+
llvm::orc::SymbolStringPtr name = KV.first;
178+
179+
const cling::DynamicLibraryManager &DLM = *fInterpreter->getDynamicLibraryManager();
180+
181+
std::string libName = DLM.searchLibrariesForSymbol((*name).str(),
182+
/*searchSystem=*/true);
183+
184+
assert(!llvm::StringRef(libName).startswith("libNew") && "We must not resolve symbols from libNew!");
185+
186+
if (libName.empty()) {
187+
missing.insert(name);
188+
continue;
189+
}
190+
191+
found[libName].push_back(name);
192+
}
193+
194+
for (auto &&KV : found) {
195+
auto MU = std::make_unique<AutoloadLibraryMU>(KV.first, std::move(KV.second));
196+
if (auto Err = JD.define(MU))
197+
return Err;
198+
}
199+
200+
if (!missing.empty())
201+
return llvm::make_error<llvm::orc::SymbolsNotFound>(std::move(missing));
202+
203+
return llvm::Error::success();
204+
}
205+
206+
private:
207+
cling::Interpreter *fInterpreter;
208+
};
209+
84210
TClingCallbacks::TClingCallbacks(cling::Interpreter *interp, bool hasCodeGen) : InterpreterCallbacks(interp)
85211
{
86212
if (hasCodeGen) {
87213
Transaction* T = 0;
88214
m_Interpreter->declare("namespace __ROOT_SpecialObjects{}", &T);
89215
fROOTSpecialNamespace = dyn_cast<NamespaceDecl>(T->getFirstDecl().getSingleDecl());
216+
217+
interp->addGenerator(std::make_unique<AutoloadLibraryGenerator>(interp));
90218
}
91219
}
92220

interpreter/cling/include/cling/Interpreter/Interpreter.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ namespace llvm {
2929
class StringRef;
3030
class Type;
3131
template <typename T> class SmallVectorImpl;
32+
namespace orc {
33+
class DefinitionGenerator;
34+
}
3235
}
3336

3437
namespace clang {
@@ -735,8 +738,9 @@ namespace cling {
735738
///\brief Create suitable default compilation options.
736739
CompilationOptions makeDefaultCompilationOpts() const;
737740

738-
//FIXME: This must be in InterpreterCallbacks.
739-
void installLazyFunctionCreator(void* (*fp)(const std::string&));
741+
/// Register a DefinitionGenerator to dynamically provide symbols for
742+
/// generated code that are not already available within the process.
743+
void addGenerator(std::unique_ptr<llvm::orc::DefinitionGenerator> G);
740744

741745
//FIXME: Lets the IncrementalParser run static inits on transaction
742746
// completed. Find a better way.

interpreter/cling/lib/Interpreter/IncrementalExecutor.cpp

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -190,21 +190,6 @@ IncrementalExecutor::HandleMissingFunction(const std::string& mangled_name) cons
190190
return utils::FunctionToVoidPtr(&unresolvedSymbol);
191191
}
192192

193-
void*
194-
IncrementalExecutor::NotifyLazyFunctionCreators(const std::string& mangled_name) const {
195-
for (auto it = m_lazyFuncCreator.begin(), et = m_lazyFuncCreator.end();
196-
it != et; ++it) {
197-
void* ret = (void*)((LazyFunctionCreatorFunc_t)*it)(mangled_name);
198-
if (ret)
199-
return ret;
200-
}
201-
void *address = nullptr;
202-
if (m_externalIncrementalExecutor)
203-
address = m_externalIncrementalExecutor->getAddressOfGlobal(mangled_name);
204-
205-
return (address ? address : HandleMissingFunction(mangled_name));
206-
}
207-
208193
#if 0
209194
// FIXME: employ to empty module dependencies *within* the *current* module.
210195
static void
@@ -315,10 +300,9 @@ void IncrementalExecutor::setCallbacks(InterpreterCallbacks* callbacks) {
315300
m_DyLibManager.setCallbacks(callbacks);
316301
}
317302

318-
void
319-
IncrementalExecutor::installLazyFunctionCreator(LazyFunctionCreatorFunc_t fp)
320-
{
321-
m_lazyFuncCreator.push_back(fp);
303+
void IncrementalExecutor::addGenerator(
304+
std::unique_ptr<llvm::orc::DefinitionGenerator> G) {
305+
m_JIT->addGenerator(std::move(G));
322306
}
323307

324308
void IncrementalExecutor::replaceSymbol(const char* Name, void* Addr) const {

interpreter/cling/lib/Interpreter/IncrementalExecutor.h

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ namespace llvm {
4242
class GlobalValue;
4343
class Module;
4444
class TargetMachine;
45+
namespace orc {
46+
class DefinitionGenerator;
47+
}
4548
}
4649

4750
namespace cling {
@@ -50,9 +53,6 @@ namespace cling {
5053
class Value;
5154

5255
class IncrementalExecutor {
53-
public:
54-
typedef void* (*LazyFunctionCreatorFunc_t)(const std::string&);
55-
5656
private:
5757
///\brief Our JIT interface.
5858
///
@@ -126,11 +126,6 @@ namespace cling {
126126
///
127127
std::vector<llvm::Module*> m_ModulesToJIT;
128128

129-
///\brief Lazy function creator, which is a final callback which the
130-
/// JIT fires if there is unresolved symbol.
131-
///
132-
std::vector<LazyFunctionCreatorFunc_t> m_lazyFuncCreator;
133-
134129
///\brief Set of the symbols that the JIT couldn't resolve.
135130
///
136131
mutable std::unordered_set<std::string> m_unresolvedSymbols;
@@ -175,7 +170,9 @@ namespace cling {
175170
return m_DyLibManager;
176171
}
177172

178-
void installLazyFunctionCreator(LazyFunctionCreatorFunc_t fp);
173+
/// Register a DefinitionGenerator to dynamically provide symbols for
174+
/// generated code that are not already available within the process.
175+
void addGenerator(std::unique_ptr<llvm::orc::DefinitionGenerator> G);
179176

180177
///\brief Unload a set of JIT symbols.
181178
llvm::Error unloadModule(const Transaction& T) const {
@@ -240,10 +237,6 @@ namespace cling {
240237
///
241238
void AddAtExitFunc(void (*func)(void*), void* arg, const Transaction* T);
242239

243-
///\brief Try to resolve a symbol through our LazyFunctionCreators;
244-
/// print an error message if that fails.
245-
void* NotifyLazyFunctionCreators(const std::string&) const;
246-
247240
private:
248241
///\brief Emit a llvm::Module to the JIT.
249242
///
@@ -262,9 +255,11 @@ namespace cling {
262255
bool diagnoseUnresolvedSymbols(llvm::StringRef trigger,
263256
llvm::StringRef title = llvm::StringRef()) const;
264257

258+
public:
265259
///\brief Remember that the symbol could not be resolved by the JIT.
266260
void* HandleMissingFunction(const std::string& symbol) const;
267261

262+
private:
268263
///\brief Runs an initializer function.
269264
ExecutionResult executeInit(llvm::StringRef function) const {
270265
typedef void (*InitFun_t)();

0 commit comments

Comments
 (0)