[clang] [llvm] Reland "[ORC] Track __emutls_t definitions in IRMaterializationUnit" (#207161) (PR #208413)
Vassil Vassilev via cfe-commits
cfe-commits at lists.llvm.org
Thu Jul 9 05:16:13 PDT 2026
https://github.com/vgvassilev updated https://github.com/llvm/llvm-project/pull/208413
>From 5697ccead47e49472ca2c091600687c2584c3317 Mon Sep 17 00:00:00 2001
From: Emery Conrad <emery.conrad at chicagotrading.com>
Date: Thu, 9 Jul 2026 05:15:58 -0500
Subject: [PATCH] Reland "[ORC] Track __emutls_t definitions in
IRMaterializationUnit" (#207161)
Reverted in #207775: the new test exposed that clang-repl on Darwin
cannot resolve __emutls_get_address at all -- it lives in the
compiler-rt builtins static archive, which nothing links into the
process (on Linux it resolves from libgcc_s.so). Reland with a Darwin
fix: force-link __emutls_get_address into clang and define it as an
absolute symbol in the process-symbols JITDylib, in-process only.
Co-developed-with-the-help-of: Claude Code (Claude Opus 4.8, human in the loop)
---
clang/lib/Interpreter/IncrementalExecutor.cpp | 24 ++++++++++++++++++
clang/test/Interpreter/emulated-tls.cpp | 25 +++++++++++++++++++
llvm/lib/ExecutionEngine/Orc/Layer.cpp | 1 +
3 files changed, 50 insertions(+)
create mode 100644 clang/test/Interpreter/emulated-tls.cpp
diff --git a/clang/lib/Interpreter/IncrementalExecutor.cpp b/clang/lib/Interpreter/IncrementalExecutor.cpp
index 65cb29a2f441a..9bf29ca97793a 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,12 @@
#include <unistd.h>
#endif
+#ifdef __APPLE__
+// Provided by the compiler-rt builtins archive, which implements the
+// emulated-TLS runtime on Darwin.
+extern "C" void *__emutls_get_address(void *);
+#endif
+
namespace clang {
IncrementalExecutorBuilder::~IncrementalExecutorBuilder() = default;
@@ -388,6 +395,23 @@ IncrementalExecutorBuilder::create(llvm::orc::ThreadSafeContext &TSC,
if (!JB)
return JB.takeError();
JITBuilder = std::move(*JB);
+#ifdef __APPLE__
+ // On Darwin, 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. Taking its address here forces
+ // the archive member into the binary; defining it as an absolute symbol
+ // makes it visible to JIT'd code. In-process execution only: the address
+ // is meaningless in an out-of-process executor.
+ JITBuilder->setNotifyCreatedCallback([](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(&__emutls_get_address),
+ llvm::JITSymbolFlags::Exported}}}));
+ });
+#endif
}
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;
}
More information about the cfe-commits
mailing list