[llvm] [WebAssembly] Make llvm.wasm.throw invokable (PR #128104)
Heejin Ahn via llvm-commits
llvm-commits at lists.llvm.org
Mon Feb 24 23:57:26 PST 2025
https://github.com/aheejin updated https://github.com/llvm/llvm-project/pull/128104
>From ddc2c9ae59d75cea719d8a21da1e760b406c9443 Mon Sep 17 00:00:00 2001
From: Heejin Ahn <aheejin at gmail.com>
Date: Thu, 20 Feb 2025 22:22:39 +0000
Subject: [PATCH 1/6] [WebAssembly] Make llvm.wasm.throw invokable
`llvm.wasm.throw` intrinsic can throw but it was not invokable. Not sure
what the rationale was when it was first written that way, but I think
at least in Emscripten's C++ exception support with the Wasm port of
libunwind, `__builtin_wasm_throw`, which is lowered down to
`llvm.wasm.rethrow`, is used only within `_Unwind_RaiseException`, which
is a one-liner and thus does not need an `invoke`:
https://github.com/emscripten-core/emscripten/blob/720e97f76d6f19e0c6a2d6988988cfe23f0517fb/system/lib/libunwind/src/Unwind-wasm.c#L69
(`_Unwind_RaiseException` is called by `__cxa_throw`, which is generated
by the `throw` C++ keyword)
But this does not address other direct uses of the builtin in C++, whose
use I'm not sure about but is not prohibited. Also other language
frontends may need to use the builtin in different functions, which has
`try`-`catch`es or destructors.
This makes `llvm.wasm.throw` invokable in the backend. To do that, this
adds a custom lowering routine to `SelectionDAGBuilder::visitInvoke`,
like we did for `llvm.wasm.rethrow`.
This does not generate `invoke`s for `__builtin_wasm_throw` yet, which
will be done by a follow-up PR.
Addresses #124710.
---
.../SelectionDAG/SelectionDAGBuilder.cpp | 19 +++++++++++---
llvm/lib/CodeGen/WasmEHPrepare.cpp | 6 ++---
llvm/lib/IR/Verifier.cpp | 4 ++-
llvm/test/CodeGen/WebAssembly/exception.ll | 26 +++++++++++++++++++
4 files changed, 47 insertions(+), 8 deletions(-)
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index 1c58a7f05446c..2b333bd81a570 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -3360,10 +3360,23 @@ void SelectionDAGBuilder::visitInvoke(const InvokeInst &I) {
case Intrinsic::experimental_gc_statepoint:
LowerStatepoint(cast<GCStatepointInst>(I), EHPadBB);
break;
+ // wasm_throw, wasm_rethrow: This is usually done in visitTargetIntrinsic,
+ // but this intrinsic is special because it can be invoked, so we manually
+ // lower it to a DAG node here.
+ case Intrinsic::wasm_throw: {
+ SmallVector<SDValue, 8> Ops;
+ Ops.push_back(getControlRoot()); // inchain for the terminator node
+ const TargetLowering &TLI = DAG.getTargetLoweringInfo();
+ Ops.push_back(
+ DAG.getTargetConstant(Intrinsic::wasm_throw, getCurSDLoc(),
+ TLI.getPointerTy(DAG.getDataLayout())));
+ Ops.push_back(getValue(I.getArgOperand(0))); // tag
+ Ops.push_back(getValue(I.getArgOperand(1))); // thrown value
+ SDVTList VTs = DAG.getVTList(ArrayRef<EVT>({MVT::Other})); // outchain
+ DAG.setRoot(DAG.getNode(ISD::INTRINSIC_VOID, getCurSDLoc(), VTs, Ops));
+ break;
+ }
case Intrinsic::wasm_rethrow: {
- // This is usually done in visitTargetIntrinsic, but this intrinsic is
- // special because it can be invoked, so we manually lower it to a DAG
- // node here.
SmallVector<SDValue, 8> Ops;
Ops.push_back(getControlRoot()); // inchain for the terminator node
const TargetLowering &TLI = DAG.getTargetLoweringInfo();
diff --git a/llvm/lib/CodeGen/WasmEHPrepare.cpp b/llvm/lib/CodeGen/WasmEHPrepare.cpp
index d18196b2217f5..fc98f594660bb 100644
--- a/llvm/lib/CodeGen/WasmEHPrepare.cpp
+++ b/llvm/lib/CodeGen/WasmEHPrepare.cpp
@@ -201,10 +201,8 @@ bool WasmEHPrepareImpl::prepareThrows(Function &F) {
// delete all following instructions within the BB, and delete all the dead
// children of the BB as well.
for (User *U : ThrowF->users()) {
- // A call to @llvm.wasm.throw() is only generated from __cxa_throw()
- // builtin call within libcxxabi, and cannot be an InvokeInst.
- auto *ThrowI = cast<CallInst>(U);
- if (ThrowI->getFunction() != &F)
+ auto *ThrowI = dyn_cast<CallInst>(U);
+ if (!ThrowI || ThrowI->getFunction() != &F)
continue;
Changed = true;
auto *BB = ThrowI->getParent();
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 8432779c107de..0ef4438450ea2 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -5203,10 +5203,12 @@ void Verifier::visitInstruction(Instruction &I) {
F->getIntrinsicID() == Intrinsic::experimental_patchpoint ||
F->getIntrinsicID() == Intrinsic::fake_use ||
F->getIntrinsicID() == Intrinsic::experimental_gc_statepoint ||
+ F->getIntrinsicID() == Intrinsic::wasm_throw ||
F->getIntrinsicID() == Intrinsic::wasm_rethrow ||
IsAttachedCallOperand(F, CBI, i),
"Cannot invoke an intrinsic other than donothing, patchpoint, "
- "statepoint, coro_resume, coro_destroy or clang.arc.attachedcall",
+ "statepoint, coro_resume, coro_destroy, clang.arc.attachedcall or "
+ "wasm.(re)throw",
&I);
Check(F->getParent() == &M, "Referencing function in another module!", &I,
&M, F, F->getParent());
diff --git a/llvm/test/CodeGen/WebAssembly/exception.ll b/llvm/test/CodeGen/WebAssembly/exception.ll
index febab822a6a9e..57d1f37c0039f 100644
--- a/llvm/test/CodeGen/WebAssembly/exception.ll
+++ b/llvm/test/CodeGen/WebAssembly/exception.ll
@@ -566,6 +566,32 @@ unreachable: ; preds = %entry
unreachable
}
+; This tests whether llvm.wasm.throw intrinsic can invoked and iseled correctly.
+
+; CHECK-LABEL: invoke_throw:
+; CHECK: try_table (catch __cpp_exception 0)
+; CHECK: local.get 0
+; CHECK: throw __cpp_exception
+; CHECK: end_try_table
+define void @invoke_throw(ptr %p) personality ptr @__gxx_wasm_personality_v0 {
+entry:
+ invoke void @llvm.wasm.throw(i32 0, ptr %p)
+ to label %try.cont unwind label %catch.dispatch
+
+catch.dispatch: ; preds = %entry
+ %0 = catchswitch within none [label %catch.start] unwind to caller
+
+catch.start: ; preds = %catch.dispatch
+ %1 = catchpad within %0 [ptr null]
+ %2 = call ptr @llvm.wasm.get.exception(token %1)
+ %3 = call i32 @llvm.wasm.get.ehselector(token %1)
+ %4 = call ptr @__cxa_begin_catch(ptr %2) #4 [ "funclet"(token %1) ]
+ call void @__cxa_end_catch() [ "funclet"(token %1) ]
+ catchret from %1 to label %try.cont
+
+try.cont: ; preds = %catch, %entry
+ ret void
+}
declare void @foo()
declare void @bar(ptr)
>From 87456cc62a85ff78b9c44b0b7f8d8f61fdd47625 Mon Sep 17 00:00:00 2001
From: Heejin Ahn <aheejin at gmail.com>
Date: Fri, 21 Feb 2025 01:41:01 +0000
Subject: [PATCH 2/6] clang-format insists this :(
---
llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index 2b333bd81a570..3f422737a4375 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -3370,8 +3370,8 @@ void SelectionDAGBuilder::visitInvoke(const InvokeInst &I) {
Ops.push_back(
DAG.getTargetConstant(Intrinsic::wasm_throw, getCurSDLoc(),
TLI.getPointerTy(DAG.getDataLayout())));
- Ops.push_back(getValue(I.getArgOperand(0))); // tag
- Ops.push_back(getValue(I.getArgOperand(1))); // thrown value
+ Ops.push_back(getValue(I.getArgOperand(0))); // tag
+ Ops.push_back(getValue(I.getArgOperand(1))); // thrown value
SDVTList VTs = DAG.getVTList(ArrayRef<EVT>({MVT::Other})); // outchain
DAG.setRoot(DAG.getNode(ISD::INTRINSIC_VOID, getCurSDLoc(), VTs, Ops));
break;
>From aa5fafda86059dded2dc5573ec160bbe0a786ba5 Mon Sep 17 00:00:00 2001
From: Heejin Ahn <aheejin at gmail.com>
Date: Fri, 21 Feb 2025 01:45:51 +0000
Subject: [PATCH 3/6] Use a static array
---
.../lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index 3f422737a4375..c7cb6a70936d5 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -3364,14 +3364,14 @@ void SelectionDAGBuilder::visitInvoke(const InvokeInst &I) {
// but this intrinsic is special because it can be invoked, so we manually
// lower it to a DAG node here.
case Intrinsic::wasm_throw: {
- SmallVector<SDValue, 8> Ops;
- Ops.push_back(getControlRoot()); // inchain for the terminator node
const TargetLowering &TLI = DAG.getTargetLoweringInfo();
- Ops.push_back(
+ SmallVector<SDValue, 8> Ops = {
+ getControlRoot(), // inchain for the terminator node
DAG.getTargetConstant(Intrinsic::wasm_throw, getCurSDLoc(),
- TLI.getPointerTy(DAG.getDataLayout())));
- Ops.push_back(getValue(I.getArgOperand(0))); // tag
- Ops.push_back(getValue(I.getArgOperand(1))); // thrown value
+ TLI.getPointerTy(DAG.getDataLayout())),
+ getValue(I.getArgOperand(0)), // tag
+ getValue(I.getArgOperand(1)) // thrown value
+ };
SDVTList VTs = DAG.getVTList(ArrayRef<EVT>({MVT::Other})); // outchain
DAG.setRoot(DAG.getNode(ISD::INTRINSIC_VOID, getCurSDLoc(), VTs, Ops));
break;
>From aa7b0f6c22348c72199d8366d1841772db4982c1 Mon Sep 17 00:00:00 2001
From: Heejin Ahn <aheejin at gmail.com>
Date: Fri, 21 Feb 2025 02:11:36 +0000
Subject: [PATCH 4/6] Use std::array (also for wasm_rethrow)
---
llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index c7cb6a70936d5..2172e396ed551 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -3365,7 +3365,7 @@ void SelectionDAGBuilder::visitInvoke(const InvokeInst &I) {
// lower it to a DAG node here.
case Intrinsic::wasm_throw: {
const TargetLowering &TLI = DAG.getTargetLoweringInfo();
- SmallVector<SDValue, 8> Ops = {
+ std::array<SDValue, 4> Ops = {
getControlRoot(), // inchain for the terminator node
DAG.getTargetConstant(Intrinsic::wasm_throw, getCurSDLoc(),
TLI.getPointerTy(DAG.getDataLayout())),
@@ -3377,12 +3377,11 @@ void SelectionDAGBuilder::visitInvoke(const InvokeInst &I) {
break;
}
case Intrinsic::wasm_rethrow: {
- SmallVector<SDValue, 8> Ops;
- Ops.push_back(getControlRoot()); // inchain for the terminator node
const TargetLowering &TLI = DAG.getTargetLoweringInfo();
- Ops.push_back(
+ std::array<SDValue, 2> Ops = {
+ getControlRoot(), // inchain for the terminator node
DAG.getTargetConstant(Intrinsic::wasm_rethrow, getCurSDLoc(),
- TLI.getPointerTy(DAG.getDataLayout())));
+ TLI.getPointerTy(DAG.getDataLayout()))};
SDVTList VTs = DAG.getVTList(ArrayRef<EVT>({MVT::Other})); // outchain
DAG.setRoot(DAG.getNode(ISD::INTRINSIC_VOID, getCurSDLoc(), VTs, Ops));
break;
>From 614293e361113bfd2e1e5df2c6555821e0e2435c Mon Sep 17 00:00:00 2001
From: Heejin Ahn <aheejin at gmail.com>
Date: Fri, 21 Feb 2025 23:11:44 +0000
Subject: [PATCH 5/6] Error message update in test
---
llvm/test/Verifier/invoke.ll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/test/Verifier/invoke.ll b/llvm/test/Verifier/invoke.ll
index 20f61b9041f21..11161e2618e03 100644
--- a/llvm/test/Verifier/invoke.ll
+++ b/llvm/test/Verifier/invoke.ll
@@ -46,7 +46,7 @@ contb:
define i8 @f2() personality ptr @__gxx_personality_v0 {
entry:
-; CHECK: Cannot invoke an intrinsic other than donothing, patchpoint, statepoint, coro_resume, coro_destroy or clang.arc.attachedcall
+; CHECK: Cannot invoke an intrinsic other than donothing, patchpoint, statepoint, coro_resume, coro_destroy, clang.arc.attachedcall or wasm.(re)throw
invoke void @llvm.trap()
to label %cont unwind label %lpad
>From 5d9f9484fa6fdb500928052ef28dbbc99fa6f6c5 Mon Sep 17 00:00:00 2001
From: Heejin Ahn <aheejin at gmail.com>
Date: Mon, 24 Feb 2025 23:57:16 -0800
Subject: [PATCH 6/6] Update
llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
Co-authored-by: Derek Schuff <dschuff at chromium.org>
---
llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index 2172e396ed551..548ef7b7b4487 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -3361,7 +3361,7 @@ void SelectionDAGBuilder::visitInvoke(const InvokeInst &I) {
LowerStatepoint(cast<GCStatepointInst>(I), EHPadBB);
break;
// wasm_throw, wasm_rethrow: This is usually done in visitTargetIntrinsic,
- // but this intrinsic is special because it can be invoked, so we manually
+ // but these intrinsics are special because they can be invoked, so we manually
// lower it to a DAG node here.
case Intrinsic::wasm_throw: {
const TargetLowering &TLI = DAG.getTargetLoweringInfo();
More information about the llvm-commits
mailing list