Skip to content

Commit 27f3808

Browse files
aheejinYutongZhuu
authored andcommitted
[WebAssembly] Make llvm.wasm.throw invokable (llvm#128104)
`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 an 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 llvm#124710.
1 parent 088fa98 commit 27f3808

File tree

5 files changed

+51
-13
lines changed

5 files changed

+51
-13
lines changed

llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp

+19-7
Original file line numberDiff line numberDiff line change
@@ -3360,16 +3360,28 @@ void SelectionDAGBuilder::visitInvoke(const InvokeInst &I) {
33603360
case Intrinsic::experimental_gc_statepoint:
33613361
LowerStatepoint(cast<GCStatepointInst>(I), EHPadBB);
33623362
break;
3363+
// wasm_throw, wasm_rethrow: This is usually done in visitTargetIntrinsic,
3364+
// but these intrinsics are special because they can be invoked, so we
3365+
// manually lower it to a DAG node here.
3366+
case Intrinsic::wasm_throw: {
3367+
const TargetLowering &TLI = DAG.getTargetLoweringInfo();
3368+
std::array<SDValue, 4> Ops = {
3369+
getControlRoot(), // inchain for the terminator node
3370+
DAG.getTargetConstant(Intrinsic::wasm_throw, getCurSDLoc(),
3371+
TLI.getPointerTy(DAG.getDataLayout())),
3372+
getValue(I.getArgOperand(0)), // tag
3373+
getValue(I.getArgOperand(1)) // thrown value
3374+
};
3375+
SDVTList VTs = DAG.getVTList(ArrayRef<EVT>({MVT::Other})); // outchain
3376+
DAG.setRoot(DAG.getNode(ISD::INTRINSIC_VOID, getCurSDLoc(), VTs, Ops));
3377+
break;
3378+
}
33633379
case Intrinsic::wasm_rethrow: {
3364-
// This is usually done in visitTargetIntrinsic, but this intrinsic is
3365-
// special because it can be invoked, so we manually lower it to a DAG
3366-
// node here.
3367-
SmallVector<SDValue, 8> Ops;
3368-
Ops.push_back(getControlRoot()); // inchain for the terminator node
33693380
const TargetLowering &TLI = DAG.getTargetLoweringInfo();
3370-
Ops.push_back(
3381+
std::array<SDValue, 2> Ops = {
3382+
getControlRoot(), // inchain for the terminator node
33713383
DAG.getTargetConstant(Intrinsic::wasm_rethrow, getCurSDLoc(),
3372-
TLI.getPointerTy(DAG.getDataLayout())));
3384+
TLI.getPointerTy(DAG.getDataLayout()))};
33733385
SDVTList VTs = DAG.getVTList(ArrayRef<EVT>({MVT::Other})); // outchain
33743386
DAG.setRoot(DAG.getNode(ISD::INTRINSIC_VOID, getCurSDLoc(), VTs, Ops));
33753387
break;

llvm/lib/CodeGen/WasmEHPrepare.cpp

+2-4
Original file line numberDiff line numberDiff line change
@@ -201,10 +201,8 @@ bool WasmEHPrepareImpl::prepareThrows(Function &F) {
201201
// delete all following instructions within the BB, and delete all the dead
202202
// children of the BB as well.
203203
for (User *U : ThrowF->users()) {
204-
// A call to @llvm.wasm.throw() is only generated from __cxa_throw()
205-
// builtin call within libcxxabi, and cannot be an InvokeInst.
206-
auto *ThrowI = cast<CallInst>(U);
207-
if (ThrowI->getFunction() != &F)
204+
auto *ThrowI = dyn_cast<CallInst>(U);
205+
if (!ThrowI || ThrowI->getFunction() != &F)
208206
continue;
209207
Changed = true;
210208
auto *BB = ThrowI->getParent();

llvm/lib/IR/Verifier.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -5227,10 +5227,12 @@ void Verifier::visitInstruction(Instruction &I) {
52275227
F->getIntrinsicID() == Intrinsic::experimental_patchpoint ||
52285228
F->getIntrinsicID() == Intrinsic::fake_use ||
52295229
F->getIntrinsicID() == Intrinsic::experimental_gc_statepoint ||
5230+
F->getIntrinsicID() == Intrinsic::wasm_throw ||
52305231
F->getIntrinsicID() == Intrinsic::wasm_rethrow ||
52315232
IsAttachedCallOperand(F, CBI, i),
52325233
"Cannot invoke an intrinsic other than donothing, patchpoint, "
5233-
"statepoint, coro_resume, coro_destroy or clang.arc.attachedcall",
5234+
"statepoint, coro_resume, coro_destroy, clang.arc.attachedcall or "
5235+
"wasm.(re)throw",
52345236
&I);
52355237
Check(F->getParent() == &M, "Referencing function in another module!", &I,
52365238
&M, F, F->getParent());

llvm/test/CodeGen/WebAssembly/exception.ll

+26
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,32 @@ unreachable: ; preds = %entry
566566
unreachable
567567
}
568568

569+
; This tests whether llvm.wasm.throw intrinsic can invoked and iseled correctly.
570+
571+
; CHECK-LABEL: invoke_throw:
572+
; CHECK: try_table (catch __cpp_exception 0)
573+
; CHECK: local.get 0
574+
; CHECK: throw __cpp_exception
575+
; CHECK: end_try_table
576+
define void @invoke_throw(ptr %p) personality ptr @__gxx_wasm_personality_v0 {
577+
entry:
578+
invoke void @llvm.wasm.throw(i32 0, ptr %p)
579+
to label %try.cont unwind label %catch.dispatch
580+
581+
catch.dispatch: ; preds = %entry
582+
%0 = catchswitch within none [label %catch.start] unwind to caller
583+
584+
catch.start: ; preds = %catch.dispatch
585+
%1 = catchpad within %0 [ptr null]
586+
%2 = call ptr @llvm.wasm.get.exception(token %1)
587+
%3 = call i32 @llvm.wasm.get.ehselector(token %1)
588+
%4 = call ptr @__cxa_begin_catch(ptr %2) #4 [ "funclet"(token %1) ]
589+
call void @__cxa_end_catch() [ "funclet"(token %1) ]
590+
catchret from %1 to label %try.cont
591+
592+
try.cont: ; preds = %catch, %entry
593+
ret void
594+
}
569595

570596
declare void @foo()
571597
declare void @bar(ptr)

llvm/test/Verifier/invoke.ll

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ contb:
4646

4747
define i8 @f2() personality ptr @__gxx_personality_v0 {
4848
entry:
49-
; CHECK: Cannot invoke an intrinsic other than donothing, patchpoint, statepoint, coro_resume, coro_destroy or clang.arc.attachedcall
49+
; CHECK: Cannot invoke an intrinsic other than donothing, patchpoint, statepoint, coro_resume, coro_destroy, clang.arc.attachedcall or wasm.(re)throw
5050
invoke void @llvm.trap()
5151
to label %cont unwind label %lpad
5252

0 commit comments

Comments
 (0)