[clang] [Clang][MSVC] Avoid native CRT TLS accesses with emulated TLS (PR #220215)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Sep 16 04:57:07 PDT 2026
Sp0tless wrote:
The immediate failure mode today is unresolved symbols at final link or JIT materialization time, and the references are produced even without JIT/ORC. Plain Clang with `--target=x86_64-pc-windows-msvc -femulated-tls` on ordinary code emits references to `__emutls_v._Init_thread_epoch` and `__emutls_v.__tls_guard` (the Compiler Explorer link in the PR description shows this). Neither symbol exists, since the CRT does not publish emutls descriptors for its own internal TLS state. The underlying CodeGen problem is therefore independent of whether the eventual consumer is an ordinary linker or a JIT symbol lookup.
I did try something close to providing the required behavior within the JIT in the #213568 prototype: a JIT-side accessor bridge for `_Init_thread_epoch` and an IR rewrite for dynamic TLS initialization. An emulated epoch initialized conservatively may be enough for function-local statics, but an emulated guard alone would not make the host CRT discover initializer functions in JIT allocations. The dynamic-init guard and initializer state also need to remain correct across incremental PTUs. My prototype tracked it in an executor-wide list with no ResourceTracker association, so unloading or undoing a PTU could leave stale state. I also did not find a good story for thread-exit destruction, since that is normally driven by `__tlregdtor` and PE TLS callbacks tied to a loader-managed module, which a JIT allocation is not. So it does not feel like just "two symbols" once I get into it, but rather an entry point into the JIT-owned TLS initialization and lifetime problem under the MSVC ABI, though I may be missing a simpler way to scope it down.
Separately, in case it is useful: even once this patch is in, ordinary `thread_local` accesses under emulated TLS still need `__emutls_get_address` to resolve in-process on Windows, and that path currently excludes MSVC. #209717's force-link/absolute-symbol workaround explicitly skips it ("MSVC has no such runtime"), and its test is `UNSUPPORTED: system-windows`. So that looks like the next thing this would run into. Beyond that, the two lifetime gaps mentioned above, PTU-scoped dynamic-init state and thread-exit destruction, still seem open as far as I can tell. Together with other COFFPlatform fixes from #213568, such as #217445, it feels like Windows COFFPlatform support may be within reach of a useful baseline, if that context is helpful.
Happy to go further in whichever direction is useful: more auditing on the Clang side, or digging into the JIT-side symbols and lifecycle gaps if that is still the direction you would like to take.
https://github.com/llvm/llvm-project/pull/220215
More information about the cfe-commits
mailing list