2 ; 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 -wasm-use-legacy-eh=false -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 -wasm-use-legacy-eh=false -exception-model=wasm -mattr=+exception-handling,bulk-memory
4 ; RUN: llc < %s -O0 -disable-wasm-fallthrough-return-opt -verify-machineinstrs -wasm-enable-eh -wasm-use-legacy-eh=false -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 -disable-block-placement -verify-machineinstrs -fast-isel=false -machine-sink-split-probability-threshold=0 -cgp-freq-ratio-to-skip-merge=1000 -wasm-enable-eh -wasm-use-legacy-eh=false -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 -wasm-use-legacy-eh=false -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:
29 ; CHECK: block () -> (i32, exnref)
30 ; CHECK: try_table (catch_ref __cpp_exception 0) # 0: down to label[[L0:[0-9]+]]
32 ; CHECK: br 2 # 2: down to label[[L1:[0-9]+]]
33 ; CHECK: end_try_table
34 ; CHECK: end_block # label[[L0]]:
38 ; CHECK: call _Unwind_CallPersonality
40 ; CHECK: br_if 0 # 0: down to label[[L2:[0-9]+]]
41 ; CHECK: call __cxa_begin_catch
42 ; CHECK: call __cxa_end_catch
43 ; CHECK: br 1 # 1: down to label[[L1]]
44 ; CHECK: end_block # label[[L2]]:
46 ; CHECK: br_if 0 # 0: down to label[[L3:[0-9]+]]
47 ; CHECK: call __cxa_begin_catch
48 ; CHECK: call __cxa_end_catch
49 ; CHECK: br 1 # 1: down to label[[L1]]
50 ; CHECK: end_block # label[[L3]]:
52 ; CHECK: end_block # label[[L1]]:
53 define void @two_catches() personality ptr @__gxx_wasm_personality_v0 {
56 to label %try.cont unwind label %catch.dispatch
58 catch.dispatch: ; preds = %entry
59 %0 = catchswitch within none [label %catch.start] unwind to caller
61 catch.start: ; preds = %catch.dispatch
62 %1 = catchpad within %0 [ptr @_ZTIi, ptr @_ZTId]
63 %2 = call ptr @llvm.wasm.get.exception(token %1)
64 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
65 %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi)
66 %matches = icmp eq i32 %3, %4
67 br i1 %matches, label %catch2, label %catch.fallthrough
69 catch2: ; preds = %catch.start
70 %5 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
71 call void @__cxa_end_catch() [ "funclet"(token %1) ]
72 catchret from %1 to label %try.cont
74 catch.fallthrough: ; preds = %catch.start
75 %6 = call i32 @llvm.eh.typeid.for(ptr @_ZTId)
76 %matches1 = icmp eq i32 %3, %6
77 br i1 %matches1, label %catch, label %rethrow
79 catch: ; preds = %catch.fallthrough
80 %7 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
81 call void @__cxa_end_catch() [ "funclet"(token %1) ]
82 catchret from %1 to label %try.cont
84 rethrow: ; preds = %catch.fallthrough
85 call void @llvm.wasm.rethrow() [ "funclet"(token %1) ]
88 try.cont: ; preds = %catch, %catch2, %entry
92 ; Nested try-catches within a catch
93 ; void nested_catch() {
105 ; CHECK-LABEL: nested_catch:
106 ; CHECK: block exnref
108 ; CHECK: block () -> (i32, exnref)
109 ; CHECK: try_table (catch_ref __cpp_exception 0) # 0: down to label[[L0:[0-9]+]]
111 ; CHECK: br 2 # 2: down to label[[L1:[0-9]+]]
112 ; CHECK: end_try_table
113 ; CHECK: end_block # label[[L0]]:
114 ; CHECK: call _Unwind_CallPersonality
117 ; CHECK: br_if 0 # 0: down to label[[L2:[0-9]+]]
118 ; CHECK: call __cxa_begin_catch
119 ; CHECK: block exnref
120 ; CHECK: try_table (catch_all_ref 0) # 0: down to label[[L3:[0-9]+]]
121 ; CHECK: block () -> (i32, exnref)
122 ; CHECK: try_table (catch_ref __cpp_exception 0) # 0: down to label[[L4:[0-9]+]]
124 ; CHECK: br 5 # 5: down to label[[L5:[0-9]+]]
125 ; CHECK: end_try_table
126 ; CHECK: end_block # label[[L4]]:
127 ; CHECK: call _Unwind_CallPersonality
130 ; CHECK: br_if 0 # 0: down to label[[L6:[0-9]+]]
131 ; CHECK: call __cxa_begin_catch
132 ; CHECK: block exnref
133 ; CHECK: try_table (catch_all_ref 0) # 0: down to label[[L7:[0-9]+]]
135 ; CHECK: br 3 # 3: down to label[[L8:[0-9]+]]
136 ; CHECK: end_try_table
137 ; CHECK: end_block # label[[L7]]:
138 ; CHECK: try_table (catch_all_ref 7) # 7: down to label[[L9:[0-9]+]]
139 ; CHECK: call __cxa_end_catch
140 ; CHECK: end_try_table
142 ; CHECK: end_block # label[[L6]]:
144 ; CHECK: end_block # label[[L8]]:
145 ; CHECK: try_table (catch_all_ref 5) # 5: down to label[[L9]]
146 ; CHECK: call __cxa_end_catch
147 ; CHECK: end_try_table
148 ; CHECK: br 3 # 3: down to label[[L5]]
149 ; CHECK: end_try_table
150 ; CHECK: end_block # label[[L3]]:
151 ; CHECK: call __cxa_end_catch
153 ; CHECK: end_block # label[[L2]]:
155 ; CHECK: end_block # label[[L5]]:
156 ; CHECK: call __cxa_end_catch
157 ; CHECK: end_block # label[[L1]]:
159 ; CHECK: end_block # label[[L9]]:
161 define void @nested_catch() personality ptr @__gxx_wasm_personality_v0 {
164 to label %try.cont11 unwind label %catch.dispatch
166 catch.dispatch: ; preds = %entry
167 %0 = catchswitch within none [label %catch.start] unwind to caller
169 catch.start: ; preds = %catch.dispatch
170 %1 = catchpad within %0 [ptr @_ZTIi]
171 %2 = call ptr @llvm.wasm.get.exception(token %1)
172 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
173 %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi)
174 %matches = icmp eq i32 %3, %4
175 br i1 %matches, label %catch, label %rethrow
177 catch: ; preds = %catch.start
178 %5 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
179 %6 = load i32, ptr %5, align 4
180 invoke void @foo() [ "funclet"(token %1) ]
181 to label %try.cont unwind label %catch.dispatch2
183 catch.dispatch2: ; preds = %catch
184 %7 = catchswitch within %1 [label %catch.start3] unwind label %ehcleanup9
186 catch.start3: ; preds = %catch.dispatch2
187 %8 = catchpad within %7 [ptr @_ZTIi]
188 %9 = call ptr @llvm.wasm.get.exception(token %8)
189 %10 = call i32 @llvm.wasm.get.ehselector(token %8)
190 %11 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi)
191 %matches4 = icmp eq i32 %10, %11
192 br i1 %matches4, label %catch6, label %rethrow5
194 catch6: ; preds = %catch.start3
195 %12 = call ptr @__cxa_begin_catch(ptr %9) [ "funclet"(token %8) ]
196 %13 = load i32, ptr %12, align 4
197 invoke void @foo() [ "funclet"(token %8) ]
198 to label %invoke.cont8 unwind label %ehcleanup
200 invoke.cont8: ; preds = %catch6
201 call void @__cxa_end_catch() [ "funclet"(token %8) ]
202 catchret from %8 to label %try.cont
204 rethrow5: ; preds = %catch.start3
205 invoke void @llvm.wasm.rethrow() [ "funclet"(token %8) ]
206 to label %unreachable unwind label %ehcleanup9
208 try.cont: ; preds = %invoke.cont8, %catch
209 call void @__cxa_end_catch() [ "funclet"(token %1) ]
210 catchret from %1 to label %try.cont11
212 rethrow: ; preds = %catch.start
213 call void @llvm.wasm.rethrow() [ "funclet"(token %1) ]
216 try.cont11: ; preds = %try.cont, %entry
219 ehcleanup: ; preds = %catch6
220 %14 = cleanuppad within %8 []
221 call void @__cxa_end_catch() [ "funclet"(token %14) ]
222 cleanupret from %14 unwind label %ehcleanup9
224 ehcleanup9: ; preds = %ehcleanup, %rethrow5, %catch.dispatch2
225 %15 = cleanuppad within %1 []
226 call void @__cxa_end_catch() [ "funclet"(token %15) ]
227 cleanupret from %15 unwind to caller
229 unreachable: ; preds = %rethrow5
233 ; Nested try-catches within a try
234 ; void nested_try() {
244 ; CHECK-LABEL: nested_try:
247 ; CHECK: try_table (catch __cpp_exception 0) # 0: down to label[[L0:[0-9]+]]
249 ; CHECK: try_table (catch __cpp_exception 0) # 0: down to label[[L1:[0-9]+]]
251 ; CHECK: br 4 # 4: down to label[[L2:[0-9]+]]
252 ; CHECK: end_try_table
253 ; CHECK: end_block # label[[L1]]:
254 ; CHECK: call __cxa_begin_catch
255 ; CHECK: call __cxa_end_catch
256 ; CHECK: br 2 # 2: down to label[[L2]]
257 ; CHECK: end_try_table
258 ; CHECK: end_block # label[[L0]]:
259 ; CHECK: call __cxa_begin_catch
260 ; CHECK: call __cxa_end_catch
261 ; CHECK: end_block # label[[L2]]:
262 define void @nested_try() personality ptr @__gxx_wasm_personality_v0 {
265 to label %try.cont7 unwind label %catch.dispatch
267 catch.dispatch: ; preds = %entry
268 %0 = catchswitch within none [label %catch.start] unwind label %catch.dispatch2
270 catch.start: ; preds = %catch.dispatch
271 %1 = catchpad within %0 [ptr null]
272 %2 = call ptr @llvm.wasm.get.exception(token %1)
273 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
274 %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
275 invoke void @__cxa_end_catch() [ "funclet"(token %1) ]
276 to label %invoke.cont1 unwind label %catch.dispatch2
278 catch.dispatch2: ; preds = %catch.start, %catch.dispatch
279 %5 = catchswitch within none [label %catch.start3] unwind to caller
281 catch.start3: ; preds = %catch.dispatch2
282 %6 = catchpad within %5 [ptr null]
283 %7 = call ptr @llvm.wasm.get.exception(token %6)
284 %8 = call i32 @llvm.wasm.get.ehselector(token %6)
285 %9 = call ptr @__cxa_begin_catch(ptr %7) [ "funclet"(token %6) ]
286 call void @__cxa_end_catch() [ "funclet"(token %6) ]
287 catchret from %6 to label %try.cont7
289 try.cont7: ; preds = %entry, %invoke.cont1, %catch.start3
292 invoke.cont1: ; preds = %catch.start
293 catchret from %1 to label %try.cont7
297 ; CHECK-LABEL: loop_within_catch:
300 ; CHECK: try_table (catch __cpp_exception 0) # 0: down to label[[L0:[0-9]+]]
302 ; CHECK: br 2 # 2: down to label[[L1:[0-9]+]]
303 ; CHECK: end_try_table
304 ; CHECK: end_block # label[[L0]]:
305 ; CHECK: call __cxa_begin_catch
306 ; CHECK: loop # label[[L2:[0-9]+]]:
309 ; CHECK: br_if 0 # 0: down to label[[L3:[0-9]+]]
310 ; CHECK: block exnref
311 ; CHECK: try_table (catch_all_ref 0) # 0: down to label[[L4:[0-9]+]]
313 ; CHECK: br 3 # 3: down to label[[L5:[0-9]+]]
314 ; CHECK: end_try_table
315 ; CHECK: end_block # label[[L4]]:
318 ; CHECK: try_table (catch_all 0) # 0: down to label[[L6:[0-9]+]]
319 ; CHECK: call __cxa_end_catch
320 ; CHECK: br 2 # 2: down to label[[L7:[0-9]+]]
321 ; CHECK: end_try_table
322 ; CHECK: end_block # label[[L6]]:
323 ; CHECK: call _ZSt9terminatev
325 ; CHECK: end_block # label[[L7]]:
327 ; CHECK: end_block # label[[L3]]:
328 ; CHECK: call __cxa_end_catch
329 ; CHECK: br 2 # 2: down to label[[L1]]
330 ; CHECK: end_block # label[[L5]]:
331 ; CHECK: br 0 # 0: up to label[[L2]]
333 ; CHECK: end_block # label[[L1]]:
334 define void @loop_within_catch() personality ptr @__gxx_wasm_personality_v0 {
337 to label %try.cont unwind label %catch.dispatch
339 catch.dispatch: ; preds = %entry
340 %0 = catchswitch within none [label %catch.start] unwind to caller
342 catch.start: ; preds = %catch.dispatch
343 %1 = catchpad within %0 [ptr null]
344 %2 = call ptr @llvm.wasm.get.exception(token %1)
345 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
346 %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
349 for.cond: ; preds = %for.inc, %catch.start
350 %i.0 = phi i32 [ 0, %catch.start ], [ %inc, %for.inc ]
351 %cmp = icmp slt i32 %i.0, 50
352 br i1 %cmp, label %for.body, label %for.end
354 for.body: ; preds = %for.cond
355 invoke void @foo() [ "funclet"(token %1) ]
356 to label %for.inc unwind label %ehcleanup
358 for.inc: ; preds = %for.body
359 %inc = add nsw i32 %i.0, 1
362 for.end: ; preds = %for.cond
363 call void @__cxa_end_catch() [ "funclet"(token %1) ]
364 catchret from %1 to label %try.cont
366 try.cont: ; preds = %for.end, %entry
369 ehcleanup: ; preds = %for.body
370 %5 = cleanuppad within %1 []
371 invoke void @__cxa_end_catch() [ "funclet"(token %5) ]
372 to label %invoke.cont2 unwind label %terminate
374 invoke.cont2: ; preds = %ehcleanup
375 cleanupret from %5 unwind to caller
377 terminate: ; preds = %ehcleanup
378 %6 = cleanuppad within %5 []
379 call void @_ZSt9terminatev() [ "funclet"(token %6) ]
383 ; Tests if block and try_table markers are correctly placed. Even if two
384 ; predecessors of the EH pad are bb2 and bb3 and their nearest common dominator
385 ; is bb1, the TRY_TABLE marker should be placed at bb0 because there's a branch
386 ; from bb0 to bb2, and scopes cannot be interleaved.
387 ; NOOPT-LABEL: block_try_table_markers:
390 ; NOOPT: try_table (catch __cpp_exception 0)
399 ; NOOPT: end_try_table
402 define void @block_try_table_markers() personality ptr @__gxx_wasm_personality_v0 {
404 br i1 undef, label %bb1, label %bb2
407 br i1 undef, label %bb3, label %bb4
414 to label %try.cont unwind label %catch.dispatch
418 to label %try.cont unwind label %catch.dispatch
420 catch.dispatch: ; preds = %bb4, %bb3
421 %0 = catchswitch within none [label %catch.start] unwind to caller
423 catch.start: ; preds = %catch.dispatch
424 %1 = catchpad within %0 [ptr null]
425 %2 = call ptr @llvm.wasm.get.exception(token %1)
426 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
427 catchret from %1 to label %try.cont
429 try.cont: ; preds = %catch.start, %bb4, %bb3, %bb2
433 ; Tests if try_table/end_try_table markers are placed correctly wrt
434 ; loop/end_loop markers, when try_table and loop markers are in the same BB and
435 ; end_try_table and end_loop are in another BB.
436 ; CHECK-LABEL: loop_try_table_markers:
439 ; CHECK: try_table (catch __cpp_exception 0)
441 ; CHECK: end_try_table
444 define void @loop_try_table_markers(ptr %p) personality ptr @__gxx_wasm_personality_v0 {
446 store volatile i32 0, ptr %p
449 loop: ; preds = %try.cont, %entry
450 store volatile i32 1, ptr %p
452 to label %try.cont unwind label %catch.dispatch
454 catch.dispatch: ; preds = %loop
455 %0 = catchswitch within none [label %catch.start] unwind to caller
457 catch.start: ; preds = %catch.dispatch
458 %1 = catchpad within %0 [ptr null]
459 %2 = call ptr @llvm.wasm.get.exception(token %1)
460 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
461 catchret from %1 to label %try.cont
463 try.cont: ; preds = %catch.start, %loop
467 ; Some of test cases below are hand-tweaked by deleting some library calls to
468 ; simplify tests and changing the order of basic blocks to cause unwind
469 ; destination mismatches. And we use -wasm-disable-ehpad-sort to create maximum
470 ; number of mismatches in several tests below.
472 ; - Call unwind mismatch
473 ; 'call bar''s original unwind destination was 'C0', but after control flow
474 ; linearization, its unwind destination incorrectly becomes 'C1'. We fix this by
475 ; wrapping the call with a nested try_table-end_try_table that targets 'C0'.
476 ; - Catch unwind mismatch
477 ; If 'call foo' throws a foreign exception, it will not be caught by C1, and
478 ; should be rethrown to the caller. But after control flow linearization, it
479 ; will instead unwind to C0, an incorrect next EH pad. We wrap the whole
480 ; try_table-end_try_table with another try_table-end_try_table that jumps to a
481 ; trampoline BB, from which we rethrow the exception to the caller to fix this.
483 ; NOSORT-LABEL: unwind_mismatches_0:
484 ; NOSORT: block exnref
487 ; NOSORT: try_table (catch __cpp_exception 0) # 0: down to label[[L0:[0-9]+]]
488 ; NOSORT: block exnref
490 ; --- nested try_table-end_try_table starts (catch unwind mismatch)
491 ; NOSORT: try_table (catch_all_ref 5) # 5: down to label[[L1:[0-9]+]]
492 ; NOSORT: try_table (catch __cpp_exception 1) # 1: down to label[[L2:[0-9]+]]
494 ; --- nested try_table-end_try_table starts (call unwind mismatch)
495 ; NOSORT: try_table (catch_all_ref 3) # 3: down to label[[L3:[0-9]+]]
497 ; NOSORT: end_try_table
498 ; --- nested try_table-end_try_table ends (call unwind mismatch)
499 ; NOSORT: end_try_table
500 ; NOSORT: end_try_table
501 ; --- nested try_table-end_try_table ends (catch unwind mismatch)
502 ; NOSORT: end_block # label[[L2]]:
503 ; NOSORT: end_block # label[[L3]]:
505 ; NOSORT: end_try_table
506 ; NOSORT: end_block # label[[L0]]:
509 ; NOSORT: end_block # label[[L1]]:
511 define void @unwind_mismatches_0() personality ptr @__gxx_wasm_personality_v0 {
514 to label %bb1 unwind label %catch.dispatch0
518 to label %try.cont unwind label %catch.dispatch1
520 catch.dispatch0: ; preds = %bb0
521 %0 = catchswitch within none [label %catch.start0] unwind to caller
523 catch.start0: ; preds = %catch.dispatch0
524 %1 = catchpad within %0 [ptr null]
525 %2 = call ptr @llvm.wasm.get.exception(token %1)
526 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
527 catchret from %1 to label %try.cont
529 catch.dispatch1: ; preds = %bb1
530 %4 = catchswitch within none [label %catch.start1] unwind to caller
532 catch.start1: ; preds = %catch.dispatch1
533 %5 = catchpad within %4 [ptr null]
534 %6 = call ptr @llvm.wasm.get.exception(token %5)
535 %7 = call i32 @llvm.wasm.get.ehselector(token %5)
536 catchret from %5 to label %try.cont
538 try.cont: ; preds = %catch.start1, %catch.start0, %bb1
542 ; 'call bar' and 'call baz''s original unwind destination was the caller, but
543 ; after control flow linearization, their unwind destination incorrectly becomes
544 ; 'C0'. We fix this by wrapping the calls with a nested try_table-end_try_table
545 ; that jumps to a trampoline BB where we rethrow the exception to the caller.
547 ; And the return value of 'baz' should NOT be stackified because the BB is split
548 ; during fixing unwind mismatches.
550 ; NOSORT-LABEL: unwind_mismatches_1:
551 ; NOSORT: block exnref
553 ; NOSORT: try_table (catch __cpp_exception 0) # 0: down to label[[L0:[0-9]+]]
555 ; --- nested try_table-end_try_table starts (call unwind mismatch)
556 ; NOSORT: try_table (catch_all_ref 2) # 2: down to label[[L1:[0-9]+]]
559 ; NOSORT: local.set [[LOCAL:[0-9]+]]
560 ; NOSORT: end_try_table
561 ; --- nested try_table-end_try_table ends (call unwind mismatch)
562 ; NOSORT: local.get [[LOCAL]]
563 ; NOSORT: call nothrow
565 ; NOSORT: end_try_table
566 ; NOSORT: end_block # label[[L0]]:
568 ; NOSORT: end_block # label[[L1]]:
570 define void @unwind_mismatches_1() personality ptr @__gxx_wasm_personality_v0 {
573 to label %bb1 unwind label %catch.dispatch0
577 %call = call i32 @baz()
578 call void @nothrow(i32 %call) #0
581 catch.dispatch0: ; preds = %bb0
582 %0 = catchswitch within none [label %catch.start0] unwind to caller
584 catch.start0: ; preds = %catch.dispatch0
585 %1 = catchpad within %0 [ptr null]
586 %2 = call ptr @llvm.wasm.get.exception(token %1)
587 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
588 catchret from %1 to label %try.cont
590 try.cont: ; preds = %catch.start0
594 ; The same as unwind_mismatches_0, but we have one more call 'call @foo' in bb1
595 ; which unwinds to the caller. IN this case bb1 has two call unwind mismatches:
596 ; 'call @foo' unwinds to the caller and 'call @bar' unwinds to catch C0.
598 ; NOSORT-LABEL: unwind_mismatches_2:
599 ; NOSORT: block exnref
602 ; NOSORT: try_table (catch __cpp_exception 0) # 0: down to label[[L0:[0-9]+]]
603 ; NOSORT: block exnref
605 ; --- nested try_table-end_try_table starts (catch unwind mismatch)
606 ; NOSORT: try_table (catch_all_ref 5) # 5: down to label[[L1:[0-9]+]]
607 ; NOSORT: try_table (catch __cpp_exception 1) # 1: down to label[[L2:[0-9]+]]
609 ; --- nested try_table-end_try_table starts (call unwind mismatch)
610 ; NOSORT: try_table (catch_all_ref 7) # 7: down to label[[L1]]
612 ; NOSORT: end_try_table
613 ; --- nested try_table-end_try_table ends (call unwind mismatch)
614 ; --- nested try_table-end_try_table starts (call unwind mismatch)
615 ; NOSORT: try_table (catch_all_ref 3) # 3: down to label[[L3:[0-9]+]]
617 ; NOSORT: end_try_table
618 ; --- nested try_table-end_try_table ends (call unwind mismatch)
619 ; NOSORT: end_try_table
620 ; NOSORT: end_try_table
621 ; --- nested try_table-end_try_table ends (catch unwind mismatch)
622 ; NOSORT: end_block # label[[L2]]:
623 ; NOSORT: end_block # label[[L3]]:
625 ; NOSORT: end_try_table
626 ; NOSORT: end_block # label[[L0]]:
629 ; NOSORT: end_block # label[[L1]]:
631 define void @unwind_mismatches_2() personality ptr @__gxx_wasm_personality_v0 {
634 to label %bb1 unwind label %catch.dispatch0
639 to label %try.cont unwind label %catch.dispatch1
641 catch.dispatch0: ; preds = %bb0
642 %0 = catchswitch within none [label %catch.start0] unwind to caller
644 catch.start0: ; preds = %catch.dispatch0
645 %1 = catchpad within %0 [ptr null]
646 %2 = call ptr @llvm.wasm.get.exception(token %1)
647 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
648 catchret from %1 to label %try.cont
650 catch.dispatch1: ; preds = %bb1
651 %4 = catchswitch within none [label %catch.start1] unwind to caller
653 catch.start1: ; preds = %catch.dispatch1
654 %5 = catchpad within %4 [ptr null]
655 %6 = call ptr @llvm.wasm.get.exception(token %5)
656 %7 = call i32 @llvm.wasm.get.ehselector(token %5)
657 catchret from %5 to label %try.cont
659 try.cont: ; preds = %catch.start1, %catch.start0, %bb1
663 ; Similar situation as @unwind_mismatches_1. Here 'call @qux''s original unwind
664 ; destination was the caller, but after control flow linearization, their unwind
665 ; destination incorrectly becomes 'C0' within the function. We fix this by
666 ; wrapping the call with a nested try_table-end_try_table that jumps to a
667 ; trampoline BB where rethrow the exception to the caller.
669 ; Because 'call @qux' pops an argument pushed by 'i32.const 5' from stack, the
670 ; nested 'try_table' should be placed before `i32.const 5', not between
671 ; 'i32.const 5' and 'call @qux'.
673 ; NOSORT-LABEL: unwind_mismatches_3:
674 ; NOSORT: block exnref
676 ; NOSORT: try_table (catch __cpp_exception 0) # 0: down to label[[L0:[0-9]+]]
678 ; --- nested try_table-end_try_table starts (call unwind mismatch)
679 ; NOSORT: try_table (catch_all_ref 2) # 2: down to label[[L1:[0-9]+]]
680 ; NOSORT: i32.const 5
682 ; NOSORT: end_try_table
683 ; --- nested try_table-end_try_table ends (call unwind mismatch)
685 ; NOSORT: end_try_table
686 ; NOSORT: end_block # label[[L0]]:
688 ; NOSORT: end_block # label[[L1]]:
690 define i32 @unwind_mismatches_3() personality ptr @__gxx_wasm_personality_v0 {
693 to label %bb1 unwind label %catch.dispatch0
696 %0 = call i32 @qux(i32 5)
699 catch.dispatch0: ; preds = %bb0
700 %1 = catchswitch within none [label %catch.start0] unwind to caller
702 catch.start0: ; preds = %catch.dispatch0
703 %2 = catchpad within %1 [ptr null]
704 %3 = call ptr @llvm.wasm.get.exception(token %2)
705 %j = call i32 @llvm.wasm.get.ehselector(token %2)
706 catchret from %2 to label %try.cont
708 try.cont: ; preds = %catch.start0
712 ; We have two call unwind unwind mismatches:
713 ; - A may-throw instruction unwinds to an incorrect EH pad after linearizing the
714 ; CFG, when it is supposed to unwind to another EH pad.
715 ; - A may-throw instruction unwinds to an incorrect EH pad after linearizing the
716 ; CFG, when it is supposed to unwind to the caller.
717 ; We also have a catch unwind mismatch: If an exception is not caught by the
718 ; first catch because it is a non-C++ exception, it shouldn't unwind to the next
719 ; catch, but it should unwind to the caller.
721 ; NOSORT-LABEL: unwind_mismatches_4:
722 ; NOSORT: block exnref
725 ; NOSORT: try_table (catch __cpp_exception 0) # 0: down to label[[L0:[0-9]+]]
726 ; NOSORT: block exnref
728 ; --- nested try_table-end_try_table starts (catch unwind mismatch)
729 ; NOSORT: try_table (catch_all_ref 5) # 5: down to label[[L1:[0-9]+]]
730 ; NOSORT: try_table (catch __cpp_exception 1) # 1: down to label[[L2:[0-9]+]]
732 ; --- nested try_table-end_try_table starts (call unwind mismatch)
733 ; NOSORT: try_table (catch_all_ref 3) # 3: down to label[[L3:[0-9]+]]
735 ; NOSORT: end_try_table
736 ; --- nested try_table-end_try_table ends (call unwind mismatch)
737 ; NOSORT: end_try_table
738 ; NOSORT: end_try_table
739 ; --- nested try_table-end_try_table ends (catch unwind mismatch)
740 ; NOSORT: end_block # label[[L2]]:
741 ; NOSORT: call __cxa_begin_catch
742 ; --- nested try_table-end_try_table starts (call unwind mismatch)
743 ; NOSORT: try_table (catch_all_ref 4) # 4: down to label[[L1]]
744 ; NOSORT: call __cxa_end_catch
745 ; NOSORT: end_try_table
746 ; --- nested try_table-end_try_table ends (call unwind mismatch)
747 ; NOSORT: end_block # label[[L3]]:
749 ; NOSORT: end_try_table
750 ; NOSORT: end_block # label[[L0]]:
751 ; NOSORT: call __cxa_begin_catch
752 ; NOSORT: call __cxa_end_catch
753 ; NOSORT: end_block # label74:
755 ; NOSORT: end_block # label[[L1]]:
757 define void @unwind_mismatches_4() personality ptr @__gxx_wasm_personality_v0 {
760 to label %bb1 unwind label %catch.dispatch0
764 to label %try.cont unwind label %catch.dispatch1
766 catch.dispatch0: ; preds = %bb0
767 %0 = catchswitch within none [label %catch.start0] unwind to caller
769 catch.start0: ; preds = %catch.dispatch0
770 %1 = catchpad within %0 [ptr null]
771 %2 = call ptr @llvm.wasm.get.exception(token %1)
772 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
773 %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
774 call void @__cxa_end_catch() [ "funclet"(token %1) ]
775 catchret from %1 to label %try.cont
777 catch.dispatch1: ; preds = %bb1
778 %5 = catchswitch within none [label %catch.start1] unwind to caller
780 catch.start1: ; preds = %catch.dispatch1
781 %6 = catchpad within %5 [ptr null]
782 %7 = call ptr @llvm.wasm.get.exception(token %6)
783 %8 = call i32 @llvm.wasm.get.ehselector(token %6)
784 %9 = call ptr @__cxa_begin_catch(ptr %7) [ "funclet"(token %6) ]
785 call void @__cxa_end_catch() [ "funclet"(token %6) ]
786 catchret from %6 to label %try.cont
788 try.cont: ; preds = %catch.start1, %catch.start0, %bb1
792 ; This crashed when updating EHPadStack within fixCallUniwindMismatch had a bug.
793 ; This should not crash and a nested try_table-end_try_table has to be created
794 ; around 'call @baz', because the initial TRY_TABLE placement for 'call @quux'
795 ; was done before 'call @baz' because 'call @baz''s return value is stackified.
797 ; CHECK-LABEL: unwind_mismatches_5:
798 ; CHECK: block exnref
800 ; CHECK: block exnref
801 ; CHECK: try_table (catch_all_ref 0) # 0: down to label[[L0:[0-9]+]]
802 ; --- nested try_table-end_try_table starts (call unwind mismatch)
803 ; CHECK: try_table (catch_all_ref 3) # 3: down to label[[L1:[0-9]+]]
805 ; CHECK: end_try_table
806 ; --- nested try_table-end_try_table ends (call unwind mismatch)
808 ; CHECK: end_try_table
809 ; CHECK: end_block # label[[L0]]:
813 ; CHECK: end_block # label[[L1]]:
815 define void @unwind_mismatches_5() personality ptr @__gxx_wasm_personality_v0 {
817 %call = call i32 @baz()
818 invoke void @quux(i32 %call)
819 to label %invoke.cont unwind label %ehcleanup
821 ehcleanup: ; preds = %entry
822 %0 = cleanuppad within none []
823 cleanupret from %0 unwind to caller
825 invoke.cont: ; preds = %entry
829 ; The structure is similar to unwind_mismatches_0, where the call to 'bar''s
830 ; original unwind destination is catch.dispatch1 but after placing markers it
831 ; unwinds to catch.dispatch0, which we fix. This additionally has a loop before
832 ; the real unwind destination (catch.dispatch1). This makes sure the code
833 ; generation works when the unwind destination has an end_loop before
834 ; end_try_table before the mismatch fixing.
836 ; NOSORT-LABEL: unwind_mismatches_with_loop:
837 ; NOSORT: block exnref
839 ; NOSORT: try_table (catch __cpp_exception 0) # 0: down to label[[L0:[0-9]+]]
840 ; NOSORT: block exnref
843 ; --- nested try_table-end_try_table starts (catch unwind mismatch)
844 ; NOSORT: try_table (catch_all_ref 5) # 5: down to label[[L1:[0-9]+]]
845 ; NOSORT: try_table (catch __cpp_exception 1) # 1: down to label[[L2:[0-9]+]]
847 ; --- nested try_table-end_try_table starts (call unwind mismatch)
848 ; NOSORT: try_table (catch_all_ref 4) # 4: down to label[[L3:[0-9]+]]
850 ; NOSORT: end_try_table
851 ; --- nested try_table-end_try_table ends (call unwind mismatch)
852 ; NOSORT: end_try_table
853 ; NOSORT: end_try_table
854 ; --- nested try_table-end_try_table ends (catch unwind mismatch)
855 ; NOSORT: end_block # label[[L2]]:
860 ; NOSORT: end_block # label[[L3]]:
862 ; NOSORT: end_try_table
863 ; NOSORT: end_block # label[[L0]]:
865 ; NOSORT: end_block # label[[L1]]:
867 define void @unwind_mismatches_with_loop() personality ptr @__gxx_wasm_personality_v0 {
870 to label %bb1 unwind label %catch.dispatch0
874 to label %bb2 unwind label %catch.dispatch1
876 catch.dispatch0: ; preds = %bb0
877 %0 = catchswitch within none [label %catch.start0] unwind to caller
879 catch.start0: ; preds = %catch.dispatch0
880 %1 = catchpad within %0 [ptr null]
881 %2 = call ptr @llvm.wasm.get.exception(token %1)
882 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
883 catchret from %1 to label %bb2
887 to label %bb3 unwind label %catch.dispatch1
892 catch.dispatch1: ; preds = %bb1
893 %4 = catchswitch within none [label %catch.start1] unwind to caller
895 catch.start1: ; preds = %catch.dispatch1
896 %5 = catchpad within %4 [ptr null]
897 %6 = call ptr @llvm.wasm.get.exception(token %5)
898 %7 = call i32 @llvm.wasm.get.ehselector(token %5)
899 catchret from %5 to label %try.cont
901 try.cont: ; preds = %catch.start1, %catch.start0, %bb1
905 ; Tests the case when TEE stackifies a register in RegStackify but it gets
906 ; unstackified in fixCallUnwindMismatches in CFGStackify.
908 ; NOSORT-LOCALS-LABEL: unstackify_when_fixing_unwind_mismatch:
909 define void @unstackify_when_fixing_unwind_mismatch(i32 %x) personality ptr @__gxx_wasm_personality_v0 {
912 to label %bb1 unwind label %catch.dispatch0
916 ; This %addr is used in multiple places, so tee is introduced in RegStackify,
917 ; which stackifies the use of %addr in store instruction. A tee has two dest
918 ; registers, the first of which is stackified and the second is not.
919 ; But when we introduce a nested try_table-end_try_table in
920 ; fixCallUnwindMismatches in CFGStackify, we end up unstackifying the first
921 ; dest register. In that case, we convert that tee into a copy.
922 %addr = inttoptr i32 %t to ptr
923 %load = load i32, ptr %addr
924 %call = call i32 @baz()
925 %add = add i32 %load, %call
926 store i32 %add, ptr %addr
928 ; NOSORT-LOCALS: i32.add
929 ; NOSORT-LOCALS-NOT: local.tee
930 ; NOSORT-LOCALS-NEXT: local.set
932 catch.dispatch0: ; preds = %bb0
933 %0 = catchswitch within none [label %catch.start0] unwind to caller
935 catch.start0: ; preds = %catch.dispatch0
936 %1 = catchpad within %0 [ptr null]
937 %2 = call ptr @llvm.wasm.get.exception(token %1)
938 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
939 catchret from %1 to label %try.cont
941 try.cont: ; preds = %catch.start0
945 ; In CFGSort, EH pads should be sorted as soon as it is available and
946 ; 'Preferred' queue and should NOT be entered into 'Ready' queue unless we are
947 ; in the middle of sorting another region that does not contain the EH pad. In
948 ; this example, 'catch.start' should be sorted right after 'if.then' is sorted
949 ; (before 'cont' is sorted) and there should not be any unwind destination
950 ; mismatches in CFGStackify.
952 ; NOOPT-LABEL: cfg_sort_order:
956 ; NOOPT: try_table (catch __cpp_exception 0)
958 ; NOOPT: end_try_table
960 ; NOOPT: call __cxa_begin_catch
961 ; NOOPT: call __cxa_end_catch
966 define void @cfg_sort_order(i32 %arg) personality ptr @__gxx_wasm_personality_v0 {
968 %tobool = icmp ne i32 %arg, 0
969 br i1 %tobool, label %if.then, label %if.end
971 catch.dispatch: ; preds = %if.then
972 %0 = catchswitch within none [label %catch.start] unwind to caller
974 catch.start: ; preds = %catch.dispatch
975 %1 = catchpad within %0 [ptr null]
976 %2 = call ptr @llvm.wasm.get.exception(token %1)
977 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
978 %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
979 call void @__cxa_end_catch() [ "funclet"(token %1) ]
980 catchret from %1 to label %if.end
982 if.then: ; preds = %entry
984 to label %cont unwind label %catch.dispatch
986 cont: ; preds = %if.then
990 if.end: ; preds = %cont, %catch.start, %entry
994 ; Intrinsics like memcpy, memmove, and memset don't throw and are lowered into
995 ; calls to external symbols (not global addresses) in instruction selection,
996 ; which will be eventually lowered to library function calls.
997 ; Because this test runs with -wasm-disable-ehpad-sort, these library calls in
998 ; invoke.cont BB fall within try~end_try, but they shouldn't cause crashes or
999 ; unwinding destination mismatches in CFGStackify.
1001 ; NOSORT-LABEL: mem_intrinsics:
1002 ; NOSORT: block exnref
1003 ; NOSORT: try_table (catch_all_ref 0)
1005 ; NOSORT: call memcpy
1006 ; NOSORT: call memmove
1007 ; NOSORT: call memset
1009 ; NOSORT: end_try_table
1012 define void @mem_intrinsics(ptr %a, ptr %b) personality ptr @__gxx_wasm_personality_v0 {
1014 %o = alloca %class.Object, align 1
1016 to label %invoke.cont unwind label %ehcleanup
1018 invoke.cont: ; preds = %entry
1019 call void @llvm.memcpy.p0.p0.i32(ptr %a, ptr %b, i32 100, i1 false)
1020 call void @llvm.memmove.p0.p0.i32(ptr %a, ptr %b, i32 100, i1 false)
1021 call void @llvm.memset.p0.i32(ptr %a, i8 0, i32 100, i1 false)
1022 %call = call ptr @_ZN6ObjectD2Ev(ptr %o)
1025 ehcleanup: ; preds = %entry
1026 %0 = cleanuppad within none []
1027 %call2 = call ptr @_ZN6ObjectD2Ev(ptr %o) [ "funclet"(token %0) ]
1028 cleanupret from %0 unwind to caller
1031 ; Tests if 'try_table' marker is placed correctly. In this test, 'try_table'
1032 ; should be placed before the call to 'nothrow_i32' and not between the call to
1033 ; 'nothrow_i32' and 'fun', because the return value of 'nothrow_i32' is
1034 ; stackified and pushed onto the stack to be consumed by the call to 'fun'.
1036 ; CHECK-LABEL: try_table_marker_with_stackified_input:
1037 ; CHECK: try_table (catch_all 0)
1038 ; CHECK: call nothrow_i32
1040 define void @try_table_marker_with_stackified_input() personality ptr @__gxx_wasm_personality_v0 {
1042 %call = call i32 @nothrow_i32()
1043 invoke void @fun(i32 %call)
1044 to label %invoke.cont unwind label %terminate
1046 invoke.cont: ; preds = %entry
1049 terminate: ; preds = %entry
1050 %0 = cleanuppad within none []
1051 call void @_ZSt9terminatev() [ "funclet"(token %0) ]
1055 ; This crashed on debug mode (= when NDEBUG is not defined) when the logic for
1056 ; computing the innermost region was not correct, in which a loop region
1057 ; contains an exception region. This should pass CFGSort without crashing.
1058 define void @loop_exception_region() personality ptr @__gxx_wasm_personality_v0 {
1060 %e = alloca %class.MyClass, align 4
1063 for.cond: ; preds = %for.inc, %entry
1064 %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.inc ]
1065 %cmp = icmp slt i32 %i.0, 9
1066 br i1 %cmp, label %for.body, label %for.end
1068 for.body: ; preds = %for.cond
1069 invoke void @quux(i32 %i.0)
1070 to label %for.inc unwind label %catch.dispatch
1072 catch.dispatch: ; preds = %for.body
1073 %0 = catchswitch within none [label %catch.start] unwind to caller
1075 catch.start: ; preds = %catch.dispatch
1076 %1 = catchpad within %0 [ptr @_ZTI7MyClass]
1077 %2 = call ptr @llvm.wasm.get.exception(token %1)
1078 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
1079 %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTI7MyClass)
1080 %matches = icmp eq i32 %3, %4
1081 br i1 %matches, label %catch, label %rethrow
1083 catch: ; preds = %catch.start
1084 %5 = call ptr @__cxa_get_exception_ptr(ptr %2) [ "funclet"(token %1) ]
1085 %call = call ptr @_ZN7MyClassC2ERKS_(ptr %e, ptr dereferenceable(4) %5) [ "funclet"(token %1) ]
1086 %6 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
1087 %7 = load i32, ptr %e, align 4
1088 invoke void @quux(i32 %7) [ "funclet"(token %1) ]
1089 to label %invoke.cont2 unwind label %ehcleanup
1091 invoke.cont2: ; preds = %catch
1092 %call3 = call ptr @_ZN7MyClassD2Ev(ptr %e) [ "funclet"(token %1) ]
1093 call void @__cxa_end_catch() [ "funclet"(token %1) ]
1094 catchret from %1 to label %for.inc
1096 rethrow: ; preds = %catch.start
1097 call void @llvm.wasm.rethrow() [ "funclet"(token %1) ]
1100 for.inc: ; preds = %invoke.cont2, %for.body
1101 %inc = add nsw i32 %i.0, 1
1104 ehcleanup: ; preds = %catch
1105 %8 = cleanuppad within %1 []
1106 %call4 = call ptr @_ZN7MyClassD2Ev(ptr %e) [ "funclet"(token %8) ]
1107 invoke void @__cxa_end_catch() [ "funclet"(token %8) ]
1108 to label %invoke.cont6 unwind label %terminate7
1110 invoke.cont6: ; preds = %ehcleanup
1111 cleanupret from %8 unwind to caller
1113 for.end: ; preds = %for.cond
1116 terminate7: ; preds = %ehcleanup
1117 %9 = cleanuppad within %8 []
1118 call void @_ZSt9terminatev() [ "funclet"(token %9) ]
1122 ; Here exceptions are semantically contained in a loop. 'ehcleanup' BB belongs
1123 ; to the exception, but does not belong to the loop (because it does not have a
1124 ; path back to the loop header), and is placed after the loop latch block
1125 ; 'invoke.cont' intentionally. This tests if 'end_loop' marker is placed
1126 ; correctly not right after 'invoke.cont' part but after 'ehcleanup' part.
1127 ; NOSORT-LABEL: loop_contains_exception:
1129 ; NOSORT: try_table (catch __cpp_exception 0)
1130 ; NOSORT: end_try_table
1131 ; NOSORT: try_table (catch_all 0)
1132 ; NOSORT: end_try_table
1134 define void @loop_contains_exception(i32 %n) personality ptr @__gxx_wasm_personality_v0 {
1136 br label %while.cond
1138 while.cond: ; preds = %invoke.cont, %entry
1139 %n.addr.0 = phi i32 [ %n, %entry ], [ %dec, %invoke.cont ]
1140 %tobool = icmp ne i32 %n.addr.0, 0
1141 br i1 %tobool, label %while.body, label %while.end
1143 while.body: ; preds = %while.cond
1144 %dec = add nsw i32 %n.addr.0, -1
1146 to label %while.end unwind label %catch.dispatch
1148 catch.dispatch: ; preds = %while.body
1149 %0 = catchswitch within none [label %catch.start] unwind to caller
1151 catch.start: ; preds = %catch.dispatch
1152 %1 = catchpad within %0 [ptr null]
1153 %2 = call ptr @llvm.wasm.get.exception(token %1)
1154 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
1155 %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
1156 invoke void @__cxa_end_catch() [ "funclet"(token %1) ]
1157 to label %invoke.cont unwind label %ehcleanup
1159 invoke.cont: ; preds = %catch.start
1160 catchret from %1 to label %while.cond
1162 ehcleanup: ; preds = %catch.start
1163 %5 = cleanuppad within %1 []
1164 call void @_ZSt9terminatev() [ "funclet"(token %5) ]
1167 while.end: ; preds = %while.body, %while.cond
1171 ; Regression test for WasmEHFuncInfo's reverse mapping bug. 'UnwindDestToSrc'
1172 ; should return a vector and not a single BB, which was incorrect.
1173 ; This was reduced by bugpoint and should not crash in CFGStackify.
1174 define void @wasm_eh_func_info_regression_test() personality ptr @__gxx_wasm_personality_v0 {
1177 to label %invoke.cont unwind label %catch.dispatch
1179 catch.dispatch: ; preds = %entry
1180 %0 = catchswitch within none [label %catch.start] unwind label %ehcleanup22
1182 catch.start: ; preds = %catch.dispatch
1183 %1 = catchpad within %0 [ptr @_ZTIi]
1184 %2 = call ptr @llvm.wasm.get.exception(token %1)
1185 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
1186 invoke void @__cxa_throw(ptr null, ptr null, ptr null) #1 [ "funclet"(token %1) ]
1187 to label %unreachable unwind label %catch.dispatch2
1189 catch.dispatch2: ; preds = %catch.start
1190 %4 = catchswitch within %1 [label %catch.start3] unwind label %ehcleanup
1192 catch.start3: ; preds = %catch.dispatch2
1193 %5 = catchpad within %4 [ptr @_ZTIi]
1194 %6 = call ptr @llvm.wasm.get.exception(token %5)
1195 %7 = call i32 @llvm.wasm.get.ehselector(token %5)
1196 catchret from %5 to label %try.cont
1198 try.cont: ; preds = %catch.start3
1199 invoke void @foo() [ "funclet"(token %1) ]
1200 to label %invoke.cont8 unwind label %ehcleanup
1202 invoke.cont8: ; preds = %try.cont
1203 invoke void @__cxa_throw(ptr null, ptr null, ptr null) #1 [ "funclet"(token %1) ]
1204 to label %unreachable unwind label %catch.dispatch11
1206 catch.dispatch11: ; preds = %invoke.cont8
1207 %8 = catchswitch within %1 [label %catch.start12] unwind label %ehcleanup
1209 catch.start12: ; preds = %catch.dispatch11
1210 %9 = catchpad within %8 [ptr @_ZTIi]
1211 %10 = call ptr @llvm.wasm.get.exception(token %9)
1212 %11 = call i32 @llvm.wasm.get.ehselector(token %9)
1215 invoke.cont: ; preds = %entry
1218 ehcleanup: ; preds = %catch.dispatch11, %try.cont, %catch.dispatch2
1219 %12 = cleanuppad within %1 []
1220 cleanupret from %12 unwind label %ehcleanup22
1222 ehcleanup22: ; preds = %ehcleanup, %catch.dispatch
1223 %13 = cleanuppad within none []
1224 cleanupret from %13 unwind to caller
1226 unreachable: ; preds = %invoke.cont8, %catch.start
1230 ; void exception_grouping_0() {
1240 ; Regression test for a WebAssemblyException grouping bug. After catchswitches
1241 ; are removed, EH pad catch.start2 is dominated by catch.start, but because
1242 ; catch.start2 is the unwind destination of catch.start, it should not be
1243 ; included in catch.start's exception. Also, after we take catch.start2's
1244 ; exception out of catch.start's exception, we have to take out try.cont8 out of
1245 ; catch.start's exception, because it has a predecessor in catch.start2.
1246 define void @exception_grouping_0() personality ptr @__gxx_wasm_personality_v0 {
1248 %exception = call ptr @__cxa_allocate_exception(i32 4) #0
1249 store i32 0, ptr %exception, align 16
1250 invoke void @__cxa_throw(ptr %exception, ptr @_ZTIi, ptr null) #1
1251 to label %unreachable unwind label %catch.dispatch
1253 catch.dispatch: ; preds = %entry
1254 %0 = catchswitch within none [label %catch.start] unwind label %catch.dispatch1
1256 catch.start: ; preds = %catch.dispatch
1257 %1 = catchpad within %0 [ptr @_ZTIi]
1258 %2 = call ptr @llvm.wasm.get.exception(token %1)
1259 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
1260 %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #0
1261 %matches = icmp eq i32 %3, %4
1262 br i1 %matches, label %catch, label %rethrow
1264 catch: ; preds = %catch.start
1265 %5 = call ptr @__cxa_begin_catch(ptr %2) #0 [ "funclet"(token %1) ]
1266 %6 = load i32, ptr %5, align 4
1267 call void @__cxa_end_catch() #0 [ "funclet"(token %1) ]
1268 catchret from %1 to label %catchret.dest
1270 catchret.dest: ; preds = %catch
1273 rethrow: ; preds = %catch.start
1274 invoke void @llvm.wasm.rethrow() #1 [ "funclet"(token %1) ]
1275 to label %unreachable unwind label %catch.dispatch1
1277 catch.dispatch1: ; preds = %rethrow, %catch.dispatch
1278 %7 = catchswitch within none [label %catch.start2] unwind to caller
1280 catch.start2: ; preds = %catch.dispatch1
1281 %8 = catchpad within %7 [ptr @_ZTIi]
1282 %9 = call ptr @llvm.wasm.get.exception(token %8)
1283 %10 = call i32 @llvm.wasm.get.ehselector(token %8)
1284 %11 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #0
1285 %matches3 = icmp eq i32 %10, %11
1286 br i1 %matches3, label %catch5, label %rethrow4
1288 catch5: ; preds = %catch.start2
1289 %12 = call ptr @__cxa_begin_catch(ptr %9) #0 [ "funclet"(token %8) ]
1290 %13 = load i32, ptr %12, align 4
1291 call void @__cxa_end_catch() #0 [ "funclet"(token %8) ]
1292 catchret from %8 to label %catchret.dest7
1294 catchret.dest7: ; preds = %catch5
1297 rethrow4: ; preds = %catch.start2
1298 call void @llvm.wasm.rethrow() #1 [ "funclet"(token %8) ]
1301 try.cont8: ; preds = %try.cont, %catchret.dest7
1304 try.cont: ; preds = %catchret.dest
1307 unreachable: ; preds = %rethrow, %entry
1311 ; Test for WebAssemblyException grouping. This test is hand-modified to generate
1313 ; catch.start dominates catch.start4 and catch.start4 dominates catch.start12,
1314 ; so the after dominator-based grouping, we end up with:
1315 ; catch.start's exception > catch4.start's exception > catch12.start's exception
1316 ; (> here represents subexception relationship)
1318 ; But the unwind destination chain is catch.start -> catch.start4 ->
1319 ; catch.start12. So all these subexception relationship should be deconstructed.
1320 ; We have to make sure to take out catch.start4's exception out of catch.start's
1321 ; exception first, before taking out catch.start12's exception out of
1322 ; catch.start4's exception; otherwise we end up with an incorrect relationship
1323 ; of catch.start's exception > catch.start12's exception.
1324 define void @exception_grouping_1() personality ptr @__gxx_wasm_personality_v0 {
1327 to label %invoke.cont unwind label %catch.dispatch
1329 invoke.cont: ; preds = %entry
1331 to label %invoke.cont1 unwind label %catch.dispatch
1333 invoke.cont1: ; preds = %invoke.cont
1335 to label %try.cont18 unwind label %catch.dispatch
1337 catch.dispatch11: ; preds = %rethrow6, %catch.dispatch3
1338 %0 = catchswitch within none [label %catch.start12] unwind to caller
1340 catch.start12: ; preds = %catch.dispatch11
1341 %1 = catchpad within %0 [ptr @_ZTIi]
1342 %2 = call ptr @llvm.wasm.get.exception(token %1)
1343 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
1344 %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #0
1345 %matches13 = icmp eq i32 %3, %4
1346 br i1 %matches13, label %catch15, label %rethrow14
1348 catch15: ; preds = %catch.start12
1349 %5 = call ptr @__cxa_begin_catch(ptr %2) #0 [ "funclet"(token %1) ]
1350 %6 = load i32, ptr %5, align 4
1351 call void @__cxa_end_catch() #0 [ "funclet"(token %1) ]
1352 catchret from %1 to label %try.cont18
1354 rethrow14: ; preds = %catch.start12
1355 call void @llvm.wasm.rethrow() #1 [ "funclet"(token %1) ]
1358 catch.dispatch3: ; preds = %rethrow, %catch.dispatch
1359 %7 = catchswitch within none [label %catch.start4] unwind label %catch.dispatch11
1361 catch.start4: ; preds = %catch.dispatch3
1362 %8 = catchpad within %7 [ptr @_ZTIi]
1363 %9 = call ptr @llvm.wasm.get.exception(token %8)
1364 %10 = call i32 @llvm.wasm.get.ehselector(token %8)
1365 %11 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #0
1366 %matches5 = icmp eq i32 %10, %11
1367 br i1 %matches5, label %catch7, label %rethrow6
1369 catch7: ; preds = %catch.start4
1370 %12 = call ptr @__cxa_begin_catch(ptr %9) #0 [ "funclet"(token %8) ]
1371 %13 = load i32, ptr %12, align 4
1372 call void @__cxa_end_catch() #0 [ "funclet"(token %8) ]
1373 catchret from %8 to label %try.cont18
1375 rethrow6: ; preds = %catch.start4
1376 invoke void @llvm.wasm.rethrow() #1 [ "funclet"(token %8) ]
1377 to label %unreachable unwind label %catch.dispatch11
1379 catch.dispatch: ; preds = %invoke.cont1, %invoke.cont, %entry
1380 %14 = catchswitch within none [label %catch.start] unwind label %catch.dispatch3
1382 catch.start: ; preds = %catch.dispatch
1383 %15 = catchpad within %14 [ptr @_ZTIi]
1384 %16 = call ptr @llvm.wasm.get.exception(token %15)
1385 %17 = call i32 @llvm.wasm.get.ehselector(token %15)
1386 %18 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #0
1387 %matches = icmp eq i32 %17, %18
1388 br i1 %matches, label %catch, label %rethrow
1390 catch: ; preds = %catch.start
1391 %19 = call ptr @__cxa_begin_catch(ptr %16) #0 [ "funclet"(token %15) ]
1392 %20 = load i32, ptr %19, align 4
1393 call void @__cxa_end_catch() #0 [ "funclet"(token %15) ]
1394 catchret from %15 to label %try.cont18
1396 rethrow: ; preds = %catch.start
1397 invoke void @llvm.wasm.rethrow() #1 [ "funclet"(token %15) ]
1398 to label %unreachable unwind label %catch.dispatch3
1400 try.cont18: ; preds = %catch, %catch7, %catch15, %invoke.cont1
1403 unreachable: ; preds = %rethrow, %rethrow6
1407 ; void exception_grouping_2() {
1411 ; } catch (int) { // (a)
1413 ; } catch (int) { // (b)
1417 ; } catch (int) { // (c)
1421 ; Regression test for an ExceptionInfo grouping bug. Because the first (inner)
1422 ; try always throws, both EH pads (b) (catch.start2) and (c) (catch.start10) are
1423 ; dominated by EH pad (a) (catch.start), even though they are not semantically
1424 ; contained in (a)'s exception. Because (a)'s unwind destination is (b), (b)'s
1425 ; exception is taken out of (a)'s. But because (c) is reachable from (b), we
1426 ; should make sure to take out (c)'s exception out of (a)'s exception too.
1427 define void @exception_grouping_2() personality ptr @__gxx_wasm_personality_v0 {
1429 %exception = call ptr @__cxa_allocate_exception(i32 4) #1
1430 store i32 0, ptr %exception, align 16
1431 invoke void @__cxa_throw(ptr %exception, ptr @_ZTIi, ptr null) #3
1432 to label %unreachable unwind label %catch.dispatch
1434 catch.dispatch: ; preds = %entry
1435 %0 = catchswitch within none [label %catch.start] unwind label %catch.dispatch1
1437 catch.start: ; preds = %catch.dispatch
1438 %1 = catchpad within %0 [ptr @_ZTIi]
1439 %2 = call ptr @llvm.wasm.get.exception(token %1)
1440 %3 = call i32 @llvm.wasm.get.ehselector(token %1)
1441 %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #1
1442 %matches = icmp eq i32 %3, %4
1443 br i1 %matches, label %catch, label %rethrow
1445 catch: ; preds = %catch.start
1446 %5 = call ptr @__cxa_begin_catch(ptr %2) #1 [ "funclet"(token %1) ]
1447 %6 = load i32, ptr %5, align 4
1448 call void @__cxa_end_catch() #1 [ "funclet"(token %1) ]
1449 catchret from %1 to label %try.cont8
1451 rethrow: ; preds = %catch.start
1452 invoke void @llvm.wasm.rethrow() #3 [ "funclet"(token %1) ]
1453 to label %unreachable unwind label %catch.dispatch1
1455 catch.dispatch1: ; preds = %rethrow, %catch.dispatch
1456 %7 = catchswitch within none [label %catch.start2] unwind to caller
1458 catch.start2: ; preds = %catch.dispatch1
1459 %8 = catchpad within %7 [ptr @_ZTIi]
1460 %9 = call ptr @llvm.wasm.get.exception(token %8)
1461 %10 = call i32 @llvm.wasm.get.ehselector(token %8)
1462 %11 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #1
1463 %matches3 = icmp eq i32 %10, %11
1464 br i1 %matches3, label %catch5, label %rethrow4
1466 catch5: ; preds = %catch.start2
1467 %12 = call ptr @__cxa_begin_catch(ptr %9) #1 [ "funclet"(token %8) ]
1468 %13 = load i32, ptr %12, align 4
1469 call void @__cxa_end_catch() #1 [ "funclet"(token %8) ]
1470 catchret from %8 to label %try.cont8
1472 rethrow4: ; preds = %catch.start2
1473 call void @llvm.wasm.rethrow() #3 [ "funclet"(token %8) ]
1476 try.cont8: ; preds = %catch, %catch5
1478 to label %try.cont16 unwind label %catch.dispatch9
1480 catch.dispatch9: ; preds = %try.cont8
1481 %14 = catchswitch within none [label %catch.start10] unwind to caller
1483 catch.start10: ; preds = %catch.dispatch9
1484 %15 = catchpad within %14 [ptr @_ZTIi]
1485 %16 = call ptr @llvm.wasm.get.exception(token %15)
1486 %17 = call i32 @llvm.wasm.get.ehselector(token %15)
1487 %18 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #1
1488 %matches11 = icmp eq i32 %17, %18
1489 br i1 %matches11, label %catch13, label %rethrow12
1491 catch13: ; preds = %catch.start10
1492 %19 = call ptr @__cxa_begin_catch(ptr %16) #1 [ "funclet"(token %15) ]
1493 %20 = load i32, ptr %19, align 4
1494 call void @__cxa_end_catch() #1 [ "funclet"(token %15) ]
1495 catchret from %15 to label %try.cont16
1497 rethrow12: ; preds = %catch.start10
1498 call void @llvm.wasm.rethrow() #3 [ "funclet"(token %15) ]
1501 try.cont16: ; preds = %try.cont8, %catch13
1504 unreachable: ; preds = %rethrow, %entry
1508 ; Check if the unwind destination mismatch stats are correct
1509 ; NOSORT: 23 wasm-cfg-stackify - Number of call unwind mismatches found
1510 ; NOSORT: 4 wasm-cfg-stackify - Number of catch unwind mismatches found
1515 declare i32 @qux(i32)
1516 declare void @quux(i32)
1517 declare void @fun(i32)
1518 ; Function Attrs: nounwind
1519 declare void @nothrow(i32) #0
1520 ; Function Attrs: nounwind
1521 declare i32 @nothrow_i32() #0
1523 ; Function Attrs: nounwind
1524 declare ptr @_ZN6ObjectD2Ev(ptr returned) #0
1525 @_ZTI7MyClass = external constant { ptr, ptr }, align 4
1526 ; Function Attrs: nounwind
1527 declare ptr @_ZN7MyClassD2Ev(ptr returned) #0
1528 ; Function Attrs: nounwind
1529 declare ptr @_ZN7MyClassC2ERKS_(ptr returned, ptr dereferenceable(4)) #0
1531 declare i32 @__gxx_wasm_personality_v0(...)
1532 ; Function Attrs: nounwind
1533 declare ptr @llvm.wasm.get.exception(token) #0
1534 ; Function Attrs: nounwind
1535 declare i32 @llvm.wasm.get.ehselector(token) #0
1536 declare ptr @__cxa_allocate_exception(i32) #0
1537 declare void @__cxa_throw(ptr, ptr, ptr)
1538 ; Function Attrs: noreturn
1539 declare void @llvm.wasm.rethrow() #1
1540 ; Function Attrs: nounwind
1541 declare i32 @llvm.eh.typeid.for(ptr) #0
1543 declare ptr @__cxa_begin_catch(ptr)
1544 declare void @__cxa_end_catch()
1545 declare ptr @__cxa_get_exception_ptr(ptr)
1546 declare void @_ZSt9terminatev()
1547 ; Function Attrs: nounwind
1548 declare void @llvm.memcpy.p0.p0.i32(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i32, i1 immarg) #0
1549 ; Function Attrs: nounwind
1550 declare void @llvm.memmove.p0.p0.i32(ptr nocapture, ptr nocapture readonly, i32, i1 immarg) #0
1551 ; Function Attrs: nounwind
1552 declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1 immarg) #0
1554 attributes #0 = { nounwind }
1555 attributes #1 = { noreturn }