llvm.org GIT mirror llvm / 421f4e7
[WebAssembly] Make rethrow take an except_ref type argument Summary: In the new wasm EH proposal, `rethrow` takes an `except_ref` argument. This change was missing in r352598. This patch adds `llvm.wasm.rethrow.in.catch` intrinsic. This is an intrinsic that's gonna eventually be lowered to wasm `rethrow` instruction, but this intrinsic can appear only within a catchpad or a cleanuppad scope. Also this intrinsic needs to be invokable - otherwise EH pad successor for it will not be correctly generated in clang. This also adds lowering logic for this intrinsic in `SelectionDAGBuilder::visitInvoke`. This routine is basically a specialized and simplified version of `SelectionDAGBuilder::visitTargetIntrinsic`, but we can't use it because if is only for `CallInst`s. This deletes the previous `llvm.wasm.rethrow` intrinsic and related tests, which was meant to be used within a `__cxa_rethrow` library function. Turned out this needs some more logic, so the intrinsic for this purpose will be added later. LateEHPrepare takes a result value of `catch` and inserts it into matching `rethrow` as an argument. `RETHROW_IN_CATCH` is a pseudo instruction that serves as a link between `llvm.wasm.rethrow.in.catch` and the real wasm `rethrow` instruction. To generate a `rethrow` instruction, we need an `except_ref` argument, which is generated from `catch` instruction. But `catch` instrutions are added in LateEHPrepare pass, so we use `RETHROW_IN_CATCH`, which takes no argument, until we are able to correctly lower it to `rethrow` in LateEHPrepare. Reviewers: dschuff Subscribers: sbc100, jgravelle-google, sunfish, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D59352 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@356316 91177308-0d34-0410-b5e6-96231b3b80d8 Heejin Ahn 1 year, 19 days ago
9 changed file(s) with 85 addition(s) and 102 deletion(s). Raw diff Collapse all Expand all
4040 // throw / rethrow
4141 def int_wasm_throw : Intrinsic<[], [llvm_i32_ty, llvm_ptr_ty],
4242 [Throws, IntrNoReturn]>;
43 def int_wasm_rethrow : Intrinsic<[], [], [Throws, IntrNoReturn]>;
43 def int_wasm_rethrow_in_catch : Intrinsic<[], [], [Throws, IntrNoReturn]>;
4444
4545 // Since wasm does not use landingpad instructions, these instructions return
4646 // exception pointer and selector values until we lower them in WasmEHPrepare.
26962696 case Intrinsic::experimental_gc_statepoint:
26972697 LowerStatepoint(ImmutableStatepoint(&I), EHPadBB);
26982698 break;
2699 case Intrinsic::wasm_rethrow_in_catch: {
2700 // This is usually done in visitTargetIntrinsic, but this intrinsic is
2701 // special because it can be invoked, so we manually lower it to a DAG
2702 // node here.
2703 SmallVector Ops;
2704 Ops.push_back(getRoot()); // inchain
2705 const TargetLowering &TLI = DAG.getTargetLoweringInfo();
2706 Ops.push_back(
2707 DAG.getTargetConstant(Intrinsic::wasm_rethrow_in_catch, getCurSDLoc(),
2708 TLI.getPointerTy(DAG.getDataLayout())));
2709 SDVTList VTs = DAG.getVTList(ArrayRef({MVT::Other})); // outchain
2710 DAG.setRoot(DAG.getNode(ISD::INTRINSIC_VOID, getCurSDLoc(), VTs, Ops));
2711 break;
2712 }
26992713 }
27002714 } else if (I.countOperandBundlesOfType(LLVMContext::OB_deopt)) {
27012715 // Currently we do not lower any intrinsic calls with deopt operand bundles.
103103 Value *LSDAField = nullptr; // lsda field
104104 Value *SelectorField = nullptr; // selector
105105
106 Function *ThrowF = nullptr; // wasm.throw() intrinsic
107 Function *RethrowF = nullptr; // wasm.rethrow() intrinsic
108 Function *LPadIndexF = nullptr; // wasm.landingpad.index() intrinsic
109 Function *LSDAF = nullptr; // wasm.lsda() intrinsic
110 Function *GetExnF = nullptr; // wasm.get.exception() intrinsic
111 Function *ExtractExnF = nullptr; // wasm.extract.exception() intrinsic
112 Function *GetSelectorF = nullptr; // wasm.get.ehselector() intrinsic
106 Function *ThrowF = nullptr; // wasm.throw() intrinsic
107 Function *LPadIndexF = nullptr; // wasm.landingpad.index() intrinsic
108 Function *LSDAF = nullptr; // wasm.lsda() intrinsic
109 Function *GetExnF = nullptr; // wasm.get.exception() intrinsic
110 Function *ExtractExnF = nullptr; // wasm.extract.exception() intrinsic
111 Function *GetSelectorF = nullptr; // wasm.get.ehselector() intrinsic
113112 FunctionCallee CallPersonalityF =
114 nullptr; // _Unwind_CallPersonality() wrapper
113 nullptr; // _Unwind_CallPersonality() wrapper
115114
116115 bool prepareEHPads(Function &F);
117116 bool prepareThrows(Function &F);
176175
177176 // wasm.throw() intinsic, which will be lowered to wasm 'throw' instruction.
178177 ThrowF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_throw);
179 // wasm.rethrow() intinsic, which will be lowered to wasm 'rethrow'
180 // instruction.
181 RethrowF = Intrinsic::getDeclaration(&M, Intrinsic::wasm_rethrow);
182
183 // Insert an unreachable instruction after a call to @llvm.wasm.throw /
184 // @llvm.wasm.rethrow and delete all following instructions within the BB, and
185 // delete all the dead children of the BB as well.
186 for (auto L : {ThrowF->users(), RethrowF->users()}) {
187 for (User *U : L) {
188 // A call to @llvm.wasm.throw() is only generated from __cxa_throw()
189 // builtin call within libcxxabi, and cannot be an InvokeInst.
190 auto *ThrowI = cast(U);
191 if (ThrowI->getFunction() != &F)
192 continue;
193 Changed = true;
194 auto *BB = ThrowI->getParent();
195 SmallVector Succs(succ_begin(BB), succ_end(BB));
196 auto &InstList = BB->getInstList();
197 InstList.erase(std::next(BasicBlock::iterator(ThrowI)), InstList.end());
198 IRB.SetInsertPoint(BB);
199 IRB.CreateUnreachable();
200 eraseDeadBBsAndChildren(Succs);
201 }
178 // Insert an unreachable instruction after a call to @llvm.wasm.throw and
179 // delete all following instructions within the BB, and delete all the dead
180 // children of the BB as well.
181 for (User *U : ThrowF->users()) {
182 // A call to @llvm.wasm.throw() is only generated from __cxa_throw()
183 // builtin call within libcxxabi, and cannot be an InvokeInst.
184 auto *ThrowI = cast(U);
185 if (ThrowI->getFunction() != &F)
186 continue;
187 Changed = true;
188 auto *BB = ThrowI->getParent();
189 SmallVector Succs(succ_begin(BB), succ_end(BB));
190 auto &InstList = BB->getInstList();
191 InstList.erase(std::next(BasicBlock::iterator(ThrowI)), InstList.end());
192 IRB.SetInsertPoint(BB);
193 IRB.CreateUnreachable();
194 eraseDeadBBsAndChildren(Succs);
202195 }
203196
204197 return Changed;
40254025 F->getIntrinsicID() == Intrinsic::coro_destroy ||
40264026 F->getIntrinsicID() == Intrinsic::experimental_patchpoint_void ||
40274027 F->getIntrinsicID() == Intrinsic::experimental_patchpoint_i64 ||
4028 F->getIntrinsicID() == Intrinsic::experimental_gc_statepoint,
4028 F->getIntrinsicID() == Intrinsic::experimental_gc_statepoint ||
4029 F->getIntrinsicID() == Intrinsic::wasm_rethrow_in_catch,
40294030 "Cannot invoke an intrinsic other than donothing, patchpoint, "
40304031 "statepoint, coro_resume or coro_destroy",
40314032 &I);
143143 (outs), (ins event_op:$tag),
144144 [(WebAssemblythrow (WebAssemblywrapper texternalsym:$tag))],
145145 "throw \t$tag", "throw \t$tag", 0x08>;
146 defm RETHROW : NRI<(outs), (ins), [(int_wasm_rethrow)], "rethrow", 0x09>;
146 defm RETHROW : I<(outs), (ins EXCEPT_REF:$exn), (outs), (ins),
147 [], "rethrow \t$exn", "rethrow", 0x09>;
148 // Pseudo instruction to be the lowering target of int_wasm_rethrow_in_catch
149 // intrinsic. Will be converted to the real rethrow instruction later.
150 let isPseudo = 1 in
151 defm RETHROW_IN_CATCH : NRI<(outs), (ins), [(int_wasm_rethrow_in_catch)],
152 "rethrow_in_catch", 0>;
147153 } // isTerminator = 1, hasCtrlDep = 1, isBarrier = 1
148154
149155 // Region within which an exception is caught: try / end_try
160160 Changed = true;
161161 break;
162162 }
163 case WebAssembly::CLEANUPRET: {
164 // Replace a cleanupret with a rethrow
165 BuildMI(MBB, TI, TI->getDebugLoc(), TII.get(WebAssembly::RETHROW));
163 case WebAssembly::CLEANUPRET:
164 case WebAssembly::RETHROW_IN_CATCH: {
165 // Replace a cleanupret/rethrow_in_catch with a rethrow
166 auto *EHPad = getMatchingEHPad(TI);
167 auto CatchPos = EHPad->begin();
168 if (CatchPos->isEHLabel()) // EH pad starts with an EH label
169 ++CatchPos;
170 MachineInstr *Catch = &*CatchPos;
171 unsigned ExnReg = Catch->getOperand(0).getReg();
172 BuildMI(MBB, TI, TI->getDebugLoc(), TII.get(WebAssembly::RETHROW))
173 .addReg(ExnReg);
166174 TI->eraseFromParent();
167175 Changed = true;
168176 break;
190198 SmallVector Succs(MBB.succ_begin(),
191199 MBB.succ_end());
192200 for (auto *Succ : Succs)
193 MBB.removeSuccessor(Succ);
201 if (!Succ->isEHPad())
202 MBB.removeSuccessor(Succ);
194203 eraseDeadBBsAndChildren(Succs);
195204 }
196205 }
336345 BuildMI(ElseMBB, DL, TII.get(WebAssembly::UNREACHABLE));
337346
338347 } else {
339 BuildMI(ElseMBB, DL, TII.get(WebAssembly::RETHROW));
348 BuildMI(ElseMBB, DL, TII.get(WebAssembly::RETHROW)).addReg(ExnReg);
340349 if (EHInfo->hasEHPadUnwindDest(EHPad))
341350 ElseMBB->addSuccessor(EHInfo->getEHPadUnwindDest(EHPad));
342351 }
3232 ; CHECK: call __cxa_end_catch
3333 ; CHECK: br 1 # 1: down to label0
3434 ; CHECK: end_block # label3:
35 ; CHECK: call __cxa_rethrow
35 ; CHECK: rethrow {{.*}} # to caller
3636 ; CHECK: end_try # label0:
3737 define void @test0() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
3838 entry:
6666 catchret from %1 to label %try.cont
6767
6868 rethrow: ; preds = %catch.fallthrough
69 call void @__cxa_rethrow() [ "funclet"(token %1) ]
69 call void @llvm.wasm.rethrow.in.catch() [ "funclet"(token %1) ]
7070 unreachable
7171
7272 try.cont: ; preds = %entry, %catch, %catch2
107107 ; CHECK: br 2 # 2: down to label9
108108 ; CHECK: catch
109109 ; CHECK: call __cxa_end_catch
110 ; CHECK: rethrow # down to catch3
110 ; CHECK: rethrow {{.*}} # down to catch3
111111 ; CHECK: end_try
112112 ; CHECK: end_block # label11:
113 ; CHECK: call __cxa_rethrow
114 ; CHECK: unreachable
113 ; CHECK: rethrow {{.*}} # down to catch3
115114 ; CHECK: catch {{.*}} # catch3:
116115 ; CHECK: call __cxa_end_catch
117 ; CHECK: rethrow # to caller
116 ; CHECK: rethrow {{.*}} # to caller
118117 ; CHECK: end_try # label9:
119118 ; CHECK: call __cxa_end_catch
120119 ; CHECK: br 2 # 2: down to label6
121120 ; CHECK: end_try
122121 ; CHECK: end_block # label7:
123 ; CHECK: call __cxa_rethrow
124 ; CHECK: unreachable
122 ; CHECK: rethrow {{.*}} # to caller
125123 ; CHECK: end_block # label6:
126124 ; CHECK: call __cxa_end_catch
127125 ; CHECK: end_try
171169 catchret from %9 to label %try.cont
172170
173171 rethrow5: ; preds = %catch.start3
174 invoke void @__cxa_rethrow() [ "funclet"(token %9) ]
172 invoke void @llvm.wasm.rethrow.in.catch() [ "funclet"(token %9) ]
175173 to label %unreachable unwind label %ehcleanup9
176174
177175 try.cont: ; preds = %catch, %invoke.cont8
179177 catchret from %1 to label %try.cont11
180178
181179 rethrow: ; preds = %catch.start
182 call void @__cxa_rethrow() [ "funclet"(token %1) ]
180 call void @llvm.wasm.rethrow.in.catch() [ "funclet"(token %1) ]
183181 unreachable
184182
185183 try.cont11: ; preds = %entry, %try.cont
228226 ; CHECK: call __clang_call_terminate
229227 ; CHECK: unreachable
230228 ; CHECK: end_try
231 ; CHECK: rethrow # to caller
229 ; CHECK: rethrow {{.*}} # to caller
232230 ; CHECK: end_try
233231 ; CHECK: end_block # label17:
234232 ; CHECK: call __cxa_end_catch
292290 declare i32 @__gxx_wasm_personality_v0(...)
293291 declare i8* @llvm.wasm.get.exception(token)
294292 declare i32 @llvm.wasm.get.ehselector(token)
293 declare void @llvm.wasm.rethrow.in.catch()
295294 declare i32 @llvm.eh.typeid.for(i8*)
296295 declare i8* @__cxa_begin_catch(i8*)
297296 declare void @__cxa_end_catch()
298 declare void @__cxa_rethrow()
299297 declare void @__clang_call_terminate(i8*)
300298 declare void @_ZSt9terminatev()
1313 ; CHECK-NOT: unreachable
1414 define void @test_throw(i8* %p) {
1515 call void @llvm.wasm.throw(i32 0, i8* %p)
16 ret void
17 }
18
19 ; CHECK-LABEL: test_rethrow:
20 ; CHECK: rethrow
21 ; CHECK-NOT: unreachable
22 define void @test_rethrow(i8* %p) {
23 call void @llvm.wasm.rethrow()
2416 ret void
2517 }
2618
4234 ; CHECK: global.set __stack_pointer
4335 ; CHECK: block i32
4436 ; CHECK: br_on_exn 0, __cpp_exception, $[[EXCEPT_REF]]
45 ; CHECK: rethrow
37 ; CHECK: rethrow $[[EXCEPT_REF]]
4638 ; CHECK: end_block
4739 ; CHECK: extract_exception $[[EXN:[0-9]+]]=
4840 ; CHECK-DAG: i32.store __wasm_lpad_context
5446 ; CHECK: call __cxa_end_catch
5547 ; CHECK: br 1
5648 ; CHECK: end_block
57 ; CHECK: call __cxa_rethrow
49 ; CHECK: rethrow $[[EXCEPT_REF]]
5850 ; CHECK: end_try
5951 define void @test_catch() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
6052 entry:
7870 catchret from %1 to label %try.cont
7971
8072 rethrow: ; preds = %catch.start
81 call void @__cxa_rethrow() [ "funclet"(token %1) ]
73 call void @llvm.wasm.rethrow.in.catch() [ "funclet"(token %1) ]
8274 unreachable
8375
8476 try.cont: ; preds = %entry, %catch
9991 ; CHECK-LABEL: test_cleanup:
10092 ; CHECK: try
10193 ; CHECK: call foo
102 ; CHECK: catch
94 ; CHECK: catch $[[EXCEPT_REF:[0-9]+]]=
10395 ; CHECK: global.set __stack_pointer
10496 ; CHECK: i32.call $drop=, _ZN4TempD2Ev
105 ; CHECK: rethrow
97 ; CHECK: rethrow $[[EXCEPT_REF]]
10698 ; CHECK: end_try
10799 define void @test_cleanup() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
108100 entry:
230222 ; CHECK-NOT: global.set __stack_pointer, $pop{{.+}}
231223 ; CHECK: end_try
232224 ; CHECK: end_block
233 ; CHECK: call __cxa_rethrow
225 ; CHECK: rethrow
234226 ; CHECK: end_block
235227 ; CHECK-NOT: global.set __stack_pointer, $pop{{.+}}
236228 ; CHECK: call __cxa_end_catch
265257 catchret from %1 to label %try.cont
266258
267259 rethrow: ; preds = %catch.start
268 call void @__cxa_rethrow() [ "funclet"(token %1) ]
260 call void @llvm.wasm.rethrow.in.catch() [ "funclet"(token %1) ]
269261 unreachable
270262
271263 try.cont: ; preds = %entry, %invoke.cont1
341333 declare void @bar(i32*)
342334 declare i32 @__gxx_wasm_personality_v0(...)
343335 declare void @llvm.wasm.throw(i32, i8*)
344 declare void @llvm.wasm.rethrow()
345336 declare i8* @llvm.wasm.get.exception(token)
346337 declare i32 @llvm.wasm.get.ehselector(token)
338 declare void @llvm.wasm.rethrow.in.catch()
347339 declare i32 @llvm.eh.typeid.for(i8*)
348340 declare i8* @__cxa_begin_catch(i8*)
349341 declare void @__cxa_end_catch()
350 declare void @__cxa_rethrow()
351342 declare void @__clang_call_terminate(i8*)
352343 declare %struct.Temp* @_ZN4TempD2Ev(%struct.Temp* returned)
353344
5353 ; CHECK-NEXT: call i8* @__cxa_begin_catch(i8* %[[EXN]])
5454
5555 rethrow: ; preds = %catch.start
56 call void @__cxa_rethrow() [ "funclet"(token %1) ]
56 call void @llvm.wasm.rethrow.in.catch() [ "funclet"(token %1) ]
5757 unreachable
5858
5959 try.cont: ; preds = %entry, %catch
124124 catchret from %6 to label %try.cont7
125125
126126 rethrow: ; preds = %catch.start3
127 call void @__cxa_rethrow() [ "funclet"(token %6) ]
127 call void @llvm.wasm.rethrow.in.catch() [ "funclet"(token %6) ]
128128 unreachable
129129
130130 try.cont7: ; preds = %try.cont, %catch4
188188 catchret from %7 to label %try.cont
189189
190190 rethrow5: ; preds = %catch.start3
191 invoke void @__cxa_rethrow() [ "funclet"(token %7) ]
191 invoke void @llvm.wasm.rethrow.in.catch() [ "funclet"(token %7) ]
192192 to label %unreachable unwind label %ehcleanup
193193
194194 try.cont: ; preds = %catch, %catch6
196196 catchret from %1 to label %try.cont9
197197
198198 rethrow: ; preds = %catch.start
199 call void @__cxa_rethrow() [ "funclet"(token %1) ]
199 call void @llvm.wasm.rethrow.in.catch() [ "funclet"(token %1) ]
200200 unreachable
201201
202202 try.cont9: ; preds = %entry, %try.cont
391391 ret i32 0
392392 }
393393
394 ; Tests if instructions after a call to @llvm.wasm.rethrow are deleted and the
395 ; BB's dead children are deleted.
396
397 ; CHECK-LABEL: @test6
398 define i32 @test6(i1 %b, i8* %p) {
399 entry:
400 br i1 %b, label %bb.true, label %bb.false
401
402 ; CHECK: bb.true:
403 ; CHECK-NEXT: call void @llvm.wasm.rethrow()
404 ; CHECK-NEXT: unreachable
405 bb.true: ; preds = %entry
406 call void @llvm.wasm.rethrow()
407 br label %bb.true.0
408
409 ; CHECK-NOT: bb.true.0
410 bb.true.0: ; preds = %bb.true
411 br label %merge
412
413 ; CHECK: bb.false
414 bb.false: ; preds = %entry
415 br label %merge
416
417 ; CHECK: merge
418 merge: ; preds = %bb.true.0, %bb.false
419 ret i32 0
420 }
421
422394 declare void @foo()
423395 declare void @bar(i32)
424396 declare %struct.Temp* @_ZN4TempD2Ev(%struct.Temp* returned)
427399 declare i32 @llvm.wasm.get.ehselector(token)
428400 declare i32 @llvm.eh.typeid.for(i8*)
429401 declare void @llvm.wasm.throw(i32, i8*)
430 declare void @llvm.wasm.rethrow()
402 declare void @llvm.wasm.rethrow.in.catch()
431403 declare i8* @__cxa_begin_catch(i8*)
432404 declare void @__cxa_end_catch()
433 declare void @__cxa_rethrow()
434405 declare void @__clang_call_terminate(i8*)
435406
436407 ; CHECK-DAG: declare void @llvm.wasm.landingpad.index(token, i32)