Author: Emery Conrad
Date: 2026-07-10T08:28:32+03:00
New Revision: 3c138b5c5958dce2daefa923a1cae612e47a4273

URL: 
https://github.com/llvm/llvm-project/commit/3c138b5c5958dce2daefa923a1cae612e47a4273
DIFF: 
https://github.com/llvm/llvm-project/commit/3c138b5c5958dce2daefa923a1cae612e47a4273.diff

LOG: Reland "[ORC] Track __emutls_t definitions in IRMaterializationUnit" 
(#207161) (#208413)

Relands #207161, which was reverted in #207775 after the new
`clang/test/Interpreter/emulated-tls.cpp` failed on Darwin with `Symbols
not found: [ ___emutls_get_address ]`.

The `IRMaterializationUnit` fix and the test are unchanged from #207161.
What the failure exposed is that thread_locals never worked in
clang-repl on Darwin: `__emutls_get_address` is implemented in the
compiler-rt builtins static archive, and nothing links it into the
process, so process-symbol lookup cannot resolve it (on Linux it
resolves from `libgcc_s.so`).

New in this reland, per @lhames's suggestion (option 2) on the original
PR:

- `clang/lib/Interpreter/IncrementalExecutor.cpp`: on `__APPLE__`,
reference `__emutls_get_address` (forcing the builtins archive member to
be linked in) and define it as an absolute symbol in the process-symbols
JITDylib, following the precedent of the MinGW `__main` workaround in
`lli.cpp`. Scoped to the default in-process JITBuilder path only — the
host address would be meaningless for an out-of-process executor, and
custom-builder clients do their own setup.

Verified on an aarch64-darwin machine: the previously failing test now
passes, along with the rest of `clang/test/Interpreter`.

@vgvassilev @lhames — could you take a look?

🤖 Done with the help of [Claude Code](https://claude.com/claude-code)
(Claude Opus 4.8, human in the loop)

Co-authored-by: Emery Conrad <[email protected]>

Added: 
    clang/test/Interpreter/emulated-tls.cpp

Modified: 
    clang/lib/Interpreter/IncrementalExecutor.cpp
    llvm/lib/ExecutionEngine/Orc/Layer.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/Interpreter/IncrementalExecutor.cpp 
b/clang/lib/Interpreter/IncrementalExecutor.cpp
index 65cb29a2f441a..8e64a3d73305e 100644
--- a/clang/lib/Interpreter/IncrementalExecutor.cpp
+++ b/clang/lib/Interpreter/IncrementalExecutor.cpp
@@ -27,6 +27,7 @@
 #include "llvm/ADT/Twine.h"
 
 #include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h"
+#include "llvm/ExecutionEngine/Orc/AbsoluteSymbols.h"
 #include "llvm/ExecutionEngine/Orc/Debugging/DebuggerSupport.h"
 #include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h"
 #include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"
@@ -60,6 +61,21 @@
 #include <unistd.h>
 #endif
 
+// Address of the host's emulated-TLS runtime entry point, or null if the host
+// cannot provide one. On Darwin, __emutls_get_address lives in the compiler-rt
+// builtins archive; referencing it here force-links the archive member into
+// the binary. The reference must not exist elsewhere: MSVC has no emulated-TLS
+// runtime (the link would fail), and ELF hosts resolve it from libgcc_s /
+// compiler-rt through regular process-symbol lookup.
+#ifdef __APPLE__
+extern "C" void *__emutls_get_address(void *);
+static void *getEmuTLSGetAddressPtr() {
+  return reinterpret_cast<void *>(&__emutls_get_address);
+}
+#else
+static void *getEmuTLSGetAddressPtr() { return nullptr; }
+#endif
+
 namespace clang {
 IncrementalExecutorBuilder::~IncrementalExecutorBuilder() = default;
 
@@ -388,6 +404,29 @@ 
IncrementalExecutorBuilder::create(llvm::orc::ThreadSafeContext &TSC,
     if (!JB)
       return JB.takeError();
     JITBuilder = std::move(*JB);
+    // TODO: Switch to native TLS on Darwin once clang-repl can adopt the ORC
+    // runtime (which provides __emutls_get_address and supports the full TLS
+    // lifecycle). That will also remove the in-process-only constraint below.
+    //
+    // For MachO targets, thread_locals are lowered to emulated TLS, but the
+    // runtime (__emutls_get_address) lives in the compiler-rt builtins archive
+    // and nothing else in this process references it, so it isn't linked in
+    // and process-symbol lookup cannot find it. Define the force-linked host
+    // symbol (see getEmuTLSGetAddressPtr) as an absolute symbol so it is
+    // visible to JIT'd code. In-process execution only: the address is
+    // meaningless in an out-of-process executor.
+    if (void *EmuTLSGetAddress = getEmuTLSGetAddressPtr();
+        EmuTLSGetAddress && TT.isOSBinFormatMachO())
+      JITBuilder->setNotifyCreatedCallback(
+          [EmuTLSGetAddress](llvm::orc::LLJIT &J) {
+            auto &JD = J.getProcessSymbolsJITDylib()
+                           ? *J.getProcessSymbolsJITDylib()
+                           : J.getMainJITDylib();
+            return JD.define(llvm::orc::absoluteSymbols(
+                {{J.mangleAndIntern("__emutls_get_address"),
+                  {llvm::orc::ExecutorAddr::fromPtr(EmuTLSGetAddress),
+                   llvm::JITSymbolFlags::Exported}}}));
+          });
   }
 
   llvm::Error Err = llvm::Error::success();

diff  --git a/clang/test/Interpreter/emulated-tls.cpp 
b/clang/test/Interpreter/emulated-tls.cpp
new file mode 100644
index 0000000000000..73afe172ef6d9
--- /dev/null
+++ b/clang/test/Interpreter/emulated-tls.cpp
@@ -0,0 +1,25 @@
+// REQUIRES: host-supports-jit
+// UNSUPPORTED: system-windows
+//
+// An inline function that odr-uses a non-zero-initialized thread_local is
+// emitted as a weak (linkonce_odr) definition into every 
PartialTranslationUnit
+// that references it. With emulated TLS that set includes an __emutls_t.<var>
+// symbol. When a later PTU re-defines the same weak set, ORC's
+// IRMaterializationUnit::discard() must find each duplicated symbol in its
+// SymbolToDefinition map. The emulated-TLS path used to register 
__emutls_t.<var>
+// in SymbolFlags but not SymbolToDefinition, so discarding it dereferenced
+// end() -- an assertion failure in +Asserts builds and heap corruption
+// otherwise. Two PTUs each pulling in the same inline worker reproduces it.
+//
+// RUN: cat %s | clang-repl | FileCheck %s
+
+extern "C" int printf(const char *, ...);
+template <int Tag> struct HeavyThing { static thread_local int tls; };
+template <int Tag> thread_local int HeavyThing<Tag>::tls = Tag + 1;
+inline int worker() { return HeavyThing<1>::tls; }
+int callA() { return worker(); }
+int callB() { return worker(); }
+auto r = printf("tls = %d, %d\n", callA(), callB());
+// CHECK: tls = 2, 2
+
+%quit

diff  --git a/llvm/lib/ExecutionEngine/Orc/Layer.cpp 
b/llvm/lib/ExecutionEngine/Orc/Layer.cpp
index eb144275da589..5e95b8c73b482 100644
--- a/llvm/lib/ExecutionEngine/Orc/Layer.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/Layer.cpp
@@ -70,6 +70,7 @@ IRMaterializationUnit::IRMaterializationUnit(
 
           auto EmuTLST = Mangle(("__emutls_t." + GV.getName()).str());
           SymbolFlags[EmuTLST] = Flags;
+          SymbolToDefinition[EmuTLST] = &GV;
         }
         continue;
       }


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to