2 ; 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 -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling | FileCheck %s
3 ; RUN: llc < %s -disable-wasm-fallthrough-return-opt -disable-block-placement -verify-machineinstrs -fast-isel=false -machine-sink-split-probability-threshold=0 -cgp-freq-ratio-to-skip-merge=1000 -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling
4 ; RUN: llc < %s -O0 -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -verify-machineinstrs -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling | FileCheck %s --check-prefix=NOOPT
5 ; 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 -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling -wasm-disable-ehpad-sort -stats 2>&1 | FileCheck %s --check-prefix=NOSORT
6 ; RUN: llc < %s -disable-wasm-fallthrough-return-opt -disable-block-placement -verify-machineinstrs -fast-isel=false -machine-sink-split-probability-threshold=0 -cgp-freq-ratio-to-skip-merge=1000 -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling -wasm-disable-ehpad-sort | FileCheck %s --check-prefix=NOSORT-LOCALS
8 target triple = "wasm32-unknown-unknown"
10 @_ZTIi = external constant ptr
11 @_ZTId = external constant ptr
13 %class.Object = type { i8 }
14 %class.MyClass = type { i32 }
16 ; Simple test case with two catch clauses
32 ; CHECK: br_if 0, {{.*}} # 0: down to label[[L0:[0-9]+]]
33 ; CHECK: call $drop=, __cxa_begin_catch
34 ; CHECK: call __cxa_end_catch
35 ; CHECK: br 1 # 1: down to label[[L1:[0-9]+]]
36 ; CHECK: end_block # label[[L0]]:
38 ; CHECK: br_if 0, {{.*}} # 0: down to label[[L2:[0-9]+]]
39 ; CHECK: call $drop=, __cxa_begin_catch
40 ; CHECK: call __cxa_end_catch
41 ; CHECK: br 1 # 1: down to label[[L1]]
42 ; CHECK: end_block # label[[L2]]:
43 ; CHECK: rethrow 0 # to caller
44 ; CHECK: end_try # label[[L1]]:
45 define void @test0() personality ptr @__gxx_wasm_personality_v0 {
48 to label %try.cont unwind label %catch.dispatch
50 catch.dispatch: ; preds = %entry
51 %0 = catchswitch within none [label %catch.start] unwind to caller
53 catch.start: ; preds = %catch.dispatch
54 %1 = catchpad within %0 [ptr @_ZTIi, ptr @_ZTId]
55 %2 = call ptr @llvm.wasm.get.exception(token %1)
56 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
57 %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi)
58 %matches = icmp eq i32 %3, %4
59 br i1 %matches, label %catch2, label %catch.fallthrough
61 catch2: ; preds = %catch.start
62 %5 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
63 call void @__cxa_end_catch() [ "funclet"(token %1) ]
64 catchret from %1 to label %try.cont
66 catch.fallthrough: ; preds = %catch.start
67 %6 = call i32 @llvm.eh.typeid.for(ptr @_ZTId)
68 %matches1 = icmp eq i32 %3, %6
69 br i1 %matches1, label %catch, label %rethrow
71 catch: ; preds = %catch.fallthrough
72 %7 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
73 call void @__cxa_end_catch() [ "funclet"(token %1) ]
74 catchret from %1 to label %try.cont
76 rethrow: ; preds = %catch.fallthrough
77 call void @llvm.wasm.rethrow() [ "funclet"(token %1) ]
80 try.cont: ; preds = %catch, %catch2, %entry
84 ; Nested try-catches within a catch
103 ; CHECK: br_if 0, {{.*}} # 0: down to label[[L0:[0-9+]]]
104 ; CHECK: call $drop=, __cxa_begin_catch, $0
108 ; CHECK: br 3 # 3: down to label[[L1:[0-9+]]]
112 ; CHECK: br_if 0, {{.*}} # 0: down to label[[L2:[0-9+]]]
113 ; CHECK: call $drop=, __cxa_begin_catch
116 ; CHECK: br 2 # 2: down to label[[L3:[0-9+]]]
118 ; CHECK: call __cxa_end_catch
119 ; CHECK: rethrow 0 # down to catch[[L4:[0-9+]]]
121 ; CHECK: end_block # label[[L2]]:
122 ; CHECK: rethrow 1 # down to catch[[L4]]
123 ; CHECK: end_block # label[[L3]]:
124 ; CHECK: call __cxa_end_catch
125 ; CHECK: br 3 # 3: down to label[[L1]]
127 ; CHECK: catch_all # catch[[L4]]:
128 ; CHECK: call __cxa_end_catch
129 ; CHECK: rethrow 0 # to caller
131 ; CHECK: end_block # label[[L0]]:
132 ; CHECK: rethrow 1 # to caller
133 ; CHECK: end_block # label[[L1]]:
134 ; CHECK: call __cxa_end_catch
136 define void @test1() personality ptr @__gxx_wasm_personality_v0 {
139 to label %try.cont11 unwind label %catch.dispatch
141 catch.dispatch: ; preds = %entry
142 %0 = catchswitch within none [label %catch.start] unwind to caller
144 catch.start: ; preds = %catch.dispatch
145 %1 = catchpad within %0 [ptr @_ZTIi]
146 %2 = call ptr @llvm.wasm.get.exception(token %1)
147 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
148 %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi)
149 %matches = icmp eq i32 %3, %4
150 br i1 %matches, label %catch, label %rethrow
152 catch: ; preds = %catch.start
153 %5 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
154 %6 = load i32, ptr %5, align 4
155 invoke void @foo() [ "funclet"(token %1) ]
156 to label %try.cont unwind label %catch.dispatch2
158 catch.dispatch2: ; preds = %catch
159 %7 = catchswitch within %1 [label %catch.start3] unwind label %ehcleanup9
161 catch.start3: ; preds = %catch.dispatch2
162 %8 = catchpad within %7 [ptr @_ZTIi]
163 %9 = call ptr @llvm.wasm.get.exception(token %8)
164 %10 = call i32 @llvm.wasm.get.ehselector(token %8)
165 %11 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi)
166 %matches4 = icmp eq i32 %10, %11
167 br i1 %matches4, label %catch6, label %rethrow5
169 catch6: ; preds = %catch.start3
170 %12 = call ptr @__cxa_begin_catch(ptr %9) [ "funclet"(token %8) ]
171 %13 = load i32, ptr %12, align 4
172 invoke void @foo() [ "funclet"(token %8) ]
173 to label %invoke.cont8 unwind label %ehcleanup
175 invoke.cont8: ; preds = %catch6
176 call void @__cxa_end_catch() [ "funclet"(token %8) ]
177 catchret from %8 to label %try.cont
179 rethrow5: ; preds = %catch.start3
180 invoke void @llvm.wasm.rethrow() [ "funclet"(token %8) ]
181 to label %unreachable unwind label %ehcleanup9
183 try.cont: ; preds = %invoke.cont8, %catch
184 call void @__cxa_end_catch() [ "funclet"(token %1) ]
185 catchret from %1 to label %try.cont11
187 rethrow: ; preds = %catch.start
188 call void @llvm.wasm.rethrow() [ "funclet"(token %1) ]
191 try.cont11: ; preds = %try.cont, %entry
194 ehcleanup: ; preds = %catch6
195 %14 = cleanuppad within %8 []
196 call void @__cxa_end_catch() [ "funclet"(token %14) ]
197 cleanupret from %14 unwind label %ehcleanup9
199 ehcleanup9: ; preds = %ehcleanup, %rethrow5, %catch.dispatch2
200 %15 = cleanuppad within %1 []
201 call void @__cxa_end_catch() [ "funclet"(token %15) ]
202 cleanupret from %15 unwind to caller
204 unreachable: ; preds = %rethrow5
208 ; Nested loop within a catch clause
213 ; for (int i = 0; i < 50; i++)
218 ; CHECK-LABEL: test2:
222 ; CHECK: call $drop=, __cxa_begin_catch
223 ; CHECK: loop # label[[L0:[0-9]+]]:
226 ; CHECK: br_if 0, {{.*}} # 0: down to label[[L1:[0-9]+]]
229 ; CHECK: br 2 # 2: down to label[[L2:[0-9]+]]
232 ; CHECK: call __cxa_end_catch
234 ; CHECK: call _ZSt9terminatev
237 ; CHECK: rethrow 0 # to caller
239 ; CHECK: end_block # label[[L1]]:
240 ; CHECK: call __cxa_end_catch
241 ; CHECK: br 2 # 2: down to label[[L3:[0-9]+]]
242 ; CHECK: end_block # label[[L2]]:
243 ; CHECK: br 0 # 0: up to label[[L0]]
245 ; CHECK: end_try # label[[L3]]:
246 define void @test2() personality ptr @__gxx_wasm_personality_v0 {
249 to label %try.cont unwind label %catch.dispatch
251 catch.dispatch: ; preds = %entry
252 %0 = catchswitch within none [label %catch.start] unwind to caller
254 catch.start: ; preds = %catch.dispatch
255 %1 = catchpad within %0 [ptr null]
256 %2 = call ptr @llvm.wasm.get.exception(token %1)
257 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
258 %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
261 for.cond: ; preds = %for.inc, %catch.start
262 %i.0 = phi i32 [ 0, %catch.start ], [ %inc, %for.inc ]
263 %cmp = icmp slt i32 %i.0, 50
264 br i1 %cmp, label %for.body, label %for.end
266 for.body: ; preds = %for.cond
267 invoke void @foo() [ "funclet"(token %1) ]
268 to label %for.inc unwind label %ehcleanup
270 for.inc: ; preds = %for.body
271 %inc = add nsw i32 %i.0, 1
274 for.end: ; preds = %for.cond
275 call void @__cxa_end_catch() [ "funclet"(token %1) ]
276 catchret from %1 to label %try.cont
278 try.cont: ; preds = %for.end, %entry
281 ehcleanup: ; preds = %for.body
282 %5 = cleanuppad within %1 []
283 invoke void @__cxa_end_catch() [ "funclet"(token %5) ]
284 to label %invoke.cont2 unwind label %terminate
286 invoke.cont2: ; preds = %ehcleanup
287 cleanupret from %5 unwind to caller
289 terminate: ; preds = %ehcleanup
290 %6 = cleanuppad within %5 []
291 call void @_ZSt9terminatev() [ "funclet"(token %6) ]
295 ; Tests if block and try markers are correctly placed. Even if two predecessors
296 ; of the EH pad are bb2 and bb3 and their nearest common dominator is bb1, the
297 ; TRY marker should be placed at bb0 because there's a branch from bb0 to bb2,
298 ; and scopes cannot be interleaved.
300 ; NOOPT-LABEL: test3:
310 ; NOOPT: catch {{.*}}
312 define void @test3() personality ptr @__gxx_wasm_personality_v0 {
314 br i1 undef, label %bb1, label %bb2
317 br i1 undef, label %bb3, label %bb4
324 to label %try.cont unwind label %catch.dispatch
328 to label %try.cont unwind label %catch.dispatch
330 catch.dispatch: ; preds = %bb4, %bb3
331 %0 = catchswitch within none [label %catch.start] unwind to caller
333 catch.start: ; preds = %catch.dispatch
334 %1 = catchpad within %0 [ptr null]
335 %2 = call ptr @llvm.wasm.get.exception(token %1)
336 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
337 catchret from %1 to label %try.cont
339 try.cont: ; preds = %catch.start, %bb4, %bb3, %bb2
343 ; Tests if try/end_try markers are placed correctly wrt loop/end_loop markers,
344 ; when try and loop markers are in the same BB and end_try and end_loop are in
352 define void @test4(ptr %p) personality ptr @__gxx_wasm_personality_v0 {
354 store volatile i32 0, ptr %p
357 loop: ; preds = %try.cont, %entry
358 store volatile i32 1, ptr %p
360 to label %try.cont unwind label %catch.dispatch
362 catch.dispatch: ; preds = %loop
363 %0 = catchswitch within none [label %catch.start] unwind to caller
365 catch.start: ; preds = %catch.dispatch
366 %1 = catchpad within %0 [ptr null]
367 %2 = call ptr @llvm.wasm.get.exception(token %1)
368 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
369 catchret from %1 to label %try.cont
371 try.cont: ; preds = %catch.start, %loop
375 ; Some of test cases below are hand-tweaked by deleting some library calls to
376 ; simplify tests and changing the order of basic blocks to cause unwind
377 ; destination mismatches. And we use -wasm-disable-ehpad-sort to create maximum
378 ; number of mismatches in several tests below.
380 ; - Call unwind mismatch
381 ; 'call bar''s original unwind destination was 'C0', but after control flow
382 ; linearization, its unwind destination incorrectly becomes 'C1'. We fix this by
383 ; wrapping the call with a nested try-delegate that targets 'C0'.
384 ; - Catch unwind mismatch
385 ; If 'call foo' throws a foreign exception, it will not be caught by C1, and
386 ; should be rethrown to the caller. But after control flow linearization, it
387 ; will instead unwind to C0, an incorrect next EH pad. We wrap the whole
388 ; try-catch with try-delegate that rethrows an exception to the caller to fix
391 ; NOSORT-LABEL: test5:
393 ; --- try-delegate starts (catch unwind mismatch)
397 ; --- try-delegate starts (call unwind mismatch)
400 ; NOSORT: delegate 2 # label/catch{{[0-9]+}}: down to catch[[C0:[0-9]+]]
401 ; --- try-delegate ends (call unwind mismatch)
402 ; NOSORT: catch {{.*}} # catch[[C1:[0-9]+]]:
404 ; NOSORT: delegate 1 # label/catch{{[0-9]+}}: to caller
405 ; --- try-delegate ends (catch unwind mismatch)
406 ; NOSORT: catch {{.*}} # catch[[C0]]:
410 define void @test5() personality ptr @__gxx_wasm_personality_v0 {
413 to label %bb1 unwind label %catch.dispatch0
417 to label %try.cont unwind label %catch.dispatch1
419 catch.dispatch0: ; preds = %bb0
420 %0 = catchswitch within none [label %catch.start0] unwind to caller
422 catch.start0: ; preds = %catch.dispatch0
423 %1 = catchpad within %0 [ptr null]
424 %2 = call ptr @llvm.wasm.get.exception(token %1)
425 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
426 catchret from %1 to label %try.cont
428 catch.dispatch1: ; preds = %bb1
429 %4 = catchswitch within none [label %catch.start1] unwind to caller
431 catch.start1: ; preds = %catch.dispatch1
432 %5 = catchpad within %4 [ptr null]
433 %6 = call ptr @llvm.wasm.get.exception(token %5)
434 %7 = call i32 @llvm.wasm.get.ehselector(token %5)
435 catchret from %5 to label %try.cont
437 try.cont: ; preds = %catch.start1, %catch.start0, %bb1
441 ; 'call bar' and 'call baz''s original unwind destination was the caller, but
442 ; after control flow linearization, their unwind destination incorrectly becomes
443 ; 'C0'. We fix this by wrapping the calls with a nested try-delegate that
444 ; rethrows exceptions to the caller.
446 ; And the return value of 'baz' should NOT be stackified because the BB is split
447 ; during fixing unwind mismatches.
449 ; NOSORT-LABEL: test6:
452 ; --- try-delegate starts (call unwind mismatch)
455 ; NOSORT: call $[[RET:[0-9]+]]=, baz
456 ; NOSORT-NOT: call $push{{.*}}=, baz
457 ; NOSORT: delegate 1 # label/catch{{[0-9]+}}: to caller
458 ; --- try-delegate ends (call unwind mismatch)
459 ; NOSORT: call nothrow, $[[RET]]
461 ; NOSORT: catch {{.*}} # catch[[C0:[0-9]+]]:
465 define void @test6() personality ptr @__gxx_wasm_personality_v0 {
468 to label %bb1 unwind label %catch.dispatch0
472 %call = call i32 @baz()
473 call void @nothrow(i32 %call) #0
476 catch.dispatch0: ; preds = %bb0
477 %0 = catchswitch within none [label %catch.start0] unwind to caller
479 catch.start0: ; preds = %catch.dispatch0
480 %1 = catchpad within %0 [ptr null]
481 %2 = call ptr @llvm.wasm.get.exception(token %1)
482 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
483 catchret from %1 to label %try.cont
485 try.cont: ; preds = %catch.start0
489 ; The same as test5, but we have one more call 'call @foo' in bb1 which unwinds
490 ; to the caller. IN this case bb1 has two call unwind mismatches: 'call @foo'
491 ; unwinds to the caller and 'call @bar' unwinds to catch C0.
493 ; NOSORT-LABEL: test7:
495 ; --- try-delegate starts (catch unwind mismatch)
499 ; --- try-delegate starts (call unwind mismatch)
502 ; NOSORT: delegate 3 # label/catch{{[0-9]+}}: to caller
503 ; --- try-delegate ends (call unwind mismatch)
504 ; --- try-delegate starts (call unwind mismatch)
507 ; NOSORT: delegate 2 # label/catch{{[0-9]+}}: down to catch[[C0:[0-9]+]]
508 ; --- try-delegate ends (call unwind mismatch)
509 ; NOSORT: catch {{.*}} # catch[[C1:[0-9]+]]:
511 ; NOSORT: delegate 1 # label/catch{{[0-9]+}}: to caller
512 ; --- try-delegate ends (catch unwind mismatch)
513 ; NOSORT: catch {{.*}} # catch[[C0]]:
517 define void @test7() personality ptr @__gxx_wasm_personality_v0 {
520 to label %bb1 unwind label %catch.dispatch0
525 to label %try.cont unwind label %catch.dispatch1
527 catch.dispatch0: ; preds = %bb0
528 %0 = catchswitch within none [label %catch.start0] unwind to caller
530 catch.start0: ; preds = %catch.dispatch0
531 %1 = catchpad within %0 [ptr null]
532 %2 = call ptr @llvm.wasm.get.exception(token %1)
533 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
534 catchret from %1 to label %try.cont
536 catch.dispatch1: ; preds = %bb1
537 %4 = catchswitch within none [label %catch.start1] unwind to caller
539 catch.start1: ; preds = %catch.dispatch1
540 %5 = catchpad within %4 [ptr null]
541 %6 = call ptr @llvm.wasm.get.exception(token %5)
542 %7 = call i32 @llvm.wasm.get.ehselector(token %5)
543 catchret from %5 to label %try.cont
545 try.cont: ; preds = %catch.start1, %catch.start0, %bb1
549 ; Similar situation as @test6. Here 'call @qux''s original unwind destination
550 ; was the caller, but after control flow linearization, their unwind destination
551 ; incorrectly becomes 'C0' within the function. We fix this by wrapping the call
552 ; with a nested try-delegate that rethrows the exception to the caller.
554 ; Because 'call @qux' pops an argument pushed by 'i32.const 5' from stack, the
555 ; nested 'try' should be placed before `i32.const 5', not between 'i32.const 5'
558 ; NOSORT-LABEL: test8:
561 ; --- try-delegate starts (call unwind mismatch)
563 ; NOSORT: i32.const $push{{[0-9]+}}=, 5
564 ; NOSORT: call ${{[0-9]+}}=, qux
565 ; NOSORT: delegate 1 # label/catch{{[0-9]+}}: to caller
566 ; --- try-delegate ends (call unwind mismatch)
568 ; NOSORT: catch {{.*}} # catch[[C0:[0-9]+]]:
572 define i32 @test8() personality ptr @__gxx_wasm_personality_v0 {
575 to label %bb1 unwind label %catch.dispatch0
578 %0 = call i32 @qux(i32 5)
581 catch.dispatch0: ; preds = %bb0
582 %1 = catchswitch within none [label %catch.start0] unwind to caller
584 catch.start0: ; preds = %catch.dispatch0
585 %2 = catchpad within %1 [ptr null]
586 %3 = call ptr @llvm.wasm.get.exception(token %2)
587 %j = call i32 @llvm.wasm.get.ehselector(token %2)
588 catchret from %2 to label %try.cont
590 try.cont: ; preds = %catch.start0
594 ; Tests the case when TEE stackifies a register in RegStackify but it gets
595 ; unstackified in fixCallUnwindMismatches in CFGStackify.
597 ; NOSORT-LOCALS-LABEL: test9:
598 define void @test9(i32 %x) personality ptr @__gxx_wasm_personality_v0 {
601 to label %bb1 unwind label %catch.dispatch0
605 ; This %addr is used in multiple places, so tee is introduced in RegStackify,
606 ; which stackifies the use of %addr in store instruction. A tee has two dest
607 ; registers, the first of which is stackified and the second is not.
608 ; But when we introduce a nested try-delegate in fixCallUnwindMismatches in
609 ; CFGStackify, it is possible that we end up unstackifying the first dest
610 ; register. In that case, we convert that tee into a copy.
611 %addr = inttoptr i32 %t to ptr
612 %load = load i32, ptr %addr
613 %call = call i32 @baz()
614 %add = add i32 %load, %call
615 store i32 %add, ptr %addr
617 ; NOSORT-LOCALS: i32.add
618 ; NOSORT-LOCALS-NOT: local.tee
619 ; NOSORT-LOCALS-NEXT: local.set
621 catch.dispatch0: ; preds = %bb0
622 %0 = catchswitch within none [label %catch.start0] unwind to caller
624 catch.start0: ; preds = %catch.dispatch0
625 %1 = catchpad within %0 [ptr null]
626 %2 = call ptr @llvm.wasm.get.exception(token %1)
627 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
628 catchret from %1 to label %try.cont
630 try.cont: ; preds = %catch.start0
634 ; We have two call unwind unwind mismatches:
635 ; - A may-throw instruction unwinds to an incorrect EH pad after linearizing the
636 ; CFG, when it is supposed to unwind to another EH pad.
637 ; - A may-throw instruction unwinds to an incorrect EH pad after linearizing the
638 ; CFG, when it is supposed to unwind to the caller.
639 ; We also have a catch unwind mismatch: If an exception is not caught by the
640 ; first catch because it is a non-C++ exception, it shouldn't unwind to the next
641 ; catch, but it should unwind to the caller.
643 ; NOSORT-LABEL: test10:
645 ; --- try-delegate starts (catch unwind mismatch)
649 ; --- try-delegate starts (call unwind mismatch)
652 ; NOSORT: delegate 2 # label/catch{{[0-9]+}}: down to catch[[C0:[0-9]+]]
653 ; --- try-delegate ends (call unwind mismatch)
655 ; NOSORT: call {{.*}} __cxa_begin_catch
656 ; --- try-delegate starts (call unwind mismatch)
658 ; NOSORT: call __cxa_end_catch
659 ; NOSORT: delegate 3 # label/catch{{[0-9]+}}: to caller
660 ; --- try-delegate ends (call unwind mismatch)
662 ; NOSORT: delegate 1 # label/catch{{[0-9]+}}: to caller
663 ; --- try-delegate ends (catch unwind mismatch)
664 ; NOSORT: catch {{.*}} # catch[[C0]]:
665 ; NOSORT: call {{.*}} __cxa_begin_catch
666 ; NOSORT: call __cxa_end_catch
670 define void @test10() personality ptr @__gxx_wasm_personality_v0 {
673 to label %bb1 unwind label %catch.dispatch0
677 to label %try.cont unwind label %catch.dispatch1
679 catch.dispatch0: ; preds = %bb0
680 %0 = catchswitch within none [label %catch.start0] unwind to caller
682 catch.start0: ; preds = %catch.dispatch0
683 %1 = catchpad within %0 [ptr null]
684 %2 = call ptr @llvm.wasm.get.exception(token %1)
685 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
686 %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
687 call void @__cxa_end_catch() [ "funclet"(token %1) ]
688 catchret from %1 to label %try.cont
690 catch.dispatch1: ; preds = %bb1
691 %5 = catchswitch within none [label %catch.start1] unwind to caller
693 catch.start1: ; preds = %catch.dispatch1
694 %6 = catchpad within %5 [ptr null]
695 %7 = call ptr @llvm.wasm.get.exception(token %6)
696 %8 = call i32 @llvm.wasm.get.ehselector(token %6)
697 %9 = call ptr @__cxa_begin_catch(ptr %7) [ "funclet"(token %6) ]
698 call void @__cxa_end_catch() [ "funclet"(token %6) ]
699 catchret from %6 to label %try.cont
701 try.cont: ; preds = %catch.start1, %catch.start0, %bb1
705 ; In CFGSort, EH pads should be sorted as soon as it is available and
706 ; 'Preferred' queue and should NOT be entered into 'Ready' queue unless we are
707 ; in the middle of sorting another region that does not contain the EH pad. In
708 ; this example, 'catch.start' should be sorted right after 'if.then' is sorted
709 ; (before 'cont' is sorted) and there should not be any unwind destination
710 ; mismatches in CFGStackify.
712 ; NOOPT-LABEL: test11:
721 define void @test11(i32 %arg) personality ptr @__gxx_wasm_personality_v0 {
723 %tobool = icmp ne i32 %arg, 0
724 br i1 %tobool, label %if.then, label %if.end
726 catch.dispatch: ; preds = %if.then
727 %0 = catchswitch within none [label %catch.start] unwind to caller
729 catch.start: ; preds = %catch.dispatch
730 %1 = catchpad within %0 [ptr null]
731 %2 = call ptr @llvm.wasm.get.exception(token %1)
732 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
733 %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
734 call void @__cxa_end_catch() [ "funclet"(token %1) ]
735 catchret from %1 to label %if.end
737 if.then: ; preds = %entry
739 to label %cont unwind label %catch.dispatch
741 cont: ; preds = %if.then
745 if.end: ; preds = %cont, %catch.start, %entry
749 ; Intrinsics like memcpy, memmove, and memset don't throw and are lowered into
750 ; calls to external symbols (not global addresses) in instruction selection,
751 ; which will be eventually lowered to library function calls.
752 ; Because this test runs with -wasm-disable-ehpad-sort, these library calls in
753 ; invoke.cont BB fall within try~end_try, but they shouldn't cause crashes or
754 ; unwinding destination mismatches in CFGStackify.
756 ; NOSORT-LABEL: test12:
759 ; NOSORT: call {{.*}} memcpy
760 ; NOSORT: call {{.*}} memmove
761 ; NOSORT: call {{.*}} memset
766 define void @test12(ptr %a, ptr %b) personality ptr @__gxx_wasm_personality_v0 {
768 %o = alloca %class.Object, align 1
770 to label %invoke.cont unwind label %ehcleanup
772 invoke.cont: ; preds = %entry
773 call void @llvm.memcpy.p0.p0.i32(ptr %a, ptr %b, i32 100, i1 false)
774 call void @llvm.memmove.p0.p0.i32(ptr %a, ptr %b, i32 100, i1 false)
775 call void @llvm.memset.p0.i32(ptr %a, i8 0, i32 100, i1 false)
776 %call = call ptr @_ZN6ObjectD2Ev(ptr %o)
779 ehcleanup: ; preds = %entry
780 %0 = cleanuppad within none []
781 %call2 = call ptr @_ZN6ObjectD2Ev(ptr %o) [ "funclet"(token %0) ]
782 cleanupret from %0 unwind to caller
785 ; Tests if 'try' marker is placed correctly. In this test, 'try' should be
786 ; placed before the call to 'nothrow_i32' and not between the call to
787 ; 'nothrow_i32' and 'fun', because the return value of 'nothrow_i32' is
788 ; stackified and pushed onto the stack to be consumed by the call to 'fun'.
790 ; CHECK-LABEL: test13:
792 ; CHECK: call $push{{.*}}=, nothrow_i32
793 ; CHECK: call fun, $pop{{.*}}
794 define void @test13() personality ptr @__gxx_wasm_personality_v0 {
796 %call = call i32 @nothrow_i32()
797 invoke void @fun(i32 %call)
798 to label %invoke.cont unwind label %terminate
800 invoke.cont: ; preds = %entry
803 terminate: ; preds = %entry
804 %0 = cleanuppad within none []
805 call void @_ZSt9terminatev() [ "funclet"(token %0) ]
809 ; This crashed on debug mode (= when NDEBUG is not defined) when the logic for
810 ; computing the innermost region was not correct, in which a loop region
811 ; contains an exception region. This should pass CFGSort without crashing.
812 define void @test14() personality ptr @__gxx_wasm_personality_v0 {
814 %e = alloca %class.MyClass, align 4
817 for.cond: ; preds = %for.inc, %entry
818 %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.inc ]
819 %cmp = icmp slt i32 %i.0, 9
820 br i1 %cmp, label %for.body, label %for.end
822 for.body: ; preds = %for.cond
823 invoke void @quux(i32 %i.0)
824 to label %for.inc unwind label %catch.dispatch
826 catch.dispatch: ; preds = %for.body
827 %0 = catchswitch within none [label %catch.start] unwind to caller
829 catch.start: ; preds = %catch.dispatch
830 %1 = catchpad within %0 [ptr @_ZTI7MyClass]
831 %2 = call ptr @llvm.wasm.get.exception(token %1)
832 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
833 %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTI7MyClass)
834 %matches = icmp eq i32 %3, %4
835 br i1 %matches, label %catch, label %rethrow
837 catch: ; preds = %catch.start
838 %5 = call ptr @__cxa_get_exception_ptr(ptr %2) [ "funclet"(token %1) ]
839 %call = call ptr @_ZN7MyClassC2ERKS_(ptr %e, ptr dereferenceable(4) %5) [ "funclet"(token %1) ]
840 %6 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
841 %7 = load i32, ptr %e, align 4
842 invoke void @quux(i32 %7) [ "funclet"(token %1) ]
843 to label %invoke.cont2 unwind label %ehcleanup
845 invoke.cont2: ; preds = %catch
846 %call3 = call ptr @_ZN7MyClassD2Ev(ptr %e) [ "funclet"(token %1) ]
847 call void @__cxa_end_catch() [ "funclet"(token %1) ]
848 catchret from %1 to label %for.inc
850 rethrow: ; preds = %catch.start
851 call void @llvm.wasm.rethrow() [ "funclet"(token %1) ]
854 for.inc: ; preds = %invoke.cont2, %for.body
855 %inc = add nsw i32 %i.0, 1
858 ehcleanup: ; preds = %catch
859 %8 = cleanuppad within %1 []
860 %call4 = call ptr @_ZN7MyClassD2Ev(ptr %e) [ "funclet"(token %8) ]
861 invoke void @__cxa_end_catch() [ "funclet"(token %8) ]
862 to label %invoke.cont6 unwind label %terminate7
864 invoke.cont6: ; preds = %ehcleanup
865 cleanupret from %8 unwind to caller
867 for.end: ; preds = %for.cond
870 terminate7: ; preds = %ehcleanup
871 %9 = cleanuppad within %8 []
872 call void @_ZSt9terminatev() [ "funclet"(token %9) ]
876 ; Tests if CFGStackify's removeUnnecessaryInstrs() removes unnecessary branches
877 ; correctly. The code is in the form below, where 'br' is unnecessary because
878 ; after running the 'try' body the control flow will fall through to bb2 anyway.
883 ; br bb2 <- Not necessary
887 ; bb2: <- Continuation BB
889 ; CHECK-LABEL: test15:
890 define void @test15(i32 %n) personality ptr @__gxx_wasm_personality_v0 {
893 to label %for.body unwind label %catch.dispatch
895 for.body: ; preds = %for.end, %entry
896 %i = phi i32 [ %inc, %for.end ], [ 0, %entry ]
898 to label %for.end unwind label %catch.dispatch
900 ; Before going to CFGStackify, this BB will have a conditional branch followed
901 ; by an unconditional branch. CFGStackify should remove only the unconditional
903 for.end: ; preds = %for.body
904 %inc = add nuw nsw i32 %i, 1
905 %exitcond = icmp eq i32 %inc, %n
906 br i1 %exitcond, label %try.cont, label %for.body
912 catch.dispatch: ; preds = %for.body, %entry
913 %0 = catchswitch within none [label %catch.start] unwind to caller
915 catch.start: ; preds = %catch.dispatch
916 %1 = catchpad within %0 [ptr null]
917 %2 = call ptr @llvm.wasm.get.exception(token %1)
918 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
919 %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
920 call void @__cxa_end_catch() [ "funclet"(token %1) ]
921 catchret from %1 to label %try.cont
923 try.cont: ; preds = %catch.start, %for.end
939 ; This tests whether the 'br' can be removed in code in the form as follows.
940 ; Here 'br' is inside an inner try, whose 'end' is in another EH pad. In this
941 ; case, after running an inner try body, the control flow should fall through to
942 ; bb3, so the 'br' in the code is unnecessary.
948 ; br bb3 <- Not necessary
955 ; bb3: <- Continuation BB
958 ; CHECK-LABEL: test16:
959 define void @test16() personality ptr @__gxx_wasm_personality_v0 {
963 to label %invoke.cont unwind label %catch.dispatch3
967 invoke.cont: ; preds = %entry
969 to label %try.cont8 unwind label %catch.dispatch
971 catch.dispatch: ; preds = %invoke.cont
972 %0 = catchswitch within none [label %catch.start] unwind label %catch.dispatch3
975 catch.start: ; preds = %catch.dispatch
976 %1 = catchpad within %0 [ptr null]
977 %2 = call ptr @llvm.wasm.get.exception(token %1)
978 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
979 %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
980 invoke void @__cxa_end_catch() [ "funclet"(token %1) ]
981 to label %invoke.cont2 unwind label %catch.dispatch3
983 catch.dispatch3: ; preds = %catch.start, %catch.dispatch, %entry
984 %5 = catchswitch within none [label %catch.start4] unwind to caller
986 catch.start4: ; preds = %catch.dispatch3
987 %6 = catchpad within %5 [ptr null]
988 %7 = call ptr @llvm.wasm.get.exception(token %6)
989 %8 = call i32 @llvm.wasm.get.ehselector(token %6)
990 %9 = call ptr @__cxa_begin_catch(ptr %7) [ "funclet"(token %6) ]
991 call void @__cxa_end_catch() [ "funclet"(token %6) ]
992 catchret from %6 to label %try.cont8
994 try.cont8: ; preds = %invoke.cont2, %catch.start4, %invoke.cont
997 invoke.cont2: ; preds = %catch.start
998 catchret from %1 to label %try.cont8
1001 ; Here an exception is semantically contained in a loop. 'ehcleanup' BB belongs
1002 ; to the exception, but does not belong to the loop (because it does not have a
1003 ; path back to the loop header), and is placed after the loop latch block
1004 ; 'invoke.cont' intentionally. This tests if 'end_loop' marker is placed
1005 ; correctly not right after 'invoke.cont' part but after 'ehcleanup' part,
1006 ; NOSORT-LABEL: test17:
1011 define void @test17(i32 %n) personality ptr @__gxx_wasm_personality_v0 {
1013 br label %while.cond
1015 while.cond: ; preds = %invoke.cont, %entry
1016 %n.addr.0 = phi i32 [ %n, %entry ], [ %dec, %invoke.cont ]
1017 %tobool = icmp ne i32 %n.addr.0, 0
1018 br i1 %tobool, label %while.body, label %while.end
1020 while.body: ; preds = %while.cond
1021 %dec = add nsw i32 %n.addr.0, -1
1023 to label %while.end unwind label %catch.dispatch
1025 catch.dispatch: ; preds = %while.body
1026 %0 = catchswitch within none [label %catch.start] unwind to caller
1028 catch.start: ; preds = %catch.dispatch
1029 %1 = catchpad within %0 [ptr null]
1030 %2 = call ptr @llvm.wasm.get.exception(token %1)
1031 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
1032 %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
1033 invoke void @__cxa_end_catch() [ "funclet"(token %1) ]
1034 to label %invoke.cont unwind label %ehcleanup
1036 invoke.cont: ; preds = %catch.start
1037 catchret from %1 to label %while.cond
1039 ehcleanup: ; preds = %catch.start
1040 %5 = cleanuppad within %1 []
1041 call void @_ZSt9terminatev() [ "funclet"(token %5) ]
1044 while.end: ; preds = %while.body, %while.cond
1048 ; When the function return type is non-void and 'end' instructions are at the
1049 ; very end of a function, CFGStackify's fixEndsAtEndOfFunction function fixes
1050 ; the corresponding block/loop/try's type to match the function's return type.
1051 ; But when a `try`'s type is fixed, we should also check `end` instructions
1052 ; before its corresponding `catch_all`, because both `try` and `catch_all` body
1053 ; should satisfy the return type requirements.
1055 ; NOSORT-LABEL: test18:
1061 ; NOSORT-NEXT: end_function
1062 define i32 @test18(i32 %n) personality ptr @__gxx_wasm_personality_v0 {
1064 %t = alloca %class.Object, align 1
1067 for.cond: ; preds = %for.inc, %entry
1068 %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.inc ]
1069 %cmp = icmp slt i32 %i.0, %n
1072 for.body: ; preds = %for.cond
1073 %div = sdiv i32 %n, 2
1074 %cmp1 = icmp eq i32 %i.0, %div
1075 br i1 %cmp1, label %if.then, label %for.inc
1077 if.then: ; preds = %for.body
1078 %call = invoke i32 @baz()
1079 to label %invoke.cont unwind label %ehcleanup
1081 invoke.cont: ; preds = %if.then
1082 %call2 = call ptr @_ZN6ObjectD2Ev(ptr %t)
1085 for.inc: ; preds = %for.body
1086 %inc = add nsw i32 %i.0, 1
1089 ehcleanup: ; preds = %if.then
1090 %0 = cleanuppad within none []
1091 %call3 = call ptr @_ZN6ObjectD2Ev(ptr %t) [ "funclet"(token %0) ]
1092 cleanupret from %0 unwind to caller
1095 ; This crashed when updating EHPadStack within fixCallUniwindMismatch had a bug.
1096 ; This should not crash and try-delegate has to be created around 'call @baz',
1097 ; because the initial TRY placement for 'call @quux' was done before 'call @baz'
1098 ; because 'call @baz''s return value is stackified.
1100 ; CHECK-LABEL: test19:
1103 ; CHECK: call $[[RET:[0-9]+]]=, baz
1105 ; CHECK: call quux, $[[RET]]
1108 define void @test19() personality ptr @__gxx_wasm_personality_v0 {
1110 %call = call i32 @baz()
1111 invoke void @quux(i32 %call)
1112 to label %invoke.cont unwind label %ehcleanup
1114 ehcleanup: ; preds = %entry
1115 %0 = cleanuppad within none []
1116 cleanupret from %0 unwind to caller
1118 invoke.cont: ; preds = %entry
1122 ; This tests if invalidated branch destinations after fixing catch unwind
1123 ; mismatches are correctly remapped. For example, we have this code and suppose
1124 ; we need to wrap this try-catch-end in this code with a try-delegate to fix a
1125 ; catch unwind mismatch:
1146 ; After adding a try-delegate, the 'br's destination BB, where (a) points,
1147 ; becomes invalid because it incorrectly branches into an inner scope. The
1148 ; destination should change to the BB where (b) points.
1150 ; NOSORT-LABEL: test20:
1153 define void @test20(i1 %arg) personality ptr @__gxx_wasm_personality_v0 {
1155 br i1 %arg, label %bb0, label %dest
1157 bb0: ; preds = %entry
1159 to label %bb1 unwind label %catch.dispatch0
1163 to label %try.cont unwind label %catch.dispatch1
1165 catch.dispatch0: ; preds = %bb0
1166 %0 = catchswitch within none [label %catch.start0] unwind to caller
1168 catch.start0: ; preds = %catch.dispatch0
1169 %1 = catchpad within %0 [ptr null]
1170 %2 = call ptr @llvm.wasm.get.exception(token %1)
1171 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
1172 catchret from %1 to label %try.cont
1174 dest: ; preds = %entry
1177 catch.dispatch1: ; preds = %bb1
1178 %4 = catchswitch within none [label %catch.start1] unwind to caller
1180 catch.start1: ; preds = %catch.dispatch1
1181 %5 = catchpad within %4 [ptr null]
1182 %6 = call ptr @llvm.wasm.get.exception(token %5)
1183 %7 = call i32 @llvm.wasm.get.ehselector(token %5)
1184 catchret from %5 to label %try.cont
1186 try.cont: ; preds = %catch.start1, %catch.start0, %bb1
1190 ; The similar case with test20, but multiple consecutive delegates are
1214 ; <- (b) The br destination should be remapped to here
1216 ; The test was reduced by bugpoint and should not crash in CFGStackify.
1217 define void @test21() personality ptr @__gxx_wasm_personality_v0 {
1219 br i1 undef, label %if.then, label %if.end12
1221 if.then: ; preds = %entry
1222 invoke void @__cxa_throw(ptr null, ptr null, ptr null) #1
1223 to label %unreachable unwind label %catch.dispatch
1225 catch.dispatch: ; preds = %if.then
1226 %0 = catchswitch within none [label %catch.start] unwind to caller
1228 catch.start: ; preds = %catch.dispatch
1229 %1 = catchpad within %0 [ptr @_ZTIi]
1230 %2 = call ptr @llvm.wasm.get.exception(token %1)
1231 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
1232 catchret from %1 to label %catchret.dest
1234 catchret.dest: ; preds = %catch.start
1236 to label %invoke.cont unwind label %catch.dispatch4
1238 invoke.cont: ; preds = %catchret.dest
1239 invoke void @__cxa_throw(ptr null, ptr null, ptr null) #1
1240 to label %unreachable unwind label %catch.dispatch4
1242 catch.dispatch4: ; preds = %invoke.cont, %catchret.dest
1243 %4 = catchswitch within none [label %catch.start5] unwind to caller
1245 catch.start5: ; preds = %catch.dispatch4
1246 %5 = catchpad within %4 [ptr @_ZTIi]
1247 %6 = call ptr @llvm.wasm.get.exception(token %5)
1248 %7 = call i32 @llvm.wasm.get.ehselector(token %5)
1251 if.end12: ; preds = %entry
1253 to label %invoke.cont14 unwind label %catch.dispatch16
1255 catch.dispatch16: ; preds = %if.end12
1256 %8 = catchswitch within none [label %catch.start17] unwind label %ehcleanup
1258 catch.start17: ; preds = %catch.dispatch16
1259 %9 = catchpad within %8 [ptr @_ZTIi]
1260 %10 = call ptr @llvm.wasm.get.exception(token %9)
1261 %11 = call i32 @llvm.wasm.get.ehselector(token %9)
1262 br i1 undef, label %catch20, label %rethrow19
1264 catch20: ; preds = %catch.start17
1265 catchret from %9 to label %catchret.dest22
1267 catchret.dest22: ; preds = %catch20
1268 br label %try.cont23
1270 rethrow19: ; preds = %catch.start17
1271 invoke void @llvm.wasm.rethrow() #1 [ "funclet"(token %9) ]
1272 to label %unreachable unwind label %ehcleanup
1274 try.cont23: ; preds = %invoke.cont14, %catchret.dest22
1276 to label %invoke.cont24 unwind label %ehcleanup
1278 invoke.cont24: ; preds = %try.cont23
1281 invoke.cont14: ; preds = %if.end12
1282 br label %try.cont23
1284 ehcleanup: ; preds = %try.cont23, %rethrow19, %catch.dispatch16
1285 %12 = cleanuppad within none []
1286 cleanupret from %12 unwind to caller
1288 unreachable: ; preds = %rethrow19, %invoke.cont, %if.then
1292 ; Regression test for WasmEHFuncInfo's reverse mapping bug. 'UnwindDestToSrc'
1293 ; should return a vector and not a single BB, which was incorrect.
1294 ; This was reduced by bugpoint and should not crash in CFGStackify.
1295 define void @test22() personality ptr @__gxx_wasm_personality_v0 {
1298 to label %invoke.cont unwind label %catch.dispatch
1300 catch.dispatch: ; preds = %entry
1301 %0 = catchswitch within none [label %catch.start] unwind label %ehcleanup22
1303 catch.start: ; preds = %catch.dispatch
1304 %1 = catchpad within %0 [ptr @_ZTIi]
1305 %2 = call ptr @llvm.wasm.get.exception(token %1)
1306 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
1307 invoke void @__cxa_throw(ptr null, ptr null, ptr null) #1 [ "funclet"(token %1) ]
1308 to label %unreachable unwind label %catch.dispatch2
1310 catch.dispatch2: ; preds = %catch.start
1311 %4 = catchswitch within %1 [label %catch.start3] unwind label %ehcleanup
1313 catch.start3: ; preds = %catch.dispatch2
1314 %5 = catchpad within %4 [ptr @_ZTIi]
1315 %6 = call ptr @llvm.wasm.get.exception(token %5)
1316 %7 = call i32 @llvm.wasm.get.ehselector(token %5)
1317 catchret from %5 to label %try.cont
1319 try.cont: ; preds = %catch.start3
1320 invoke void @foo() [ "funclet"(token %1) ]
1321 to label %invoke.cont8 unwind label %ehcleanup
1323 invoke.cont8: ; preds = %try.cont
1324 invoke void @__cxa_throw(ptr null, ptr null, ptr null) #1 [ "funclet"(token %1) ]
1325 to label %unreachable unwind label %catch.dispatch11
1327 catch.dispatch11: ; preds = %invoke.cont8
1328 %8 = catchswitch within %1 [label %catch.start12] unwind label %ehcleanup
1330 catch.start12: ; preds = %catch.dispatch11
1331 %9 = catchpad within %8 [ptr @_ZTIi]
1332 %10 = call ptr @llvm.wasm.get.exception(token %9)
1333 %11 = call i32 @llvm.wasm.get.ehselector(token %9)
1336 invoke.cont: ; preds = %entry
1339 ehcleanup: ; preds = %catch.dispatch11, %try.cont, %catch.dispatch2
1340 %12 = cleanuppad within %1 []
1341 cleanupret from %12 unwind label %ehcleanup22
1343 ehcleanup22: ; preds = %ehcleanup, %catch.dispatch
1344 %13 = cleanuppad within none []
1345 cleanupret from %13 unwind to caller
1347 unreachable: ; preds = %invoke.cont8, %catch.start
1361 ; Regression test for a WebAssemblyException grouping bug. After catchswitches
1362 ; are removed, EH pad catch.start2 is dominated by catch.start, but because
1363 ; catch.start2 is the unwind destination of catch.start, it should not be
1364 ; included in catch.start's exception. Also, after we take catch.start2's
1365 ; exception out of catch.start's exception, we have to take out try.cont8 out of
1366 ; catch.start's exception, because it has a predecessor in catch.start2.
1367 define void @test23() personality ptr @__gxx_wasm_personality_v0 {
1369 %exception = call ptr @__cxa_allocate_exception(i32 4) #0
1370 store i32 0, ptr %exception, align 16
1371 invoke void @__cxa_throw(ptr %exception, ptr @_ZTIi, ptr null) #1
1372 to label %unreachable unwind label %catch.dispatch
1374 catch.dispatch: ; preds = %entry
1375 %0 = catchswitch within none [label %catch.start] unwind label %catch.dispatch1
1377 catch.start: ; preds = %catch.dispatch
1378 %1 = catchpad within %0 [ptr @_ZTIi]
1379 %2 = call ptr @llvm.wasm.get.exception(token %1)
1380 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
1381 %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #0
1382 %matches = icmp eq i32 %3, %4
1383 br i1 %matches, label %catch, label %rethrow
1385 catch: ; preds = %catch.start
1386 %5 = call ptr @__cxa_begin_catch(ptr %2) #0 [ "funclet"(token %1) ]
1387 %6 = load i32, ptr %5, align 4
1388 call void @__cxa_end_catch() #0 [ "funclet"(token %1) ]
1389 catchret from %1 to label %catchret.dest
1391 catchret.dest: ; preds = %catch
1394 rethrow: ; preds = %catch.start
1395 invoke void @llvm.wasm.rethrow() #1 [ "funclet"(token %1) ]
1396 to label %unreachable unwind label %catch.dispatch1
1398 catch.dispatch1: ; preds = %rethrow, %catch.dispatch
1399 %7 = catchswitch within none [label %catch.start2] unwind to caller
1401 catch.start2: ; preds = %catch.dispatch1
1402 %8 = catchpad within %7 [ptr @_ZTIi]
1403 %9 = call ptr @llvm.wasm.get.exception(token %8)
1404 %10 = call i32 @llvm.wasm.get.ehselector(token %8)
1405 %11 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #0
1406 %matches3 = icmp eq i32 %10, %11
1407 br i1 %matches3, label %catch5, label %rethrow4
1409 catch5: ; preds = %catch.start2
1410 %12 = call ptr @__cxa_begin_catch(ptr %9) #0 [ "funclet"(token %8) ]
1411 %13 = load i32, ptr %12, align 4
1412 call void @__cxa_end_catch() #0 [ "funclet"(token %8) ]
1413 catchret from %8 to label %catchret.dest7
1415 catchret.dest7: ; preds = %catch5
1418 rethrow4: ; preds = %catch.start2
1419 call void @llvm.wasm.rethrow() #1 [ "funclet"(token %8) ]
1422 try.cont8: ; preds = %try.cont, %catchret.dest7
1425 try.cont: ; preds = %catchret.dest
1428 unreachable: ; preds = %rethrow, %entry
1432 ; Test for WebAssemblyException grouping. This test is hand-modified to generate
1434 ; catch.start dominates catch.start4 and catch.start4 dominates catch.start12,
1435 ; so the after dominator-based grouping, we end up with:
1436 ; catch.start's exception > catch4.start's exception > catch12.start's exception
1437 ; (> here represents subexception relationship)
1439 ; But the unwind destination chain is catch.start -> catch.start4 ->
1440 ; catch.start12. So all these subexception relationship should be deconstructed.
1441 ; We have to make sure to take out catch.start4's exception out of catch.start's
1442 ; exception first, before taking out catch.start12's exception out of
1443 ; catch.start4's exception; otherwise we end up with an incorrect relationship
1444 ; of catch.start's exception > catch.start12's exception.
1445 define void @test24() personality ptr @__gxx_wasm_personality_v0 {
1448 to label %invoke.cont unwind label %catch.dispatch
1450 invoke.cont: ; preds = %entry
1452 to label %invoke.cont1 unwind label %catch.dispatch
1454 invoke.cont1: ; preds = %invoke.cont
1456 to label %try.cont18 unwind label %catch.dispatch
1458 catch.dispatch11: ; preds = %rethrow6, %catch.dispatch3
1459 %0 = catchswitch within none [label %catch.start12] unwind to caller
1461 catch.start12: ; preds = %catch.dispatch11
1462 %1 = catchpad within %0 [ptr @_ZTIi]
1463 %2 = call ptr @llvm.wasm.get.exception(token %1)
1464 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
1465 %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #0
1466 %matches13 = icmp eq i32 %3, %4
1467 br i1 %matches13, label %catch15, label %rethrow14
1469 catch15: ; preds = %catch.start12
1470 %5 = call ptr @__cxa_begin_catch(ptr %2) #0 [ "funclet"(token %1) ]
1471 %6 = load i32, ptr %5, align 4
1472 call void @__cxa_end_catch() #0 [ "funclet"(token %1) ]
1473 catchret from %1 to label %try.cont18
1475 rethrow14: ; preds = %catch.start12
1476 call void @llvm.wasm.rethrow() #1 [ "funclet"(token %1) ]
1479 catch.dispatch3: ; preds = %rethrow, %catch.dispatch
1480 %7 = catchswitch within none [label %catch.start4] unwind label %catch.dispatch11
1482 catch.start4: ; preds = %catch.dispatch3
1483 %8 = catchpad within %7 [ptr @_ZTIi]
1484 %9 = call ptr @llvm.wasm.get.exception(token %8)
1485 %10 = call i32 @llvm.wasm.get.ehselector(token %8)
1486 %11 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #0
1487 %matches5 = icmp eq i32 %10, %11
1488 br i1 %matches5, label %catch7, label %rethrow6
1490 catch7: ; preds = %catch.start4
1491 %12 = call ptr @__cxa_begin_catch(ptr %9) #0 [ "funclet"(token %8) ]
1492 %13 = load i32, ptr %12, align 4
1493 call void @__cxa_end_catch() #0 [ "funclet"(token %8) ]
1494 catchret from %8 to label %try.cont18
1496 rethrow6: ; preds = %catch.start4
1497 invoke void @llvm.wasm.rethrow() #1 [ "funclet"(token %8) ]
1498 to label %unreachable unwind label %catch.dispatch11
1500 catch.dispatch: ; preds = %invoke.cont1, %invoke.cont, %entry
1501 %14 = catchswitch within none [label %catch.start] unwind label %catch.dispatch3
1503 catch.start: ; preds = %catch.dispatch
1504 %15 = catchpad within %14 [ptr @_ZTIi]
1505 %16 = call ptr @llvm.wasm.get.exception(token %15)
1506 %17 = call i32 @llvm.wasm.get.ehselector(token %15)
1507 %18 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #0
1508 %matches = icmp eq i32 %17, %18
1509 br i1 %matches, label %catch, label %rethrow
1511 catch: ; preds = %catch.start
1512 %19 = call ptr @__cxa_begin_catch(ptr %16) #0 [ "funclet"(token %15) ]
1513 %20 = load i32, ptr %19, align 4
1514 call void @__cxa_end_catch() #0 [ "funclet"(token %15) ]
1515 catchret from %15 to label %try.cont18
1517 rethrow: ; preds = %catch.start
1518 invoke void @llvm.wasm.rethrow() #1 [ "funclet"(token %15) ]
1519 to label %unreachable unwind label %catch.dispatch3
1521 try.cont18: ; preds = %catch, %catch7, %catch15, %invoke.cont1
1524 unreachable: ; preds = %rethrow, %rethrow6
1532 ; } catch (int) { // (a)
1534 ; } catch (int) { // (b)
1538 ; } catch (int) { // (c)
1542 ; Regression test for an ExceptionInfo grouping bug. Because the first (inner)
1543 ; try always throws, both EH pads (b) (catch.start2) and (c) (catch.start10) are
1544 ; dominated by EH pad (a) (catch.start), even though they are not semantically
1545 ; contained in (a)'s exception. Because (a)'s unwind destination is (b), (b)'s
1546 ; exception is taken out of (a)'s. But because (c) is reachable from (b), we
1547 ; should make sure to take out (c)'s exception out of (a)'s exception too.
1548 define void @test25() personality ptr @__gxx_wasm_personality_v0 {
1550 %exception = call ptr @__cxa_allocate_exception(i32 4) #1
1551 store i32 0, ptr %exception, align 16
1552 invoke void @__cxa_throw(ptr %exception, ptr @_ZTIi, ptr null) #3
1553 to label %unreachable unwind label %catch.dispatch
1555 catch.dispatch: ; preds = %entry
1556 %0 = catchswitch within none [label %catch.start] unwind label %catch.dispatch1
1558 catch.start: ; preds = %catch.dispatch
1559 %1 = catchpad within %0 [ptr @_ZTIi]
1560 %2 = call ptr @llvm.wasm.get.exception(token %1)
1561 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
1562 %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #1
1563 %matches = icmp eq i32 %3, %4
1564 br i1 %matches, label %catch, label %rethrow
1566 catch: ; preds = %catch.start
1567 %5 = call ptr @__cxa_begin_catch(ptr %2) #1 [ "funclet"(token %1) ]
1568 %6 = load i32, ptr %5, align 4
1569 call void @__cxa_end_catch() #1 [ "funclet"(token %1) ]
1570 catchret from %1 to label %try.cont8
1572 rethrow: ; preds = %catch.start
1573 invoke void @llvm.wasm.rethrow() #3 [ "funclet"(token %1) ]
1574 to label %unreachable unwind label %catch.dispatch1
1576 catch.dispatch1: ; preds = %rethrow, %catch.dispatch
1577 %7 = catchswitch within none [label %catch.start2] unwind to caller
1579 catch.start2: ; preds = %catch.dispatch1
1580 %8 = catchpad within %7 [ptr @_ZTIi]
1581 %9 = call ptr @llvm.wasm.get.exception(token %8)
1582 %10 = call i32 @llvm.wasm.get.ehselector(token %8)
1583 %11 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #1
1584 %matches3 = icmp eq i32 %10, %11
1585 br i1 %matches3, label %catch5, label %rethrow4
1587 catch5: ; preds = %catch.start2
1588 %12 = call ptr @__cxa_begin_catch(ptr %9) #1 [ "funclet"(token %8) ]
1589 %13 = load i32, ptr %12, align 4
1590 call void @__cxa_end_catch() #1 [ "funclet"(token %8) ]
1591 catchret from %8 to label %try.cont8
1593 rethrow4: ; preds = %catch.start2
1594 call void @llvm.wasm.rethrow() #3 [ "funclet"(token %8) ]
1597 try.cont8: ; preds = %catch, %catch5
1599 to label %try.cont16 unwind label %catch.dispatch9
1601 catch.dispatch9: ; preds = %try.cont8
1602 %14 = catchswitch within none [label %catch.start10] unwind to caller
1604 catch.start10: ; preds = %catch.dispatch9
1605 %15 = catchpad within %14 [ptr @_ZTIi]
1606 %16 = call ptr @llvm.wasm.get.exception(token %15)
1607 %17 = call i32 @llvm.wasm.get.ehselector(token %15)
1608 %18 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #1
1609 %matches11 = icmp eq i32 %17, %18
1610 br i1 %matches11, label %catch13, label %rethrow12
1612 catch13: ; preds = %catch.start10
1613 %19 = call ptr @__cxa_begin_catch(ptr %16) #1 [ "funclet"(token %15) ]
1614 %20 = load i32, ptr %19, align 4
1615 call void @__cxa_end_catch() #1 [ "funclet"(token %15) ]
1616 catchret from %15 to label %try.cont16
1618 rethrow12: ; preds = %catch.start10
1619 call void @llvm.wasm.rethrow() #3 [ "funclet"(token %15) ]
1622 try.cont16: ; preds = %try.cont8, %catch13
1625 unreachable: ; preds = %rethrow, %entry
1629 ; Check if the unwind destination mismatch stats are correct
1630 ; NOSORT: 23 wasm-cfg-stackify - Number of call unwind mismatches found
1631 ; NOSORT: 4 wasm-cfg-stackify - Number of catch unwind mismatches found
1636 declare i32 @qux(i32)
1637 declare void @quux(i32)
1638 declare void @fun(i32)
1639 ; Function Attrs: nounwind
1640 declare void @nothrow(i32) #0
1641 ; Function Attrs: nounwind
1642 declare i32 @nothrow_i32() #0
1644 ; Function Attrs: nounwind
1645 declare ptr @_ZN6ObjectD2Ev(ptr returned) #0
1646 @_ZTI7MyClass = external constant { ptr, ptr }, align 4
1647 ; Function Attrs: nounwind
1648 declare ptr @_ZN7MyClassD2Ev(ptr returned) #0
1649 ; Function Attrs: nounwind
1650 declare ptr @_ZN7MyClassC2ERKS_(ptr returned, ptr dereferenceable(4)) #0
1652 declare i32 @__gxx_wasm_personality_v0(...)
1653 ; Function Attrs: nounwind
1654 declare ptr @llvm.wasm.get.exception(token) #0
1655 ; Function Attrs: nounwind
1656 declare i32 @llvm.wasm.get.ehselector(token) #0
1657 declare ptr @__cxa_allocate_exception(i32) #0
1658 declare void @__cxa_throw(ptr, ptr, ptr)
1659 ; Function Attrs: noreturn
1660 declare void @llvm.wasm.rethrow() #1
1661 ; Function Attrs: nounwind
1662 declare i32 @llvm.eh.typeid.for(ptr) #0
1664 declare ptr @__cxa_begin_catch(ptr)
1665 declare void @__cxa_end_catch()
1666 declare ptr @__cxa_get_exception_ptr(ptr)
1667 declare void @_ZSt9terminatev()
1668 ; Function Attrs: nounwind
1669 declare void @llvm.memcpy.p0.p0.i32(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i32, i1 immarg) #0
1670 ; Function Attrs: nounwind
1671 declare void @llvm.memmove.p0.p0.i32(ptr nocapture, ptr nocapture readonly, i32, i1 immarg) #0
1672 ; Function Attrs: nounwind
1673 declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1 immarg) #0
1675 attributes #0 = { nounwind }
1676 attributes #1 = { noreturn }