https://github.com/dicej updated 
https://github.com/llvm/llvm-project/pull/222747

>From aa0c3543e79615b1b3f8bb771ca4187e91eaf4b6 Mon Sep 17 00:00:00 2001
From: Joel Dice <[email protected]>
Date: Tue, 8 Sep 2026 17:34:36 -0600
Subject: [PATCH] fix Wasm exceptions + coop threading + shared libraries

Prior to this commit, the combination of Wasm exception handling, cooperative
multithreading, and shared libraries was broken.  Specifically, the code
generation in `WasmEHPrepare.cpp` involved direct, cross-library access to
`libunwind.so`'s thread-local `__wasm_lpad_context` variable.  However, the ABI
used for cooperative multithreading does not support cross-library access to
thread-local variables.

The solution used here is to add a new `_Unwind_GetWasmLPadContext` function to
`libunwind.so` and use that to get address of the `__wasm_lpad_context` for the
current thread, both in the code generated by `WasmEHPrepare.cpp` and in the
`__gxx_wasm_personality_v0` function defined in `cxa_personality.cpp`.  I've
used this strategy unconditionally for all targets, regardless of whether
cooperative multithreading and/or position-independent are enabled.  If desired
(e.g. for performance or code complexity reasons), I could make it conditional
on both of those features being enabled and fall back to using
`__wasm_lpad_context` directly otherwise.
---
 libcxxabi/src/cxa_personality.cpp             |  8 ++-
 libunwind/include/unwind_wasm.h               |  7 ++-
 libunwind/src/Unwind-wasm.c                   |  8 ++-
 llvm/include/llvm/IR/RuntimeLibcalls.td       |  6 +-
 llvm/lib/CodeGen/WasmEHPrepare.cpp            | 56 ++++++++++---------
 llvm/test/CodeGen/WebAssembly/eh-lsda.ll      | 15 ++---
 .../CodeGen/WebAssembly/exception-legacy.ll   |  2 +-
 llvm/test/CodeGen/WebAssembly/exception.ll    |  2 +-
 .../CodeGen/WebAssembly/wasm-eh-prepare.ll    | 14 +++--
 9 files changed, 67 insertions(+), 51 deletions(-)

