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,bulk-memory | 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,bulk-memory
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,-bulk-memory,-bulk-memory-opt | 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,-bulk-memory,-bulk-memory-opt -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,-bulk-memory,-bulk-memory-opt -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
19 ; void two_catches() {
27 ; CHECK-LABEL: two_catches:
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 @two_catches() 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
85 ; void nested_catch() {
97 ; CHECK-LABEL: nested_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 @nested_catch() 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 try-catches within a try
209 ; void nested_try() {
219 ; CHECK-LABEL: nested_try:
224 ; CHECK: call $drop=, __cxa_begin_catch
225 ; CHECK: call __cxa_end_catch
228 ; CHECK: call $drop=, __cxa_begin_catch
229 ; CHECK: call __cxa_end_catch
231 define void @nested_try() personality ptr @__gxx_wasm_personality_v0 {
234 to label %try.cont7 unwind label %catch.dispatch
236 catch.dispatch: ; preds = %entry
237 %0 = catchswitch within none [label %catch.start] unwind label %catch.dispatch2
239 catch.start: ; preds = %catch.dispatch
240 %1 = catchpad within %0 [ptr null]
241 %2 = call ptr @llvm.wasm.get.exception(token %1)
242 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
243 %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
244 invoke void @__cxa_end_catch() [ "funclet"(token %1) ]
245 to label %invoke.cont1 unwind label %catch.dispatch2
247 catch.dispatch2: ; preds = %catch.start, %catch.dispatch
248 %5 = catchswitch within none [label %catch.start3] unwind to caller
250 catch.start3: ; preds = %catch.dispatch2
251 %6 = catchpad within %5 [ptr null]
252 %7 = call ptr @llvm.wasm.get.exception(token %6)
253 %8 = call i32 @llvm.wasm.get.ehselector(token %6)
254 %9 = call ptr @__cxa_begin_catch(ptr %7) [ "funclet"(token %6) ]
255 call void @__cxa_end_catch() [ "funclet"(token %6) ]
256 catchret from %6 to label %try.cont7
258 try.cont7: ; preds = %entry, %invoke.cont1, %catch.start3
261 invoke.cont1: ; preds = %catch.start
262 catchret from %1 to label %try.cont7
266 ; CHECK-LABEL: loop_within_catch:
270 ; CHECK: call $drop=, __cxa_begin_catch
271 ; CHECK: loop # label[[L0:[0-9]+]]:
274 ; CHECK: br_if 0, {{.*}} # 0: down to label[[L1:[0-9]+]]
277 ; CHECK: br 2 # 2: down to label[[L2:[0-9]+]]
280 ; CHECK: call __cxa_end_catch
282 ; CHECK: call _ZSt9terminatev
285 ; CHECK: rethrow 0 # to caller
287 ; CHECK: end_block # label[[L1]]:
288 ; CHECK: call __cxa_end_catch
289 ; CHECK: br 2 # 2: down to label[[L3:[0-9]+]]
290 ; CHECK: end_block # label[[L2]]:
291 ; CHECK: br 0 # 0: up to label[[L0]]
293 ; CHECK: end_try # label[[L3]]:
294 define void @loop_within_catch() personality ptr @__gxx_wasm_personality_v0 {
297 to label %try.cont unwind label %catch.dispatch
299 catch.dispatch: ; preds = %entry
300 %0 = catchswitch within none [label %catch.start] unwind to caller
302 catch.start: ; preds = %catch.dispatch
303 %1 = catchpad within %0 [ptr null]
304 %2 = call ptr @llvm.wasm.get.exception(token %1)
305 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
306 %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
309 for.cond: ; preds = %for.inc, %catch.start
310 %i.0 = phi i32 [ 0, %catch.start ], [ %inc, %for.inc ]
311 %cmp = icmp slt i32 %i.0, 50
312 br i1 %cmp, label %for.body, label %for.end
314 for.body: ; preds = %for.cond
315 invoke void @foo() [ "funclet"(token %1) ]
316 to label %for.inc unwind label %ehcleanup
318 for.inc: ; preds = %for.body
319 %inc = add nsw i32 %i.0, 1
322 for.end: ; preds = %for.cond
323 call void @__cxa_end_catch() [ "funclet"(token %1) ]
324 catchret from %1 to label %try.cont
326 try.cont: ; preds = %for.end, %entry
329 ehcleanup: ; preds = %for.body
330 %5 = cleanuppad within %1 []
331 invoke void @__cxa_end_catch() [ "funclet"(token %5) ]
332 to label %invoke.cont2 unwind label %terminate
334 invoke.cont2: ; preds = %ehcleanup
335 cleanupret from %5 unwind to caller
337 terminate: ; preds = %ehcleanup
338 %6 = cleanuppad within %5 []
339 call void @_ZSt9terminatev() [ "funclet"(token %6) ]
343 ; Tests if block and try markers are correctly placed. Even if two predecessors
344 ; of the EH pad are bb2 and bb3 and their nearest common dominator is bb1, the
345 ; TRY marker should be placed at bb0 because there's a branch from bb0 to bb2,
346 ; and scopes cannot be interleaved.
348 ; NOOPT-LABEL: block_try_markers:
358 ; NOOPT: catch {{.*}}
360 define void @block_try_markers() personality ptr @__gxx_wasm_personality_v0 {
362 br i1 undef, label %bb1, label %bb2
365 br i1 undef, label %bb3, label %bb4
372 to label %try.cont unwind label %catch.dispatch
376 to label %try.cont unwind label %catch.dispatch
378 catch.dispatch: ; preds = %bb4, %bb3
379 %0 = catchswitch within none [label %catch.start] unwind to caller
381 catch.start: ; preds = %catch.dispatch
382 %1 = catchpad within %0 [ptr null]
383 %2 = call ptr @llvm.wasm.get.exception(token %1)
384 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
385 catchret from %1 to label %try.cont
387 try.cont: ; preds = %catch.start, %bb4, %bb3, %bb2
391 ; Tests if try/end_try markers are placed correctly wrt loop/end_loop markers,
392 ; when try and loop markers are in the same BB and end_try and end_loop are in
394 ; CHECK-LABEL: loop_try_markers:
401 define void @loop_try_markers(ptr %p) personality ptr @__gxx_wasm_personality_v0 {
403 store volatile i32 0, ptr %p
406 loop: ; preds = %try.cont, %entry
407 store volatile i32 1, ptr %p
409 to label %try.cont unwind label %catch.dispatch
411 catch.dispatch: ; preds = %loop
412 %0 = catchswitch within none [label %catch.start] unwind to caller
414 catch.start: ; preds = %catch.dispatch
415 %1 = catchpad within %0 [ptr null]
416 %2 = call ptr @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 try.cont: ; preds = %catch.start, %loop
424 ; Some of test cases below are hand-tweaked by deleting some library calls to
425 ; simplify tests and changing the order of basic blocks to cause unwind
426 ; destination mismatches. And we use -wasm-disable-ehpad-sort to create maximum
427 ; number of mismatches in several tests below.
429 ; - Call unwind mismatch
430 ; 'call bar''s original unwind destination was 'C0', but after control flow
431 ; linearization, its unwind destination incorrectly becomes 'C1'. We fix this by
432 ; wrapping the call with a nested try-delegate that targets 'C0'.
433 ; - Catch unwind mismatch
434 ; If 'call foo' throws a foreign exception, it will not be caught by C1, and
435 ; should be rethrown to the caller. But after control flow linearization, it
436 ; will instead unwind to C0, an incorrect next EH pad. We wrap the whole
437 ; try-catch with try-delegate that rethrows the exception to the caller to fix
440 ; NOSORT-LABEL: unwind_mismatches_0:
442 ; --- try-delegate starts (catch unwind mismatch)
446 ; --- try-delegate starts (call unwind mismatch)
449 ; NOSORT: delegate 2 # label/catch{{[0-9]+}}: down to catch[[C0:[0-9]+]]
450 ; --- try-delegate ends (call unwind mismatch)
451 ; NOSORT: catch {{.*}} # catch[[C1:[0-9]+]]:
453 ; NOSORT: delegate 1 # label/catch{{[0-9]+}}: to caller
454 ; --- try-delegate ends (catch unwind mismatch)
455 ; NOSORT: catch {{.*}} # catch[[C0]]:
458 define void @unwind_mismatches_0() personality ptr @__gxx_wasm_personality_v0 {
461 to label %bb1 unwind label %catch.dispatch0
465 to label %try.cont unwind label %catch.dispatch1
467 catch.dispatch0: ; preds = %bb0
468 %0 = catchswitch within none [label %catch.start0] unwind to caller
470 catch.start0: ; preds = %catch.dispatch0
471 %1 = catchpad within %0 [ptr null]
472 %2 = call ptr @llvm.wasm.get.exception(token %1)
473 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
474 catchret from %1 to label %try.cont
476 catch.dispatch1: ; preds = %bb1
477 %4 = catchswitch within none [label %catch.start1] unwind to caller
479 catch.start1: ; preds = %catch.dispatch1
480 %5 = catchpad within %4 [ptr null]
481 %6 = call ptr @llvm.wasm.get.exception(token %5)
482 %7 = call i32 @llvm.wasm.get.ehselector(token %5)
483 catchret from %5 to label %try.cont
485 try.cont: ; preds = %catch.start1, %catch.start0, %bb1
489 ; 'call bar' and 'call baz''s original unwind destination was the caller, but
490 ; after control flow linearization, their unwind destination incorrectly becomes
491 ; 'C0'. We fix this by wrapping the calls with a nested try-delegate that
492 ; rethrows the exception to the caller.
494 ; And the return value of 'baz' should NOT be stackified because the BB is split
495 ; during fixing unwind mismatches.
497 ; NOSORT-LABEL: unwind_mismatches_1:
500 ; --- try-delegate starts (call unwind mismatch)
503 ; NOSORT: call $[[RET:[0-9]+]]=, baz
504 ; NOSORT-NOT: call $push{{.*}}=, baz
505 ; NOSORT: delegate 1 # label/catch{{[0-9]+}}: to caller
506 ; --- try-delegate ends (call unwind mismatch)
507 ; NOSORT: call nothrow, $[[RET]]
509 ; NOSORT: catch {{.*}} # catch[[C0:[0-9]+]]:
512 define void @unwind_mismatches_1() personality ptr @__gxx_wasm_personality_v0 {
515 to label %bb1 unwind label %catch.dispatch0
519 %call = call i32 @baz()
520 call void @nothrow(i32 %call) #0
523 catch.dispatch0: ; preds = %bb0
524 %0 = catchswitch within none [label %catch.start0] unwind to caller
526 catch.start0: ; preds = %catch.dispatch0
527 %1 = catchpad within %0 [ptr null]
528 %2 = call ptr @llvm.wasm.get.exception(token %1)
529 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
530 catchret from %1 to label %try.cont
532 try.cont: ; preds = %catch.start0
536 ; The same as unwind_mismatches_0, but we have one more call 'call @foo' in bb1
537 ; which unwinds to the caller. IN this case bb1 has two call unwind mismatches:
538 ; 'call @foo' unwinds to the caller and 'call @bar' unwinds to catch C0.
540 ; NOSORT-LABEL: unwind_mismatches_2:
542 ; --- try-delegate starts (catch unwind mismatch)
546 ; --- try-delegate starts (call unwind mismatch)
549 ; NOSORT: delegate 3 # label/catch{{[0-9]+}}: to caller
550 ; --- try-delegate ends (call unwind mismatch)
551 ; --- try-delegate starts (call unwind mismatch)
554 ; NOSORT: delegate 2 # label/catch{{[0-9]+}}: down to catch[[C0:[0-9]+]]
555 ; --- try-delegate ends (call unwind mismatch)
556 ; NOSORT: catch {{.*}} # catch[[C1:[0-9]+]]:
558 ; NOSORT: delegate 1 # label/catch{{[0-9]+}}: to caller
559 ; --- try-delegate ends (catch unwind mismatch)
560 ; NOSORT: catch {{.*}} # catch[[C0]]:
563 define void @unwind_mismatches_2() personality ptr @__gxx_wasm_personality_v0 {
566 to label %bb1 unwind label %catch.dispatch0
571 to label %try.cont unwind label %catch.dispatch1
573 catch.dispatch0: ; preds = %bb0
574 %0 = catchswitch within none [label %catch.start0] unwind to caller
576 catch.start0: ; preds = %catch.dispatch0
577 %1 = catchpad within %0 [ptr null]
578 %2 = call ptr @llvm.wasm.get.exception(token %1)
579 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
580 catchret from %1 to label %try.cont
582 catch.dispatch1: ; preds = %bb1
583 %4 = catchswitch within none [label %catch.start1] unwind to caller
585 catch.start1: ; preds = %catch.dispatch1
586 %5 = catchpad within %4 [ptr null]
587 %6 = call ptr @llvm.wasm.get.exception(token %5)
588 %7 = call i32 @llvm.wasm.get.ehselector(token %5)
589 catchret from %5 to label %try.cont
591 try.cont: ; preds = %catch.start1, %catch.start0, %bb1
595 ; Similar situation as @unwind_mismatches_1. Here 'call @qux''s original unwind
596 ; destination was the caller, but after control flow linearization, their unwind
597 ; destination incorrectly becomes 'C0' within the function. We fix this by
598 ; wrapping the call with a nested try-delegate that rethrows the exception to
601 ; Because 'call @qux' pops an argument pushed by 'i32.const 5' from stack, the
602 ; nested 'try' should be placed before `i32.const 5', not between 'i32.const 5'
605 ; NOSORT-LABEL: unwind_mismatches_3:
608 ; --- try-delegate starts (call unwind mismatch)
610 ; NOSORT: i32.const $push{{[0-9]+}}=, 5
611 ; NOSORT: call ${{[0-9]+}}=, qux
612 ; NOSORT: delegate 1 # label/catch{{[0-9]+}}: to caller
613 ; --- try-delegate ends (call unwind mismatch)
615 ; NOSORT: catch {{.*}} # catch[[C0:[0-9]+]]:
618 define i32 @unwind_mismatches_3() personality ptr @__gxx_wasm_personality_v0 {
621 to label %bb1 unwind label %catch.dispatch0
624 %0 = call i32 @qux(i32 5)
627 catch.dispatch0: ; preds = %bb0
628 %1 = catchswitch within none [label %catch.start0] unwind to caller
630 catch.start0: ; preds = %catch.dispatch0
631 %2 = catchpad within %1 [ptr null]
632 %3 = call ptr @llvm.wasm.get.exception(token %2)
633 %j = call i32 @llvm.wasm.get.ehselector(token %2)
634 catchret from %2 to label %try.cont
636 try.cont: ; preds = %catch.start0
640 ; We have two call unwind unwind mismatches:
641 ; - A may-throw instruction unwinds to an incorrect EH pad after linearizing the
642 ; CFG, when it is supposed to unwind to another EH pad.
643 ; - A may-throw instruction unwinds to an incorrect EH pad after linearizing the
644 ; CFG, when it is supposed to unwind to the caller.
645 ; We also have a catch unwind mismatch: If an exception is not caught by the
646 ; first catch because it is a non-C++ exception, it shouldn't unwind to the next
647 ; catch, but it should unwind to the caller.
649 ; NOSORT-LABEL: unwind_mismatches_4:
651 ; --- try-delegate starts (catch unwind mismatch)
655 ; --- try-delegate starts (call unwind mismatch)
658 ; NOSORT: delegate 2 # label/catch{{[0-9]+}}: down to catch[[C0:[0-9]+]]
659 ; --- try-delegate ends (call unwind mismatch)
661 ; NOSORT: call {{.*}} __cxa_begin_catch
662 ; --- try-delegate starts (call unwind mismatch)
664 ; NOSORT: call __cxa_end_catch
665 ; NOSORT: delegate 3 # label/catch{{[0-9]+}}: to caller
666 ; --- try-delegate ends (call unwind mismatch)
668 ; NOSORT: delegate 1 # label/catch{{[0-9]+}}: to caller
669 ; --- try-delegate ends (catch unwind mismatch)
670 ; NOSORT: catch {{.*}} # catch[[C0]]:
671 ; NOSORT: call {{.*}} __cxa_begin_catch
672 ; NOSORT: call __cxa_end_catch
675 define void @unwind_mismatches_4() personality ptr @__gxx_wasm_personality_v0 {
678 to label %bb1 unwind label %catch.dispatch0
682 to label %try.cont unwind label %catch.dispatch1
684 catch.dispatch0: ; preds = %bb0
685 %0 = catchswitch within none [label %catch.start0] unwind to caller
687 catch.start0: ; preds = %catch.dispatch0
688 %1 = catchpad within %0 [ptr null]
689 %2 = call ptr @llvm.wasm.get.exception(token %1)
690 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
691 %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
692 call void @__cxa_end_catch() [ "funclet"(token %1) ]
693 catchret from %1 to label %try.cont
695 catch.dispatch1: ; preds = %bb1
696 %5 = catchswitch within none [label %catch.start1] unwind to caller
698 catch.start1: ; preds = %catch.dispatch1
699 %6 = catchpad within %5 [ptr null]
700 %7 = call ptr @llvm.wasm.get.exception(token %6)
701 %8 = call i32 @llvm.wasm.get.ehselector(token %6)
702 %9 = call ptr @__cxa_begin_catch(ptr %7) [ "funclet"(token %6) ]
703 call void @__cxa_end_catch() [ "funclet"(token %6) ]
704 catchret from %6 to label %try.cont
706 try.cont: ; preds = %catch.start1, %catch.start0, %bb1
710 ; This crashed when updating EHPadStack within fixCallUniwindMismatch had a bug.
711 ; This should not crash and try-delegate has to be created around 'call @baz',
712 ; because the initial TRY placement for 'call @quux' was done before 'call @baz'
713 ; because 'call @baz''s return value is stackified.
715 ; CHECK-LABEL: unwind_mismatches_5:
717 ; --- try-delegate starts (call unwind mismatch)
719 ; CHECK: call $[[RET:[0-9]+]]=, baz
721 ; --- try-delegate ends (call unwind mismatch)
722 ; CHECK: call quux, $[[RET]]
725 define void @unwind_mismatches_5() personality ptr @__gxx_wasm_personality_v0 {
727 %call = call i32 @baz()
728 invoke void @quux(i32 %call)
729 to label %invoke.cont unwind label %ehcleanup
731 ehcleanup: ; preds = %entry
732 %0 = cleanuppad within none []
733 cleanupret from %0 unwind to caller
735 invoke.cont: ; preds = %entry
739 ; The structure is similar to unwind_mismatches_0, where the call to 'bar''s
740 ; original unwind destination is catch.dispatch1 but after placing markers it
741 ; unwinds to catch.dispatch0, which we fix. This additionally has a loop before
742 ; the real unwind destination (catch.dispatch1).
744 ; NOSORT-LABEL: unwind_mismatches_with_loop:
751 ; NOSORT: delegate 3 # label/catch{{[0-9]+}}: down to catch[[C0:[0-9]+]]
752 ; NOSORT: catch $drop=, __cpp_exception
754 ; NOSORT: delegate 2 # label/catch{{[0-9]+}}: to caller
758 ; NOSORT: catch $drop=, __cpp_exception # catch[[C0]]:
761 define void @unwind_mismatches_with_loop() personality ptr @__gxx_wasm_personality_v0 {
764 to label %bb1 unwind label %catch.dispatch0
768 to label %bb2 unwind label %catch.dispatch1
770 catch.dispatch0: ; preds = %bb0
771 %0 = catchswitch within none [label %catch.start0] unwind to caller
773 catch.start0: ; preds = %catch.dispatch0
774 %1 = catchpad within %0 [ptr null]
775 %2 = call ptr @llvm.wasm.get.exception(token %1)
776 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
777 catchret from %1 to label %bb2
781 to label %bb3 unwind label %catch.dispatch1
786 catch.dispatch1: ; preds = %bb1
787 %4 = catchswitch within none [label %catch.start1] unwind to caller
789 catch.start1: ; preds = %catch.dispatch1
790 %5 = catchpad within %4 [ptr null]
791 %6 = call ptr @llvm.wasm.get.exception(token %5)
792 %7 = call i32 @llvm.wasm.get.ehselector(token %5)
793 catchret from %5 to label %try.cont
795 try.cont: ; preds = %catch.start1, %catch.start0, %bb1
799 ; Tests the case when TEE stackifies a register in RegStackify but it gets
800 ; unstackified in fixCallUnwindMismatches in CFGStackify.
802 ; NOSORT-LOCALS-LABEL: unstackify_when_fixing_unwind_mismatch:
803 define void @unstackify_when_fixing_unwind_mismatch(i32 %x) personality ptr @__gxx_wasm_personality_v0 {
806 to label %bb1 unwind label %catch.dispatch0
810 ; This %addr is used in multiple places, so tee is introduced in RegStackify,
811 ; which stackifies the use of %addr in store instruction. A tee has two dest
812 ; registers, the first of which is stackified and the second is not.
813 ; But when we introduce a nested try-delegate in fixCallUnwindMismatches in
814 ; CFGStackify, we end up unstackifying the first dest register. In that case,
815 ; we convert that tee into a copy.
816 %addr = inttoptr i32 %t to ptr
817 %load = load i32, ptr %addr
818 %call = call i32 @baz()
819 %add = add i32 %load, %call
820 store i32 %add, ptr %addr
822 ; NOSORT-LOCALS: i32.add
823 ; NOSORT-LOCALS-NOT: local.tee
824 ; NOSORT-LOCALS-NEXT: local.set
826 catch.dispatch0: ; preds = %bb0
827 %0 = catchswitch within none [label %catch.start0] unwind to caller
829 catch.start0: ; preds = %catch.dispatch0
830 %1 = catchpad within %0 [ptr null]
831 %2 = call ptr @llvm.wasm.get.exception(token %1)
832 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
833 catchret from %1 to label %try.cont
835 try.cont: ; preds = %catch.start0
839 ; In CFGSort, EH pads should be sorted as soon as it is available and
840 ; 'Preferred' queue and should NOT be entered into 'Ready' queue unless we are
841 ; in the middle of sorting another region that does not contain the EH pad. In
842 ; this example, 'catch.start' should be sorted right after 'if.then' is sorted
843 ; (before 'cont' is sorted) and there should not be any unwind destination
844 ; mismatches in CFGStackify.
846 ; NOOPT-LABEL: cfg_sort_order:
855 define void @cfg_sort_order(i32 %arg) personality ptr @__gxx_wasm_personality_v0 {
857 %tobool = icmp ne i32 %arg, 0
858 br i1 %tobool, label %if.then, label %if.end
860 catch.dispatch: ; preds = %if.then
861 %0 = catchswitch within none [label %catch.start] unwind to caller
863 catch.start: ; preds = %catch.dispatch
864 %1 = catchpad within %0 [ptr null]
865 %2 = call ptr @llvm.wasm.get.exception(token %1)
866 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
867 %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
868 call void @__cxa_end_catch() [ "funclet"(token %1) ]
869 catchret from %1 to label %if.end
871 if.then: ; preds = %entry
873 to label %cont unwind label %catch.dispatch
875 cont: ; preds = %if.then
879 if.end: ; preds = %cont, %catch.start, %entry
883 ; Intrinsics like memcpy, memmove, and memset don't throw and are lowered into
884 ; calls to external symbols (not global addresses) in instruction selection,
885 ; which will be eventually lowered to library function calls.
886 ; Because this test runs with -wasm-disable-ehpad-sort, these library calls in
887 ; invoke.cont BB fall within try~end_try, but they shouldn't cause crashes or
888 ; unwinding destination mismatches in CFGStackify.
890 ; NOSORT-LABEL: mem_intrinsics:
893 ; NOSORT: call {{.*}} memcpy
894 ; NOSORT: call {{.*}} memmove
895 ; NOSORT: call {{.*}} memset
900 define void @mem_intrinsics(ptr %a, ptr %b) personality ptr @__gxx_wasm_personality_v0 {
902 %o = alloca %class.Object, align 1
904 to label %invoke.cont unwind label %ehcleanup
906 invoke.cont: ; preds = %entry
907 call void @llvm.memcpy.p0.p0.i32(ptr %a, ptr %b, i32 100, i1 false)
908 call void @llvm.memmove.p0.p0.i32(ptr %a, ptr %b, i32 100, i1 false)
909 call void @llvm.memset.p0.i32(ptr %a, i8 0, i32 100, i1 false)
910 %call = call ptr @_ZN6ObjectD2Ev(ptr %o)
913 ehcleanup: ; preds = %entry
914 %0 = cleanuppad within none []
915 %call2 = call ptr @_ZN6ObjectD2Ev(ptr %o) [ "funclet"(token %0) ]
916 cleanupret from %0 unwind to caller
919 ; Tests if 'try' marker is placed correctly. In this test, 'try' should be
920 ; placed before the call to 'nothrow_i32' and not between the call to
921 ; 'nothrow_i32' and 'fun', because the return value of 'nothrow_i32' is
922 ; stackified and pushed onto the stack to be consumed by the call to 'fun'.
924 ; CHECK-LABEL: try_marker_with_stackified_input:
926 ; CHECK: call $push{{.*}}=, nothrow_i32
927 ; CHECK: call fun, $pop{{.*}}
928 define void @try_marker_with_stackified_input() personality ptr @__gxx_wasm_personality_v0 {
930 %call = call i32 @nothrow_i32()
931 invoke void @fun(i32 %call)
932 to label %invoke.cont unwind label %terminate
934 invoke.cont: ; preds = %entry
937 terminate: ; preds = %entry
938 %0 = cleanuppad within none []
939 call void @_ZSt9terminatev() [ "funclet"(token %0) ]
943 ; This crashed on debug mode (= when NDEBUG is not defined) when the logic for
944 ; computing the innermost region was not correct, in which a loop region
945 ; contains an exception region. This should pass CFGSort without crashing.
946 define void @loop_exception_region() personality ptr @__gxx_wasm_personality_v0 {
948 %e = alloca %class.MyClass, align 4
951 for.cond: ; preds = %for.inc, %entry
952 %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.inc ]
953 %cmp = icmp slt i32 %i.0, 9
954 br i1 %cmp, label %for.body, label %for.end
956 for.body: ; preds = %for.cond
957 invoke void @quux(i32 %i.0)
958 to label %for.inc unwind label %catch.dispatch
960 catch.dispatch: ; preds = %for.body
961 %0 = catchswitch within none [label %catch.start] unwind to caller
963 catch.start: ; preds = %catch.dispatch
964 %1 = catchpad within %0 [ptr @_ZTI7MyClass]
965 %2 = call ptr @llvm.wasm.get.exception(token %1)
966 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
967 %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTI7MyClass)
968 %matches = icmp eq i32 %3, %4
969 br i1 %matches, label %catch, label %rethrow
971 catch: ; preds = %catch.start
972 %5 = call ptr @__cxa_get_exception_ptr(ptr %2) [ "funclet"(token %1) ]
973 %call = call ptr @_ZN7MyClassC2ERKS_(ptr %e, ptr dereferenceable(4) %5) [ "funclet"(token %1) ]
974 %6 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
975 %7 = load i32, ptr %e, align 4
976 invoke void @quux(i32 %7) [ "funclet"(token %1) ]
977 to label %invoke.cont2 unwind label %ehcleanup
979 invoke.cont2: ; preds = %catch
980 %call3 = call ptr @_ZN7MyClassD2Ev(ptr %e) [ "funclet"(token %1) ]
981 call void @__cxa_end_catch() [ "funclet"(token %1) ]
982 catchret from %1 to label %for.inc
984 rethrow: ; preds = %catch.start
985 call void @llvm.wasm.rethrow() [ "funclet"(token %1) ]
988 for.inc: ; preds = %invoke.cont2, %for.body
989 %inc = add nsw i32 %i.0, 1
992 ehcleanup: ; preds = %catch
993 %8 = cleanuppad within %1 []
994 %call4 = call ptr @_ZN7MyClassD2Ev(ptr %e) [ "funclet"(token %8) ]
995 invoke void @__cxa_end_catch() [ "funclet"(token %8) ]
996 to label %invoke.cont6 unwind label %terminate7
998 invoke.cont6: ; preds = %ehcleanup
999 cleanupret from %8 unwind to caller
1001 for.end: ; preds = %for.cond
1004 terminate7: ; preds = %ehcleanup
1005 %9 = cleanuppad within %8 []
1006 call void @_ZSt9terminatev() [ "funclet"(token %9) ]
1010 ; Tests if CFGStackify's removeUnnecessaryInstrs() removes unnecessary branches
1011 ; correctly. The code is in the form below, where 'br' is unnecessary because
1012 ; after running the 'try' body the control flow will fall through to bb2 anyway.
1017 ; br bb2 <- Not necessary
1021 ; bb2: <- Continuation BB
1023 ; CHECK-LABEL: remove_unnecessary_instrs:
1024 define void @remove_unnecessary_instrs(i32 %n) personality ptr @__gxx_wasm_personality_v0 {
1027 to label %for.body unwind label %catch.dispatch
1029 for.body: ; preds = %for.end, %entry
1030 %i = phi i32 [ %inc, %for.end ], [ 0, %entry ]
1032 to label %for.end unwind label %catch.dispatch
1034 ; Before going to CFGStackify, this BB will have a conditional branch followed
1035 ; by an unconditional branch. CFGStackify should remove only the unconditional
1037 for.end: ; preds = %for.body
1038 %inc = add nuw nsw i32 %i, 1
1039 %exitcond = icmp eq i32 %inc, %n
1040 br i1 %exitcond, label %try.cont, label %for.body
1046 catch.dispatch: ; preds = %for.body, %entry
1047 %0 = catchswitch within none [label %catch.start] unwind to caller
1049 catch.start: ; preds = %catch.dispatch
1050 %1 = catchpad within %0 [ptr null]
1051 %2 = call ptr @llvm.wasm.get.exception(token %1)
1052 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
1053 %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
1054 call void @__cxa_end_catch() [ "funclet"(token %1) ]
1055 catchret from %1 to label %try.cont
1057 try.cont: ; preds = %catch.start, %for.end
1062 ; void remove_unnecessary_br() {
1073 ; This tests whether the 'br' can be removed in code in the form as follows.
1074 ; Here 'br' is inside an inner try, whose 'end' is in another EH pad. In this
1075 ; case, after running an inner try body, the control flow should fall through to
1076 ; bb3, so the 'br' in the code is unnecessary.
1082 ; br bb3 <- Not necessary
1089 ; bb3: <- Continuation BB
1092 ; CHECK-LABEL: remove_unnecessary_br:
1093 define void @remove_unnecessary_br() personality ptr @__gxx_wasm_personality_v0 {
1097 to label %invoke.cont unwind label %catch.dispatch3
1101 invoke.cont: ; preds = %entry
1103 to label %try.cont8 unwind label %catch.dispatch
1105 catch.dispatch: ; preds = %invoke.cont
1106 %0 = catchswitch within none [label %catch.start] unwind label %catch.dispatch3
1109 catch.start: ; preds = %catch.dispatch
1110 %1 = catchpad within %0 [ptr null]
1111 %2 = call ptr @llvm.wasm.get.exception(token %1)
1112 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
1113 %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
1114 invoke void @__cxa_end_catch() [ "funclet"(token %1) ]
1115 to label %invoke.cont2 unwind label %catch.dispatch3
1117 catch.dispatch3: ; preds = %catch.start, %catch.dispatch, %entry
1118 %5 = catchswitch within none [label %catch.start4] unwind to caller
1120 catch.start4: ; preds = %catch.dispatch3
1121 %6 = catchpad within %5 [ptr null]
1122 %7 = call ptr @llvm.wasm.get.exception(token %6)
1123 %8 = call i32 @llvm.wasm.get.ehselector(token %6)
1124 %9 = call ptr @__cxa_begin_catch(ptr %7) [ "funclet"(token %6) ]
1125 call void @__cxa_end_catch() [ "funclet"(token %6) ]
1126 catchret from %6 to label %try.cont8
1128 try.cont8: ; preds = %invoke.cont2, %catch.start4, %invoke.cont
1131 invoke.cont2: ; preds = %catch.start
1132 catchret from %1 to label %try.cont8
1135 ; Here an exception is semantically contained in a loop. 'ehcleanup' BB belongs
1136 ; to the exception, but does not belong to the loop (because it does not have a
1137 ; path back to the loop header), and is placed after the loop latch block
1138 ; 'invoke.cont' intentionally. This tests if 'end_loop' marker is placed
1139 ; correctly not right after 'invoke.cont' part but after 'ehcleanup' part.
1140 ; NOSORT-LABEL: loop_contains_exception:
1147 define void @loop_contains_exception(i32 %n) personality ptr @__gxx_wasm_personality_v0 {
1149 br label %while.cond
1151 while.cond: ; preds = %invoke.cont, %entry
1152 %n.addr.0 = phi i32 [ %n, %entry ], [ %dec, %invoke.cont ]
1153 %tobool = icmp ne i32 %n.addr.0, 0
1154 br i1 %tobool, label %while.body, label %while.end
1156 while.body: ; preds = %while.cond
1157 %dec = add nsw i32 %n.addr.0, -1
1159 to label %while.end unwind label %catch.dispatch
1161 catch.dispatch: ; preds = %while.body
1162 %0 = catchswitch within none [label %catch.start] unwind to caller
1164 catch.start: ; preds = %catch.dispatch
1165 %1 = catchpad within %0 [ptr null]
1166 %2 = call ptr @llvm.wasm.get.exception(token %1)
1167 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
1168 %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
1169 invoke void @__cxa_end_catch() [ "funclet"(token %1) ]
1170 to label %invoke.cont unwind label %ehcleanup
1172 invoke.cont: ; preds = %catch.start
1173 catchret from %1 to label %while.cond
1175 ehcleanup: ; preds = %catch.start
1176 %5 = cleanuppad within %1 []
1177 call void @_ZSt9terminatev() [ "funclet"(token %5) ]
1180 while.end: ; preds = %while.body, %while.cond
1184 ; When the function return type is non-void and 'end' instructions are at the
1185 ; very end of a function, CFGStackify's fixEndsAtEndOfFunction function fixes
1186 ; the corresponding block/loop/try's type to match the function's return type.
1187 ; But when a `try`'s type is fixed, we should also check `end` instructions
1188 ; before its corresponding `catch_all`, because both `try` and `catch_all` body
1189 ; should satisfy the return type requirements.
1191 ; NOSORT-LABEL: fix_function_end_return_type_with_try_catch:
1197 ; NOSORT-NEXT: end_function
1198 define i32 @fix_function_end_return_type_with_try_catch(i32 %n) personality ptr @__gxx_wasm_personality_v0 {
1200 %t = alloca %class.Object, align 1
1203 for.cond: ; preds = %for.inc, %entry
1204 %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.inc ]
1205 %cmp = icmp slt i32 %i.0, %n
1208 for.body: ; preds = %for.cond
1209 %div = sdiv i32 %n, 2
1210 %cmp1 = icmp eq i32 %i.0, %div
1211 br i1 %cmp1, label %if.then, label %for.inc
1213 if.then: ; preds = %for.body
1214 %call = invoke i32 @baz()
1215 to label %invoke.cont unwind label %ehcleanup
1217 invoke.cont: ; preds = %if.then
1218 %call2 = call ptr @_ZN6ObjectD2Ev(ptr %t)
1221 for.inc: ; preds = %for.body
1222 %inc = add nsw i32 %i.0, 1
1225 ehcleanup: ; preds = %if.then
1226 %0 = cleanuppad within none []
1227 %call3 = call ptr @_ZN6ObjectD2Ev(ptr %t) [ "funclet"(token %0) ]
1228 cleanupret from %0 unwind to caller
1231 ; This tests if invalidated branch destinations after fixing catch unwind
1232 ; mismatches are correctly remapped. For example, we have this code and suppose
1233 ; we need to wrap this try-catch-end in this code with a try-delegate to fix a
1234 ; catch unwind mismatch:
1255 ; After adding a try-delegate, the 'br's destination BB, where (a) points,
1256 ; becomes invalid because it incorrectly branches into an inner scope. The
1257 ; destination should change to the BB where (b) points.
1259 ; NOSORT-LABEL: branch_remapping_after_fixing_unwind_mismatches_0:
1262 define void @branch_remapping_after_fixing_unwind_mismatches_0(i1 %arg) personality ptr @__gxx_wasm_personality_v0 {
1264 br i1 %arg, label %bb0, label %dest
1266 bb0: ; preds = %entry
1268 to label %bb1 unwind label %catch.dispatch0
1272 to label %try.cont unwind label %catch.dispatch1
1274 catch.dispatch0: ; preds = %bb0
1275 %0 = catchswitch within none [label %catch.start0] unwind to caller
1277 catch.start0: ; preds = %catch.dispatch0
1278 %1 = catchpad within %0 [ptr null]
1279 %2 = call ptr @llvm.wasm.get.exception(token %1)
1280 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
1281 catchret from %1 to label %try.cont
1283 dest: ; preds = %entry
1286 catch.dispatch1: ; preds = %bb1
1287 %4 = catchswitch within none [label %catch.start1] unwind to caller
1289 catch.start1: ; preds = %catch.dispatch1
1290 %5 = catchpad within %4 [ptr null]
1291 %6 = call ptr @llvm.wasm.get.exception(token %5)
1292 %7 = call i32 @llvm.wasm.get.ehselector(token %5)
1293 catchret from %5 to label %try.cont
1295 try.cont: ; preds = %catch.start1, %catch.start0, %bb1
1299 ; The similar case with branch_remapping_after_fixing_unwind_mismatches_0, but
1300 ; multiple consecutive delegates are generated:
1323 ; <- (b) The br destination should be remapped to here
1325 ; The test was reduced by bugpoint and should not crash in CFGStackify.
1326 define void @branch_remapping_after_fixing_unwind_mismatches_1() personality ptr @__gxx_wasm_personality_v0 {
1328 br i1 undef, label %if.then, label %if.end12
1330 if.then: ; preds = %entry
1331 invoke void @__cxa_throw(ptr null, ptr null, ptr null) #1
1332 to label %unreachable unwind label %catch.dispatch
1334 catch.dispatch: ; preds = %if.then
1335 %0 = catchswitch within none [label %catch.start] unwind to caller
1337 catch.start: ; preds = %catch.dispatch
1338 %1 = catchpad within %0 [ptr @_ZTIi]
1339 %2 = call ptr @llvm.wasm.get.exception(token %1)
1340 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
1341 catchret from %1 to label %catchret.dest
1343 catchret.dest: ; preds = %catch.start
1345 to label %invoke.cont unwind label %catch.dispatch4
1347 invoke.cont: ; preds = %catchret.dest
1348 invoke void @__cxa_throw(ptr null, ptr null, ptr null) #1
1349 to label %unreachable unwind label %catch.dispatch4
1351 catch.dispatch4: ; preds = %invoke.cont, %catchret.dest
1352 %4 = catchswitch within none [label %catch.start5] unwind to caller
1354 catch.start5: ; preds = %catch.dispatch4
1355 %5 = catchpad within %4 [ptr @_ZTIi]
1356 %6 = call ptr @llvm.wasm.get.exception(token %5)
1357 %7 = call i32 @llvm.wasm.get.ehselector(token %5)
1360 if.end12: ; preds = %entry
1362 to label %invoke.cont14 unwind label %catch.dispatch16
1364 catch.dispatch16: ; preds = %if.end12
1365 %8 = catchswitch within none [label %catch.start17] unwind label %ehcleanup
1367 catch.start17: ; preds = %catch.dispatch16
1368 %9 = catchpad within %8 [ptr @_ZTIi]
1369 %10 = call ptr @llvm.wasm.get.exception(token %9)
1370 %11 = call i32 @llvm.wasm.get.ehselector(token %9)
1371 br i1 undef, label %catch20, label %rethrow19
1373 catch20: ; preds = %catch.start17
1374 catchret from %9 to label %catchret.dest22
1376 catchret.dest22: ; preds = %catch20
1377 br label %try.cont23
1379 rethrow19: ; preds = %catch.start17
1380 invoke void @llvm.wasm.rethrow() #1 [ "funclet"(token %9) ]
1381 to label %unreachable unwind label %ehcleanup
1383 try.cont23: ; preds = %invoke.cont14, %catchret.dest22
1385 to label %invoke.cont24 unwind label %ehcleanup
1387 invoke.cont24: ; preds = %try.cont23
1390 invoke.cont14: ; preds = %if.end12
1391 br label %try.cont23
1393 ehcleanup: ; preds = %try.cont23, %rethrow19, %catch.dispatch16
1394 %12 = cleanuppad within none []
1395 cleanupret from %12 unwind to caller
1397 unreachable: ; preds = %rethrow19, %invoke.cont, %if.then
1401 ; Regression test for WasmEHFuncInfo's reverse mapping bug. 'UnwindDestToSrc'
1402 ; should return a vector and not a single BB, which was incorrect.
1403 ; This was reduced by bugpoint and should not crash in CFGStackify.
1404 define void @wasm_eh_func_info_regression_test() personality ptr @__gxx_wasm_personality_v0 {
1407 to label %invoke.cont unwind label %catch.dispatch
1409 catch.dispatch: ; preds = %entry
1410 %0 = catchswitch within none [label %catch.start] unwind label %ehcleanup22
1412 catch.start: ; preds = %catch.dispatch
1413 %1 = catchpad within %0 [ptr @_ZTIi]
1414 %2 = call ptr @llvm.wasm.get.exception(token %1)
1415 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
1416 invoke void @__cxa_throw(ptr null, ptr null, ptr null) #1 [ "funclet"(token %1) ]
1417 to label %unreachable unwind label %catch.dispatch2
1419 catch.dispatch2: ; preds = %catch.start
1420 %4 = catchswitch within %1 [label %catch.start3] unwind label %ehcleanup
1422 catch.start3: ; preds = %catch.dispatch2
1423 %5 = catchpad within %4 [ptr @_ZTIi]
1424 %6 = call ptr @llvm.wasm.get.exception(token %5)
1425 %7 = call i32 @llvm.wasm.get.ehselector(token %5)
1426 catchret from %5 to label %try.cont
1428 try.cont: ; preds = %catch.start3
1429 invoke void @foo() [ "funclet"(token %1) ]
1430 to label %invoke.cont8 unwind label %ehcleanup
1432 invoke.cont8: ; preds = %try.cont
1433 invoke void @__cxa_throw(ptr null, ptr null, ptr null) #1 [ "funclet"(token %1) ]
1434 to label %unreachable unwind label %catch.dispatch11
1436 catch.dispatch11: ; preds = %invoke.cont8
1437 %8 = catchswitch within %1 [label %catch.start12] unwind label %ehcleanup
1439 catch.start12: ; preds = %catch.dispatch11
1440 %9 = catchpad within %8 [ptr @_ZTIi]
1441 %10 = call ptr @llvm.wasm.get.exception(token %9)
1442 %11 = call i32 @llvm.wasm.get.ehselector(token %9)
1445 invoke.cont: ; preds = %entry
1448 ehcleanup: ; preds = %catch.dispatch11, %try.cont, %catch.dispatch2
1449 %12 = cleanuppad within %1 []
1450 cleanupret from %12 unwind label %ehcleanup22
1452 ehcleanup22: ; preds = %ehcleanup, %catch.dispatch
1453 %13 = cleanuppad within none []
1454 cleanupret from %13 unwind to caller
1456 unreachable: ; preds = %invoke.cont8, %catch.start
1460 ; void exception_grouping_0() {
1470 ; Regression test for a WebAssemblyException grouping bug. After catchswitches
1471 ; are removed, EH pad catch.start2 is dominated by catch.start, but because
1472 ; catch.start2 is the unwind destination of catch.start, it should not be
1473 ; included in catch.start's exception. Also, after we take catch.start2's
1474 ; exception out of catch.start's exception, we have to take out try.cont8 out of
1475 ; catch.start's exception, because it has a predecessor in catch.start2.
1476 define void @exception_grouping_0() personality ptr @__gxx_wasm_personality_v0 {
1478 %exception = call ptr @__cxa_allocate_exception(i32 4) #0
1479 store i32 0, ptr %exception, align 16
1480 invoke void @__cxa_throw(ptr %exception, ptr @_ZTIi, ptr null) #1
1481 to label %unreachable unwind label %catch.dispatch
1483 catch.dispatch: ; preds = %entry
1484 %0 = catchswitch within none [label %catch.start] unwind label %catch.dispatch1
1486 catch.start: ; preds = %catch.dispatch
1487 %1 = catchpad within %0 [ptr @_ZTIi]
1488 %2 = call ptr @llvm.wasm.get.exception(token %1)
1489 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
1490 %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #0
1491 %matches = icmp eq i32 %3, %4
1492 br i1 %matches, label %catch, label %rethrow
1494 catch: ; preds = %catch.start
1495 %5 = call ptr @__cxa_begin_catch(ptr %2) #0 [ "funclet"(token %1) ]
1496 %6 = load i32, ptr %5, align 4
1497 call void @__cxa_end_catch() #0 [ "funclet"(token %1) ]
1498 catchret from %1 to label %catchret.dest
1500 catchret.dest: ; preds = %catch
1503 rethrow: ; preds = %catch.start
1504 invoke void @llvm.wasm.rethrow() #1 [ "funclet"(token %1) ]
1505 to label %unreachable unwind label %catch.dispatch1
1507 catch.dispatch1: ; preds = %rethrow, %catch.dispatch
1508 %7 = catchswitch within none [label %catch.start2] unwind to caller
1510 catch.start2: ; preds = %catch.dispatch1
1511 %8 = catchpad within %7 [ptr @_ZTIi]
1512 %9 = call ptr @llvm.wasm.get.exception(token %8)
1513 %10 = call i32 @llvm.wasm.get.ehselector(token %8)
1514 %11 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #0
1515 %matches3 = icmp eq i32 %10, %11
1516 br i1 %matches3, label %catch5, label %rethrow4
1518 catch5: ; preds = %catch.start2
1519 %12 = call ptr @__cxa_begin_catch(ptr %9) #0 [ "funclet"(token %8) ]
1520 %13 = load i32, ptr %12, align 4
1521 call void @__cxa_end_catch() #0 [ "funclet"(token %8) ]
1522 catchret from %8 to label %catchret.dest7
1524 catchret.dest7: ; preds = %catch5
1527 rethrow4: ; preds = %catch.start2
1528 call void @llvm.wasm.rethrow() #1 [ "funclet"(token %8) ]
1531 try.cont8: ; preds = %try.cont, %catchret.dest7
1534 try.cont: ; preds = %catchret.dest
1537 unreachable: ; preds = %rethrow, %entry
1541 ; Test for WebAssemblyException grouping. This test is hand-modified to generate
1543 ; catch.start dominates catch.start4 and catch.start4 dominates catch.start12,
1544 ; so the after dominator-based grouping, we end up with:
1545 ; catch.start's exception > catch4.start's exception > catch12.start's exception
1546 ; (> here represents subexception relationship)
1548 ; But the unwind destination chain is catch.start -> catch.start4 ->
1549 ; catch.start12. So all these subexception relationship should be deconstructed.
1550 ; We have to make sure to take out catch.start4's exception out of catch.start's
1551 ; exception first, before taking out catch.start12's exception out of
1552 ; catch.start4's exception; otherwise we end up with an incorrect relationship
1553 ; of catch.start's exception > catch.start12's exception.
1554 define void @exception_grouping_1() personality ptr @__gxx_wasm_personality_v0 {
1557 to label %invoke.cont unwind label %catch.dispatch
1559 invoke.cont: ; preds = %entry
1561 to label %invoke.cont1 unwind label %catch.dispatch
1563 invoke.cont1: ; preds = %invoke.cont
1565 to label %try.cont18 unwind label %catch.dispatch
1567 catch.dispatch11: ; preds = %rethrow6, %catch.dispatch3
1568 %0 = catchswitch within none [label %catch.start12] unwind to caller
1570 catch.start12: ; preds = %catch.dispatch11
1571 %1 = catchpad within %0 [ptr @_ZTIi]
1572 %2 = call ptr @llvm.wasm.get.exception(token %1)
1573 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
1574 %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #0
1575 %matches13 = icmp eq i32 %3, %4
1576 br i1 %matches13, label %catch15, label %rethrow14
1578 catch15: ; preds = %catch.start12
1579 %5 = call ptr @__cxa_begin_catch(ptr %2) #0 [ "funclet"(token %1) ]
1580 %6 = load i32, ptr %5, align 4
1581 call void @__cxa_end_catch() #0 [ "funclet"(token %1) ]
1582 catchret from %1 to label %try.cont18
1584 rethrow14: ; preds = %catch.start12
1585 call void @llvm.wasm.rethrow() #1 [ "funclet"(token %1) ]
1588 catch.dispatch3: ; preds = %rethrow, %catch.dispatch
1589 %7 = catchswitch within none [label %catch.start4] unwind label %catch.dispatch11
1591 catch.start4: ; preds = %catch.dispatch3
1592 %8 = catchpad within %7 [ptr @_ZTIi]
1593 %9 = call ptr @llvm.wasm.get.exception(token %8)
1594 %10 = call i32 @llvm.wasm.get.ehselector(token %8)
1595 %11 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #0
1596 %matches5 = icmp eq i32 %10, %11
1597 br i1 %matches5, label %catch7, label %rethrow6
1599 catch7: ; preds = %catch.start4
1600 %12 = call ptr @__cxa_begin_catch(ptr %9) #0 [ "funclet"(token %8) ]
1601 %13 = load i32, ptr %12, align 4
1602 call void @__cxa_end_catch() #0 [ "funclet"(token %8) ]
1603 catchret from %8 to label %try.cont18
1605 rethrow6: ; preds = %catch.start4
1606 invoke void @llvm.wasm.rethrow() #1 [ "funclet"(token %8) ]
1607 to label %unreachable unwind label %catch.dispatch11
1609 catch.dispatch: ; preds = %invoke.cont1, %invoke.cont, %entry
1610 %14 = catchswitch within none [label %catch.start] unwind label %catch.dispatch3
1612 catch.start: ; preds = %catch.dispatch
1613 %15 = catchpad within %14 [ptr @_ZTIi]
1614 %16 = call ptr @llvm.wasm.get.exception(token %15)
1615 %17 = call i32 @llvm.wasm.get.ehselector(token %15)
1616 %18 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #0
1617 %matches = icmp eq i32 %17, %18
1618 br i1 %matches, label %catch, label %rethrow
1620 catch: ; preds = %catch.start
1621 %19 = call ptr @__cxa_begin_catch(ptr %16) #0 [ "funclet"(token %15) ]
1622 %20 = load i32, ptr %19, align 4
1623 call void @__cxa_end_catch() #0 [ "funclet"(token %15) ]
1624 catchret from %15 to label %try.cont18
1626 rethrow: ; preds = %catch.start
1627 invoke void @llvm.wasm.rethrow() #1 [ "funclet"(token %15) ]
1628 to label %unreachable unwind label %catch.dispatch3
1630 try.cont18: ; preds = %catch, %catch7, %catch15, %invoke.cont1
1633 unreachable: ; preds = %rethrow, %rethrow6
1637 ; void exception_grouping_2() {
1641 ; } catch (int) { // (a)
1643 ; } catch (int) { // (b)
1647 ; } catch (int) { // (c)
1651 ; Regression test for an ExceptionInfo grouping bug. Because the first (inner)
1652 ; try always throws, both EH pads (b) (catch.start2) and (c) (catch.start10) are
1653 ; dominated by EH pad (a) (catch.start), even though they are not semantically
1654 ; contained in (a)'s exception. Because (a)'s unwind destination is (b), (b)'s
1655 ; exception is taken out of (a)'s. But because (c) is reachable from (b), we
1656 ; should make sure to take out (c)'s exception out of (a)'s exception too.
1657 define void @exception_grouping_2() personality ptr @__gxx_wasm_personality_v0 {
1659 %exception = call ptr @__cxa_allocate_exception(i32 4) #1
1660 store i32 0, ptr %exception, align 16
1661 invoke void @__cxa_throw(ptr %exception, ptr @_ZTIi, ptr null) #3
1662 to label %unreachable unwind label %catch.dispatch
1664 catch.dispatch: ; preds = %entry
1665 %0 = catchswitch within none [label %catch.start] unwind label %catch.dispatch1
1667 catch.start: ; preds = %catch.dispatch
1668 %1 = catchpad within %0 [ptr @_ZTIi]
1669 %2 = call ptr @llvm.wasm.get.exception(token %1)
1670 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
1671 %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #1
1672 %matches = icmp eq i32 %3, %4
1673 br i1 %matches, label %catch, label %rethrow
1675 catch: ; preds = %catch.start
1676 %5 = call ptr @__cxa_begin_catch(ptr %2) #1 [ "funclet"(token %1) ]
1677 %6 = load i32, ptr %5, align 4
1678 call void @__cxa_end_catch() #1 [ "funclet"(token %1) ]
1679 catchret from %1 to label %try.cont8
1681 rethrow: ; preds = %catch.start
1682 invoke void @llvm.wasm.rethrow() #3 [ "funclet"(token %1) ]
1683 to label %unreachable unwind label %catch.dispatch1
1685 catch.dispatch1: ; preds = %rethrow, %catch.dispatch
1686 %7 = catchswitch within none [label %catch.start2] unwind to caller
1688 catch.start2: ; preds = %catch.dispatch1
1689 %8 = catchpad within %7 [ptr @_ZTIi]
1690 %9 = call ptr @llvm.wasm.get.exception(token %8)
1691 %10 = call i32 @llvm.wasm.get.ehselector(token %8)
1692 %11 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #1
1693 %matches3 = icmp eq i32 %10, %11
1694 br i1 %matches3, label %catch5, label %rethrow4
1696 catch5: ; preds = %catch.start2
1697 %12 = call ptr @__cxa_begin_catch(ptr %9) #1 [ "funclet"(token %8) ]
1698 %13 = load i32, ptr %12, align 4
1699 call void @__cxa_end_catch() #1 [ "funclet"(token %8) ]
1700 catchret from %8 to label %try.cont8
1702 rethrow4: ; preds = %catch.start2
1703 call void @llvm.wasm.rethrow() #3 [ "funclet"(token %8) ]
1706 try.cont8: ; preds = %catch, %catch5
1708 to label %try.cont16 unwind label %catch.dispatch9
1710 catch.dispatch9: ; preds = %try.cont8
1711 %14 = catchswitch within none [label %catch.start10] unwind to caller
1713 catch.start10: ; preds = %catch.dispatch9
1714 %15 = catchpad within %14 [ptr @_ZTIi]
1715 %16 = call ptr @llvm.wasm.get.exception(token %15)
1716 %17 = call i32 @llvm.wasm.get.ehselector(token %15)
1717 %18 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #1
1718 %matches11 = icmp eq i32 %17, %18
1719 br i1 %matches11, label %catch13, label %rethrow12
1721 catch13: ; preds = %catch.start10
1722 %19 = call ptr @__cxa_begin_catch(ptr %16) #1 [ "funclet"(token %15) ]
1723 %20 = load i32, ptr %19, align 4
1724 call void @__cxa_end_catch() #1 [ "funclet"(token %15) ]
1725 catchret from %15 to label %try.cont16
1727 rethrow12: ; preds = %catch.start10
1728 call void @llvm.wasm.rethrow() #3 [ "funclet"(token %15) ]
1731 try.cont16: ; preds = %try.cont8, %catch13
1734 unreachable: ; preds = %rethrow, %entry
1738 ; Check if the unwind destination mismatch stats are correct
1739 ; NOSORT: 24 wasm-cfg-stackify - Number of call unwind mismatches found
1740 ; NOSORT: 5 wasm-cfg-stackify - Number of catch unwind mismatches found
1745 declare i32 @qux(i32)
1746 declare void @quux(i32)
1747 declare void @fun(i32)
1748 ; Function Attrs: nounwind
1749 declare void @nothrow(i32) #0
1750 ; Function Attrs: nounwind
1751 declare i32 @nothrow_i32() #0
1753 ; Function Attrs: nounwind
1754 declare ptr @_ZN6ObjectD2Ev(ptr returned) #0
1755 @_ZTI7MyClass = external constant { ptr, ptr }, align 4
1756 ; Function Attrs: nounwind
1757 declare ptr @_ZN7MyClassD2Ev(ptr returned) #0
1758 ; Function Attrs: nounwind
1759 declare ptr @_ZN7MyClassC2ERKS_(ptr returned, ptr dereferenceable(4)) #0
1761 declare i32 @__gxx_wasm_personality_v0(...)
1762 ; Function Attrs: nounwind
1763 declare ptr @llvm.wasm.get.exception(token) #0
1764 ; Function Attrs: nounwind
1765 declare i32 @llvm.wasm.get.ehselector(token) #0
1766 declare ptr @__cxa_allocate_exception(i32) #0
1767 declare void @__cxa_throw(ptr, ptr, ptr)
1768 ; Function Attrs: noreturn
1769 declare void @llvm.wasm.rethrow() #1
1770 ; Function Attrs: nounwind
1771 declare i32 @llvm.eh.typeid.for(ptr) #0
1773 declare ptr @__cxa_begin_catch(ptr)
1774 declare void @__cxa_end_catch()
1775 declare ptr @__cxa_get_exception_ptr(ptr)
1776 declare void @_ZSt9terminatev()
1777 ; Function Attrs: nounwind
1778 declare void @llvm.memcpy.p0.p0.i32(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i32, i1 immarg) #0
1779 ; Function Attrs: nounwind
1780 declare void @llvm.memmove.p0.p0.i32(ptr nocapture, ptr nocapture readonly, i32, i1 immarg) #0
1781 ; Function Attrs: nounwind
1782 declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1 immarg) #0
1784 attributes #0 = { nounwind }
1785 attributes #1 = { noreturn }