xref: /llvm-project/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll (revision c3dfd34e54c1cb9e0e6c7472a6d30d03a63f6f0a)
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
7
8target triple = "wasm32-unknown-unknown"
9
10@_ZTIi = external constant ptr
11@_ZTId = external constant ptr
12
13%class.Object = type { i8 }
14%class.MyClass = type { i32 }
15
16; Simple test case with two catch clauses
17;
18; void foo();
19; void two_catches() {
20;   try {
21;     foo();
22;   } catch (int) {
23;   } catch (double) {
24;   }
25; }
26
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]]:
53define void @two_catches() personality ptr @__gxx_wasm_personality_v0 {
54entry:
55  invoke void @foo()
56          to label %try.cont unwind label %catch.dispatch
57
58catch.dispatch:                                   ; preds = %entry
59  %0 = catchswitch within none [label %catch.start] unwind to caller
60
61catch.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
68
69catch2:                                           ; 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
73
74catch.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
78
79catch:                                            ; 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
83
84rethrow:                                          ; preds = %catch.fallthrough
85  call void @llvm.wasm.rethrow() [ "funclet"(token %1) ]
86  unreachable
87
88try.cont:                                         ; preds = %catch, %catch2, %entry
89  ret void
90}
91
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; }
104
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
161define void @nested_catch() personality ptr @__gxx_wasm_personality_v0 {
162entry:
163  invoke void @foo()
164          to label %try.cont11 unwind label %catch.dispatch
165
166catch.dispatch:                                   ; preds = %entry
167  %0 = catchswitch within none [label %catch.start] unwind to caller
168
169catch.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
176
177catch:                                            ; 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
182
183catch.dispatch2:                                  ; preds = %catch
184  %7 = catchswitch within %1 [label %catch.start3] unwind label %ehcleanup9
185
186catch.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
193
194catch6:                                           ; 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
199
200invoke.cont8:                                     ; preds = %catch6
201  call void @__cxa_end_catch() [ "funclet"(token %8) ]
202  catchret from %8 to label %try.cont
203
204rethrow5:                                         ; preds = %catch.start3
205  invoke void @llvm.wasm.rethrow() [ "funclet"(token %8) ]
206          to label %unreachable unwind label %ehcleanup9
207
208try.cont:                                         ; preds = %invoke.cont8, %catch
209  call void @__cxa_end_catch() [ "funclet"(token %1) ]
210  catchret from %1 to label %try.cont11
211
212rethrow:                                          ; preds = %catch.start
213  call void @llvm.wasm.rethrow() [ "funclet"(token %1) ]
214  unreachable
215
216try.cont11:                                       ; preds = %try.cont, %entry
217  ret void
218
219ehcleanup:                                        ; preds = %catch6
220  %14 = cleanuppad within %8 []
221  call void @__cxa_end_catch() [ "funclet"(token %14) ]
222  cleanupret from %14 unwind label %ehcleanup9
223
224ehcleanup9:                                       ; 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
228
229unreachable:                                      ; preds = %rethrow5
230  unreachable
231}
232
233; Nested try-catches within a try
234; void nested_try() {
235;   try {
236;     try {
237;       foo();
238;     } catch (...) {
239;     }
240;   } catch (...) {
241;   }
242; }
243
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]]:
262define void @nested_try() personality ptr @__gxx_wasm_personality_v0 {
263entry:
264  invoke void @foo()
265          to label %try.cont7 unwind label %catch.dispatch
266
267catch.dispatch:                                   ; preds = %entry
268  %0 = catchswitch within none [label %catch.start] unwind label %catch.dispatch2
269
270catch.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
277
278catch.dispatch2:                                  ; preds = %catch.start, %catch.dispatch
279  %5 = catchswitch within none [label %catch.start3] unwind to caller
280
281catch.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
288
289try.cont7:                                        ; preds = %entry, %invoke.cont1, %catch.start3
290  ret void
291
292invoke.cont1:                                     ; preds = %catch.start
293  catchret from %1 to label %try.cont7
294}
295
296
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]]:
334define void @loop_within_catch() personality ptr @__gxx_wasm_personality_v0 {
335entry:
336  invoke void @foo()
337          to label %try.cont unwind label %catch.dispatch
338
339catch.dispatch:                                   ; preds = %entry
340  %0 = catchswitch within none [label %catch.start] unwind to caller
341
342catch.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
348
349for.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
353
354for.body:                                         ; preds = %for.cond
355  invoke void @foo() [ "funclet"(token %1) ]
356          to label %for.inc unwind label %ehcleanup
357
358for.inc:                                          ; preds = %for.body
359  %inc = add nsw i32 %i.0, 1
360  br label %for.cond
361
362for.end:                                          ; preds = %for.cond
363  call void @__cxa_end_catch() [ "funclet"(token %1) ]
364  catchret from %1 to label %try.cont
365
366try.cont:                                         ; preds = %for.end, %entry
367  ret void
368
369ehcleanup:                                        ; 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
373
374invoke.cont2:                                     ; preds = %ehcleanup
375  cleanupret from %5 unwind to caller
376
377terminate:                                        ; preds = %ehcleanup
378  %6 = cleanuppad within %5 []
379  call void @_ZSt9terminatev() [ "funclet"(token %6) ]
380  unreachable
381}
382
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
402define void @block_try_table_markers() personality ptr @__gxx_wasm_personality_v0 {
403bb0:
404  br i1 undef, label %bb1, label %bb2
405
406bb1:                                              ; preds = %bb0
407  br i1 undef, label %bb3, label %bb4
408
409bb2:                                              ; preds = %bb0
410  br label %try.cont
411
412bb3:                                              ; preds = %bb1
413  invoke void @foo()
414          to label %try.cont unwind label %catch.dispatch
415
416bb4:                                              ; preds = %bb1
417  invoke void @bar()
418          to label %try.cont unwind label %catch.dispatch
419
420catch.dispatch:                                   ; preds = %bb4, %bb3
421  %0 = catchswitch within none [label %catch.start] unwind to caller
422
423catch.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
428
429try.cont:                                         ; preds = %catch.start, %bb4, %bb3, %bb2
430  ret void
431}
432
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
444define void @loop_try_table_markers(ptr %p) personality ptr @__gxx_wasm_personality_v0 {
445entry:
446  store volatile i32 0, ptr %p
447  br label %loop
448
449loop:                                             ; preds = %try.cont, %entry
450  store volatile i32 1, ptr %p
451  invoke void @foo()
452          to label %try.cont unwind label %catch.dispatch
453
454catch.dispatch:                                   ; preds = %loop
455  %0 = catchswitch within none [label %catch.start] unwind to caller
456
457catch.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
462
463try.cont:                                         ; preds = %catch.start, %loop
464  br label %loop
465}
466
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.
471
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.
482
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
511define void @unwind_mismatches_0() personality ptr @__gxx_wasm_personality_v0 {
512bb0:
513  invoke void @foo()
514          to label %bb1 unwind label %catch.dispatch0
515
516bb1:                                              ; preds = %bb0
517  invoke void @bar()
518          to label %try.cont unwind label %catch.dispatch1
519
520catch.dispatch0:                                  ; preds = %bb0
521  %0 = catchswitch within none [label %catch.start0] unwind to caller
522
523catch.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
528
529catch.dispatch1:                                  ; preds = %bb1
530  %4 = catchswitch within none [label %catch.start1] unwind to caller
531
532catch.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
537
538try.cont:                                         ; preds = %catch.start1, %catch.start0, %bb1
539  ret void
540}
541
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.
546
547; And the return value of 'baz' should NOT be stackified because the BB is split
548; during fixing unwind mismatches.
549
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
570define void @unwind_mismatches_1() personality ptr @__gxx_wasm_personality_v0 {
571bb0:
572  invoke void @foo()
573          to label %bb1 unwind label %catch.dispatch0
574
575bb1:                                              ; preds = %bb0
576  call void @bar()
577  %call = call i32 @baz()
578  call void @nothrow(i32 %call) #0
579  ret void
580
581catch.dispatch0:                                  ; preds = %bb0
582  %0 = catchswitch within none [label %catch.start0] unwind to caller
583
584catch.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
589
590try.cont:                                         ; preds = %catch.start0
591  ret void
592}
593
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.
597
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
631define void @unwind_mismatches_2() personality ptr @__gxx_wasm_personality_v0 {
632bb0:
633  invoke void @foo()
634          to label %bb1 unwind label %catch.dispatch0
635
636bb1:                                              ; preds = %bb0
637  call void @foo()
638  invoke void @bar()
639          to label %try.cont unwind label %catch.dispatch1
640
641catch.dispatch0:                                  ; preds = %bb0
642  %0 = catchswitch within none [label %catch.start0] unwind to caller
643
644catch.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
649
650catch.dispatch1:                                  ; preds = %bb1
651  %4 = catchswitch within none [label %catch.start1] unwind to caller
652
653catch.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
658
659try.cont:                                         ; preds = %catch.start1, %catch.start0, %bb1
660  ret void
661}
662
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.
668
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'.
672
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
690define i32 @unwind_mismatches_3() personality ptr @__gxx_wasm_personality_v0 {
691bb0:
692  invoke void @foo()
693          to label %bb1 unwind label %catch.dispatch0
694
695bb1:                                              ; preds = %bb0
696  %0 = call i32 @qux(i32 5)
697  ret i32 %0
698
699catch.dispatch0:                                  ; preds = %bb0
700  %1 = catchswitch within none [label %catch.start0] unwind to caller
701
702catch.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
707
708try.cont:                                         ; preds = %catch.start0
709  ret i32 0
710}
711
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.
720
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
757define void @unwind_mismatches_4() personality ptr @__gxx_wasm_personality_v0 {
758bb0:
759  invoke void @foo()
760          to label %bb1 unwind label %catch.dispatch0
761
762bb1:                                              ; preds = %bb0
763  invoke void @bar()
764          to label %try.cont unwind label %catch.dispatch1
765
766catch.dispatch0:                                  ; preds = %bb0
767  %0 = catchswitch within none [label %catch.start0] unwind to caller
768
769catch.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
776
777catch.dispatch1:                                  ; preds = %bb1
778  %5 = catchswitch within none [label %catch.start1] unwind to caller
779
780catch.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
787
788try.cont:                                         ; preds = %catch.start1, %catch.start0, %bb1
789  ret void
790}
791
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.
796
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
815define void @unwind_mismatches_5() personality ptr @__gxx_wasm_personality_v0 {
816entry:
817  %call = call i32 @baz()
818  invoke void @quux(i32 %call)
819          to label %invoke.cont unwind label %ehcleanup
820
821ehcleanup:                                        ; preds = %entry
822  %0 = cleanuppad within none []
823  cleanupret from %0 unwind to caller
824
825invoke.cont:                                      ; preds = %entry
826  unreachable
827}
828
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.
835
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:         unreachable
861; NOSORT:       end_block                                      # label[[L3]]:
862; NOSORT:       throw_ref
863; NOSORT:     end_try_table
864; NOSORT:   end_block                                          # label[[L0]]:
865; NOSORT:   return
866; NOSORT: end_block                                            # label[[L1]]:
867; NOSORT: throw_ref
868define void @unwind_mismatches_with_loop() personality ptr @__gxx_wasm_personality_v0 {
869bb0:
870  invoke void @foo()
871          to label %bb1 unwind label %catch.dispatch0
872
873bb1:                                              ; preds = %bb0
874  invoke void @bar()
875          to label %bb2 unwind label %catch.dispatch1
876
877catch.dispatch0:                                  ; preds = %bb0
878  %0 = catchswitch within none [label %catch.start0] unwind to caller
879
880catch.start0:                                     ; preds = %catch.dispatch0
881  %1 = catchpad within %0 [ptr null]
882  %2 = call ptr @llvm.wasm.get.exception(token %1)
883  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
884  catchret from %1 to label %bb2
885
886bb2:
887  invoke void @foo()
888          to label %bb3 unwind label %catch.dispatch1
889
890bb3:                                             ; preds = %bb14
891  br label %bb2
892
893catch.dispatch1:                                  ; preds = %bb1
894  %4 = catchswitch within none [label %catch.start1] unwind to caller
895
896catch.start1:                                     ; preds = %catch.dispatch1
897  %5 = catchpad within %4 [ptr null]
898  %6 = call ptr @llvm.wasm.get.exception(token %5)
899  %7 = call i32 @llvm.wasm.get.ehselector(token %5)
900  catchret from %5 to label %try.cont
901
902try.cont:                                         ; preds = %catch.start1, %catch.start0, %bb1
903  ret void
904}
905
906; Tests the case when TEE stackifies a register in RegStackify but it gets
907; unstackified in fixCallUnwindMismatches in CFGStackify.
908
909; NOSORT-LOCALS-LABEL: unstackify_when_fixing_unwind_mismatch:
910define void @unstackify_when_fixing_unwind_mismatch(i32 %x) personality ptr @__gxx_wasm_personality_v0 {
911bb0:
912  invoke void @foo()
913          to label %bb1 unwind label %catch.dispatch0
914
915bb1:                                              ; preds = %bb0
916  %t = add i32 %x, 4
917  ; This %addr is used in multiple places, so tee is introduced in RegStackify,
918  ; which stackifies the use of %addr in store instruction. A tee has two dest
919  ; registers, the first of which is stackified and the second is not.
920  ; But when we introduce a nested try_table-end_try_table in
921  ; fixCallUnwindMismatches in CFGStackify, we end up unstackifying the first
922  ; dest register. In that case, we convert that tee into a copy.
923  %addr = inttoptr i32 %t to ptr
924  %load = load i32, ptr %addr
925  %call = call i32 @baz()
926  %add = add i32 %load, %call
927  store i32 %add, ptr %addr
928  ret void
929; NOSORT-LOCALS:       i32.add
930; NOSORT-LOCALS-NOT:   local.tee
931; NOSORT-LOCALS-NEXT:  local.set
932
933catch.dispatch0:                                  ; preds = %bb0
934  %0 = catchswitch within none [label %catch.start0] unwind to caller
935
936catch.start0:                                     ; preds = %catch.dispatch0
937  %1 = catchpad within %0 [ptr null]
938  %2 = call ptr @llvm.wasm.get.exception(token %1)
939  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
940  catchret from %1 to label %try.cont
941
942try.cont:                                         ; preds = %catch.start0
943  ret void
944}
945
946; In CFGSort, EH pads should be sorted as soon as it is available and
947; 'Preferred' queue and should NOT be entered into 'Ready' queue unless we are
948; in the middle of sorting another region that does not contain the EH pad. In
949; this example, 'catch.start' should be sorted right after 'if.then' is sorted
950; (before 'cont' is sorted) and there should not be any unwind destination
951; mismatches in CFGStackify.
952
953; NOOPT-LABEL: cfg_sort_order:
954; NOOPT: block
955; NOOPT:   block
956; NOOPT:     block     i32
957; NOOPT:       try_table    (catch __cpp_exception 0)
958; NOOPT:         call  foo
959; NOOPT:       end_try_table
960; NOOPT:     end_block
961; NOOPT:     call  __cxa_begin_catch
962; NOOPT:     call  __cxa_end_catch
963; NOOPT:   end_block
964; NOOPT:   call  foo
965; NOOPT: end_block
966; NOOPT: return
967define void @cfg_sort_order(i32 %arg) personality ptr @__gxx_wasm_personality_v0 {
968entry:
969  %tobool = icmp ne i32 %arg, 0
970  br i1 %tobool, label %if.then, label %if.end
971
972catch.dispatch:                                   ; preds = %if.then
973  %0 = catchswitch within none [label %catch.start] unwind to caller
974
975catch.start:                                      ; preds = %catch.dispatch
976  %1 = catchpad within %0 [ptr null]
977  %2 = call ptr @llvm.wasm.get.exception(token %1)
978  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
979  %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
980  call void @__cxa_end_catch() [ "funclet"(token %1) ]
981  catchret from %1 to label %if.end
982
983if.then:                                          ; preds = %entry
984  invoke void @foo()
985          to label %cont unwind label %catch.dispatch
986
987cont:                                             ; preds = %if.then
988  call void @foo()
989  br label %if.end
990
991if.end:                                           ; preds = %cont, %catch.start, %entry
992  ret void
993}
994
995; Intrinsics like memcpy, memmove, and memset don't throw and are lowered into
996; calls to external symbols (not global addresses) in instruction selection,
997; which will be eventually lowered to library function calls.
998; Because this test runs with -wasm-disable-ehpad-sort, these library calls in
999; invoke.cont BB fall within try~end_try, but they shouldn't cause crashes or
1000; unwinding destination mismatches in CFGStackify.
1001
1002; NOSORT-LABEL: mem_intrinsics:
1003; NOSORT: block     exnref
1004; NOSORT:   try_table    (catch_all_ref 0)
1005; NOSORT:     call  foo
1006; NOSORT:     call  memcpy
1007; NOSORT:     call  memmove
1008; NOSORT:     call  memset
1009; NOSORT:     return
1010; NOSORT:   end_try_table
1011; NOSORT: end_block
1012; NOSORT: throw_ref
1013define void @mem_intrinsics(ptr %a, ptr %b) personality ptr @__gxx_wasm_personality_v0 {
1014entry:
1015  %o = alloca %class.Object, align 1
1016  invoke void @foo()
1017          to label %invoke.cont unwind label %ehcleanup
1018
1019invoke.cont:                                      ; preds = %entry
1020  call void @llvm.memcpy.p0.p0.i32(ptr %a, ptr %b, i32 100, i1 false)
1021  call void @llvm.memmove.p0.p0.i32(ptr %a, ptr %b, i32 100, i1 false)
1022  call void @llvm.memset.p0.i32(ptr %a, i8 0, i32 100, i1 false)
1023  %call = call ptr @_ZN6ObjectD2Ev(ptr %o)
1024  ret void
1025
1026ehcleanup:                                        ; preds = %entry
1027  %0 = cleanuppad within none []
1028  %call2 = call ptr @_ZN6ObjectD2Ev(ptr %o) [ "funclet"(token %0) ]
1029  cleanupret from %0 unwind to caller
1030}
1031
1032; Tests if 'try_table' marker is placed correctly. In this test, 'try_table'
1033; should be placed before the call to 'nothrow_i32' and not between the call to
1034; 'nothrow_i32' and 'fun', because the return value of 'nothrow_i32' is
1035; stackified and pushed onto the stack to be consumed by the call to 'fun'.
1036
1037; CHECK-LABEL: try_table_marker_with_stackified_input:
1038; CHECK: try_table    (catch_all 0)
1039; CHECK: call  nothrow_i32
1040; CHECK: call  fun
1041define void @try_table_marker_with_stackified_input() personality ptr @__gxx_wasm_personality_v0 {
1042entry:
1043  %call = call i32 @nothrow_i32()
1044  invoke void @fun(i32 %call)
1045          to label %invoke.cont unwind label %terminate
1046
1047invoke.cont:                                      ; preds = %entry
1048  ret void
1049
1050terminate:                                        ; preds = %entry
1051  %0 = cleanuppad within none []
1052  call void @_ZSt9terminatev() [ "funclet"(token %0) ]
1053  unreachable
1054}
1055
1056; This crashed on debug mode (= when NDEBUG is not defined) when the logic for
1057; computing the innermost region was not correct, in which a loop region
1058; contains an exception region. This should pass CFGSort without crashing.
1059define void @loop_exception_region() personality ptr @__gxx_wasm_personality_v0 {
1060entry:
1061  %e = alloca %class.MyClass, align 4
1062  br label %for.cond
1063
1064for.cond:                                         ; preds = %for.inc, %entry
1065  %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.inc ]
1066  %cmp = icmp slt i32 %i.0, 9
1067  br i1 %cmp, label %for.body, label %for.end
1068
1069for.body:                                         ; preds = %for.cond
1070  invoke void @quux(i32 %i.0)
1071          to label %for.inc unwind label %catch.dispatch
1072
1073catch.dispatch:                                   ; preds = %for.body
1074  %0 = catchswitch within none [label %catch.start] unwind to caller
1075
1076catch.start:                                      ; preds = %catch.dispatch
1077  %1 = catchpad within %0 [ptr @_ZTI7MyClass]
1078  %2 = call ptr @llvm.wasm.get.exception(token %1)
1079  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
1080  %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTI7MyClass)
1081  %matches = icmp eq i32 %3, %4
1082  br i1 %matches, label %catch, label %rethrow
1083
1084catch:                                            ; preds = %catch.start
1085  %5 = call ptr @__cxa_get_exception_ptr(ptr %2) [ "funclet"(token %1) ]
1086  %call = call ptr @_ZN7MyClassC2ERKS_(ptr %e, ptr dereferenceable(4) %5) [ "funclet"(token %1) ]
1087  %6 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
1088  %7 = load i32, ptr %e, align 4
1089  invoke void @quux(i32 %7) [ "funclet"(token %1) ]
1090          to label %invoke.cont2 unwind label %ehcleanup
1091
1092invoke.cont2:                                     ; preds = %catch
1093  %call3 = call ptr @_ZN7MyClassD2Ev(ptr %e) [ "funclet"(token %1) ]
1094  call void @__cxa_end_catch() [ "funclet"(token %1) ]
1095  catchret from %1 to label %for.inc
1096
1097rethrow:                                          ; preds = %catch.start
1098  call void @llvm.wasm.rethrow() [ "funclet"(token %1) ]
1099  unreachable
1100
1101for.inc:                                          ; preds = %invoke.cont2, %for.body
1102  %inc = add nsw i32 %i.0, 1
1103  br label %for.cond
1104
1105ehcleanup:                                        ; preds = %catch
1106  %8 = cleanuppad within %1 []
1107  %call4 = call ptr @_ZN7MyClassD2Ev(ptr %e) [ "funclet"(token %8) ]
1108  invoke void @__cxa_end_catch() [ "funclet"(token %8) ]
1109          to label %invoke.cont6 unwind label %terminate7
1110
1111invoke.cont6:                                     ; preds = %ehcleanup
1112  cleanupret from %8 unwind to caller
1113
1114for.end:                                          ; preds = %for.cond
1115  ret void
1116
1117terminate7:                                       ; preds = %ehcleanup
1118  %9 = cleanuppad within %8 []
1119  call void @_ZSt9terminatev() [ "funclet"(token %9) ]
1120  unreachable
1121}
1122
1123; Here exceptions are semantically contained in a loop. 'ehcleanup' BB belongs
1124; to the exception, but does not belong to the loop (because it does not have a
1125; path back to the loop header), and is placed after the loop latch block
1126; 'invoke.cont' intentionally. This tests if 'end_loop' marker is placed
1127; correctly not right after 'invoke.cont' part but after 'ehcleanup' part.
1128; NOSORT-LABEL: loop_contains_exception:
1129; NOSORT: loop
1130; NOSORT:   try_table    (catch __cpp_exception 0)
1131; NOSORT:   end_try_table
1132; NOSORT:   try_table    (catch_all 0)
1133; NOSORT:   end_try_table
1134; NOSORT: end_loop
1135define void @loop_contains_exception(i32 %n) personality ptr @__gxx_wasm_personality_v0 {
1136entry:
1137  br label %while.cond
1138
1139while.cond:                                       ; preds = %invoke.cont, %entry
1140  %n.addr.0 = phi i32 [ %n, %entry ], [ %dec, %invoke.cont ]
1141  %tobool = icmp ne i32 %n.addr.0, 0
1142  br i1 %tobool, label %while.body, label %while.end
1143
1144while.body:                                       ; preds = %while.cond
1145  %dec = add nsw i32 %n.addr.0, -1
1146  invoke void @foo()
1147          to label %while.end unwind label %catch.dispatch
1148
1149catch.dispatch:                                   ; preds = %while.body
1150  %0 = catchswitch within none [label %catch.start] unwind to caller
1151
1152catch.start:                                      ; preds = %catch.dispatch
1153  %1 = catchpad within %0 [ptr null]
1154  %2 = call ptr @llvm.wasm.get.exception(token %1)
1155  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
1156  %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
1157  invoke void @__cxa_end_catch() [ "funclet"(token %1) ]
1158          to label %invoke.cont unwind label %ehcleanup
1159
1160invoke.cont:                                      ; preds = %catch.start
1161  catchret from %1 to label %while.cond
1162
1163ehcleanup:                                        ; preds = %catch.start
1164  %5 = cleanuppad within %1 []
1165  call void @_ZSt9terminatev() [ "funclet"(token %5) ]
1166  unreachable
1167
1168while.end:                                        ; preds = %while.body, %while.cond
1169  ret void
1170}
1171
1172; Regression test for WasmEHFuncInfo's reverse mapping bug. 'UnwindDestToSrc'
1173; should return a vector and not a single BB, which was incorrect.
1174; This was reduced by bugpoint and should not crash in CFGStackify.
1175define void @wasm_eh_func_info_regression_test() personality ptr @__gxx_wasm_personality_v0 {
1176entry:
1177  invoke void @foo()
1178          to label %invoke.cont unwind label %catch.dispatch
1179
1180catch.dispatch:                                   ; preds = %entry
1181  %0 = catchswitch within none [label %catch.start] unwind label %ehcleanup22
1182
1183catch.start:                                      ; preds = %catch.dispatch
1184  %1 = catchpad within %0 [ptr @_ZTIi]
1185  %2 = call ptr @llvm.wasm.get.exception(token %1)
1186  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
1187  invoke void @__cxa_throw(ptr null, ptr null, ptr null) #1 [ "funclet"(token %1) ]
1188          to label %unreachable unwind label %catch.dispatch2
1189
1190catch.dispatch2:                                  ; preds = %catch.start
1191  %4 = catchswitch within %1 [label %catch.start3] unwind label %ehcleanup
1192
1193catch.start3:                                     ; preds = %catch.dispatch2
1194  %5 = catchpad within %4 [ptr @_ZTIi]
1195  %6 = call ptr @llvm.wasm.get.exception(token %5)
1196  %7 = call i32 @llvm.wasm.get.ehselector(token %5)
1197  catchret from %5 to label %try.cont
1198
1199try.cont:                                         ; preds = %catch.start3
1200  invoke void @foo() [ "funclet"(token %1) ]
1201          to label %invoke.cont8 unwind label %ehcleanup
1202
1203invoke.cont8:                                     ; preds = %try.cont
1204  invoke void @__cxa_throw(ptr null, ptr null, ptr null) #1 [ "funclet"(token %1) ]
1205          to label %unreachable unwind label %catch.dispatch11
1206
1207catch.dispatch11:                                 ; preds = %invoke.cont8
1208  %8 = catchswitch within %1 [label %catch.start12] unwind label %ehcleanup
1209
1210catch.start12:                                    ; preds = %catch.dispatch11
1211  %9 = catchpad within %8 [ptr @_ZTIi]
1212  %10 = call ptr @llvm.wasm.get.exception(token %9)
1213  %11 = call i32 @llvm.wasm.get.ehselector(token %9)
1214  unreachable
1215
1216invoke.cont:                                      ; preds = %entry
1217  unreachable
1218
1219ehcleanup:                                        ; preds = %catch.dispatch11, %try.cont, %catch.dispatch2
1220  %12 = cleanuppad within %1 []
1221  cleanupret from %12 unwind label %ehcleanup22
1222
1223ehcleanup22:                                      ; preds = %ehcleanup, %catch.dispatch
1224  %13 = cleanuppad within none []
1225  cleanupret from %13 unwind to caller
1226
1227unreachable:                                      ; preds = %invoke.cont8, %catch.start
1228  unreachable
1229}
1230
1231; void exception_grouping_0() {
1232;   try {
1233;     try {
1234;       throw 0;
1235;     } catch (int) {
1236;     }
1237;   } catch (int) {
1238;   }
1239; }
1240;
1241; Regression test for a WebAssemblyException grouping bug. After catchswitches
1242; are removed, EH pad catch.start2 is dominated by catch.start, but because
1243; catch.start2 is the unwind destination of catch.start, it should not be
1244; included in catch.start's exception. Also, after we take catch.start2's
1245; exception out of catch.start's exception, we have to take out try.cont8 out of
1246; catch.start's exception, because it has a predecessor in catch.start2.
1247define void @exception_grouping_0() personality ptr @__gxx_wasm_personality_v0 {
1248entry:
1249  %exception = call ptr @__cxa_allocate_exception(i32 4) #0
1250  store i32 0, ptr %exception, align 16
1251  invoke void @__cxa_throw(ptr %exception, ptr @_ZTIi, ptr null) #1
1252          to label %unreachable unwind label %catch.dispatch
1253
1254catch.dispatch:                                   ; preds = %entry
1255  %0 = catchswitch within none [label %catch.start] unwind label %catch.dispatch1
1256
1257catch.start:                                      ; preds = %catch.dispatch
1258  %1 = catchpad within %0 [ptr @_ZTIi]
1259  %2 = call ptr @llvm.wasm.get.exception(token %1)
1260  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
1261  %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #0
1262  %matches = icmp eq i32 %3, %4
1263  br i1 %matches, label %catch, label %rethrow
1264
1265catch:                                            ; preds = %catch.start
1266  %5 = call ptr @__cxa_begin_catch(ptr %2) #0 [ "funclet"(token %1) ]
1267  %6 = load i32, ptr %5, align 4
1268  call void @__cxa_end_catch() #0 [ "funclet"(token %1) ]
1269  catchret from %1 to label %catchret.dest
1270
1271catchret.dest:                                    ; preds = %catch
1272  br label %try.cont
1273
1274rethrow:                                          ; preds = %catch.start
1275  invoke void @llvm.wasm.rethrow() #1 [ "funclet"(token %1) ]
1276          to label %unreachable unwind label %catch.dispatch1
1277
1278catch.dispatch1:                                  ; preds = %rethrow, %catch.dispatch
1279  %7 = catchswitch within none [label %catch.start2] unwind to caller
1280
1281catch.start2:                                     ; preds = %catch.dispatch1
1282  %8 = catchpad within %7 [ptr @_ZTIi]
1283  %9 = call ptr @llvm.wasm.get.exception(token %8)
1284  %10 = call i32 @llvm.wasm.get.ehselector(token %8)
1285  %11 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #0
1286  %matches3 = icmp eq i32 %10, %11
1287  br i1 %matches3, label %catch5, label %rethrow4
1288
1289catch5:                                           ; preds = %catch.start2
1290  %12 = call ptr @__cxa_begin_catch(ptr %9) #0 [ "funclet"(token %8) ]
1291  %13 = load i32, ptr %12, align 4
1292  call void @__cxa_end_catch() #0 [ "funclet"(token %8) ]
1293  catchret from %8 to label %catchret.dest7
1294
1295catchret.dest7:                                   ; preds = %catch5
1296  br label %try.cont8
1297
1298rethrow4:                                         ; preds = %catch.start2
1299  call void @llvm.wasm.rethrow() #1 [ "funclet"(token %8) ]
1300  unreachable
1301
1302try.cont8:                                        ; preds = %try.cont, %catchret.dest7
1303  ret void
1304
1305try.cont:                                         ; preds = %catchret.dest
1306  br label %try.cont8
1307
1308unreachable:                                      ; preds = %rethrow, %entry
1309  unreachable
1310}
1311
1312; Test for WebAssemblyException grouping. This test is hand-modified to generate
1313; this structure:
1314; catch.start dominates catch.start4 and catch.start4 dominates catch.start12,
1315; so the after dominator-based grouping, we end up with:
1316; catch.start's exception > catch4.start's exception > catch12.start's exception
1317; (> here represents subexception relationship)
1318;
1319; But the unwind destination chain is catch.start -> catch.start4 ->
1320; catch.start12. So all these subexception relationship should be deconstructed.
1321; We have to make sure to take out catch.start4's exception out of catch.start's
1322; exception first, before taking out catch.start12's exception out of
1323; catch.start4's exception; otherwise we end up with an incorrect relationship
1324; of catch.start's exception > catch.start12's exception.
1325define void @exception_grouping_1() personality ptr @__gxx_wasm_personality_v0 {
1326entry:
1327  invoke void @foo()
1328          to label %invoke.cont unwind label %catch.dispatch
1329
1330invoke.cont:                                      ; preds = %entry
1331  invoke void @foo()
1332          to label %invoke.cont1 unwind label %catch.dispatch
1333
1334invoke.cont1:                                     ; preds = %invoke.cont
1335  invoke void @foo()
1336          to label %try.cont18 unwind label %catch.dispatch
1337
1338catch.dispatch11:                                 ; preds = %rethrow6, %catch.dispatch3
1339  %0 = catchswitch within none [label %catch.start12] unwind to caller
1340
1341catch.start12:                                    ; preds = %catch.dispatch11
1342  %1 = catchpad within %0 [ptr @_ZTIi]
1343  %2 = call ptr @llvm.wasm.get.exception(token %1)
1344  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
1345  %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #0
1346  %matches13 = icmp eq i32 %3, %4
1347  br i1 %matches13, label %catch15, label %rethrow14
1348
1349catch15:                                          ; preds = %catch.start12
1350  %5 = call ptr @__cxa_begin_catch(ptr %2) #0 [ "funclet"(token %1) ]
1351  %6 = load i32, ptr %5, align 4
1352  call void @__cxa_end_catch() #0 [ "funclet"(token %1) ]
1353  catchret from %1 to label %try.cont18
1354
1355rethrow14:                                        ; preds = %catch.start12
1356  call void @llvm.wasm.rethrow() #1 [ "funclet"(token %1) ]
1357  unreachable
1358
1359catch.dispatch3:                                  ; preds = %rethrow, %catch.dispatch
1360  %7 = catchswitch within none [label %catch.start4] unwind label %catch.dispatch11
1361
1362catch.start4:                                     ; preds = %catch.dispatch3
1363  %8 = catchpad within %7 [ptr @_ZTIi]
1364  %9 = call ptr @llvm.wasm.get.exception(token %8)
1365  %10 = call i32 @llvm.wasm.get.ehselector(token %8)
1366  %11 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #0
1367  %matches5 = icmp eq i32 %10, %11
1368  br i1 %matches5, label %catch7, label %rethrow6
1369
1370catch7:                                           ; preds = %catch.start4
1371  %12 = call ptr @__cxa_begin_catch(ptr %9) #0 [ "funclet"(token %8) ]
1372  %13 = load i32, ptr %12, align 4
1373  call void @__cxa_end_catch() #0 [ "funclet"(token %8) ]
1374  catchret from %8 to label %try.cont18
1375
1376rethrow6:                                         ; preds = %catch.start4
1377  invoke void @llvm.wasm.rethrow() #1 [ "funclet"(token %8) ]
1378          to label %unreachable unwind label %catch.dispatch11
1379
1380catch.dispatch:                                   ; preds = %invoke.cont1, %invoke.cont, %entry
1381  %14 = catchswitch within none [label %catch.start] unwind label %catch.dispatch3
1382
1383catch.start:                                      ; preds = %catch.dispatch
1384  %15 = catchpad within %14 [ptr @_ZTIi]
1385  %16 = call ptr @llvm.wasm.get.exception(token %15)
1386  %17 = call i32 @llvm.wasm.get.ehselector(token %15)
1387  %18 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #0
1388  %matches = icmp eq i32 %17, %18
1389  br i1 %matches, label %catch, label %rethrow
1390
1391catch:                                            ; preds = %catch.start
1392  %19 = call ptr @__cxa_begin_catch(ptr %16) #0 [ "funclet"(token %15) ]
1393  %20 = load i32, ptr %19, align 4
1394  call void @__cxa_end_catch() #0 [ "funclet"(token %15) ]
1395  catchret from %15 to label %try.cont18
1396
1397rethrow:                                          ; preds = %catch.start
1398  invoke void @llvm.wasm.rethrow() #1 [ "funclet"(token %15) ]
1399          to label %unreachable unwind label %catch.dispatch3
1400
1401try.cont18:                                       ; preds = %catch, %catch7, %catch15, %invoke.cont1
1402  ret void
1403
1404unreachable:                                      ; preds = %rethrow, %rethrow6
1405  unreachable
1406}
1407
1408; void exception_grouping_2() {
1409;   try {
1410;     try {
1411;       throw 0;
1412;     } catch (int) { // (a)
1413;     }
1414;   } catch (int) {   // (b)
1415;   }
1416;   try {
1417;     foo();
1418;   } catch (int) {   // (c)
1419;   }
1420; }
1421;
1422; Regression test for an ExceptionInfo grouping bug. Because the first (inner)
1423; try always throws, both EH pads (b) (catch.start2) and (c) (catch.start10) are
1424; dominated by EH pad (a) (catch.start), even though they are not semantically
1425; contained in (a)'s exception. Because (a)'s unwind destination is (b), (b)'s
1426; exception is taken out of (a)'s. But because (c) is reachable from (b), we
1427; should make sure to take out (c)'s exception out of (a)'s exception too.
1428define void @exception_grouping_2() personality ptr @__gxx_wasm_personality_v0 {
1429entry:
1430  %exception = call ptr @__cxa_allocate_exception(i32 4) #1
1431  store i32 0, ptr %exception, align 16
1432  invoke void @__cxa_throw(ptr %exception, ptr @_ZTIi, ptr null) #3
1433          to label %unreachable unwind label %catch.dispatch
1434
1435catch.dispatch:                                   ; preds = %entry
1436  %0 = catchswitch within none [label %catch.start] unwind label %catch.dispatch1
1437
1438catch.start:                                      ; preds = %catch.dispatch
1439  %1 = catchpad within %0 [ptr @_ZTIi]
1440  %2 = call ptr @llvm.wasm.get.exception(token %1)
1441  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
1442  %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #1
1443  %matches = icmp eq i32 %3, %4
1444  br i1 %matches, label %catch, label %rethrow
1445
1446catch:                                            ; preds = %catch.start
1447  %5 = call ptr @__cxa_begin_catch(ptr %2) #1 [ "funclet"(token %1) ]
1448  %6 = load i32, ptr %5, align 4
1449  call void @__cxa_end_catch() #1 [ "funclet"(token %1) ]
1450  catchret from %1 to label %try.cont8
1451
1452rethrow:                                          ; preds = %catch.start
1453  invoke void @llvm.wasm.rethrow() #3 [ "funclet"(token %1) ]
1454          to label %unreachable unwind label %catch.dispatch1
1455
1456catch.dispatch1:                                  ; preds = %rethrow, %catch.dispatch
1457  %7 = catchswitch within none [label %catch.start2] unwind to caller
1458
1459catch.start2:                                     ; preds = %catch.dispatch1
1460  %8 = catchpad within %7 [ptr @_ZTIi]
1461  %9 = call ptr @llvm.wasm.get.exception(token %8)
1462  %10 = call i32 @llvm.wasm.get.ehselector(token %8)
1463  %11 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #1
1464  %matches3 = icmp eq i32 %10, %11
1465  br i1 %matches3, label %catch5, label %rethrow4
1466
1467catch5:                                           ; preds = %catch.start2
1468  %12 = call ptr @__cxa_begin_catch(ptr %9) #1 [ "funclet"(token %8) ]
1469  %13 = load i32, ptr %12, align 4
1470  call void @__cxa_end_catch() #1 [ "funclet"(token %8) ]
1471  catchret from %8 to label %try.cont8
1472
1473rethrow4:                                         ; preds = %catch.start2
1474  call void @llvm.wasm.rethrow() #3 [ "funclet"(token %8) ]
1475  unreachable
1476
1477try.cont8:                                        ; preds = %catch, %catch5
1478  invoke void @foo()
1479          to label %try.cont16 unwind label %catch.dispatch9
1480
1481catch.dispatch9:                                  ; preds = %try.cont8
1482  %14 = catchswitch within none [label %catch.start10] unwind to caller
1483
1484catch.start10:                                    ; preds = %catch.dispatch9
1485  %15 = catchpad within %14 [ptr @_ZTIi]
1486  %16 = call ptr @llvm.wasm.get.exception(token %15)
1487  %17 = call i32 @llvm.wasm.get.ehselector(token %15)
1488  %18 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #1
1489  %matches11 = icmp eq i32 %17, %18
1490  br i1 %matches11, label %catch13, label %rethrow12
1491
1492catch13:                                          ; preds = %catch.start10
1493  %19 = call ptr @__cxa_begin_catch(ptr %16) #1 [ "funclet"(token %15) ]
1494  %20 = load i32, ptr %19, align 4
1495  call void @__cxa_end_catch() #1 [ "funclet"(token %15) ]
1496  catchret from %15 to label %try.cont16
1497
1498rethrow12:                                        ; preds = %catch.start10
1499  call void @llvm.wasm.rethrow() #3 [ "funclet"(token %15) ]
1500  unreachable
1501
1502try.cont16:                                       ; preds = %try.cont8, %catch13
1503  ret void
1504
1505unreachable:                                      ; preds = %rethrow, %entry
1506  unreachable
1507}
1508
1509; Check if the unwind destination mismatch stats are correct
1510; NOSORT: 23 wasm-cfg-stackify    - Number of call unwind mismatches found
1511; NOSORT:  4 wasm-cfg-stackify    - Number of catch unwind mismatches found
1512
1513declare void @foo()
1514declare void @bar()
1515declare i32 @baz()
1516declare i32 @qux(i32)
1517declare void @quux(i32)
1518declare void @fun(i32)
1519; Function Attrs: nounwind
1520declare void @nothrow(i32) #0
1521; Function Attrs: nounwind
1522declare i32 @nothrow_i32() #0
1523
1524; Function Attrs: nounwind
1525declare ptr @_ZN6ObjectD2Ev(ptr returned) #0
1526@_ZTI7MyClass = external constant { ptr, ptr }, align 4
1527; Function Attrs: nounwind
1528declare ptr @_ZN7MyClassD2Ev(ptr returned) #0
1529; Function Attrs: nounwind
1530declare ptr @_ZN7MyClassC2ERKS_(ptr returned, ptr dereferenceable(4)) #0
1531
1532declare i32 @__gxx_wasm_personality_v0(...)
1533; Function Attrs: nounwind
1534declare ptr @llvm.wasm.get.exception(token) #0
1535; Function Attrs: nounwind
1536declare i32 @llvm.wasm.get.ehselector(token) #0
1537declare ptr @__cxa_allocate_exception(i32) #0
1538declare void @__cxa_throw(ptr, ptr, ptr)
1539; Function Attrs: noreturn
1540declare void @llvm.wasm.rethrow() #1
1541; Function Attrs: nounwind
1542declare i32 @llvm.eh.typeid.for(ptr) #0
1543
1544declare ptr @__cxa_begin_catch(ptr)
1545declare void @__cxa_end_catch()
1546declare ptr @__cxa_get_exception_ptr(ptr)
1547declare void @_ZSt9terminatev()
1548; Function Attrs: nounwind
1549declare void @llvm.memcpy.p0.p0.i32(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i32, i1 immarg) #0
1550; Function Attrs: nounwind
1551declare void @llvm.memmove.p0.p0.i32(ptr nocapture, ptr nocapture readonly, i32, i1 immarg) #0
1552; Function Attrs: nounwind
1553declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1 immarg) #0
1554
1555attributes #0 = { nounwind }
1556attributes #1 = { noreturn }
1557