[libc] implement unistd/getentropy (#122692)
[llvm-project.git] / llvm / test / CodeGen / WebAssembly / cfg-stackify-eh.ll
blob683b03d16d57bded51d79490e45b44eb6da045db
1 ; REQUIRES: asserts
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
18 ; void foo();
19 ; void two_catches() {
20 ;   try {
21 ;     foo();
22 ;   } catch (int) {
23 ;   } catch (double) {
24 ;   }
25 ; }
27 ; CHECK-LABEL: two_catches:
28 ; CHECK: block
29 ; CHECK:   block     () -> (i32, exnref)
30 ; CHECK:     try_table    (catch_ref __cpp_exception 0) # 0: down to label[[L0:[0-9]+]]
31 ; CHECK:       call  foo
32 ; CHECK:       br        2                               # 2: down to label[[L1:[0-9]+]]
33 ; CHECK:     end_try_table
34 ; CHECK:   end_block                                     # label[[L0]]:
35 ; CHECK:   local.set  2
36 ; CHECK:   local.set  1
37 ; CHECK:   local.get  0
38 ; CHECK:   call  _Unwind_CallPersonality
39 ; CHECK:   block
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]]:
45 ; CHECK:   block
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]]:
51 ; CHECK:   throw_ref
52 ; CHECK: end_block                                       # label[[L1]]:
53 define void @two_catches() personality ptr @__gxx_wasm_personality_v0 {
54 entry:
55   invoke void @foo()
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) ]
86   unreachable
88 try.cont:                                         ; preds = %catch, %catch2, %entry
89   ret void
92 ; Nested try-catches within a catch
93 ; void nested_catch() {
94 ;   try {
95 ;     foo();
96 ;   } catch (int) {
97 ;     try {
98 ;       foo();
99 ;     } catch (int) {
100 ;       foo();
101 ;     }
102 ;   }
103 ; }
105 ; CHECK-LABEL: nested_catch:
106 ; CHECK: block     exnref
107 ; CHECK:   block
108 ; CHECK:     block     () -> (i32, exnref)
109 ; CHECK:       try_table    (catch_ref __cpp_exception 0)         # 0: down to label[[L0:[0-9]+]]
110 ; CHECK:         call  foo
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
115 ; CHECK:     block
116 ; CHECK:       block
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]+]]
123 ; CHECK:                 call  foo
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
128 ; CHECK:             block
129 ; CHECK:               block
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]+]]
134 ; CHECK:                     call  foo
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
141 ; CHECK:                 throw_ref
142 ; CHECK:               end_block                                  # label[[L6]]:
143 ; CHECK:               throw_ref
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
152 ; CHECK:         throw_ref
153 ; CHECK:       end_block                                          # label[[L2]]:
154 ; CHECK:       throw_ref
155 ; CHECK:     end_block                                            # label[[L5]]:
156 ; CHECK:     call  __cxa_end_catch
157 ; CHECK:   end_block                                              # label[[L1]]:
158 ; CHECK:   return
159 ; CHECK: end_block                                                # label[[L9]]:
160 ; CHECK: throw_ref
161 define void @nested_catch() personality ptr @__gxx_wasm_personality_v0 {
162 entry:
163   invoke void @foo()
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) ]
214   unreachable
216 try.cont11:                                       ; preds = %try.cont, %entry
217   ret void
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
230   unreachable
233 ; Nested try-catches within a try
234 ; void nested_try() {
235 ;   try {
236 ;     try {
237 ;       foo();
238 ;     } catch (...) {
239 ;     }
240 ;   } catch (...) {
241 ;   }
242 ; }
244 ; CHECK-LABEL: nested_try:
245 ; CHECK: block
246 ; CHECK:   block     i32
247 ; CHECK:     try_table    (catch __cpp_exception 0)   # 0: down to label[[L0:[0-9]+]]
248 ; CHECK:     block     i32
249 ; CHECK:       try_table    (catch __cpp_exception 0) # 0: down to label[[L1:[0-9]+]]
250 ; CHECK:         call  foo
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 {
263 entry:
264   invoke void @foo()
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
290   ret void
292 invoke.cont1:                                     ; preds = %catch.start
293   catchret from %1 to label %try.cont7
297 ; CHECK-LABEL: loop_within_catch:
298 ; CHECK: block
299 ; CHECK:   block     i32
300 ; CHECK:     try_table    (catch __cpp_exception 0) # 0: down to label[[L0:[0-9]+]]
301 ; CHECK:       call  foo
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]+]]:
307 ; CHECK:     block
308 ; CHECK:       block
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]+]]
312 ; CHECK:             call  foo
313 ; CHECK:             br        3                    # 3: down to label[[L5:[0-9]+]]
314 ; CHECK:           end_try_table
315 ; CHECK:         end_block                          # label[[L4]]:
316 ; CHECK:         block
317 ; CHECK:           block
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
324 ; CHECK:           unreachable
325 ; CHECK:         end_block                          # label[[L7]]:
326 ; CHECK:         throw_ref
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]]
332 ; CHECK:   end_loop
333 ; CHECK: end_block                                  # label[[L1]]:
334 define void @loop_within_catch() personality ptr @__gxx_wasm_personality_v0 {
335 entry:
336   invoke void @foo()
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) ]
347   br label %for.cond
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
360   br label %for.cond
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
367   ret void
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) ]
380   unreachable
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:
388 ; NOOPT: block
389 ; NOOPT:   block     i32
390 ; NOOPT:   try_table    (catch __cpp_exception 0)
391 ; NOOPT:     block
392 ; NOOPT:       block
393 ; NOOPT:         block
394 ; NOOPT:         end_block
395 ; NOOPT:       end_block
396 ; NOOPT:       call  foo
397 ; NOOPT:     end_block
398 ; NOOPT:     call  bar
399 ; NOOPT:   end_try_table
400 ; NOOPT:   end_block
401 ; NOOPT: end_block
402 define void @block_try_table_markers() personality ptr @__gxx_wasm_personality_v0 {
403 bb0:
404   br i1 undef, label %bb1, label %bb2
406 bb1:                                              ; preds = %bb0
407   br i1 undef, label %bb3, label %bb4
409 bb2:                                              ; preds = %bb0
410   br label %try.cont
412 bb3:                                              ; preds = %bb1
413   invoke void @foo()
414           to label %try.cont unwind label %catch.dispatch
416 bb4:                                              ; preds = %bb1
417   invoke void @bar()
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
430   ret void
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:
437 ; CHECK: loop
438 ; CHECK:   block     i32
439 ; CHECK:     try_table    (catch __cpp_exception 0)
440 ; CHECK:       call  foo
441 ; CHECK:     end_try_table
442 ; CHECK:   end_block
443 ; CHECK: end_loop
444 define void @loop_try_table_markers(ptr %p) personality ptr @__gxx_wasm_personality_v0 {
445 entry:
446   store volatile i32 0, ptr %p
447   br label %loop
449 loop:                                             ; preds = %try.cont, %entry
450   store volatile i32 1, ptr %p
451   invoke void @foo()
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
464   br label %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
485 ; NOSORT:   block
486 ; NOSORT:     block     i32
487 ; NOSORT:       try_table    (catch __cpp_exception 0)         # 0: down to label[[L0:[0-9]+]]
488 ; NOSORT:         block     exnref
489 ; NOSORT:           block     i32
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]+]]
493 ; NOSORT:                 call  foo
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]+]]
496 ; NOSORT:                   call  bar
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]]:
504 ; NOSORT:         throw_ref
505 ; NOSORT:       end_try_table
506 ; NOSORT:     end_block                                        # label[[L0]]:
507 ; NOSORT:   end_block
508 ; NOSORT:   return
509 ; NOSORT: end_block                                            # label[[L1]]:
510 ; NOSORT: throw_ref
511 define void @unwind_mismatches_0() personality ptr @__gxx_wasm_personality_v0 {
512 bb0:
513   invoke void @foo()
514           to label %bb1 unwind label %catch.dispatch0
516 bb1:                                              ; preds = %bb0
517   invoke void @bar()
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
539   ret void
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
552 ; NOSORT:   block     i32
553 ; NOSORT:     try_table    (catch __cpp_exception 0) # 0: down to label[[L0:[0-9]+]]
554 ; NOSORT:       call  foo
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]+]]
557 ; NOSORT:         call  bar
558 ; NOSORT:         call  baz
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
564 ; NOSORT:       return
565 ; NOSORT:     end_try_table
566 ; NOSORT:   end_block                                # label[[L0]]:
567 ; NOSORT:   return
568 ; NOSORT: end_block                                  # label[[L1]]:
569 ; NOSORT: throw_ref
570 define void @unwind_mismatches_1() personality ptr @__gxx_wasm_personality_v0 {
571 bb0:
572   invoke void @foo()
573           to label %bb1 unwind label %catch.dispatch0
575 bb1:                                              ; preds = %bb0
576   call void @bar()
577   %call = call i32 @baz()
578   call void @nothrow(i32 %call) #0
579   ret void
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
591   ret void
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
600 ; NOSORT:   block
601 ; NOSORT:     block     i32
602 ; NOSORT:       try_table    (catch __cpp_exception 0)         # 0: down to label[[L0:[0-9]+]]
603 ; NOSORT:         block     exnref
604 ; NOSORT:           block     i32
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]+]]
608 ; NOSORT:                 call  foo
609 ; --- nested try_table-end_try_table starts (call unwind mismatch)
610 ; NOSORT:                 try_table    (catch_all_ref 7)       # 7: down to label[[L1]]
611 ; NOSORT:                   call  foo
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]+]]
616 ; NOSORT:                   call  bar
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]]:
624 ; NOSORT:         throw_ref
625 ; NOSORT:       end_try_table
626 ; NOSORT:     end_block                                        # label[[L0]]:
627 ; NOSORT:   end_block
628 ; NOSORT:   return
629 ; NOSORT: end_block                                            # label[[L1]]:
630 ; NOSORT: throw_ref
631 define void @unwind_mismatches_2() personality ptr @__gxx_wasm_personality_v0 {
632 bb0:
633   invoke void @foo()
634           to label %bb1 unwind label %catch.dispatch0
636 bb1:                                              ; preds = %bb0
637   call void @foo()
638   invoke void @bar()
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
660   ret void
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
675 ; NOSORT:   block     i32
676 ; NOSORT:     try_table    (catch __cpp_exception 0) # 0: down to label[[L0:[0-9]+]]
677 ; NOSORT:       call  foo
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
681 ; NOSORT:         call  qux
682 ; NOSORT:       end_try_table
683 ; --- nested try_table-end_try_table ends (call unwind mismatch)
684 ; NOSORT:       return
685 ; NOSORT:     end_try_table
686 ; NOSORT:   end_block                               # label[[L0]]:
687 ; NOSORT:   return
688 ; NOSORT: end_block                               # label[[L1]]:
689 ; NOSORT: throw_ref
690 define i32 @unwind_mismatches_3() personality ptr @__gxx_wasm_personality_v0 {
691 bb0:
692   invoke void @foo()
693           to label %bb1 unwind label %catch.dispatch0
695 bb1:                                              ; preds = %bb0
696   %0 = call i32 @qux(i32 5)
697   ret i32 %0
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
709   ret i32 0
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
723 ; NOSORT:   block
724 ; NOSORT:     block     i32
725 ; NOSORT:       try_table    (catch __cpp_exception 0)       # 0: down to label[[L0:[0-9]+]]
726 ; NOSORT:         block     exnref
727 ; NOSORT:           block     i32
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]+]]
731 ; NOSORT:               call  foo
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]+]]
734 ; NOSORT:                 call  bar
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]]:
748 ; NOSORT:         throw_ref
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:
754 ; NOSORT:   return
755 ; NOSORT: end_block                                          # label[[L1]]:
756 ; NOSORT: throw_ref
757 define void @unwind_mismatches_4() personality ptr @__gxx_wasm_personality_v0 {
758 bb0:
759   invoke void @foo()
760           to label %bb1 unwind label %catch.dispatch0
762 bb1:                                              ; preds = %bb0
763   invoke void @bar()
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
789   ret void
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
799 ; CHECK:   block
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]+]]
804 ; CHECK:           call  baz
805 ; CHECK:         end_try_table
806 ; --- nested try_table-end_try_table ends (call unwind mismatch)
807 ; CHECK:         call  quux
808 ; CHECK:       end_try_table
809 ; CHECK:     end_block                           # label[[L0]]:
810 ; CHECK:     throw_ref
811 ; CHECK:   end_block
812 ; CHECK:   unreachable
813 ; CHECK: end_block                               # label[[L1]]:
814 ; CHECK: throw_ref
815 define void @unwind_mismatches_5() personality ptr @__gxx_wasm_personality_v0 {
816 entry:
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
826   unreachable
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
838 ; NOSORT:   block     i32
839 ; NOSORT:     try_table    (catch __cpp_exception 0)           # 0: down to label[[L0:[0-9]+]]
840 ; NOSORT:       block     exnref
841 ; NOSORT:         block
842 ; NOSORT:           block     i32
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]+]]
846 ; NOSORT:                 call  foo
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]+]]
849 ; NOSORT:                   call  bar
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]]:
856 ; NOSORT:         end_block
857 ; NOSORT:         loop
858 ; NOSORT:           call  foo
859 ; NOSORT:         end_loop
860 ; NOSORT:       end_block                                      # label[[L3]]:
861 ; NOSORT:       throw_ref
862 ; NOSORT:     end_try_table
863 ; NOSORT:   end_block                                          # label[[L0]]:
864 ; NOSORT:   return
865 ; NOSORT: end_block                                            # label[[L1]]:
866 ; NOSORT: throw_ref
867 define void @unwind_mismatches_with_loop() personality ptr @__gxx_wasm_personality_v0 {
868 bb0:
869   invoke void @foo()
870           to label %bb1 unwind label %catch.dispatch0
872 bb1:                                              ; preds = %bb0
873   invoke void @bar()
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
885 bb2:
886   invoke void @foo()
887           to label %bb3 unwind label %catch.dispatch1
889 bb3:                                             ; preds = %bb14
890   br label %bb2
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
902   ret void
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 {
910 bb0:
911   invoke void @foo()
912           to label %bb1 unwind label %catch.dispatch0
914 bb1:                                              ; preds = %bb0
915   %t = add i32 %x, 4
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
927   ret void
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
942   ret void
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:
953 ; NOOPT: block
954 ; NOOPT:   block
955 ; NOOPT:     block     i32
956 ; NOOPT:       try_table    (catch __cpp_exception 0)
957 ; NOOPT:         call  foo
958 ; NOOPT:       end_try_table
959 ; NOOPT:     end_block
960 ; NOOPT:     call  __cxa_begin_catch
961 ; NOOPT:     call  __cxa_end_catch
962 ; NOOPT:   end_block
963 ; NOOPT:   call  foo
964 ; NOOPT: end_block
965 ; NOOPT: return
966 define void @cfg_sort_order(i32 %arg) personality ptr @__gxx_wasm_personality_v0 {
967 entry:
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
983   invoke void @foo()
984           to label %cont unwind label %catch.dispatch
986 cont:                                             ; preds = %if.then
987   call void @foo()
988   br label %if.end
990 if.end:                                           ; preds = %cont, %catch.start, %entry
991   ret void
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)
1004 ; NOSORT:     call  foo
1005 ; NOSORT:     call  memcpy
1006 ; NOSORT:     call  memmove
1007 ; NOSORT:     call  memset
1008 ; NOSORT:     return
1009 ; NOSORT:   end_try_table
1010 ; NOSORT: end_block
1011 ; NOSORT: throw_ref
1012 define void @mem_intrinsics(ptr %a, ptr %b) personality ptr @__gxx_wasm_personality_v0 {
1013 entry:
1014   %o = alloca %class.Object, align 1
1015   invoke void @foo()
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)
1023   ret void
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
1039 ; CHECK: call  fun
1040 define void @try_table_marker_with_stackified_input() personality ptr @__gxx_wasm_personality_v0 {
1041 entry:
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
1047   ret void
1049 terminate:                                        ; preds = %entry
1050   %0 = cleanuppad within none []
1051   call void @_ZSt9terminatev() [ "funclet"(token %0) ]
1052   unreachable
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 {
1059 entry:
1060   %e = alloca %class.MyClass, align 4
1061   br label %for.cond
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) ]
1098   unreachable
1100 for.inc:                                          ; preds = %invoke.cont2, %for.body
1101   %inc = add nsw i32 %i.0, 1
1102   br label %for.cond
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
1114   ret void
1116 terminate7:                                       ; preds = %ehcleanup
1117   %9 = cleanuppad within %8 []
1118   call void @_ZSt9terminatev() [ "funclet"(token %9) ]
1119   unreachable
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:
1128 ; NOSORT: loop
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
1133 ; NOSORT: end_loop
1134 define void @loop_contains_exception(i32 %n) personality ptr @__gxx_wasm_personality_v0 {
1135 entry:
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
1145   invoke void @foo()
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) ]
1165   unreachable
1167 while.end:                                        ; preds = %while.body, %while.cond
1168   ret void
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 {
1175 entry:
1176   invoke void @foo()
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)
1213   unreachable
1215 invoke.cont:                                      ; preds = %entry
1216   unreachable
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
1227   unreachable
1230 ; void exception_grouping_0() {
1231 ;   try {
1232 ;     try {
1233 ;       throw 0;
1234 ;     } catch (int) {
1235 ;     }
1236 ;   } catch (int) {
1237 ;   }
1238 ; }
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 {
1247 entry:
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
1271   br label %try.cont
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
1295   br label %try.cont8
1297 rethrow4:                                         ; preds = %catch.start2
1298   call void @llvm.wasm.rethrow() #1 [ "funclet"(token %8) ]
1299   unreachable
1301 try.cont8:                                        ; preds = %try.cont, %catchret.dest7
1302   ret void
1304 try.cont:                                         ; preds = %catchret.dest
1305   br label %try.cont8
1307 unreachable:                                      ; preds = %rethrow, %entry
1308   unreachable
1311 ; Test for WebAssemblyException grouping. This test is hand-modified to generate
1312 ; this structure:
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 {
1325 entry:
1326   invoke void @foo()
1327           to label %invoke.cont unwind label %catch.dispatch
1329 invoke.cont:                                      ; preds = %entry
1330   invoke void @foo()
1331           to label %invoke.cont1 unwind label %catch.dispatch
1333 invoke.cont1:                                     ; preds = %invoke.cont
1334   invoke void @foo()
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) ]
1356   unreachable
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
1401   ret void
1403 unreachable:                                      ; preds = %rethrow, %rethrow6
1404   unreachable
1407 ; void exception_grouping_2() {
1408 ;   try {
1409 ;     try {
1410 ;       throw 0;
1411 ;     } catch (int) { // (a)
1412 ;     }
1413 ;   } catch (int) {   // (b)
1414 ;   }
1415 ;   try {
1416 ;     foo();
1417 ;   } catch (int) {   // (c)
1418 ;   }
1419 ; }
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 {
1428 entry:
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) ]
1474   unreachable
1476 try.cont8:                                        ; preds = %catch, %catch5
1477   invoke void @foo()
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) ]
1499   unreachable
1501 try.cont16:                                       ; preds = %try.cont8, %catch13
1502   ret void
1504 unreachable:                                      ; preds = %rethrow, %entry
1505   unreachable
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
1512 declare void @foo()
1513 declare void @bar()
1514 declare i32 @baz()
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 }