[WebAssembly] Make llvm.wasm.throw invokable (#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`:
720e97f76d/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.
This commit is contained in:
parent
041b7f5085
commit
d2d469eb79
@ -3360,16 +3360,28 @@ void SelectionDAGBuilder::visitInvoke(const InvokeInst &I) {
|
||||
case Intrinsic::experimental_gc_statepoint:
|
||||
LowerStatepoint(cast<GCStatepointInst>(I), EHPadBB);
|
||||
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
|
||||
// wasm_throw, wasm_rethrow: This is usually done in visitTargetIntrinsic,
|
||||
// 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();
|
||||
Ops.push_back(
|
||||
std::array<SDValue, 4> Ops = {
|
||||
getControlRoot(), // inchain for the terminator node
|
||||
DAG.getTargetConstant(Intrinsic::wasm_throw, getCurSDLoc(),
|
||||
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;
|
||||
}
|
||||
case Intrinsic::wasm_rethrow: {
|
||||
const TargetLowering &TLI = DAG.getTargetLoweringInfo();
|
||||
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;
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -5227,10 +5227,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());
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user