diff --git a/libcxxabi/src/cxa_personality.cpp 
b/libcxxabi/src/cxa_personality.cpp
index 3fdcd8a0c13497..da2d8b2599d9d5 100644
--- a/libcxxabi/src/cxa_personality.cpp
+++ b/libcxxabi/src/cxa_personality.cpp
@@ -1116,13 +1116,15 @@ __gxx_personality_seh0(PEXCEPTION_RECORD ms_exc, void 
*this_frame,
 extern "C" _LIBCXXABI_FUNC_VIS _Unwind_Reason_Code 
__gxx_wasm_personality_v0(void* exception_ptr) {
   struct _Unwind_Exception* exception_object = (struct 
_Unwind_Exception*)exception_ptr;
 
+  struct _Unwind_LandingPadContext* context = _Unwind_GetWasmLPadContext();
+
   // Reset the selector.
-  __wasm_lpad_context.selector = 0;
+  context->selector = 0;
 
   // Call personality function. Wasm does not have two-phase unwinding, so we
   // only do the search phase.
-  return __gxx_personality_imp(1, _UA_SEARCH_PHASE, 
exception_object->exception_class, exception_object,
-                               (struct _Unwind_Context*)&__wasm_lpad_context);
+  return __gxx_personality_imp(
+      1, _UA_SEARCH_PHASE, exception_object->exception_class, 
exception_object, (struct _Unwind_Context*)context);
 }
 #endif
 
diff --git a/libunwind/include/unwind_wasm.h b/libunwind/include/unwind_wasm.h
index 7bf3f30562bd84..66d1792919ab02 100644
--- a/libunwind/include/unwind_wasm.h
+++ b/libunwind/include/unwind_wasm.h
@@ -9,8 +9,6 @@
 #ifndef __WASM_UNWIND_H__
 #define __WASM_UNWIND_H__
 
-#include <threads.h>
-
 struct _Unwind_LandingPadContext {
   // Input information to personality function
   uintptr_t lpad_index; // landing pad index
@@ -22,6 +20,9 @@ struct _Unwind_LandingPadContext {
 
 // Communication channel between compiler-generated user code and personality
 // function
-extern thread_local struct _Unwind_LandingPadContext __wasm_lpad_context;
+#ifdef __cplusplus
+extern "C"
+#endif
+    struct _Unwind_LandingPadContext *_Unwind_GetWasmLPadContext(void);
 
 #endif // __WASM_UNWIND_H__
diff --git a/libunwind/src/Unwind-wasm.c b/libunwind/src/Unwind-wasm.c
index 963019ea0efc35..f17eeb27e97bb3 100644
--- a/libunwind/src/Unwind-wasm.c
+++ b/libunwind/src/Unwind-wasm.c
@@ -19,8 +19,12 @@
 #include "unwind.h"
 #include <threads.h>
 
-_LIBUNWIND_EXPORT thread_local struct _Unwind_LandingPadContext
-    __wasm_lpad_context;
+thread_local struct _Unwind_LandingPadContext __wasm_lpad_context;
+
+_LIBUNWIND_EXPORT struct _Unwind_LandingPadContext *
+_Unwind_GetWasmLPadContext(void) {
+  return &__wasm_lpad_context;
+}
 
 /// Called by __cxa_throw.
 _LIBUNWIND_EXPORT _Unwind_Reason_Code
diff --git a/llvm/include/llvm/IR/RuntimeLibcalls.td 
b/llvm/include/llvm/IR/RuntimeLibcalls.td
index 69a2793d23b7ab..5803e7cfe8dd2f 100644
--- a/llvm/include/llvm/IR/RuntimeLibcalls.td
+++ b/llvm/include/llvm/IR/RuntimeLibcalls.td
@@ -615,7 +615,7 @@ foreach MemSize = [1, 2, 4, 8, 16] in {
 def UNWIND_RESUME : RuntimeLibcall;
 def UNWIND_REGISTER : RuntimeLibcall;
 def UNWIND_UNREGISTER : RuntimeLibcall;
-def UNWIND_CALL_PERSONALITY : RuntimeLibcall;
+def UNWIND_GET_WASM_LPAD_CONTEXT : RuntimeLibcall;
 def CXA_END_CLEANUP : RuntimeLibcall;
 
 // Note: there are two sets of atomics libcalls; see
@@ -1886,6 +1886,9 @@ defset list<RuntimeLibcallImpl> 
SjLjExceptionHandlingLibcalls = {
   def _Unwind_SjLj_Unregister : RuntimeLibcallImpl<UNWIND_UNREGISTER>;
 }
 
+// Only used on wasm
+def _Unwind_GetWasmLPadContext : 
RuntimeLibcallImpl<UNWIND_GET_WASM_LPAD_CONTEXT>;
+
 // Used on OpenBSD
 def __stack_smash_handler : RuntimeLibcallImpl<STACK_SMASH_HANDLER>;
 
@@ -3642,6 +3645,7 @@ def WasmSystemLibrary
     : SystemRuntimeLibrary<isWasm,
       (add DefaultRuntimeLibcallImpls, Int128RTLibcalls,
            CompilerRTOnlyInt64Libcalls, CompilerRTOnlyInt128Libcalls,
+           _Unwind_GetWasmLPadContext,
            exp10f, exp10,
            emscripten_return_address,
            LibcallImpls<(add __small_printf,
diff --git a/llvm/lib/CodeGen/WasmEHPrepare.cpp 
b/llvm/lib/CodeGen/WasmEHPrepare.cpp
index 3ac0126c9c9a14..37cd2048dfc85b 100644
--- a/llvm/lib/CodeGen/WasmEHPrepare.cpp
+++ b/llvm/lib/CodeGen/WasmEHPrepare.cpp
@@ -86,12 +86,6 @@ class WasmEHPrepareImpl {
   friend class WasmEHPrepare;
 
   Type *LPadContextTy = nullptr; // type of 'struct _Unwind_LandingPadContext'
-  GlobalVariable *LPadContextGV = nullptr; // __wasm_lpad_context
-
-  // Field addresses of struct _Unwind_LandingPadContext
-  Value *LPadIndexField = nullptr; // lpad_index field
-  Value *LSDAField = nullptr;      // lsda field
-  Value *SelectorField = nullptr;  // selector
 
   Function *ThrowF = nullptr;       // wasm.throw() intrinsic
   Function *LPadIndexF = nullptr;   // wasm.landingpad.index() intrinsic
@@ -100,6 +94,8 @@ class WasmEHPrepareImpl {
   Function *CatchF = nullptr;       // wasm.catch() intrinsic
   Function *GetSelectorF = nullptr; // wasm.get.ehselector() intrinsic
   FunctionCallee PersonalityF = nullptr;
+  FunctionCallee GetWasmLPadContextF =
+      nullptr; // _Unwind_GetWasmLPadContext() wrapper
 
   bool prepareThrows(Function &F);
   bool prepareEHPads(Function &F);
@@ -207,7 +203,6 @@ bool WasmEHPrepareImpl::prepareThrows(Function &F) {
 bool WasmEHPrepareImpl::prepareEHPads(Function &F) {
   Module &M = *F.getParent();
   LLVMContext &Ctx = M.getContext();
-  const DataLayout &DL = M.getDataLayout();
 
   SmallVector<BasicBlock *, 16> CatchPads;
   SmallVector<BasicBlock *, 16> CleanupPads;
@@ -234,26 +229,6 @@ bool WasmEHPrepareImpl::prepareEHPads(Function &F) {
   }
   assert(F.hasPersonalityFn() && "Personality function not found");
 
-  // __wasm_lpad_context global variable.
-  // This variable should be thread local. If the target does not support TLS,
-  // we depend on CoalesceFeaturesAndStripAtomics to downgrade it to
-  // non-thread-local ones, in which case we don't allow this object to be
-  // linked with other objects using shared memory.
-  LPadContextGV = M.getOrInsertGlobal("__wasm_lpad_context", LPadContextTy);
-  LPadContextGV->setThreadLocalMode(GlobalValue::GeneralDynamicTLSModel);
-
-  LPadIndexField = LPadContextGV;
-  LSDAField =
-      ConstantExpr::getGetElementPtr(DL, LPadContextTy, LPadContextGV,
-                                     {ConstantInt::get(Ctx, APInt(32, 0)),
-                                      ConstantInt::get(Ctx, APInt(32, 1))},
-                                     GEPNoWrapFlags::inBounds());
-  SelectorField =
-      ConstantExpr::getGetElementPtr(DL, LPadContextTy, LPadContextGV,
-                                     {ConstantInt::get(Ctx, APInt(32, 0)),
-                                      ConstantInt::get(Ctx, APInt(32, 2))},
-                                     GEPNoWrapFlags::inBounds());
-
   // wasm.landingpad.index() intrinsic, which is to specify landingpad index
   LPadIndexF =
       Intrinsic::getOrInsertDeclaration(&M, Intrinsic::wasm_landingpad_index);
@@ -279,6 +254,24 @@ bool WasmEHPrepareImpl::prepareEHPads(Function &F) {
   if (Function *F = dyn_cast<Function>(PersonalityF.getCallee()))
     F->setDoesNotThrow();
 
+  StringRef UnwindGetWasmLPadContextName =
+      RTLIB::RuntimeLibcallsInfo::getLibcallImplName(
+          RTLIB::impl__Unwind_GetWasmLPadContext);
+
+  // _Unwind_GetWasmLPadContext() wrapper function
+  //
+  // We use this function to get the address of `libunwind`'s thread-local
+  // `__wasm_lpad_context` variable for the current thread.  Note that we
+  // cannot, in general, access the `__wasm_lpad_context` directly here 
because,
+  // when the cooperative multithreading feature is enabled, direct,
+  // cross-library access to thread local variables is not supported.
+  auto *UnwindGetWasmLPadContextType =
+      FunctionType::get(PointerType::getUnqual(Ctx), {}, false);
+  GetWasmLPadContextF = M.getOrInsertFunction(UnwindGetWasmLPadContextName,
+                                              UnwindGetWasmLPadContextType);
+  if (Function *F = dyn_cast<Function>(GetWasmLPadContextF.getCallee()))
+    F->setDoesNotThrow();
+
   unsigned Index = 0;
   for (auto *BB : CatchPads) {
     auto *CPI = cast<CatchPadInst>(BB->getFirstNonPHIIt());
@@ -346,6 +339,15 @@ void WasmEHPrepareImpl::prepareEHPad(BasicBlock *BB, bool 
NeedPersonality,
   }
   IRB.SetInsertPoint(CatchCI->getNextNode());
 
+  Instruction *LPadContext =
+      IRB.CreateCall(GetWasmLPadContextF, {}, OperandBundleDef("funclet", 
FPI));
+
+  Value *LPadIndexField = LPadContext;
+  Value *LSDAField = IRB.CreateConstInBoundsGEP2_32(LPadContextTy, LPadContext,
+                                                    0, 1, "lsda_gep");
+  Value *SelectorField = IRB.CreateConstInBoundsGEP2_32(
+      LPadContextTy, LPadContext, 0, 2, "selector_gep");
+
   // This is to create a map of <landingpad EH label, landingpad index> in
   // SelectionDAGISel, which is to be used in EHStreamer to emit LSDA tables.
   // Pseudocode: wasm.landingpad.index(Index);
diff --git a/llvm/test/CodeGen/WebAssembly/eh-lsda.ll 
b/llvm/test/CodeGen/WebAssembly/eh-lsda.ll
index 517de9c307b493..917b3882a8e96a 100644
--- a/llvm/test/CodeGen/WebAssembly/eh-lsda.ll
+++ b/llvm/test/CodeGen/WebAssembly/eh-lsda.ll
@@ -66,18 +66,19 @@ try.cont:                                         ; preds = 
%entry, %catch.start
 
 ; CHECK-LABEL: test1:
 ; In static linking, we load GCC_except_table as a constant directly.
-; NOPIC:      i[[PTR]].const  $push[[CONTEXT:.*]]=, {{[48]}}
+; NOPIC:                call  $push[[CONTEXT:.*]]=, _Unwind_GetWasmLPadContext
+; NOPIC-NEXT:      local.tee  $push[[CONTEXT_LOCAL:.*]]=, $1=, $pop[[CONTEXT]]
 ; NOPIC-NEXT: i[[PTR]].const  $push[[EXCEPT_TABLE:.*]]=, GCC_except_table1
-; NOPIC-NEXT: i[[PTR]].store  __wasm_lpad_context($pop[[CONTEXT]]), 
$pop[[EXCEPT_TABLE]]
+; NOPIC-NEXT: i[[PTR]].store  {{[48]}}($pop[[CONTEXT_LOCAL]]), 
$pop[[EXCEPT_TABLE]]
 
 ; In case of PIC, we make GCC_except_table symbols a relative on based on
 ; __memory_base.
-; PIC:        global.get  $push[[CONTEXT:.*]]=, __wasm_lpad_context@GOT
-; PIC-NEXT:   local.tee  $push{{.*}}=, $[[CONTEXT_LOCAL:.*]]=, $pop[[CONTEXT]]
-; PIC:        global.get  $push[[MEMORY_BASE:.*]]=, __memory_base
+; PIC:        global.get  $[[MEMORY_BASE:.*]]=, __memory_base
+; PIC-NEXT:   call  $push[[CONTEXT:.*]]=, _Unwind_GetWasmLPadContext
+; PIC-NEXT:   local.tee  $push[[CONTEXT_LOCAL:.*]]=, $2=, $pop[[CONTEXT]]
 ; PIC-NEXT:   i[[PTR]].const  $push[[EXCEPT_TABLE_REL:.*]]=, 
GCC_except_table1@MBREL
-; PIC-NEXT:   i[[PTR]].add   $push[[EXCEPT_TABLE:.*]]=, $pop[[MEMORY_BASE]], 
$pop[[EXCEPT_TABLE_REL]]
-; PIC-NEXT:   i[[PTR]].store  {{[48]}}($[[CONTEXT_LOCAL]]), 
$pop[[EXCEPT_TABLE]]
+; PIC-NEXT:   i[[PTR]].add   $push[[EXCEPT_TABLE:.*]]=, $[[MEMORY_BASE]], 
$pop[[EXCEPT_TABLE_REL]]
+; PIC-NEXT:   i[[PTR]].store  {{[48]}}($pop[[CONTEXT_LOCAL]]), 
$pop[[EXCEPT_TABLE]]
 
 ; CHECK: .section  .rodata.gcc_except_table,"",@
 ; CHECK-NEXT:   .p2align  2
diff --git a/llvm/test/CodeGen/WebAssembly/exception-legacy.ll 
b/llvm/test/CodeGen/WebAssembly/exception-legacy.ll
index 0fa360116baedb..5795c4ccced418 100644
--- a/llvm/test/CodeGen/WebAssembly/exception-legacy.ll
+++ b/llvm/test/CodeGen/WebAssembly/exception-legacy.ll
@@ -34,7 +34,7 @@ define void @throw(ptr %p) {
 ; CHECK:       call      foo
 ; CHECK:     catch     $[[EXN:[0-9]+]]=, __cpp_exception
 ; CHECK:       global.set  __stack_pointer
-; CHECK:       i32.store __wasm_lpad_context
+; CHECK:       call       $push[[CONTEXT:.*]]=, _Unwind_GetWasmLPadContext
 ; CHECK:       call       $drop=, __gxx_wasm_personality_v0, $[[EXN]]
 ; CHECK:       block
 ; CHECK:         br_if     0
diff --git a/llvm/test/CodeGen/WebAssembly/exception.ll 
b/llvm/test/CodeGen/WebAssembly/exception.ll
index e2f44b4e8c2631..86df0f961eb9d6 100644
--- a/llvm/test/CodeGen/WebAssembly/exception.ll
+++ b/llvm/test/CodeGen/WebAssembly/exception.ll
@@ -46,7 +46,7 @@ define void @throw(ptr %p) {
 ; CHECK:   local.set  2
 ; CHECK:   local.get  0
 ; CHECK:   global.set  __stack_pointer
-; CHECK:   i32.store  __wasm_lpad_context
+; CHECK:   call  _Unwind_GetWasmLPadContext
 ; CHECK:   call  __gxx_wasm_personality_v0
 ; CHECK:   block
 ; CHECK:     br_if     0
diff --git a/llvm/test/CodeGen/WebAssembly/wasm-eh-prepare.ll 
b/llvm/test/CodeGen/WebAssembly/wasm-eh-prepare.ll
index 2eacaed8223f4e..5639f1aa047159 100644
--- a/llvm/test/CodeGen/WebAssembly/wasm-eh-prepare.ll
+++ b/llvm/test/CodeGen/WebAssembly/wasm-eh-prepare.ll
@@ -7,8 +7,6 @@
 target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
 target triple = "wasm32-unknown-unknown"
 
-; CHECK: @__wasm_lpad_context = external thread_local global { i32, ptr, i32 }
-
 @_ZTIi = external constant ptr
 %struct.Temp = type { i8 }
 
@@ -42,12 +40,15 @@ catch.start:                                      ; preds = 
%catch.dispatch
 ; CHECK: catch.start:
 ; CHECK-NEXT:   %[[CATCHPAD:.*]] = catchpad
 ; CHECK-NEXT:   %[[EXN:.*]] = call ptr @llvm.wasm.catch(i32 0)
+; CHECK-NEXT:   %[[CONTEXT:.*]] = call ptr @_Unwind_GetWasmLPadContext()
+; CHECK-NEXT:   %lsda_gep = getelementptr inbounds { i32, ptr, i32 }, ptr 
%[[CONTEXT]], i32 0, i32 1
+; CHECK-NEXT:   %selector_gep = getelementptr inbounds { i32, ptr, i32 }, ptr 
%[[CONTEXT]], i32 0, i32 2
 ; CHECK-NEXT:   call void @llvm.wasm.landingpad.index(token %[[CATCHPAD]], i32 
0)
-; CHECK-NEXT:   store i32 0, ptr @__wasm_lpad_context
+; CHECK-NEXT:   store i32 0, ptr %[[CONTEXT]]
 ; CHECK-NEXT:   %[[LSDA:.*]] = call ptr @llvm.wasm.lsda()
-; CHECK-NEXT:   store ptr %[[LSDA]], ptr getelementptr inbounds (i8, ptr 
@__wasm_lpad_context, i32 4)
+; CHECK-NEXT:   store ptr %[[LSDA]], ptr %lsda_gep
 ; CHECK-NEXT:   call i32 @__gxx_wasm_personality_v0(ptr %[[EXN]]) {{.*}} [ 
"funclet"(token %[[CATCHPAD]]) ]
-; CHECK-NEXT:   %[[SELECTOR:.*]] = load i32, ptr getelementptr inbounds (i8, 
ptr @__wasm_lpad_context, i32 8)
+; CHECK-NEXT:   %[[SELECTOR:.*]] = load i32, ptr %selector_gep
 ; CHECK:   icmp eq i32 %[[SELECTOR]]
 
 catch:                                            ; preds = %catch.start
@@ -101,7 +102,7 @@ catch.start:                                      ; preds = 
%catch.dispatch
 ; CHECK: catch.start:
 ; CHECK-NEXT:   catchpad within %0 [ptr null]
 ; CHECK-NOT:   call void @llvm.wasm.landingpad.index
-; CHECK-NOT:   store {{.*}} @__wasm_lpad_context
+; CHECK-NOT:   call ptr @_Unwind_GetWasmLPadContext()
 ; CHECK-NOT:   call ptr @llvm.wasm.lsda()
 ; CHECK-NOT:   call i32 @__gxx_wasm_personality_v0
 ; CHECK-NOT:   load {{.*}} @__wasm_lpad_context
@@ -278,3 +279,4 @@ attributes #1 = { noreturn }
 ; CHECK-DAG: declare void @llvm.wasm.landingpad.index(token, i32 immarg)
 ; CHECK-DAG: declare ptr @llvm.wasm.lsda()
 ; CHECK-DAG: declare i32 @__gxx_wasm_personality_v0(ptr)
+; CHECK-DAG: declare ptr @_Unwind_GetWasmLPadContext()

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

Reply via email to