1 ; RUN: llc < %s -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -disable-block-placement -verify-machineinstrs -fast-isel=false -machine-sink-split-probability-threshold=0 -cgp-freq-ratio-to-skip-merge=1000 -exception-model=wasm -mattr=+exception-handling | FileCheck %s
2 ; RUN: llc < %s -O0 -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -verify-machineinstrs -exception-model=wasm -mattr=+exception-handling | FileCheck %s --check-prefix=NOOPT
3 ; RUN: llc < %s -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -disable-block-placement -verify-machineinstrs -fast-isel=false -machine-sink-split-probability-threshold=0 -cgp-freq-ratio-to-skip-merge=1000 -exception-model=wasm -mattr=+exception-handling -wasm-disable-ehpad-sort | FileCheck %s --check-prefix=NOSORT
5 target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
6 target triple = "wasm32-unknown-unknown"
8 @_ZTIi = external constant i8*
9 @_ZTId = external constant i8*
11 ; Simple test case with two catch clauses
27 ; CHECK: br_if 0, {{.*}} # 0: down to label2
28 ; CHECK: i32.call $drop=, __cxa_begin_catch
29 ; CHECK: call __cxa_end_catch
30 ; CHECK: br 1 # 1: down to label0
31 ; CHECK: end_block # label2:
33 ; CHECK: br_if 0, {{.*}} # 0: down to label3
34 ; CHECK: i32.call $drop=, __cxa_begin_catch
35 ; CHECK: call __cxa_end_catch
36 ; CHECK: br 1 # 1: down to label0
37 ; CHECK: end_block # label3:
38 ; CHECK: rethrow {{.*}} # to caller
39 ; CHECK: end_try # label0:
40 define void @test0() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
43 to label %try.cont unwind label %catch.dispatch
45 catch.dispatch: ; preds = %entry
46 %0 = catchswitch within none [label %catch.start] unwind to caller
48 catch.start: ; preds = %catch.dispatch
49 %1 = catchpad within %0 [i8* bitcast (i8** @_ZTIi to i8*), i8* bitcast (i8** @_ZTId to i8*)]
50 %2 = call i8* @llvm.wasm.get.exception(token %1)
51 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
52 %4 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*))
53 %matches = icmp eq i32 %3, %4
54 br i1 %matches, label %catch2, label %catch.fallthrough
56 catch2: ; preds = %catch.start
57 %5 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ]
58 call void @__cxa_end_catch() [ "funclet"(token %1) ]
59 catchret from %1 to label %try.cont
61 catch.fallthrough: ; preds = %catch.start
62 %6 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTId to i8*))
63 %matches1 = icmp eq i32 %3, %6
64 br i1 %matches1, label %catch, label %rethrow
66 catch: ; preds = %catch.fallthrough
67 %7 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ]
68 call void @__cxa_end_catch() [ "funclet"(token %1) ]
69 catchret from %1 to label %try.cont
71 rethrow: ; preds = %catch.fallthrough
72 call void @llvm.wasm.rethrow.in.catch() [ "funclet"(token %1) ]
75 try.cont: ; preds = %catch, %catch2, %entry
79 ; Nested try-catches within a catch
98 ; CHECK: br_if 0, {{.*}} # 0: down to label7
99 ; CHECK: i32.call $drop=, __cxa_begin_catch
102 ; CHECK: br 2 # 2: down to label6
106 ; CHECK: br_if 0, {{.*}} # 0: down to label11
107 ; CHECK: i32.call $drop=, __cxa_begin_catch
110 ; CHECK: br 2 # 2: down to label9
112 ; CHECK: call __cxa_end_catch
113 ; CHECK: rethrow {{.*}} # down to catch3
115 ; CHECK: end_block # label11:
116 ; CHECK: rethrow {{.*}} # down to catch3
117 ; CHECK: catch {{.*}} # catch3:
118 ; CHECK: call __cxa_end_catch
119 ; CHECK: rethrow {{.*}} # to caller
120 ; CHECK: end_try # label9:
121 ; CHECK: call __cxa_end_catch
122 ; CHECK: br 2 # 2: down to label6
124 ; CHECK: end_block # label7:
125 ; CHECK: rethrow {{.*}} # to caller
126 ; CHECK: end_block # label6:
127 ; CHECK: call __cxa_end_catch
129 define void @test1() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
132 to label %try.cont11 unwind label %catch.dispatch
134 catch.dispatch: ; preds = %entry
135 %0 = catchswitch within none [label %catch.start] unwind to caller
137 catch.start: ; preds = %catch.dispatch
138 %1 = catchpad within %0 [i8* bitcast (i8** @_ZTIi to i8*)]
139 %2 = call i8* @llvm.wasm.get.exception(token %1)
140 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
141 %4 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*))
142 %matches = icmp eq i32 %3, %4
143 br i1 %matches, label %catch, label %rethrow
145 catch: ; preds = %catch.start
146 %5 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ]
147 %6 = bitcast i8* %5 to i32*
148 %7 = load i32, i32* %6, align 4
149 invoke void @foo() [ "funclet"(token %1) ]
150 to label %try.cont unwind label %catch.dispatch2
152 catch.dispatch2: ; preds = %catch
153 %8 = catchswitch within %1 [label %catch.start3] unwind label %ehcleanup9
155 catch.start3: ; preds = %catch.dispatch2
156 %9 = catchpad within %8 [i8* bitcast (i8** @_ZTIi to i8*)]
157 %10 = call i8* @llvm.wasm.get.exception(token %9)
158 %11 = call i32 @llvm.wasm.get.ehselector(token %9)
159 %12 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*))
160 %matches4 = icmp eq i32 %11, %12
161 br i1 %matches4, label %catch6, label %rethrow5
163 catch6: ; preds = %catch.start3
164 %13 = call i8* @__cxa_begin_catch(i8* %10) [ "funclet"(token %9) ]
165 %14 = bitcast i8* %13 to i32*
166 %15 = load i32, i32* %14, align 4
167 invoke void @foo() [ "funclet"(token %9) ]
168 to label %invoke.cont8 unwind label %ehcleanup
170 invoke.cont8: ; preds = %catch6
171 call void @__cxa_end_catch() [ "funclet"(token %9) ]
172 catchret from %9 to label %try.cont
174 rethrow5: ; preds = %catch.start3
175 invoke void @llvm.wasm.rethrow.in.catch() [ "funclet"(token %9) ]
176 to label %unreachable unwind label %ehcleanup9
178 try.cont: ; preds = %invoke.cont8, %catch
179 call void @__cxa_end_catch() [ "funclet"(token %1) ]
180 catchret from %1 to label %try.cont11
182 rethrow: ; preds = %catch.start
183 call void @llvm.wasm.rethrow.in.catch() [ "funclet"(token %1) ]
186 try.cont11: ; preds = %try.cont, %entry
189 ehcleanup: ; preds = %catch6
190 %16 = cleanuppad within %9 []
191 call void @__cxa_end_catch() [ "funclet"(token %16) ]
192 cleanupret from %16 unwind label %ehcleanup9
194 ehcleanup9: ; preds = %ehcleanup, %rethrow5, %catch.dispatch2
195 %17 = cleanuppad within %1 []
196 call void @__cxa_end_catch() [ "funclet"(token %17) ]
197 cleanupret from %17 unwind to caller
199 unreachable: ; preds = %rethrow5
203 ; Nested loop within a catch clause
208 ; for (int i = 0; i < 50; i++)
217 ; CHECK: i32.call $drop=, __cxa_begin_catch
218 ; CHECK: loop # label15:
221 ; CHECK: br_if 0, {{.*}} # 0: down to label17
224 ; CHECK: br 2 # 2: down to label16
227 ; CHECK: call __cxa_end_catch
229 ; CHECK: call __clang_call_terminate
232 ; CHECK: rethrow {{.*}} # to caller
234 ; CHECK: end_block # label17:
235 ; CHECK: call __cxa_end_catch
236 ; CHECK: br 2 # 2: down to label13
237 ; CHECK: end_block # label16:
238 ; CHECK: br 0 # 0: up to label15
240 ; CHECK: end_try # label13:
241 define void @test2() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
244 to label %try.cont unwind label %catch.dispatch
246 catch.dispatch: ; preds = %entry
247 %0 = catchswitch within none [label %catch.start] unwind to caller
249 catch.start: ; preds = %catch.dispatch
250 %1 = catchpad within %0 [i8* null]
251 %2 = call i8* @llvm.wasm.get.exception(token %1)
252 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
253 %4 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ]
256 for.cond: ; preds = %for.inc, %catch.start
257 %i.0 = phi i32 [ 0, %catch.start ], [ %inc, %for.inc ]
258 %cmp = icmp slt i32 %i.0, 50
259 br i1 %cmp, label %for.body, label %for.end
261 for.body: ; preds = %for.cond
262 invoke void @foo() [ "funclet"(token %1) ]
263 to label %for.inc unwind label %ehcleanup
265 for.inc: ; preds = %for.body
266 %inc = add nsw i32 %i.0, 1
269 for.end: ; preds = %for.cond
270 call void @__cxa_end_catch() [ "funclet"(token %1) ]
271 catchret from %1 to label %try.cont
273 try.cont: ; preds = %for.end, %entry
276 ehcleanup: ; preds = %for.body
277 %5 = cleanuppad within %1 []
278 invoke void @__cxa_end_catch() [ "funclet"(token %5) ]
279 to label %invoke.cont2 unwind label %terminate
281 invoke.cont2: ; preds = %ehcleanup
282 cleanupret from %5 unwind to caller
284 terminate: ; preds = %ehcleanup
285 %6 = cleanuppad within %5 []
286 %7 = call i8* @llvm.wasm.get.exception(token %6)
287 call void @__clang_call_terminate(i8* %7) [ "funclet"(token %6) ]
291 ; Tests if block and try markers are correctly placed. Even if two predecessors
292 ; of the EH pad are bb2 and bb3 and their nearest common dominator is bb1, the
293 ; TRY marker should be placed at bb0 because there's a branch from bb0 to bb2,
294 ; and scopes cannot be interleaved.
306 ; NOOPT: catch {{.*}}
308 define void @test3() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
310 br i1 undef, label %bb1, label %bb2
313 br i1 undef, label %bb3, label %bb4
320 to label %try.cont unwind label %catch.dispatch
324 to label %try.cont unwind label %catch.dispatch
326 catch.dispatch: ; preds = %bb4, %bb3
327 %0 = catchswitch within none [label %catch.start] unwind to caller
329 catch.start: ; preds = %catch.dispatch
330 %1 = catchpad within %0 [i8* null]
331 %2 = call i8* @llvm.wasm.get.exception(token %1)
332 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
333 catchret from %1 to label %try.cont
335 try.cont: ; preds = %catch.start, %bb4, %bb3, %bb2
339 ; Tests if try/end_try markers are placed correctly wrt loop/end_loop markers,
340 ; when try and loop markers are in the same BB and end_try and end_loop are in
348 define void @test4(i32* %p) personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
350 store volatile i32 0, i32* %p
353 loop: ; preds = %try.cont, %entry
354 store volatile i32 1, i32* %p
356 to label %try.cont unwind label %catch.dispatch
358 catch.dispatch: ; preds = %loop
359 %0 = catchswitch within none [label %catch.start] unwind to caller
361 catch.start: ; preds = %catch.dispatch
362 %1 = catchpad within %0 [i8* null]
363 %2 = call i8* @llvm.wasm.get.exception(token %1)
364 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
365 catchret from %1 to label %try.cont
367 try.cont: ; preds = %catch.start, %loop
371 ; Some of test cases below are hand-tweaked by deleting some library calls to
372 ; simplify tests and changing the order of basic blocks to cause unwind
373 ; destination mismatches. And we use -wasm-disable-ehpad-sort to create maximum
374 ; number of mismatches in several tests below.
376 ; 'call bar''s original unwind destination was 'catch14', but after control flow
377 ; linearization, its unwind destination incorrectly becomes 'catch15'. We fix
378 ; this by wrapping the call with a nested try/catch/end_try and branching to the
379 ; right destination (label32).
381 ; NOSORT-LABEL: test5
386 ; --- Nested try/catch/end_try starts
389 ; NOSORT: catch $drop=
390 ; NOSORT: br 2 # 2: down to label32
392 ; --- Nested try/catch/end_try ends
393 ; NOSORT: br 2 # 2: down to label31
394 ; NOSORT: catch $drop= # catch15:
395 ; NOSORT: br 2 # 2: down to label31
397 ; NOSORT: catch $drop= # catch14:
398 ; NOSORT: end_try # label32:
399 ; NOSORT: end_block # label31:
402 define void @test5() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
405 to label %bb1 unwind label %catch.dispatch0
409 to label %try.cont unwind label %catch.dispatch1
411 catch.dispatch0: ; preds = %bb0
412 %0 = catchswitch within none [label %catch.start0] unwind to caller
414 catch.start0: ; preds = %catch.dispatch0
415 %1 = catchpad within %0 [i8* null]
416 %2 = call i8* @llvm.wasm.get.exception(token %1)
417 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
418 catchret from %1 to label %try.cont
420 catch.dispatch1: ; preds = %bb1
421 %4 = catchswitch within none [label %catch.start1] unwind to caller
423 catch.start1: ; preds = %catch.dispatch1
424 %5 = catchpad within %4 [i8* null]
425 %6 = call i8* @llvm.wasm.get.exception(token %5)
426 %7 = call i32 @llvm.wasm.get.ehselector(token %5)
427 catchret from %5 to label %try.cont
429 try.cont: ; preds = %catch.start1, %catch.start0, %bb1
433 ; Two 'call bar''s original unwind destination was the caller, but after control
434 ; flow linearization, their unwind destination incorrectly becomes 'catch17'. We
435 ; fix this by wrapping the call with a nested try/catch/end_try and branching to
436 ; the right destination (label4), from which we rethrow the exception to the
439 ; NOSORT-LABEL: test6
442 ; --- Nested try/catch/end_try starts
446 ; NOSORT: catch $[[REG:[0-9]+]]=
447 ; NOSORT: br 1 # 1: down to label35
449 ; --- Nested try/catch/end_try ends
451 ; NOSORT: catch $drop= # catch17:
453 ; NOSORT: end_try # label35:
454 ; NOSORT: rethrow $[[REG]] # to caller
456 define void @test6() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
459 to label %bb1 unwind label %catch.dispatch0
466 catch.dispatch0: ; preds = %bb0
467 %0 = catchswitch within none [label %catch.start0] unwind to caller
469 catch.start0: ; preds = %catch.dispatch0
470 %1 = catchpad within %0 [i8* null]
471 %2 = call i8* @llvm.wasm.get.exception(token %1)
472 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
473 catchret from %1 to label %try.cont
475 try.cont: ; preds = %catch.start0
479 ; If not for the unwind destination mismatch, the LOOP marker here would have an
480 ; i32 signature. But because we add a rethrow instruction at the end of the
481 ; appendix block, now the LOOP marker does not have a signature (= has a void
482 ; signature). Here the two calls two 'bar' are supposed to throw up to the
483 ; caller, but incorrectly unwind to 'catch19' after linearizing the CFG.
485 ; NOSORT-LABEL: test7
487 ; NOSORT-NOT: loop i32
488 ; NOSORT: loop # label38:
491 ; --- Nested try/catch/end_try starts
495 ; NOSORT: catch $[[REG:[0-9]+]]=
496 ; NOSORT: br 1 # 1: down to label39
498 ; --- Nested try/catch/end_try ends
499 ; NOSORT: return {{.*}}
500 ; NOSORT: catch $drop= # catch19:
501 ; NOSORT: br 1 # 1: up to label38
502 ; NOSORT: end_try # label39:
505 ; NOSORT: rethrow $[[REG]] # to caller
507 define i32 @test7(i32* %p) personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
509 store volatile i32 0, i32* %p
512 loop: ; preds = %try.cont, %entry
513 store volatile i32 1, i32* %p
515 to label %bb unwind label %catch.dispatch
522 catch.dispatch: ; preds = %loop
523 %0 = catchswitch within none [label %catch.start] unwind to caller
525 catch.start: ; preds = %catch.dispatch
526 %1 = catchpad within %0 [i8* null]
527 %2 = call i8* @llvm.wasm.get.exception(token %1)
528 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
529 catchret from %1 to label %try.cont
531 try.cont: ; preds = %catch.start
535 ; When we have both kinds of EH pad unwind mismatches:
536 ; - A may-throw instruction unwinds to an incorrect EH pad after linearizing the
537 ; CFG, when it is supposed to unwind to another EH pad.
538 ; - A may-throw instruction unwinds to an incorrect EH pad after linearizing the
539 ; CFG, when it is supposed to unwind to the caller.
541 ; NOSORT-LABEL: test8
547 ; --- Nested try/catch/end_try starts
550 ; NOSORT: catch $[[REG0:[0-9]+]]=
551 ; NOSORT: br 2 # 2: down to label43
553 ; --- Nested try/catch/end_try ends
554 ; NOSORT: br 2 # 2: down to label42
555 ; NOSORT: catch {{.*}}
557 ; NOSORT: br_on_exn 0, {{.*}} # 0: down to label46
558 ; --- Nested try/catch/end_try starts
560 ; NOSORT: rethrow {{.*}} # down to catch24
561 ; NOSORT: catch $[[REG1:[0-9]+]]= # catch24:
562 ; NOSORT: br 5 # 5: down to label41
564 ; --- Nested try/catch/end_try ends
565 ; NOSORT: end_block # label46:
566 ; NOSORT: i32.call $drop=, __cxa_begin_catch
567 ; --- Nested try/catch/end_try starts
569 ; NOSORT: call __cxa_end_catch
570 ; NOSORT: catch $[[REG1]]=
571 ; NOSORT: br 4 # 4: down to label41
573 ; --- Nested try/catch/end_try ends
574 ; NOSORT: br 2 # 2: down to label42
576 ; NOSORT: catch $[[REG0]]=
577 ; NOSORT: end_try # label43:
578 ; NOSORT: i32.call $drop=, __cxa_begin_catch
579 ; NOSORT: call __cxa_end_catch
580 ; NOSORT: end_block # label42:
582 ; NOSORT: end_block # label41:
583 ; NOSORT: rethrow $[[REG1]] # to caller
584 define void @test8() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) {
587 to label %bb1 unwind label %catch.dispatch0
591 to label %try.cont unwind label %catch.dispatch1
593 catch.dispatch0: ; preds = %bb0
594 %0 = catchswitch within none [label %catch.start0] unwind to caller
596 catch.start0: ; preds = %catch.dispatch0
597 %1 = catchpad within %0 [i8* null]
598 %2 = call i8* @llvm.wasm.get.exception(token %1)
599 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
600 %4 = call i8* @__cxa_begin_catch(i8* %2) [ "funclet"(token %1) ]
601 call void @__cxa_end_catch() [ "funclet"(token %1) ]
602 catchret from %1 to label %try.cont
604 catch.dispatch1: ; preds = %bb1
605 %5 = catchswitch within none [label %catch.start1] unwind to caller
607 catch.start1: ; preds = %catch.dispatch1
608 %6 = catchpad within %5 [i8* null]
609 %7 = call i8* @llvm.wasm.get.exception(token %6)
610 %8 = call i32 @llvm.wasm.get.ehselector(token %6)
611 %9 = call i8* @__cxa_begin_catch(i8* %7) [ "funclet"(token %6) ]
612 call void @__cxa_end_catch() [ "funclet"(token %6) ]
613 catchret from %6 to label %try.cont
615 try.cont: ; preds = %catch.start1, %catch.start0, %bb1
621 declare i32 @__gxx_wasm_personality_v0(...)
622 declare i8* @llvm.wasm.get.exception(token)
623 declare i32 @llvm.wasm.get.ehselector(token)
624 declare void @llvm.wasm.rethrow.in.catch()
625 declare i32 @llvm.eh.typeid.for(i8*)
626 declare i8* @__cxa_begin_catch(i8*)
627 declare void @__cxa_end_catch()
628 declare void @__clang_call_terminate(i8*)
629 declare void @_ZSt9terminatev()