[libcxx-commits] [libcxxabi] [libunwind] [llvm] [WebAssembly] Directly call EH personality function (PR #175202)
Hugo Melder via libcxx-commits
libcxx-commits at lists.llvm.org
Fri Jan 9 09:05:48 PST 2026
https://github.com/hmelder created https://github.com/llvm/llvm-project/pull/175202
Currently, the personality function is not called directly, but via the veneer function `_Unwind_CallPersonality`. This function resets the selector in the landing pad and calls `__gxx_personality_wasm0 `. The result is that other languages with EH, such as Objective-C, cannot provide their own personality function.
This PR removes `_Unwind_CallPersonality` and implements its functionality in codegen (`WasmEHPrepareImpl::prepareEHPad`) instead.
It turns out that there is a mismatch between the personality function name in LLVM and in libcxxabi/libunwind. In `CGException` and all other unit tests, the Wasm CXX personality function is called `__gxx_wasm_personality_v0 `. However it is defined as `__gxx_personality_wasm0 ` in `libcxxabi/src/cxa_personality.cpp`. This was never caught because the personality function was never called directly in codegen.
I have also fixed up the type definition of `_Unwind_LandingPadContext` in `WasmEHPrepare` for WASM64.
>From b37d5288c3c5f547b2043cf2a6ff0ccaa2a6f129 Mon Sep 17 00:00:00 2001
From: hmelder <service at hugomelder.com>
Date: Fri, 9 Jan 2026 09:09:07 +0000
Subject: [PATCH 1/5] [WebAssembly] Call EH personality fn directly
Move away from using _Unwind_CallPersonality and instead call the EH
personality function directly.
_Unwind_CallPersonality clears the selector field in the landing pad and
then calls the hard-coded personality function
'__gxx_personality_wasm0'. This is now done in codegen instead to allow
the use of other personality functions
---
llvm/include/llvm/IR/RuntimeLibcalls.td | 1 -
llvm/lib/CodeGen/WasmEHPrepare.cpp | 97 ++++++++++++++-----------
2 files changed, 54 insertions(+), 44 deletions(-)
diff --git a/llvm/include/llvm/IR/RuntimeLibcalls.td b/llvm/include/llvm/IR/RuntimeLibcalls.td
index b9e68247de94d..8e032f20905f0 100644
--- a/llvm/include/llvm/IR/RuntimeLibcalls.td
+++ b/llvm/include/llvm/IR/RuntimeLibcalls.td
@@ -3500,7 +3500,6 @@ def WasmSystemLibrary
(add DefaultRuntimeLibcallImpls, Int128RTLibcalls,
CompilerRTOnlyInt64Libcalls, CompilerRTOnlyInt128Libcalls,
exp10f, exp10,
- _Unwind_CallPersonality,
emscripten_return_address,
LibcallImpls<(add __small_printf,
__small_sprintf,
diff --git a/llvm/lib/CodeGen/WasmEHPrepare.cpp b/llvm/lib/CodeGen/WasmEHPrepare.cpp
index 2f54578da5113..ab75372a50b06 100644
--- a/llvm/lib/CodeGen/WasmEHPrepare.cpp
+++ b/llvm/lib/CodeGen/WasmEHPrepare.cpp
@@ -28,7 +28,9 @@
// wasm.landingpad.index(index);
// __wasm_lpad_context.lpad_index = index;
// __wasm_lpad_context.lsda = wasm.lsda();
-// _Unwind_CallPersonality(exn);
+// __wasm_lpad_context.selector = 0;
+// personality_fn(1, _UA_SEARCH_PHASE, exn->exception_class, exn,
+// (struct _Unwind_Context *)&__wasm_lpad_context);
// selector = __wasm_lpad_context.selector;
// ...
//
@@ -38,18 +40,13 @@
// exception is thrown. After the stack is unwound, the control flow is
// transfered to WebAssembly 'catch' instruction.
//
-// Unwinding the stack is not done by libunwind but the VM, so the personality
-// function in libcxxabi cannot be called from libunwind during the unwinding
-// process. So after a catch instruction, we insert a call to a wrapper function
-// in libunwind that in turn calls the real personality function.
-//
// In Itanium EH, if the personality function decides there is no matching catch
// clause in a call frame and no cleanup action to perform, the unwinder doesn't
// stop there and continues unwinding. But in Wasm EH, the unwinder stops at
// every call frame with a catch intruction, after which the personality
// function is called from the compiler-generated user code here.
//
-// In libunwind, we have this struct that serves as a communincation channel
+// In libunwind, we have this struct that serves as a communication channel
// between the compiler-generated user code and the personality function in
// libcxxabi.
//
@@ -60,20 +57,8 @@
// };
// struct _Unwind_LandingPadContext __wasm_lpad_context = ...;
//
-// And this wrapper in libunwind calls the personality function.
-//
-// _Unwind_Reason_Code _Unwind_CallPersonality(void *exception_ptr) {
-// struct _Unwind_Exception *exception_obj =
-// (struct _Unwind_Exception *)exception_ptr;
-// _Unwind_Reason_Code ret = __gxx_personality_v0(
-// 1, _UA_CLEANUP_PHASE, exception_obj->exception_class, exception_obj,
-// (struct _Unwind_Context *)__wasm_lpad_context);
-// return ret;
-// }
-//
// We pass a landing pad index, and the address of LSDA for the current function
-// to the wrapper function _Unwind_CallPersonality in libunwind, and we retrieve
-// the selector after it returns.
+// to the personality function, and we retrieve the selector after it returns.
//
//===----------------------------------------------------------------------===//
@@ -82,6 +67,7 @@
#include "llvm/CodeGen/Passes.h"
#include "llvm/CodeGen/WasmEHFuncInfo.h"
#include "llvm/IR/EHPersonalities.h"
+#include "llvm/IR/GlobalValue.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/IntrinsicsWebAssembly.h"
#include "llvm/IR/Module.h"
@@ -98,6 +84,7 @@ class WasmEHPrepareImpl {
friend class WasmEHPrepare;
Type *LPadContextTy = nullptr; // type of 'struct _Unwind_LandingPadContext'
+ Type *UnwindExceptionTy = nullptr; // type of 'struct _Unwind_Exception'
GlobalVariable *LPadContextGV = nullptr; // __wasm_lpad_context
// Field addresses of struct _Unwind_LandingPadContext
@@ -111,8 +98,7 @@ class WasmEHPrepareImpl {
Function *GetExnF = nullptr; // wasm.get.exception() intrinsic
Function *CatchF = nullptr; // wasm.catch() intrinsic
Function *GetSelectorF = nullptr; // wasm.get.ehselector() intrinsic
- FunctionCallee CallPersonalityF =
- nullptr; // _Unwind_CallPersonality() wrapper
+ FunctionCallee PersonalityF = nullptr;
bool prepareThrows(Function &F);
bool prepareEHPads(Function &F);
@@ -120,7 +106,8 @@ class WasmEHPrepareImpl {
public:
WasmEHPrepareImpl() = default;
- WasmEHPrepareImpl(Type *LPadContextTy_) : LPadContextTy(LPadContextTy_) {}
+ WasmEHPrepareImpl(Type *LPadContextTy_, Type *UnwindExceptionTy_)
+ : LPadContextTy(LPadContextTy_), UnwindExceptionTy(UnwindExceptionTy_) {}
bool runOnFunction(Function &F);
};
@@ -145,10 +132,14 @@ PreservedAnalyses WasmEHPreparePass::run(Function &F,
FunctionAnalysisManager &) {
auto &Context = F.getContext();
auto *I32Ty = Type::getInt32Ty(Context);
+ auto *I64Ty = Type::getInt64Ty(Context);
auto *PtrTy = PointerType::get(Context, 0);
auto *LPadContextTy =
StructType::get(I32Ty /*lpad_index*/, PtrTy /*lsda*/, I32Ty /*selector*/);
- WasmEHPrepareImpl P(LPadContextTy);
+ auto *UnwindExceptionTy =
+ StructType::get(I64Ty /* exception_class*/, PtrTy /* exception_cleanup */,
+ I32Ty /* private_1 */, I32Ty /* private_2 */);
+ WasmEHPrepareImpl P(LPadContextTy, UnwindExceptionTy);
bool Changed = P.runOnFunction(F);
return Changed ? PreservedAnalyses::none() : PreservedAnalyses ::all();
}
@@ -163,9 +154,15 @@ FunctionPass *llvm::createWasmEHPass() { return new WasmEHPrepare(); }
bool WasmEHPrepare::doInitialization(Module &M) {
IRBuilder<> IRB(M.getContext());
- P.LPadContextTy = StructType::get(IRB.getInt32Ty(), // lpad_index
- IRB.getPtrTy(), // lsda
- IRB.getInt32Ty() // selector
+ auto *I32Ty = IRB.getInt32Ty();
+ auto *I64Ty = IRB.getInt64Ty();
+ auto *PtrTy = IRB.getPtrTy();
+ P.LPadContextTy = StructType::get(I32Ty, // lpad_index
+ PtrTy, // lsda
+ I32Ty // selector
+ );
+ P.UnwindExceptionTy = StructType::get(I64Ty, /* exception_class*/
+ PtrTy /* exception_cleanup */
);
return false;
}
@@ -235,11 +232,15 @@ bool WasmEHPrepareImpl::prepareEHPads(Function &F) {
if (CatchPads.empty() && CleanupPads.empty())
return false;
- if (!F.hasPersonalityFn() ||
- !isScopedEHPersonality(classifyEHPersonality(F.getPersonalityFn()))) {
+ if (!F.hasPersonalityFn())
+ return false;
+
+ auto Personality = classifyEHPersonality(F.getPersonalityFn());
+
+ if (!isScopedEHPersonality(Personality)) {
report_fatal_error("Function '" + F.getName() +
"' does not have a correct Wasm personality function "
- "'__gxx_wasm_personality_v0'");
+ "'__gxx_personality_wasm0'");
}
assert(F.hasPersonalityFn() && "Personality function not found");
@@ -274,16 +275,13 @@ bool WasmEHPrepareImpl::prepareEHPads(Function &F) {
// instruction selection.
CatchF = Intrinsic::getOrInsertDeclaration(&M, Intrinsic::wasm_catch);
- // FIXME: Verify this is really supported for current module.
- StringRef UnwindCallPersonalityName =
- RTLIB::RuntimeLibcallsInfo::getLibcallImplName(
- RTLIB::impl__Unwind_CallPersonality);
-
- // _Unwind_CallPersonality() wrapper function, which calls the personality
- CallPersonalityF = M.getOrInsertFunction(UnwindCallPersonalityName,
- IRB.getInt32Ty(), IRB.getPtrTy());
- if (Function *F = dyn_cast<Function>(CallPersonalityF.getCallee()))
- F->setDoesNotThrow();
+ auto *I32Ty = IRB.getInt32Ty();
+ auto *I64Ty = IRB.getInt64Ty();
+ auto *PtrTy = IRB.getPtrTy();
+ auto *PersPrototype =
+ FunctionType::get(I32Ty, {I32Ty, I32Ty, I64Ty, PtrTy, PtrTy}, false);
+ PersonalityF =
+ M.getOrInsertFunction(getEHPersonalityName(Personality), PersPrototype);
unsigned Index = 0;
for (auto *BB : CatchPads) {
@@ -367,9 +365,22 @@ void WasmEHPrepareImpl::prepareEHPad(BasicBlock *BB, bool NeedPersonality,
// Pseudocode: __wasm_lpad_context.lsda = wasm.lsda();
IRB.CreateStore(IRB.CreateCall(LSDAF), LSDAField);
- // Pseudocode: _Unwind_CallPersonality(exn);
- CallInst *PersCI = IRB.CreateCall(CallPersonalityF, CatchCI,
- OperandBundleDef("funclet", CPI));
+ // Pseudocode: __wasm_lpad_context.selector = 0;
+ IRB.CreateStore(IRB.getInt32(0), SelectorField);
+
+ // Pseudocode: &exn->exception_class
+ auto *ExceptionClassPtr = IRB.CreateConstInBoundsGEP2_32(
+ UnwindExceptionTy, CatchCI, 0, 0, "exception_class_gep");
+ auto *ExceptionClass = IRB.CreateLoad(IRB.getInt64Ty(), ExceptionClassPtr);
+ auto *UASearchPhase = IRB.getInt32(1);
+
+ // Pseudocode:
+ // personality_fn(1, _UA_SEARCH_PHASE, exn->exception_class, exn,
+ // (struct _Unwind_Context *)&__wasm_lpad_context);
+ CallInst *PersCI = IRB.CreateCall(
+ PersonalityF,
+ {IRB.getInt32(1), UASearchPhase, ExceptionClass, CatchCI, LPadContextGV},
+ OperandBundleDef("funclet", CPI));
PersCI->setDoesNotThrow();
// Pseudocode: int selector = __wasm_lpad_context.selector;
>From e4a209636ddee64e684b6805b3e435dad129ef58 Mon Sep 17 00:00:00 2001
From: hmelder <service at hugomelder.com>
Date: Fri, 9 Jan 2026 09:17:31 +0000
Subject: [PATCH 2/5] [WebAssembly] Remove _Unwind_CallPersonality
This is now unused and implemented directly in codegen.
---
libunwind/src/Unwind-wasm.c | 23 -----------------------
llvm/include/llvm/IR/RuntimeLibcalls.td | 3 ---
2 files changed, 26 deletions(-)
diff --git a/libunwind/src/Unwind-wasm.c b/libunwind/src/Unwind-wasm.c
index b8b7bc2779f17..4ffd74f34c238 100644
--- a/libunwind/src/Unwind-wasm.c
+++ b/libunwind/src/Unwind-wasm.c
@@ -37,29 +37,6 @@ struct _Unwind_LandingPadContext {
// function
thread_local struct _Unwind_LandingPadContext __wasm_lpad_context;
-/// Calls to this function is in landing pads in compiler-generated user code.
-/// In other EH schemes, stack unwinding is done by libunwind library, which
-/// calls the personality function for each each frame it lands. On the other
-/// hand, WebAssembly stack unwinding process is performed by a VM, and the
-/// personality function cannot be called from there. So the compiler inserts
-/// a call to this function in landing pads in the user code, which in turn
-/// calls the personality function.
-_Unwind_Reason_Code _Unwind_CallPersonality(void *exception_ptr) {
- struct _Unwind_Exception *exception_object =
- (struct _Unwind_Exception *)exception_ptr;
- _LIBUNWIND_TRACE_API("_Unwind_CallPersonality(exception_object=%p)",
- (void *)exception_object);
-
- // Reset the selector.
- __wasm_lpad_context.selector = 0;
-
- // Call personality function. Wasm does not have two-phase unwinding, so we
- // only do the cleanup phase.
- return __gxx_personality_wasm0(
- 1, _UA_SEARCH_PHASE, exception_object->exception_class, exception_object,
- (struct _Unwind_Context *)&__wasm_lpad_context);
-}
-
/// Called by __cxa_throw.
_LIBUNWIND_EXPORT _Unwind_Reason_Code
_Unwind_RaiseException(_Unwind_Exception *exception_object) {
diff --git a/llvm/include/llvm/IR/RuntimeLibcalls.td b/llvm/include/llvm/IR/RuntimeLibcalls.td
index 8e032f20905f0..b6f5b8497b93a 100644
--- a/llvm/include/llvm/IR/RuntimeLibcalls.td
+++ b/llvm/include/llvm/IR/RuntimeLibcalls.td
@@ -1833,9 +1833,6 @@ defset list<RuntimeLibcallImpl> SjLjExceptionHandlingLibcalls = {
def _Unwind_SjLj_Unregister : RuntimeLibcallImpl<UNWIND_UNREGISTER>;
}
-// Only used on wasm?
-def _Unwind_CallPersonality : RuntimeLibcallImpl<UNWIND_CALL_PERSONALITY>;
-
// Used on OpenBSD
def __stack_smash_handler : RuntimeLibcallImpl<STACK_SMASH_HANDLER>;
>From 560598b59f095fb7ebdd5b6816264cbe8f6e81f6 Mon Sep 17 00:00:00 2001
From: hmelder <service at hugomelder.com>
Date: Fri, 9 Jan 2026 16:17:11 +0000
Subject: [PATCH 3/5] [WebAssembly] Fix WebAssembly EH unit tests
Fix WebAssembly EH unit tests after moving away from _Unwind_CallPersonality.
---
.../CodeGen/WebAssembly/cfg-stackify-eh-legacy.ll | 12 ++++++------
llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll | 10 +++++-----
llvm/test/CodeGen/WebAssembly/eh-lsda.ll | 5 ++++-
llvm/test/CodeGen/WebAssembly/exception-legacy.ll | 6 +++++-
llvm/test/CodeGen/WebAssembly/exception.ll | 11 ++++++++++-
.../WebAssembly/wasm-eh-invalid-personality.ll | 2 +-
llvm/test/CodeGen/WebAssembly/wasm-eh-prepare.ll | 9 ++++++---
7 files changed, 37 insertions(+), 18 deletions(-)
diff --git a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.ll b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.ll
index f0a1d9805c806..b1156f07b1274 100644
--- a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.ll
+++ b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.ll
@@ -100,23 +100,23 @@ try.cont: ; preds = %catch, %catch2, %en
; CHECK: catch
; CHECK: block
; CHECK: block
-; CHECK: br_if 0, {{.*}} # 0: down to label[[L0:[0-9+]]]
+; CHECK: br_if 0, {{.*}} # 0: down to label[[L0:[0-9]+]]
; CHECK: call $drop=, __cxa_begin_catch, $0
; CHECK: try
; CHECK: try
; CHECK: call foo
-; CHECK: br 3 # 3: down to label[[L1:[0-9+]]]
+; CHECK: br 3 # 3: down to label[[L1:[0-9]+]]
; CHECK: catch
; CHECK: block
; CHECK: block
-; CHECK: br_if 0, {{.*}} # 0: down to label[[L2:[0-9+]]]
+; CHECK: br_if 0, {{.*}} # 0: down to label[[L2:[0-9]+]]
; CHECK: call $drop=, __cxa_begin_catch
; CHECK: try
; CHECK: call foo
-; CHECK: br 2 # 2: down to label[[L3:[0-9+]]]
+; CHECK: br 2 # 2: down to label[[L3:[0-9]+]]
; CHECK: catch_all
; CHECK: call __cxa_end_catch
-; CHECK: rethrow 0 # down to catch[[L4:[0-9+]]]
+; CHECK: rethrow 0 # down to catch[[L4:[0-9]+]]
; CHECK: end_try
; CHECK: end_block # label[[L2]]:
; CHECK: rethrow 1 # down to catch[[L4]]
@@ -1736,7 +1736,7 @@ unreachable: ; preds = %rethrow, %entry
}
; Check if the unwind destination mismatch stats are correct
-; NOSORT: 24 wasm-cfg-stackify - Number of call unwind mismatches found
+; NOSORT: 32 wasm-cfg-stackify - Number of call unwind mismatches found
; NOSORT: 5 wasm-cfg-stackify - Number of catch unwind mismatches found
declare void @foo()
diff --git a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll
index 98de9a267b95a..c89f88c67ac46 100644
--- a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll
+++ b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll
@@ -35,7 +35,7 @@ target triple = "wasm32-unknown-unknown"
; CHECK: local.set 2
; CHECK: local.set 1
; CHECK: local.get 0
-; CHECK: call _Unwind_CallPersonality
+; CHECK: call __gxx_wasm_personality_v0
; CHECK: block
; CHECK: br_if 0 # 0: down to label[[L2:[0-9]+]]
; CHECK: call __cxa_begin_catch
@@ -111,7 +111,7 @@ try.cont: ; preds = %catch, %catch2, %en
; CHECK: br 2 # 2: down to label[[L1:[0-9]+]]
; CHECK: end_try_table
; CHECK: end_block # label[[L0]]:
-; CHECK: call _Unwind_CallPersonality
+; CHECK: call __gxx_wasm_personality_v0
; CHECK: block
; CHECK: block
; CHECK: br_if 0 # 0: down to label[[L2:[0-9]+]]
@@ -124,7 +124,7 @@ try.cont: ; preds = %catch, %catch2, %en
; CHECK: br 5 # 5: down to label[[L5:[0-9]+]]
; CHECK: end_try_table
; CHECK: end_block # label[[L4]]:
-; CHECK: call _Unwind_CallPersonality
+; CHECK: call __gxx_wasm_personality_v0
; CHECK: block
; CHECK: block
; CHECK: br_if 0 # 0: down to label[[L6:[0-9]+]]
@@ -750,7 +750,7 @@ try.cont: ; preds = %catch.start0
; NOSORT: end_block # label[[L0]]:
; NOSORT: call __cxa_begin_catch
; NOSORT: call __cxa_end_catch
-; NOSORT: end_block # label74:
+; NOSORT: end_block
; NOSORT: return
; NOSORT: end_block # label[[L1]]:
; NOSORT: throw_ref
@@ -1507,7 +1507,7 @@ unreachable: ; preds = %rethrow, %entry
}
; Check if the unwind destination mismatch stats are correct
-; NOSORT: 23 wasm-cfg-stackify - Number of call unwind mismatches found
+; NOSORT: 30 wasm-cfg-stackify - Number of call unwind mismatches found
; NOSORT: 4 wasm-cfg-stackify - Number of catch unwind mismatches found
declare void @foo()
diff --git a/llvm/test/CodeGen/WebAssembly/eh-lsda.ll b/llvm/test/CodeGen/WebAssembly/eh-lsda.ll
index d5b28b279d419..f069f77147e57 100644
--- a/llvm/test/CodeGen/WebAssembly/eh-lsda.ll
+++ b/llvm/test/CodeGen/WebAssembly/eh-lsda.ll
@@ -66,7 +66,10 @@ 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: i[[PTR]].const $push[[SELECTOR_OFF:.*]]=, {{(8|16)}}
+; NOPIC-NEXT: i[[PTR]].const $push[[SELECTOR:.*]]=, 0
+; NOPIC-NEXT: i[[PTR]].store __wasm_lpad_context($pop[[SELECTOR_OFF]]), $pop[[SELECTOR]]
+; NOPIC-NEXT: i[[PTR]].const $push[[CONTEXT:.*]]=, {{[48]}}
; NOPIC-NEXT: i[[PTR]].const $push[[EXCEPT_TABLE:.*]]=, GCC_except_table1
; NOPIC-NEXT: i[[PTR]].store __wasm_lpad_context($pop[[CONTEXT]]), $pop[[EXCEPT_TABLE]]
diff --git a/llvm/test/CodeGen/WebAssembly/exception-legacy.ll b/llvm/test/CodeGen/WebAssembly/exception-legacy.ll
index b96a664166f42..e638af4ad3bb2 100644
--- a/llvm/test/CodeGen/WebAssembly/exception-legacy.ll
+++ b/llvm/test/CodeGen/WebAssembly/exception-legacy.ll
@@ -35,7 +35,11 @@ define void @throw(ptr %p) {
; CHECK: catch $[[EXN:[0-9]+]]=, __cpp_exception
; CHECK: global.set __stack_pointer
; CHECK: i32.store __wasm_lpad_context
-; CHECK: call $drop=, _Unwind_CallPersonality, $[[EXN]]
+; CHECK: i32.const $push[[ACTION:[0-9]+]]=, 1
+; CHECK: i32.const $push[[PHASE:[0-9]+]]=, 1
+; CHECK: i64.load $push[[EH_CLASS:[0-9]+]]=, 0($[[EXN]])
+; CHECK: i32.const $push[[LPAD_CTX:[0-9]+]]=, __wasm_lpad_context
+; CHECK: call $drop=, __gxx_wasm_personality_v0, $pop[[ACTION]], $pop[[PHASE]], $pop[[EH_CLASS]], $0, $pop[[LPAD_CTX]]
; CHECK: block
; CHECK: br_if 0
; CHECK: call $drop=, __cxa_begin_catch
diff --git a/llvm/test/CodeGen/WebAssembly/exception.ll b/llvm/test/CodeGen/WebAssembly/exception.ll
index 873386b3dcc6d..f48ce2fc9d537 100644
--- a/llvm/test/CodeGen/WebAssembly/exception.ll
+++ b/llvm/test/CodeGen/WebAssembly/exception.ll
@@ -47,7 +47,16 @@ define void @throw(ptr %p) {
; CHECK: local.get 0
; CHECK: global.set __stack_pointer
; CHECK: i32.store __wasm_lpad_context
-; CHECK: call _Unwind_CallPersonality
+
+; CHECK: i32.const 1
+; CHECK: i32.const 1
+; CHECK: local.get 1
+; CHECK: i64.load 0
+; CHECK: local.get 1
+; CHECK: i32.const __wasm_lpad_context
+; CHECK: call __gxx_wasm_personality_v0
+; CHECK: drop
+
; CHECK: block
; CHECK: br_if 0
; CHECK: call __cxa_begin_catch
diff --git a/llvm/test/CodeGen/WebAssembly/wasm-eh-invalid-personality.ll b/llvm/test/CodeGen/WebAssembly/wasm-eh-invalid-personality.ll
index 1a4e888a26ef5..36340c1444525 100644
--- a/llvm/test/CodeGen/WebAssembly/wasm-eh-invalid-personality.ll
+++ b/llvm/test/CodeGen/WebAssembly/wasm-eh-invalid-personality.ll
@@ -6,7 +6,7 @@ target triple = "wasm32-unknown-unknown"
; not have a correct Wasm personality function.
define void @test() personality ptr @invalid_personality {
-; CHECK: LLVM ERROR: Function 'test' does not have a correct Wasm personality function '__gxx_wasm_personality_v0'
+; CHECK: LLVM ERROR: Function 'test' does not have a supported Wasm personality function
entry:
invoke void @foo()
to label %try.cont unwind label %catch.dispatch
diff --git a/llvm/test/CodeGen/WebAssembly/wasm-eh-prepare.ll b/llvm/test/CodeGen/WebAssembly/wasm-eh-prepare.ll
index 164c138cb7578..a88a6f01351e7 100644
--- a/llvm/test/CodeGen/WebAssembly/wasm-eh-prepare.ll
+++ b/llvm/test/CodeGen/WebAssembly/wasm-eh-prepare.ll
@@ -46,7 +46,10 @@ catch.start: ; preds = %catch.dispatch
; CHECK-NEXT: store i32 0, ptr @__wasm_lpad_context
; CHECK-NEXT: %[[LSDA:.*]] = call ptr @llvm.wasm.lsda()
; CHECK-NEXT: store ptr %[[LSDA]], ptr getelementptr inbounds ({ i32, ptr, i32 }, ptr @__wasm_lpad_context, i32 0, i32 1)
-; CHECK-NEXT: call i32 @_Unwind_CallPersonality(ptr %[[EXN]]) {{.*}} [ "funclet"(token %[[CATCHPAD]]) ]
+; CHECK-NEXT: store i32 0, ptr getelementptr inbounds ({ i32, ptr, i32 }, ptr @__wasm_lpad_context, i32 0, i32 2), align 4
+; CHECK-NEXT: %exception_class_gep = getelementptr inbounds { i64, ptr }, ptr %[[EXN]], i32 0, i32 0
+; CHECK-NEXT: %[[CLASS:.*]] = load i64, ptr %exception_class_gep, align 8
+; CHECK-NEXT: %{{.*}} = call i32 @__gxx_wasm_personality_v0(i32 1, i32 1, i64 %[[CLASS]], ptr %[[EXN]], ptr @__wasm_lpad_context) #{{.*}} [ "funclet"(token %[[CATCHPAD]]) ]
; CHECK-NEXT: %[[SELECTOR:.*]] = load i32, ptr getelementptr inbounds ({ i32, ptr, i32 }, ptr @__wasm_lpad_context, i32 0, i32 2)
; CHECK: icmp eq i32 %[[SELECTOR]]
@@ -103,7 +106,7 @@ catch.start: ; preds = %catch.dispatch
; CHECK-NOT: call void @llvm.wasm.landingpad.index
; CHECK-NOT: store {{.*}} @__wasm_lpad_context
; CHECK-NOT: call ptr @llvm.wasm.lsda()
-; CHECK-NOT: call i32 @_Unwind_CallPersonality
+; CHECK-NOT: call i32 @__gxx_wasm_personality_v0
; CHECK-NOT: load {{.*}} @__wasm_lpad_context
try.cont: ; preds = %entry, %catch.start
@@ -277,4 +280,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 @_Unwind_CallPersonality(ptr)
+; CHECK-DAG: declare i32 @__gxx_wasm_personality_v0(...)
>From 9cab568516ad238897974f1853e0213091e64569 Mon Sep 17 00:00:00 2001
From: hmelder <service at hugomelder.com>
Date: Fri, 9 Jan 2026 16:18:45 +0000
Subject: [PATCH 4/5] [WebAssembly] Use uintptr_t in WasmEHPrepare
WasmEHPrepare is used by both wasm32 and wasm64 targets. The
fields in _Unwind_LandingPadContext are uintptr_t.
Fix the fields, constants and offsets accordingly.
---
llvm/lib/CodeGen/WasmEHPrepare.cpp | 52 ++++++++++++++++++------------
1 file changed, 31 insertions(+), 21 deletions(-)
diff --git a/llvm/lib/CodeGen/WasmEHPrepare.cpp b/llvm/lib/CodeGen/WasmEHPrepare.cpp
index ab75372a50b06..cb97da33edef9 100644
--- a/llvm/lib/CodeGen/WasmEHPrepare.cpp
+++ b/llvm/lib/CodeGen/WasmEHPrepare.cpp
@@ -66,10 +66,12 @@
#include "llvm/CodeGen/MachineBasicBlock.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/CodeGen/WasmEHFuncInfo.h"
+#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/EHPersonalities.h"
#include "llvm/IR/GlobalValue.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/IntrinsicsWebAssembly.h"
+#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/RuntimeLibcalls.h"
#include "llvm/InitializePasses.h"
@@ -100,6 +102,11 @@ class WasmEHPrepareImpl {
Function *GetSelectorF = nullptr; // wasm.get.ehselector() intrinsic
FunctionCallee PersonalityF = nullptr;
+ IntegerType *Int32Ty = nullptr;
+ IntegerType *Int64Ty = nullptr;
+ PointerType *PtrTy = nullptr;
+ IntegerType *IntPtrTy = nullptr;
+
bool prepareThrows(Function &F);
bool prepareEHPads(Function &F);
void prepareEHPad(BasicBlock *BB, bool NeedPersonality, unsigned Index = 0);
@@ -131,14 +138,14 @@ class WasmEHPrepare : public FunctionPass {
PreservedAnalyses WasmEHPreparePass::run(Function &F,
FunctionAnalysisManager &) {
auto &Context = F.getContext();
- auto *I32Ty = Type::getInt32Ty(Context);
+ const DataLayout &DL = F.getParent()->getDataLayout();
auto *I64Ty = Type::getInt64Ty(Context);
auto *PtrTy = PointerType::get(Context, 0);
- auto *LPadContextTy =
- StructType::get(I32Ty /*lpad_index*/, PtrTy /*lsda*/, I32Ty /*selector*/);
- auto *UnwindExceptionTy =
- StructType::get(I64Ty /* exception_class*/, PtrTy /* exception_cleanup */,
- I32Ty /* private_1 */, I32Ty /* private_2 */);
+ auto *IntPtrTy = DL.getIntPtrType(Context, /*AddressSpace=*/0);
+ auto *LPadContextTy = StructType::get(IntPtrTy /*lpad_index*/, PtrTy /*lsda*/,
+ IntPtrTy /*selector*/);
+ auto *UnwindExceptionTy = StructType::get(I64Ty /* exception_class*/,
+ PtrTy /* exception_cleanup */);
WasmEHPrepareImpl P(LPadContextTy, UnwindExceptionTy);
bool Changed = P.runOnFunction(F);
return Changed ? PreservedAnalyses::none() : PreservedAnalyses ::all();
@@ -154,16 +161,16 @@ FunctionPass *llvm::createWasmEHPass() { return new WasmEHPrepare(); }
bool WasmEHPrepare::doInitialization(Module &M) {
IRBuilder<> IRB(M.getContext());
- auto *I32Ty = IRB.getInt32Ty();
+ const DataLayout &DL = M.getDataLayout();
auto *I64Ty = IRB.getInt64Ty();
auto *PtrTy = IRB.getPtrTy();
- P.LPadContextTy = StructType::get(I32Ty, // lpad_index
- PtrTy, // lsda
- I32Ty // selector
- );
- P.UnwindExceptionTy = StructType::get(I64Ty, /* exception_class*/
- PtrTy /* exception_cleanup */
+ auto *IntPtrTy = DL.getIntPtrType(M.getContext(), /*AddressSpace=*/0);
+ P.LPadContextTy = StructType::get(IntPtrTy, // lpad_index
+ PtrTy, // lsda
+ IntPtrTy // selector
);
+ P.UnwindExceptionTy = StructType::get(I64Ty /* exception_class*/,
+ PtrTy /* exception_cleanup */);
return false;
}
@@ -239,8 +246,7 @@ bool WasmEHPrepareImpl::prepareEHPads(Function &F) {
if (!isScopedEHPersonality(Personality)) {
report_fatal_error("Function '" + F.getName() +
- "' does not have a correct Wasm personality function "
- "'__gxx_personality_wasm0'");
+ "' does not have a supported Wasm personality function");
}
assert(F.hasPersonalityFn() && "Personality function not found");
@@ -275,11 +281,15 @@ bool WasmEHPrepareImpl::prepareEHPads(Function &F) {
// instruction selection.
CatchF = Intrinsic::getOrInsertDeclaration(&M, Intrinsic::wasm_catch);
- auto *I32Ty = IRB.getInt32Ty();
- auto *I64Ty = IRB.getInt64Ty();
- auto *PtrTy = IRB.getPtrTy();
- auto *PersPrototype =
- FunctionType::get(I32Ty, {I32Ty, I32Ty, I64Ty, PtrTy, PtrTy}, false);
+ auto DL = F.getParent()->getDataLayout();
+
+ Int32Ty = IRB.getInt32Ty();
+ Int64Ty = IRB.getInt64Ty();
+ PtrTy = IRB.getPtrTy();
+ IntPtrTy = DL.getIntPtrType(M.getContext(), /*AddressSpace=*/0);
+
+ auto *PersPrototype = FunctionType::get(
+ Int32Ty, {Int32Ty, Int32Ty, Int64Ty, PtrTy, PtrTy}, false);
PersonalityF =
M.getOrInsertFunction(getEHPersonalityName(Personality), PersPrototype);
@@ -366,7 +376,7 @@ void WasmEHPrepareImpl::prepareEHPad(BasicBlock *BB, bool NeedPersonality,
IRB.CreateStore(IRB.CreateCall(LSDAF), LSDAField);
// Pseudocode: __wasm_lpad_context.selector = 0;
- IRB.CreateStore(IRB.getInt32(0), SelectorField);
+ IRB.CreateStore(ConstantInt::get(IntPtrTy, 0), SelectorField);
// Pseudocode: &exn->exception_class
auto *ExceptionClassPtr = IRB.CreateConstInBoundsGEP2_32(
>From 47ca19431cbab3509c5b2454559be7dcae97a7ca Mon Sep 17 00:00:00 2001
From: hmelder <service at hugomelder.com>
Date: Fri, 9 Jan 2026 16:50:31 +0000
Subject: [PATCH 5/5] [libcxxabi] Rename Wasm EH Personality Function
There is a function name mismatch between the declarations in codegen
and libcxxabi. This was never caught because the personality function
was never directly called.
---
libcxxabi/src/cxa_personality.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxxabi/src/cxa_personality.cpp b/libcxxabi/src/cxa_personality.cpp
index 77b2eb53af0e4..258e4d1f49444 100644
--- a/libcxxabi/src/cxa_personality.cpp
+++ b/libcxxabi/src/cxa_personality.cpp
@@ -1021,7 +1021,7 @@ static inline void get_landing_pad(__cxa_catch_temp_type &dest,
}
#ifdef __WASM_EXCEPTIONS__
-_Unwind_Reason_Code __gxx_personality_wasm0
+_Unwind_Reason_Code __gxx_wasm_personality_v0
#elif defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__)
static _Unwind_Reason_Code __gxx_personality_imp
#else
More information about the libcxx-commits
mailing list