Repository navigation
[core] Export CppInterOp's dispatch entry point from libCore - #23693
Open
vgvassilev wants to merge 2 commits into
Open
vgvassilev wants to merge 2 commits into
vgvassilev wants to merge 2 commits into
Conversation
CppInterOp clients such as cppjit attach to an interpreter through Cpp::LoadDispatchAPI(), which loads a library and looks up CppGetProcAddress in it. In ROOT that entry point lives in libCling, which TROOT loads RTLD_LOCAL and only when the interpreter starts. A client running next to ROOT cannot reach it without knowing libCling's path and starting ROOT first, so it loads its own CppInterOp and LLVM instead: a second interpreter, more memory, and two LLVMs in one process. Export CppGetProcAddress from libCore and forward it to libCling's through the handle TROOT already holds, starting the interpreter on first use. A client can then pass libCore, found through $ROOTSYS, to Cpp::LoadDispatchAPI() and share ROOT's interpreter; libCling stays loaded once and RTLD_LOCAL. PyROOT is unaffected and keeps loading the dispatch API from libCling. The test loads libCore the way such a client does, declares a variable through the dispatched Process() and reads it back through gInterpreter.
Test Results 24 files 24 suites 4d 1h 6m 20s ⏱️ For more details on these failures, see this check. Results for commit 7b0c6e3. ♻️ This comment has been updated with latest results. |
The test calls dlopen() and dlsym() itself. With glibc older than 2.34, for instance on alma8, these live in libdl, which must be linked explicitly.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This Pull request:
Lets a CppInterOp client such as cppjit use ROOT's interpreter instead of loading its own copy of CppInterOp and LLVM. This is a first step. Sharing the interpreter works but is not reliable yet, and this draft is meant to discuss what is missing.
Changes or fixes:
A client attaches to an interpreter with
Cpp::LoadDispatchAPI(lib), which loadsliband looks upCppGetProcAddressin it. In ROOT that function lives in libCling. TROOT loads libCling withRTLD_LOCALand only when the interpreter starts, so a client running next to ROOT cannot find it. The client then loads a second interpreter, which costs about 128 MB and puts two LLVMs in one process.With this change libCore exports
CppGetProcAddress. It starts the interpreter if needed and forwards the call to libCling through the handle TROOT already holds. A client can pass$ROOTSYS/lib/libCore.sotoCpp::LoadDispatchAPI(). libCling is still loaded once andRTLD_LOCAL, and PyROOT is not affected. The new testTClingDispatchloads libCore the way a client would, declares a variable through the dispatchedProcess()and reads it back throughgInterpreter. It fails without the change.I tried this with upstream cppjit (
3df629f) on Linux x86_64 and LLVM 22. I patched cppjit to load libCore whenROOTSYSis set and to skip C++ destructors while Python shuts down. Withoutimport ROOT, cppjit used ROOT's interpreter: libCling was mapped once, no second CppInterOp was loaded, and ROOT classes such asTH1FandTFileworked.Reliability
It is not reliable yet, and most of what is missing belongs in CppInterOp and cppjit rather than here.
The first problem is that the dispatch table is matched by name only. The released cppjit wheel (CppInterOp
c8126249) and ROOT have functions with the same names but different signatures:LoadLibrarytakes an extra argument in ROOT, andIsAllocatorandGetEnumConstantValuereturn different types. Reusing ROOT from that wheel crashes insideLoadLibrary. Comparing LLVM versions does not catch this. CppInterOp should hash the signature of each table entry and haveLoadDispatchAPI()check it. Doing it per entry keeps old clients working when functions are added, and only rejects a client when a signature it uses has changed.cppjit also parses the CppInterOp headers it ships. When it reuses ROOT it has to use ROOT's headers from
$ROOTSYS/etc/cppinterop, otherwise calls fail against ROOT's table.Exit crashes when a
TFilestill holds a histogram. ROOT'sEndOfProcessCleanups()then runs late, from a libCling static destructor. Skipping destructors during Python shutdown is not enough on its own, and neither is running the cleanup on its own. With both, as PyROOT does it from Pythonatexit, the case passed every run. Objects that ROOT deletes while the program runs are still a problem:f.Close(); del hdeleteshtwice. PyROOT avoids this with its memory regulator and by giving C++ the ownership of objects attached to a directory, and cppjit would need the same.cppjit's own test suite shows 16 failures that only appear when it shares ROOT's interpreter. Three are tests marked as expected failures that now pass under cling. Four come from ROOT's global state: ROOT's names show up in
cppjit.gbl,stdis visible globally, and cppjit's-march=nativeis not applied. Eight are template instantiations with explicit arguments such asstd::get<0>. One,test33_direct_base_methods, returns a wrong result. cppjit's ROOT branch may already handle some of these, and if so, reuse should require it.This PR is the first step towards unifying cppjit and ROOT on a deeper level. Note that, until the table check, the headers and the exit handling are in place, cppjit should keep its bundled backend by default and reuse ROOT only when the user asks for it.
The rest of the required changes are needed elsewhere.