Index: include/llvm/IR/IntrinsicsWebAssembly.td =================================================================== --- include/llvm/IR/IntrinsicsWebAssembly.td +++ include/llvm/IR/IntrinsicsWebAssembly.td @@ -41,7 +41,7 @@ // throw / rethrow def int_wasm_throw : Intrinsic<[], [llvm_i32_ty, llvm_ptr_ty], [Throws, IntrNoReturn]>; -def int_wasm_rethrow : Intrinsic<[], [], [Throws, IntrNoReturn]>; +def int_wasm_rethrow_in_catch : Intrinsic<[], [], [Throws, IntrNoReturn]>; // Since wasm does not use landingpad instructions, these instructions return // exception pointer and selector values until we lower them in WasmEHPrepare. Index: lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp =================================================================== --- lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -2697,6 +2697,20 @@ case Intrinsic::experimental_gc_statepoint: LowerStatepoint(ImmutableStatepoint(&I), EHPadBB); break; + case Intrinsic::wasm_rethrow_in_catch: { + // 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 Ops; + Ops.push_back(getRoot()); // inchain + const TargetLowering &TLI = DAG.getTargetLoweringInfo(); + Ops.push_back( + DAG.getTargetConstant(Intrinsic::wasm_rethrow_in_catch, getCurSDLoc(), + TLI.getPointerTy(DAG.getDataLayout()))); + SDVTList VTs = DAG.getVTList(ArrayRef({MVT::Other})); // outchain + DAG.setRoot(DAG.getNode(ISD::INTRINSIC_VOID, getCurSDLoc(), VTs, Ops)); + break; + } } } else if (I.countOperandBundlesOfType(LLVMContext::OB_deopt)) { // Currently we do not lower any intrinsic calls with deopt operand bundles. Index: lib/CodeGen/WasmEHPrepare.cpp =================================================================== --- lib/CodeGen/WasmEHPrepare.cpp +++ lib/CodeGen/WasmEHPrepare.cpp @@ -104,15 +104,14 @@ Value *LSDAField = nullptr; // lsda field Value *SelectorField = nullptr; // selector - Function *ThrowF = nullptr; // wasm.throw() intrinsic - Function *RethrowF = nullptr; // wasm.rethrow() intrinsic - Function *LPadIndexF = nullptr; // wasm.landingpad.index() intrinsic - Function *LSDAF = nullptr; // wasm.lsda() intrinsic - Function *GetExnF = nullptr; // wasm.get.exception() intrinsic - Function *ExtractExnF = nullptr; // wasm.extract.exception() intrinsic - Function *GetSelectorF = nullptr; // wasm.get.ehselector() intrinsic + Function *ThrowF = nullptr; // wasm.throw() intrinsic + Function *LPadIndexF = nullptr; // wasm.landingpad.index() intrinsic + Function *LSDAF = nullptr; // wasm.lsda() intrinsic + Function *GetExnF = nullptr; // wasm.get.exception() intrinsic + Function *ExtractExnF = nullptr; // wasm.extract.exception() intrinsic + Function *GetSelectorF = nullptr; // wasm.get.ehselector() intrinsic FunctionCallee CallPersonalityF = - nullptr; // _Unwind_CallPersonality() wrapper + nullptr; // _Unwind_CallPersonality() wrapper bool prepareEHPads(Function &F); bool prepareThrows(Function &F); @@ -177,29 +176,23 @@ // wasm.throw() intinsic, which will be lowered to wasm 'throw' instruction. ThrowF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_throw); - // wasm.rethrow() intinsic, which will be lowered to wasm 'rethrow' - // instruction. - RethrowF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_rethrow); - // Insert an unreachable instruction after a call to @llvm.wasm.throw / // @llvm.wasm.rethrow and delete all following instructions within the BB, and // delete all the dead children of the BB as well. - for (auto L : {ThrowF->users(), RethrowF->users()}) { - for (User *U : L) { - // A call to @llvm.wasm.throw() is only generated from __cxa_throw() - // builtin call within libcxxabi, and cannot be an InvokeInst. - auto *ThrowI = cast(U); - if (ThrowI->getFunction() != &F) - continue; - Changed = true; - auto *BB = ThrowI->getParent(); - SmallVector Succs(succ_begin(BB), succ_end(BB)); - auto &InstList = BB->getInstList(); - InstList.erase(std::next(BasicBlock::iterator(ThrowI)), InstList.end()); - IRB.SetInsertPoint(BB); - IRB.CreateUnreachable(); - eraseDeadBBsAndChildren(Succs); - } + 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(U); + if (ThrowI->getFunction() != &F) + continue; + Changed = true; + auto *BB = ThrowI->getParent(); + SmallVector Succs(succ_begin(BB), succ_end(BB)); + auto &InstList = BB->getInstList(); + InstList.erase(std::next(BasicBlock::iterator(ThrowI)), InstList.end()); + IRB.SetInsertPoint(BB); + IRB.CreateUnreachable(); + eraseDeadBBsAndChildren(Succs); } return Changed; Index: lib/IR/Verifier.cpp =================================================================== --- lib/IR/Verifier.cpp +++ lib/IR/Verifier.cpp @@ -4026,7 +4026,8 @@ F->getIntrinsicID() == Intrinsic::coro_destroy || F->getIntrinsicID() == Intrinsic::experimental_patchpoint_void || F->getIntrinsicID() == Intrinsic::experimental_patchpoint_i64 || - F->getIntrinsicID() == Intrinsic::experimental_gc_statepoint, + F->getIntrinsicID() == Intrinsic::experimental_gc_statepoint || + F->getIntrinsicID() == Intrinsic::wasm_rethrow_in_catch, "Cannot invoke an intrinsic other than donothing, patchpoint, " "statepoint, coro_resume or coro_destroy", &I); Index: lib/Target/WebAssembly/WebAssemblyInstrControl.td =================================================================== --- lib/Target/WebAssembly/WebAssemblyInstrControl.td +++ lib/Target/WebAssembly/WebAssemblyInstrControl.td @@ -144,7 +144,12 @@ (outs), (ins event_op:$tag), [(WebAssemblythrow (WebAssemblywrapper texternalsym:$tag))], "throw \t$tag", "throw \t$tag", 0x08>; -defm RETHROW : NRI<(outs), (ins), [(int_wasm_rethrow)], "rethrow", 0x09>; +defm RETHROW : NRI<(outs), (ins EXCEPT_REF:$exn), [], "rethrow \t$exn", 0x09>; +// Pseudo instruction to be the lowering target of int_wasm_rethrow_in_catch +// intrinsic. Will be converted to the real rethrow instruction later. +let isPseudo = 1 in +defm RETHROW_IN_CATCH : NRI<(outs), (ins), [(int_wasm_rethrow_in_catch)], + "rethrow_in_catch", 0>; } // isTerminator = 1, hasCtrlDep = 1, isBarrier = 1 // Region within which an exception is caught: try / end_try Index: lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp +++ lib/Target/WebAssembly/WebAssemblyLateEHPrepare.cpp @@ -161,9 +161,17 @@ Changed = true; break; } - case WebAssembly::CLEANUPRET: { - // Replace a cleanupret with a rethrow - BuildMI(MBB, TI, TI->getDebugLoc(), TII.get(WebAssembly::RETHROW)); + case WebAssembly::CLEANUPRET: + case WebAssembly::RETHROW_IN_CATCH: { + // Replace a cleanupret/rethrow_in_catch with a rethrow + auto *EHPad = getMatchingEHPad(TI); + auto CatchPos = EHPad->begin(); + if (CatchPos->isEHLabel()) // EH pad starts with an EH label + ++CatchPos; + MachineInstr *Catch = &*CatchPos; + unsigned ExnReg = Catch->getOperand(0).getReg(); + BuildMI(MBB, TI, TI->getDebugLoc(), TII.get(WebAssembly::RETHROW)) + .addReg(ExnReg); TI->eraseFromParent(); Changed = true; break; @@ -191,7 +199,8 @@ SmallVector Succs(MBB.succ_begin(), MBB.succ_end()); for (auto *Succ : Succs) - MBB.removeSuccessor(Succ); + if (!Succ->isEHPad()) + MBB.removeSuccessor(Succ); eraseDeadBBsAndChildren(Succs); } } @@ -337,7 +346,7 @@ BuildMI(ElseMBB, DL, TII.get(WebAssembly::UNREACHABLE)); } else { - BuildMI(ElseMBB, DL, TII.get(WebAssembly::RETHROW)); + BuildMI(ElseMBB, DL, TII.get(WebAssembly::RETHROW)).addReg(ExnReg); if (EHInfo->hasEHPadUnwindDest(EHPad)) ElseMBB->addSuccessor(EHInfo->getEHPadUnwindDest(EHPad)); } Index: test/CodeGen/WebAssembly/cfg-stackify-eh.ll =================================================================== --- test/CodeGen/WebAssembly/cfg-stackify-eh.ll +++ test/CodeGen/WebAssembly/cfg-stackify-eh.ll @@ -33,7 +33,7 @@ ; CHECK: call __cxa_end_catch ; CHECK: br 1 # 1: down to label0 ; CHECK: end_block # label3: -; CHECK: call __cxa_rethrow +; CHECK: rethrow ${{.}} # to caller ; CHECK: end_try # label0: define void @test0() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { entry: @@ -67,7 +67,7 @@ catchret from %1 to label %try.cont rethrow: ; preds = %catch.fallthrough - call void @__cxa_rethrow() [ "funclet"(token %1) ] + call void @llvm.wasm.rethrow.in.catch() [ "funclet"(token %1) ] unreachable try.cont: ; preds = %entry, %catch, %catch2 @@ -108,21 +108,19 @@ ; CHECK: br 2 # 2: down to label9 ; CHECK: catch ; CHECK: call __cxa_end_catch -; CHECK: rethrow # down to catch3 +; CHECK: rethrow {{.*}} # down to catch3 ; CHECK: end_try ; CHECK: end_block # label11: -; CHECK: call __cxa_rethrow -; CHECK: unreachable +; CHECK: rethrow {{.*}} # down to catch3 ; CHECK: catch {{.*}} # catch3: ; CHECK: call __cxa_end_catch -; CHECK: rethrow # to caller +; CHECK: rethrow {{.*}} # to caller ; CHECK: end_try # label9: ; CHECK: call __cxa_end_catch ; CHECK: br 2 # 2: down to label6 ; CHECK: end_try ; CHECK: end_block # label7: -; CHECK: call __cxa_rethrow -; CHECK: unreachable +; CHECK: rethrow {{.*}} # to caller ; CHECK: end_block # label6: ; CHECK: call __cxa_end_catch ; CHECK: end_try @@ -172,7 +170,7 @@ catchret from %9 to label %try.cont rethrow5: ; preds = %catch.start3 - invoke void @__cxa_rethrow() [ "funclet"(token %9) ] + invoke void @llvm.wasm.rethrow.in.catch() [ "funclet"(token %9) ] to label %unreachable unwind label %ehcleanup9 try.cont: ; preds = %catch, %invoke.cont8 @@ -180,7 +178,7 @@ catchret from %1 to label %try.cont11 rethrow: ; preds = %catch.start - call void @__cxa_rethrow() [ "funclet"(token %1) ] + call void @llvm.wasm.rethrow.in.catch() [ "funclet"(token %1) ] unreachable try.cont11: ; preds = %entry, %try.cont @@ -229,7 +227,7 @@ ; CHECK: call __clang_call_terminate ; CHECK: unreachable ; CHECK: end_try -; CHECK: rethrow # to caller +; CHECK: rethrow {{.*}} # to caller ; CHECK: end_try ; CHECK: end_block # label17: ; CHECK: call __cxa_end_catch @@ -293,9 +291,9 @@ declare i32 @__gxx_wasm_personality_v0(...) declare i8* @llvm.wasm.get.exception(token) declare i32 @llvm.wasm.get.ehselector(token) +declare void @llvm.wasm.rethrow.in.catch() declare i32 @llvm.eh.typeid.for(i8*) declare i8* @__cxa_begin_catch(i8*) declare void @__cxa_end_catch() -declare void @__cxa_rethrow() declare void @__clang_call_terminate(i8*) declare void @_ZSt9terminatev() Index: test/CodeGen/WebAssembly/exception.ll =================================================================== --- test/CodeGen/WebAssembly/exception.ll +++ test/CodeGen/WebAssembly/exception.ll @@ -17,14 +17,6 @@ ret void } -; CHECK-LABEL: test_rethrow: -; CHECK: rethrow -; CHECK-NOT: unreachable -define void @test_rethrow(i8* %p) { - call void @llvm.wasm.rethrow() - ret void -} - ; Simple test with a try-catch ; ; void foo(); @@ -43,7 +35,7 @@ ; CHECK: global.set __stack_pointer ; CHECK: block i32 ; CHECK: br_on_exn 0, __cpp_exception, $[[EXCEPT_REF]] -; CHECK: rethrow +; CHECK: rethrow $[[EXCEPT_REF]] ; CHECK: end_block ; CHECK: extract_exception $[[EXN:[0-9]+]]= ; CHECK-DAG: i32.store __wasm_lpad_context @@ -55,7 +47,7 @@ ; CHECK: call __cxa_end_catch ; CHECK: br 1 ; CHECK: end_block -; CHECK: call __cxa_rethrow +; CHECK: rethrow $[[EXCEPT_REF]] ; CHECK: end_try define void @test_catch() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { entry: @@ -79,7 +71,7 @@ catchret from %1 to label %try.cont rethrow: ; preds = %catch.start - call void @__cxa_rethrow() [ "funclet"(token %1) ] + call void @llvm.wasm.rethrow.in.catch() [ "funclet"(token %1) ] unreachable try.cont: ; preds = %entry, %catch @@ -100,10 +92,10 @@ ; CHECK-LABEL: test_cleanup: ; CHECK: try ; CHECK: call foo -; CHECK: catch +; CHECK: catch $[[EXCEPT_REF:[0-9]+]]= ; CHECK: global.set __stack_pointer ; CHECK: i32.call $drop=, _ZN4TempD2Ev -; CHECK: rethrow +; CHECK: rethrow $[[EXCEPT_REF]] ; CHECK: end_try define void @test_cleanup() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { entry: @@ -231,7 +223,7 @@ ; CHECK-NOT: global.set __stack_pointer, $pop{{.+}} ; CHECK: end_try ; CHECK: end_block -; CHECK: call __cxa_rethrow +; CHECK: rethrow ; CHECK: end_block ; CHECK-NOT: global.set __stack_pointer, $pop{{.+}} ; CHECK: call __cxa_end_catch @@ -266,7 +258,7 @@ catchret from %1 to label %try.cont rethrow: ; preds = %catch.start - call void @__cxa_rethrow() [ "funclet"(token %1) ] + call void @llvm.wasm.rethrow.in.catch() [ "funclet"(token %1) ] unreachable try.cont: ; preds = %entry, %invoke.cont1 @@ -342,13 +334,12 @@ declare void @bar(i32*) declare i32 @__gxx_wasm_personality_v0(...) declare void @llvm.wasm.throw(i32, i8*) -declare void @llvm.wasm.rethrow() declare i8* @llvm.wasm.get.exception(token) declare i32 @llvm.wasm.get.ehselector(token) +declare void @llvm.wasm.rethrow.in.catch() declare i32 @llvm.eh.typeid.for(i8*) declare i8* @__cxa_begin_catch(i8*) declare void @__cxa_end_catch() -declare void @__cxa_rethrow() declare void @__clang_call_terminate(i8*) declare %struct.Temp* @_ZN4TempD2Ev(%struct.Temp* returned) Index: test/CodeGen/WebAssembly/wasmehprepare.ll =================================================================== --- test/CodeGen/WebAssembly/wasmehprepare.ll +++ test/CodeGen/WebAssembly/wasmehprepare.ll @@ -54,7 +54,7 @@ ; CHECK-NEXT: call i8* @__cxa_begin_catch(i8* %[[EXN]]) rethrow: ; preds = %catch.start - call void @__cxa_rethrow() [ "funclet"(token %1) ] + call void @llvm.wasm.rethrow.in.catch() [ "funclet"(token %1) ] unreachable try.cont: ; preds = %entry, %catch @@ -125,7 +125,7 @@ catchret from %6 to label %try.cont7 rethrow: ; preds = %catch.start3 - call void @__cxa_rethrow() [ "funclet"(token %6) ] + call void @llvm.wasm.rethrow.in.catch() [ "funclet"(token %6) ] unreachable try.cont7: ; preds = %try.cont, %catch4 @@ -189,7 +189,7 @@ catchret from %7 to label %try.cont rethrow5: ; preds = %catch.start3 - invoke void @__cxa_rethrow() [ "funclet"(token %7) ] + invoke void @llvm.wasm.rethrow.in.catch() [ "funclet"(token %7) ] to label %unreachable unwind label %ehcleanup try.cont: ; preds = %catch, %catch6 @@ -197,7 +197,7 @@ catchret from %1 to label %try.cont9 rethrow: ; preds = %catch.start - call void @__cxa_rethrow() [ "funclet"(token %1) ] + call void @llvm.wasm.rethrow.in.catch() [ "funclet"(token %1) ] unreachable try.cont9: ; preds = %entry, %try.cont @@ -392,34 +392,6 @@ ret i32 0 } -; Tests if instructions after a call to @llvm.wasm.rethrow are deleted and the -; BB's dead children are deleted. - -; CHECK-LABEL: @test6 -define i32 @test6(i1 %b, i8* %p) { -entry: - br i1 %b, label %bb.true, label %bb.false - -; CHECK: bb.true: -; CHECK-NEXT: call void @llvm.wasm.rethrow() -; CHECK-NEXT: unreachable -bb.true: ; preds = %entry - call void @llvm.wasm.rethrow() - br label %bb.true.0 - -; CHECK-NOT: bb.true.0 -bb.true.0: ; preds = %bb.true - br label %merge - -; CHECK: bb.false -bb.false: ; preds = %entry - br label %merge - -; CHECK: merge -merge: ; preds = %bb.true.0, %bb.false - ret i32 0 -} - declare void @foo() declare void @bar(i32) declare %struct.Temp* @_ZN4TempD2Ev(%struct.Temp* returned) @@ -428,10 +400,9 @@ declare i32 @llvm.wasm.get.ehselector(token) declare i32 @llvm.eh.typeid.for(i8*) declare void @llvm.wasm.throw(i32, i8*) -declare void @llvm.wasm.rethrow() +declare void @llvm.wasm.rethrow.in.catch() declare i8* @__cxa_begin_catch(i8*) declare void @__cxa_end_catch() -declare void @__cxa_rethrow() declare void @__clang_call_terminate(i8*) ; CHECK-DAG: declare void @llvm.wasm.landingpad.index(token, i32)