Share native TLS with prebuilt libraries on the in-process JIT - #1103
Open
conrade-ctc wants to merge 1 commit into
Open
Share native TLS with prebuilt libraries on the in-process JIT#1103conrade-ctc wants to merge 1 commit into
conrade-ctc wants to merge 1 commit into
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1103 +/- ##
==========================================
+ Coverage 87.74% 87.77% +0.03%
==========================================
Files 23 23
Lines 6429 6504 +75
==========================================
+ Hits 5641 5709 +68
- Misses 788 795 +7
🚀 New features to boost your workflow:
|
conrade-ctc
force-pushed
the
pr-i-native-tls
branch
from
September 1, 2026 19:13
f393fa5 to
e51b42d
Compare
Collaborator
Author
|
@vgvassilev one design point I would like your view on. To rewrite the module before it reaches the JIT, the pass splits the no-value |
conrade-ctc
force-pushed
the
pr-i-native-tls
branch
2 times, most recently
from
September 2, 2026 13:31
1d8d921 to
10913a1
Compare
The ORC JIT compiles with emulated TLS, so jitted code referencing a thread_local defined in a prebuilt library resolves __emutls_v.<sym> companions that nothing defines -- libstdc++'s __once_callable/__once_call behind std::call_once being the canonical case. Synthesizing the companions would still be wrong: the library's own code reads the native copies (libstdc++'s __once_proxy reads __once_call), so the two sides would silently diverge. Instead, rewrite each external thread_local declaration's llvm.threadlocal.address accesses into calls to a runtime helper returning the calling thread's native copy via dlsym, before the module reaches the JIT. Jitted and native code then share per-thread storage, so cross-boundary protocols like the std::call_once machinery work unchanged. thread_locals the JIT itself defines keep the emulated-TLS path. Only symbols the dynamic linker already resolves are redirected; anything else keeps its clear link-time error. Scoped to ELF and the in-process JIT (the helper is the process's own dlsym). Co-developed-with-the-help-of: Claude Code (Fable 5, human in the loop)
conrade-ctc
force-pushed
the
pr-i-native-tls
branch
from
September 3, 2026 16:59
10913a1 to
4d5c4da
Compare
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.
The in-process ORC JIT compiles with emulated TLS. A jitted reference to a prebuilt library's
thread_localthen needs a__emutls_v.<sym>companion that nothing defines.std::call_onceis the usual break: jitted code writes libstdc++'s__once_call, but__once_proxyreads the native copy. Defining the companions does not help, because the two sides then use different storage.This PR adds an interim pass, before the module reaches the JIT. It turns each
llvm.threadlocal.addressuse of an externalthread_localdeclaration into adlsym-based helper call. Jitted and native code then share the per-thread storage. The scope is narrow: ELF, in-process, and only symbols the dynamic linker already resolves.Is native TLS support for the in-process JIT the intended long-term fix? If the ORC owners plan it, this pass retires.
Known gap: to run the pass between
ParseandExecute, the no-valueParseAndExecutepath skips clang'sLastValuedump-and-clear. A value-producing input on that path no longer prints and can leave a staleLastValuefor a laterevaluate. AnIRTransformLayeron the LLJIT would avoid the split.