Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions clang/lib/Interpreter/IncrementalExecutor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -60,6 +61,30 @@
#include <unistd.h>
#endif

// Address of the host's emulated-TLS runtime entry point, or null if the host
// cannot provide one. clang-repl's JIT always lowers thread_local to emulated
// TLS (JITTargetMachineBuilder forces EmulatedTLS on), so JIT'd code references
// __emutls_get_address on every target. That symbol lives in the compiler
// runtime -- libgcc_s.so on a glibc toolchain, or the compiler-rt builtins
// static archive on Darwin and on compiler-rt-rtlib toolchains. When it is only
// in a static archive and nothing else references it, it is never linked in and
// ORC's process-symbol lookup cannot resolve it. Referencing it here
// force-links the archive member so it is present regardless of how the host
// provides it. Excluded where an emulated-TLS runtime is not guaranteed on the
// link line, so the reference would fail to link: non-Unix (MSVC has no such
// runtime), Emscripten (the wasm executor below does not use this JIT path),
// and AIX / z/OS (whose runtimes may not provide the symbol). On those hosts
// thread_locals instead rely on process-symbol lookup, unchanged from before.
#if defined(LLVM_ON_UNIX) && !defined(__EMSCRIPTEN__) && !defined(_AIX) && \
!defined(__MVS__)
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;

Expand Down Expand Up @@ -388,6 +413,31 @@ IncrementalExecutorBuilder::create(llvm::orc::ThreadSafeContext &TSC,
if (!JB)
return JB.takeError();
JITBuilder = std::move(*JB);
// TODO: Switch to native TLS 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.
//
// clang-repl lowers thread_local to emulated TLS on every target (see
// JITTargetMachineBuilder), so JIT'd code calls __emutls_get_address. When
// the host cannot resolve that symbol through process-symbol lookup
// (Darwin, and ELF toolchains that link compiler-rt builtins rather than
// libgcc_s), define the force-linked host symbol (see
// getEmuTLSGetAddressPtr) as an absolute symbol so it is visible to JIT'd
// code. This is harmless where process-symbol lookup would already resolve
// it: an already-defined symbol shadows the process-symbols generator.
// In-process execution only -- the host address is meaningless in an
// out-of-process executor.
if (void *EmuTLSGetAddress = getEmuTLSGetAddressPtr())
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();
Expand Down
28 changes: 28 additions & 0 deletions clang/test/Interpreter/emulated-tls.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// REQUIRES: host-supports-jit
// UNSUPPORTED: system-windows
//
// Emulated TLS is not supported by the LoongArch backend.
// UNSUPPORTED: target=loongarch{{.*}}
//
// 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
1 change: 1 addition & 0 deletions llvm/lib/ExecutionEngine/Orc/Layer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ IRMaterializationUnit::IRMaterializationUnit(

auto EmuTLST = Mangle(("__emutls_t." + GV.getName()).str());
SymbolFlags[EmuTLST] = Flags;
SymbolToDefinition[EmuTLST] = &GV;
}
continue;
}
Expand Down
Loading