xref: /llvm-project/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.ll (revision a8e1135baa9074f7c088c8e1999561f88699b56e)
1; REQUIRES: asserts
2; RUN: llc < %s -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -disable-block-placement -verify-machineinstrs -fast-isel=false -machine-sink-split-probability-threshold=0 -cgp-freq-ratio-to-skip-merge=1000 -wasm-enable-eh -wasm-use-legacy-eh -exception-model=wasm -mattr=+exception-handling,bulk-memory | FileCheck %s
3; RUN: llc < %s -disable-wasm-fallthrough-return-opt -disable-block-placement -verify-machineinstrs -fast-isel=false -machine-sink-split-probability-threshold=0 -cgp-freq-ratio-to-skip-merge=1000 -wasm-enable-eh -wasm-use-legacy-eh -exception-model=wasm -mattr=+exception-handling,bulk-memory
4; RUN: llc < %s -O0 -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -verify-machineinstrs -wasm-enable-eh -wasm-use-legacy-eh -exception-model=wasm -mattr=+exception-handling,-bulk-memory,-bulk-memory-opt | FileCheck %s --check-prefix=NOOPT
5; RUN: llc < %s -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -disable-block-placement -verify-machineinstrs -fast-isel=false -machine-sink-split-probability-threshold=0 -cgp-freq-ratio-to-skip-merge=1000 -wasm-enable-eh -wasm-use-legacy-eh -exception-model=wasm -mattr=+exception-handling,-bulk-memory,-bulk-memory-opt -wasm-disable-ehpad-sort -stats 2>&1 | FileCheck %s --check-prefix=NOSORT
6; RUN: llc < %s -disable-wasm-fallthrough-return-opt -disable-block-placement -verify-machineinstrs -fast-isel=false -machine-sink-split-probability-threshold=0 -cgp-freq-ratio-to-skip-merge=1000 -wasm-enable-eh -wasm-use-legacy-eh -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: try
29; CHECK:   call      foo
30; CHECK: catch
31; CHECK:   block
32; CHECK:     br_if     0, {{.*}}                       # 0: down to label[[L0:[0-9]+]]
33; CHECK:     call      $drop=, __cxa_begin_catch
34; CHECK:     call      __cxa_end_catch
35; CHECK:     br        1                               # 1: down to label[[L1:[0-9]+]]
36; CHECK:   end_block                                   # label[[L0]]:
37; CHECK:   block
38; CHECK:     br_if     0, {{.*}}                       # 0: down to label[[L2:[0-9]+]]
39; CHECK:     call      $drop=, __cxa_begin_catch
40; CHECK:     call      __cxa_end_catch
41; CHECK:     br        1                               # 1: down to label[[L1]]
42; CHECK:   end_block                                   # label[[L2]]:
43; CHECK:   rethrow   0                                 # to caller
44; CHECK: end_try                                       # label[[L1]]:
45define void @two_catches() personality ptr @__gxx_wasm_personality_v0 {
46entry:
47  invoke void @foo()
48          to label %try.cont unwind label %catch.dispatch
49
50catch.dispatch:                                   ; preds = %entry
51  %0 = catchswitch within none [label %catch.start] unwind to caller
52
53catch.start:                                      ; preds = %catch.dispatch
54  %1 = catchpad within %0 [ptr @_ZTIi, ptr @_ZTId]
55  %2 = call ptr @llvm.wasm.get.exception(token %1)
56  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
57  %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi)
58  %matches = icmp eq i32 %3, %4
59  br i1 %matches, label %catch2, label %catch.fallthrough
60
61catch2:                                           ; preds = %catch.start
62  %5 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
63  call void @__cxa_end_catch() [ "funclet"(token %1) ]
64  catchret from %1 to label %try.cont
65
66catch.fallthrough:                                ; preds = %catch.start
67  %6 = call i32 @llvm.eh.typeid.for(ptr @_ZTId)
68  %matches1 = icmp eq i32 %3, %6
69  br i1 %matches1, label %catch, label %rethrow
70
71catch:                                            ; preds = %catch.fallthrough
72  %7 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
73  call void @__cxa_end_catch() [ "funclet"(token %1) ]
74  catchret from %1 to label %try.cont
75
76rethrow:                                          ; preds = %catch.fallthrough
77  call void @llvm.wasm.rethrow() [ "funclet"(token %1) ]
78  unreachable
79
80try.cont:                                         ; preds = %catch, %catch2, %entry
81  ret void
82}
83
84; Nested try-catches within a catch
85; void nested_catch() {
86;   try {
87;     foo();
88;   } catch (int) {
89;     try {
90;       foo();
91;     } catch (int) {
92;       foo();
93;     }
94;   }
95; }
96
97; CHECK-LABEL: nested_catch:
98; CHECK: try
99; CHECK:   call  foo
100; CHECK: catch
101; CHECK:   block
102; CHECK:     block
103; CHECK:       br_if     0, {{.*}}                     # 0: down to label[[L0:[0-9+]]]
104; CHECK:       call  $drop=, __cxa_begin_catch, $0
105; CHECK:       try
106; CHECK:         try
107; CHECK:           call  foo
108; CHECK:           br        3                         # 3: down to label[[L1:[0-9+]]]
109; CHECK:         catch
110; CHECK:           block
111; CHECK:             block
112; CHECK:               br_if     0, {{.*}}             # 0: down to label[[L2:[0-9+]]]
113; CHECK:               call  $drop=, __cxa_begin_catch
114; CHECK:               try
115; CHECK:                 call  foo
116; CHECK:                 br        2                   # 2: down to label[[L3:[0-9+]]]
117; CHECK:               catch_all
118; CHECK:                 call  __cxa_end_catch
119; CHECK:                 rethrow   0                   # down to catch[[L4:[0-9+]]]
120; CHECK:               end_try
121; CHECK:             end_block                         # label[[L2]]:
122; CHECK:             rethrow   1                       # down to catch[[L4]]
123; CHECK:           end_block                           # label[[L3]]:
124; CHECK:           call  __cxa_end_catch
125; CHECK:           br        3                         # 3: down to label[[L1]]
126; CHECK:         end_try
127; CHECK:       catch_all                               # catch[[L4]]:
128; CHECK:         call  __cxa_end_catch
129; CHECK:         rethrow   0                           # to caller
130; CHECK:       end_try
131; CHECK:     end_block                                 # label[[L0]]:
132; CHECK:     rethrow   1                               # to caller
133; CHECK:   end_block                                   # label[[L1]]:
134; CHECK:   call  __cxa_end_catch
135; CHECK: end_try
136define void @nested_catch() personality ptr @__gxx_wasm_personality_v0 {
137entry:
138  invoke void @foo()
139          to label %try.cont11 unwind label %catch.dispatch
140
141catch.dispatch:                                   ; preds = %entry
142  %0 = catchswitch within none [label %catch.start] unwind to caller
143
144catch.start:                                      ; preds = %catch.dispatch
145  %1 = catchpad within %0 [ptr @_ZTIi]
146  %2 = call ptr @llvm.wasm.get.exception(token %1)
147  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
148  %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi)
149  %matches = icmp eq i32 %3, %4
150  br i1 %matches, label %catch, label %rethrow
151
152catch:                                            ; preds = %catch.start
153  %5 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
154  %6 = load i32, ptr %5, align 4
155  invoke void @foo() [ "funclet"(token %1) ]
156          to label %try.cont unwind label %catch.dispatch2
157
158catch.dispatch2:                                  ; preds = %catch
159  %7 = catchswitch within %1 [label %catch.start3] unwind label %ehcleanup9
160
161catch.start3:                                     ; preds = %catch.dispatch2
162  %8 = catchpad within %7 [ptr @_ZTIi]
163  %9 = call ptr @llvm.wasm.get.exception(token %8)
164  %10 = call i32 @llvm.wasm.get.ehselector(token %8)
165  %11 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi)
166  %matches4 = icmp eq i32 %10, %11
167  br i1 %matches4, label %catch6, label %rethrow5
168
169catch6:                                           ; preds = %catch.start3
170  %12 = call ptr @__cxa_begin_catch(ptr %9) [ "funclet"(token %8) ]
171  %13 = load i32, ptr %12, align 4
172  invoke void @foo() [ "funclet"(token %8) ]
173          to label %invoke.cont8 unwind label %ehcleanup
174
175invoke.cont8:                                     ; preds = %catch6
176  call void @__cxa_end_catch() [ "funclet"(token %8) ]
177  catchret from %8 to label %try.cont
178
179rethrow5:                                         ; preds = %catch.start3
180  invoke void @llvm.wasm.rethrow() [ "funclet"(token %8) ]
181          to label %unreachable unwind label %ehcleanup9
182
183try.cont:                                         ; preds = %invoke.cont8, %catch
184  call void @__cxa_end_catch() [ "funclet"(token %1) ]
185  catchret from %1 to label %try.cont11
186
187rethrow:                                          ; preds = %catch.start
188  call void @llvm.wasm.rethrow() [ "funclet"(token %1) ]
189  unreachable
190
191try.cont11:                                       ; preds = %try.cont, %entry
192  ret void
193
194ehcleanup:                                        ; preds = %catch6
195  %14 = cleanuppad within %8 []
196  call void @__cxa_end_catch() [ "funclet"(token %14) ]
197  cleanupret from %14 unwind label %ehcleanup9
198
199ehcleanup9:                                       ; preds = %ehcleanup, %rethrow5, %catch.dispatch2
200  %15 = cleanuppad within %1 []
201  call void @__cxa_end_catch() [ "funclet"(token %15) ]
202  cleanupret from %15 unwind to caller
203
204unreachable:                                      ; preds = %rethrow5
205  unreachable
206}
207
208; Nested try-catches within a try
209; void nested_try() {
210;   try {
211;     try {
212;       foo();
213;     } catch (...) {
214;     }
215;   } catch (...) {
216;   }
217; }
218
219; CHECK-LABEL: nested_try:
220; CHECK:   try
221; CHECK:     try
222; CHECK:       call  foo
223; CHECK:     catch
224; CHECK:       call  $drop=, __cxa_begin_catch
225; CHECK:       call  __cxa_end_catch
226; CHECK:     end_try
227; CHECK:     catch
228; CHECK:     call  $drop=, __cxa_begin_catch
229; CHECK:     call  __cxa_end_catch
230; CHECK:   end_try
231define void @nested_try() personality ptr @__gxx_wasm_personality_v0 {
232entry:
233  invoke void @foo()
234          to label %try.cont7 unwind label %catch.dispatch
235
236catch.dispatch:                                   ; preds = %entry
237  %0 = catchswitch within none [label %catch.start] unwind label %catch.dispatch2
238
239catch.start:                                      ; preds = %catch.dispatch
240  %1 = catchpad within %0 [ptr null]
241  %2 = call ptr @llvm.wasm.get.exception(token %1)
242  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
243  %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
244  invoke void @__cxa_end_catch() [ "funclet"(token %1) ]
245          to label %invoke.cont1 unwind label %catch.dispatch2
246
247catch.dispatch2:                                  ; preds = %catch.start, %catch.dispatch
248  %5 = catchswitch within none [label %catch.start3] unwind to caller
249
250catch.start3:                                     ; preds = %catch.dispatch2
251  %6 = catchpad within %5 [ptr null]
252  %7 = call ptr @llvm.wasm.get.exception(token %6)
253  %8 = call i32 @llvm.wasm.get.ehselector(token %6)
254  %9 = call ptr @__cxa_begin_catch(ptr %7) [ "funclet"(token %6) ]
255  call void @__cxa_end_catch() [ "funclet"(token %6) ]
256  catchret from %6 to label %try.cont7
257
258try.cont7:                                        ; preds = %entry, %invoke.cont1, %catch.start3
259  ret void
260
261invoke.cont1:                                     ; preds = %catch.start
262  catchret from %1 to label %try.cont7
263}
264
265
266; CHECK-LABEL: loop_within_catch:
267; CHECK: try
268; CHECK:   call      foo
269; CHECK: catch
270; CHECK:   call      $drop=, __cxa_begin_catch
271; CHECK:   loop                                        # label[[L0:[0-9]+]]:
272; CHECK:     block
273; CHECK:       block
274; CHECK:         br_if     0, {{.*}}                   # 0: down to label[[L1:[0-9]+]]
275; CHECK:         try
276; CHECK:           call      foo
277; CHECK:           br        2                         # 2: down to label[[L2:[0-9]+]]
278; CHECK:         catch
279; CHECK:           try
280; CHECK:             call      __cxa_end_catch
281; CHECK:           catch_all
282; CHECK:             call      _ZSt9terminatev
283; CHECK:             unreachable
284; CHECK:           end_try
285; CHECK:           rethrow   0                         # to caller
286; CHECK:         end_try
287; CHECK:       end_block                               # label[[L1]]:
288; CHECK:       call      __cxa_end_catch
289; CHECK:       br        2                             # 2: down to label[[L3:[0-9]+]]
290; CHECK:     end_block                                 # label[[L2]]:
291; CHECK:     br        0                               # 0: up to label[[L0]]
292; CHECK:   end_loop
293; CHECK: end_try                                       # label[[L3]]:
294define void @loop_within_catch() personality ptr @__gxx_wasm_personality_v0 {
295entry:
296  invoke void @foo()
297          to label %try.cont unwind label %catch.dispatch
298
299catch.dispatch:                                   ; preds = %entry
300  %0 = catchswitch within none [label %catch.start] unwind to caller
301
302catch.start:                                      ; preds = %catch.dispatch
303  %1 = catchpad within %0 [ptr null]
304  %2 = call ptr @llvm.wasm.get.exception(token %1)
305  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
306  %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
307  br label %for.cond
308
309for.cond:                                         ; preds = %for.inc, %catch.start
310  %i.0 = phi i32 [ 0, %catch.start ], [ %inc, %for.inc ]
311  %cmp = icmp slt i32 %i.0, 50
312  br i1 %cmp, label %for.body, label %for.end
313
314for.body:                                         ; preds = %for.cond
315  invoke void @foo() [ "funclet"(token %1) ]
316          to label %for.inc unwind label %ehcleanup
317
318for.inc:                                          ; preds = %for.body
319  %inc = add nsw i32 %i.0, 1
320  br label %for.cond
321
322for.end:                                          ; preds = %for.cond
323  call void @__cxa_end_catch() [ "funclet"(token %1) ]
324  catchret from %1 to label %try.cont
325
326try.cont:                                         ; preds = %for.end, %entry
327  ret void
328
329ehcleanup:                                        ; preds = %for.body
330  %5 = cleanuppad within %1 []
331  invoke void @__cxa_end_catch() [ "funclet"(token %5) ]
332          to label %invoke.cont2 unwind label %terminate
333
334invoke.cont2:                                     ; preds = %ehcleanup
335  cleanupret from %5 unwind to caller
336
337terminate:                                        ; preds = %ehcleanup
338  %6 = cleanuppad within %5 []
339  call void @_ZSt9terminatev() [ "funclet"(token %6) ]
340  unreachable
341}
342
343; Tests if block and try markers are correctly placed. Even if two predecessors
344; of the EH pad are bb2 and bb3 and their nearest common dominator is bb1, the
345; TRY marker should be placed at bb0 because there's a branch from bb0 to bb2,
346; and scopes cannot be interleaved.
347
348; NOOPT-LABEL: block_try_markers:
349; NOOPT: try
350; NOOPT:   block
351; NOOPT:     block
352; NOOPT:       block
353; NOOPT:       end_block
354; NOOPT:     end_block
355; NOOPT:     call      foo
356; NOOPT:   end_block
357; NOOPT:   call      bar
358; NOOPT: catch     {{.*}}
359; NOOPT: end_try
360define void @block_try_markers() personality ptr @__gxx_wasm_personality_v0 {
361bb0:
362  br i1 undef, label %bb1, label %bb2
363
364bb1:                                              ; preds = %bb0
365  br i1 undef, label %bb3, label %bb4
366
367bb2:                                              ; preds = %bb0
368  br label %try.cont
369
370bb3:                                              ; preds = %bb1
371  invoke void @foo()
372          to label %try.cont unwind label %catch.dispatch
373
374bb4:                                              ; preds = %bb1
375  invoke void @bar()
376          to label %try.cont unwind label %catch.dispatch
377
378catch.dispatch:                                   ; preds = %bb4, %bb3
379  %0 = catchswitch within none [label %catch.start] unwind to caller
380
381catch.start:                                      ; preds = %catch.dispatch
382  %1 = catchpad within %0 [ptr null]
383  %2 = call ptr @llvm.wasm.get.exception(token %1)
384  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
385  catchret from %1 to label %try.cont
386
387try.cont:                                         ; preds = %catch.start, %bb4, %bb3, %bb2
388  ret void
389}
390
391; Tests if try/end_try markers are placed correctly wrt loop/end_loop markers,
392; when try and loop markers are in the same BB and end_try and end_loop are in
393; another BB.
394; CHECK-LABEL: loop_try_markers:
395; CHECK: loop
396; CHECK:   try
397; CHECK:     call      foo
398; CHECK:   catch
399; CHECK:   end_try
400; CHECK: end_loop
401define void @loop_try_markers(ptr %p) personality ptr @__gxx_wasm_personality_v0 {
402entry:
403  store volatile i32 0, ptr %p
404  br label %loop
405
406loop:                                             ; preds = %try.cont, %entry
407  store volatile i32 1, ptr %p
408  invoke void @foo()
409          to label %try.cont unwind label %catch.dispatch
410
411catch.dispatch:                                   ; preds = %loop
412  %0 = catchswitch within none [label %catch.start] unwind to caller
413
414catch.start:                                      ; preds = %catch.dispatch
415  %1 = catchpad within %0 [ptr null]
416  %2 = call ptr @llvm.wasm.get.exception(token %1)
417  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
418  catchret from %1 to label %try.cont
419
420try.cont:                                         ; preds = %catch.start, %loop
421  br label %loop
422}
423
424; Some of test cases below are hand-tweaked by deleting some library calls to
425; simplify tests and changing the order of basic blocks to cause unwind
426; destination mismatches. And we use -wasm-disable-ehpad-sort to create maximum
427; number of mismatches in several tests below.
428
429; - Call unwind mismatch
430; 'call bar''s original unwind destination was 'C0', but after control flow
431; linearization, its unwind destination incorrectly becomes 'C1'. We fix this by
432; wrapping the call with a nested try-delegate that targets 'C0'.
433; - Catch unwind mismatch
434; If 'call foo' throws a foreign exception, it will not be caught by C1, and
435; should be rethrown to the caller. But after control flow linearization, it
436; will instead unwind to C0, an incorrect next EH pad. We wrap the whole
437; try-catch with try-delegate that rethrows the exception to the caller to fix
438; this.
439
440; NOSORT-LABEL: unwind_mismatches_0:
441; NOSORT: try
442; --- try-delegate starts (catch unwind mismatch)
443; NOSORT    try
444; NOSORT:     try
445; NOSORT:       call  foo
446; --- try-delegate starts (call unwind mismatch)
447; NOSORT:       try
448; NOSORT:         call  bar
449; NOSORT:       delegate    2     # label/catch{{[0-9]+}}: down to catch[[C0:[0-9]+]]
450; --- try-delegate ends (call unwind mismatch)
451; NOSORT:     catch   {{.*}}      # catch[[C1:[0-9]+]]:
452; NOSORT:     end_try
453; NOSORT:   delegate    1         # label/catch{{[0-9]+}}: to caller
454; --- try-delegate ends (catch unwind mismatch)
455; NOSORT: catch   {{.*}}          # catch[[C0]]:
456; NOSORT: end_try
457; NOSORT: return
458define void @unwind_mismatches_0() personality ptr @__gxx_wasm_personality_v0 {
459bb0:
460  invoke void @foo()
461          to label %bb1 unwind label %catch.dispatch0
462
463bb1:                                              ; preds = %bb0
464  invoke void @bar()
465          to label %try.cont unwind label %catch.dispatch1
466
467catch.dispatch0:                                  ; preds = %bb0
468  %0 = catchswitch within none [label %catch.start0] unwind to caller
469
470catch.start0:                                     ; preds = %catch.dispatch0
471  %1 = catchpad within %0 [ptr null]
472  %2 = call ptr @llvm.wasm.get.exception(token %1)
473  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
474  catchret from %1 to label %try.cont
475
476catch.dispatch1:                                  ; preds = %bb1
477  %4 = catchswitch within none [label %catch.start1] unwind to caller
478
479catch.start1:                                     ; preds = %catch.dispatch1
480  %5 = catchpad within %4 [ptr null]
481  %6 = call ptr @llvm.wasm.get.exception(token %5)
482  %7 = call i32 @llvm.wasm.get.ehselector(token %5)
483  catchret from %5 to label %try.cont
484
485try.cont:                                         ; preds = %catch.start1, %catch.start0, %bb1
486  ret void
487}
488
489; 'call bar' and 'call baz''s original unwind destination was the caller, but
490; after control flow linearization, their unwind destination incorrectly becomes
491; 'C0'. We fix this by wrapping the calls with a nested try-delegate that
492; rethrows the exception to the caller.
493
494; And the return value of 'baz' should NOT be stackified because the BB is split
495; during fixing unwind mismatches.
496
497; NOSORT-LABEL: unwind_mismatches_1:
498; NOSORT: try
499; NOSORT:   call  foo
500; --- try-delegate starts (call unwind mismatch)
501; NOSORT:   try
502; NOSORT:     call  bar
503; NOSORT:     call  $[[RET:[0-9]+]]=, baz
504; NOSORT-NOT: call  $push{{.*}}=, baz
505; NOSORT:   delegate    1                     # label/catch{{[0-9]+}}: to caller
506; --- try-delegate ends (call unwind mismatch)
507; NOSORT:   call  nothrow, $[[RET]]
508; NOSORT:   return
509; NOSORT: catch   {{.*}}                      # catch[[C0:[0-9]+]]:
510; NOSORT:   return
511; NOSORT: end_try
512define void @unwind_mismatches_1() personality ptr @__gxx_wasm_personality_v0 {
513bb0:
514  invoke void @foo()
515          to label %bb1 unwind label %catch.dispatch0
516
517bb1:                                              ; preds = %bb0
518  call void @bar()
519  %call = call i32 @baz()
520  call void @nothrow(i32 %call) #0
521  ret void
522
523catch.dispatch0:                                  ; preds = %bb0
524  %0 = catchswitch within none [label %catch.start0] unwind to caller
525
526catch.start0:                                     ; preds = %catch.dispatch0
527  %1 = catchpad within %0 [ptr null]
528  %2 = call ptr @llvm.wasm.get.exception(token %1)
529  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
530  catchret from %1 to label %try.cont
531
532try.cont:                                         ; preds = %catch.start0
533  ret void
534}
535
536; The same as unwind_mismatches_0, but we have one more call 'call @foo' in bb1
537; which unwinds to the caller. IN this case bb1 has two call unwind mismatches:
538; 'call @foo' unwinds to the caller and 'call @bar' unwinds to catch C0.
539
540; NOSORT-LABEL: unwind_mismatches_2:
541; NOSORT: try
542; --- try-delegate starts (catch unwind mismatch)
543; NOSORT    try
544; NOSORT:     try
545; NOSORT:       call  foo
546; --- try-delegate starts (call unwind mismatch)
547; NOSORT:       try
548; NOSORT:         call  foo
549; NOSORT:       delegate    3     # label/catch{{[0-9]+}}: to caller
550; --- try-delegate ends (call unwind mismatch)
551; --- try-delegate starts (call unwind mismatch)
552; NOSORT:       try
553; NOSORT:         call  bar
554; NOSORT:       delegate    2     # label/catch{{[0-9]+}}: down to catch[[C0:[0-9]+]]
555; --- try-delegate ends (call unwind mismatch)
556; NOSORT:     catch   {{.*}}      # catch[[C1:[0-9]+]]:
557; NOSORT:     end_try
558; NOSORT:   delegate    1         # label/catch{{[0-9]+}}: to caller
559; --- try-delegate ends (catch unwind mismatch)
560; NOSORT: catch   {{.*}}        # catch[[C0]]:
561; NOSORT: end_try
562; NOSORT: return
563define void @unwind_mismatches_2() personality ptr @__gxx_wasm_personality_v0 {
564bb0:
565  invoke void @foo()
566          to label %bb1 unwind label %catch.dispatch0
567
568bb1:                                              ; preds = %bb0
569  call void @foo()
570  invoke void @bar()
571          to label %try.cont unwind label %catch.dispatch1
572
573catch.dispatch0:                                  ; preds = %bb0
574  %0 = catchswitch within none [label %catch.start0] unwind to caller
575
576catch.start0:                                     ; preds = %catch.dispatch0
577  %1 = catchpad within %0 [ptr null]
578  %2 = call ptr @llvm.wasm.get.exception(token %1)
579  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
580  catchret from %1 to label %try.cont
581
582catch.dispatch1:                                  ; preds = %bb1
583  %4 = catchswitch within none [label %catch.start1] unwind to caller
584
585catch.start1:                                     ; preds = %catch.dispatch1
586  %5 = catchpad within %4 [ptr null]
587  %6 = call ptr @llvm.wasm.get.exception(token %5)
588  %7 = call i32 @llvm.wasm.get.ehselector(token %5)
589  catchret from %5 to label %try.cont
590
591try.cont:                                         ; preds = %catch.start1, %catch.start0, %bb1
592  ret void
593}
594
595; Similar situation as @unwind_mismatches_1. Here 'call @qux''s original unwind
596; destination was the caller, but after control flow linearization, their unwind
597; destination incorrectly becomes 'C0' within the function. We fix this by
598; wrapping the call with a nested try-delegate that rethrows the exception to
599; the caller.
600
601; Because 'call @qux' pops an argument pushed by 'i32.const 5' from stack, the
602; nested 'try' should be placed before `i32.const 5', not between 'i32.const 5'
603; and 'call @qux'.
604
605; NOSORT-LABEL: unwind_mismatches_3:
606; NOSORT: try       i32
607; NOSORT:   call  foo
608; --- try-delegate starts (call unwind mismatch)
609; NOSORT:   try
610; NOSORT:     i32.const  $push{{[0-9]+}}=, 5
611; NOSORT:     call  ${{[0-9]+}}=, qux
612; NOSORT:   delegate    1                     # label/catch{{[0-9]+}}: to caller
613; --- try-delegate ends (call unwind mismatch)
614; NOSORT:   return
615; NOSORT: catch   {{.*}}                      # catch[[C0:[0-9]+]]:
616; NOSORT:   return
617; NOSORT: end_try
618define i32 @unwind_mismatches_3() personality ptr @__gxx_wasm_personality_v0 {
619bb0:
620  invoke void @foo()
621          to label %bb1 unwind label %catch.dispatch0
622
623bb1:                                              ; preds = %bb0
624  %0 = call i32 @qux(i32 5)
625  ret i32 %0
626
627catch.dispatch0:                                  ; preds = %bb0
628  %1 = catchswitch within none [label %catch.start0] unwind to caller
629
630catch.start0:                                     ; preds = %catch.dispatch0
631  %2 = catchpad within %1 [ptr null]
632  %3 = call ptr @llvm.wasm.get.exception(token %2)
633  %j = call i32 @llvm.wasm.get.ehselector(token %2)
634  catchret from %2 to label %try.cont
635
636try.cont:                                         ; preds = %catch.start0
637  ret i32 0
638}
639
640; We have two call unwind unwind mismatches:
641; - A may-throw instruction unwinds to an incorrect EH pad after linearizing the
642;   CFG, when it is supposed to unwind to another EH pad.
643; - A may-throw instruction unwinds to an incorrect EH pad after linearizing the
644;   CFG, when it is supposed to unwind to the caller.
645; We also have a catch unwind mismatch: If an exception is not caught by the
646; first catch because it is a non-C++ exception, it shouldn't unwind to the next
647; catch, but it should unwind to the caller.
648
649; NOSORT-LABEL: unwind_mismatches_4:
650; NOSORT: try
651; --- try-delegate starts (catch unwind mismatch)
652; NOSORT:   try
653; NOSORT:     try
654; NOSORT:       call  foo
655; --- try-delegate starts (call unwind mismatch)
656; NOSORT:       try
657; NOSORT:         call  bar
658; NOSORT:       delegate    2            # label/catch{{[0-9]+}}: down to catch[[C0:[0-9]+]]
659; --- try-delegate ends (call unwind mismatch)
660; NOSORT:     catch
661; NOSORT:       call  {{.*}} __cxa_begin_catch
662; --- try-delegate starts (call unwind mismatch)
663; NOSORT:       try
664; NOSORT:         call  __cxa_end_catch
665; NOSORT:       delegate    3            # label/catch{{[0-9]+}}: to caller
666; --- try-delegate ends (call unwind mismatch)
667; NOSORT:     end_try
668; NOSORT:   delegate    1                # label/catch{{[0-9]+}}: to caller
669; --- try-delegate ends (catch unwind mismatch)
670; NOSORT: catch  {{.*}}                  # catch[[C0]]:
671; NOSORT:   call  {{.*}} __cxa_begin_catch
672; NOSORT:   call  __cxa_end_catch
673; NOSORT: end_try
674; NOSORT: return
675define void @unwind_mismatches_4() personality ptr @__gxx_wasm_personality_v0 {
676bb0:
677  invoke void @foo()
678          to label %bb1 unwind label %catch.dispatch0
679
680bb1:                                              ; preds = %bb0
681  invoke void @bar()
682          to label %try.cont unwind label %catch.dispatch1
683
684catch.dispatch0:                                  ; preds = %bb0
685  %0 = catchswitch within none [label %catch.start0] unwind to caller
686
687catch.start0:                                     ; preds = %catch.dispatch0
688  %1 = catchpad within %0 [ptr null]
689  %2 = call ptr @llvm.wasm.get.exception(token %1)
690  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
691  %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
692  call void @__cxa_end_catch() [ "funclet"(token %1) ]
693  catchret from %1 to label %try.cont
694
695catch.dispatch1:                                  ; preds = %bb1
696  %5 = catchswitch within none [label %catch.start1] unwind to caller
697
698catch.start1:                                     ; preds = %catch.dispatch1
699  %6 = catchpad within %5 [ptr null]
700  %7 = call ptr @llvm.wasm.get.exception(token %6)
701  %8 = call i32 @llvm.wasm.get.ehselector(token %6)
702  %9 = call ptr @__cxa_begin_catch(ptr %7) [ "funclet"(token %6) ]
703  call void @__cxa_end_catch() [ "funclet"(token %6) ]
704  catchret from %6 to label %try.cont
705
706try.cont:                                         ; preds = %catch.start1, %catch.start0, %bb1
707  ret void
708}
709
710; This crashed when updating EHPadStack within fixCallUniwindMismatch had a bug.
711; This should not crash and try-delegate has to be created around 'call @baz',
712; because the initial TRY placement for 'call @quux' was done before 'call @baz'
713; because 'call @baz''s return value is stackified.
714
715; CHECK-LABEL: unwind_mismatches_5:
716; CHECK: try
717; --- try-delegate starts (call unwind mismatch)
718; CHECK:   try
719; CHECK:     call $[[RET:[0-9]+]]=, baz
720; CHECK:   delegate  1
721; --- try-delegate ends (call unwind mismatch)
722; CHECK:    call  quux, $[[RET]]
723; CHECK: catch_all
724; CHECK: end_try
725define void @unwind_mismatches_5() personality ptr @__gxx_wasm_personality_v0 {
726entry:
727  %call = call i32 @baz()
728  invoke void @quux(i32 %call)
729          to label %invoke.cont unwind label %ehcleanup
730
731ehcleanup:                                        ; preds = %entry
732  %0 = cleanuppad within none []
733  cleanupret from %0 unwind to caller
734
735invoke.cont:                                      ; preds = %entry
736  unreachable
737}
738
739; The structure is similar to unwind_mismatches_0, where the call to 'bar''s
740; original unwind destination is catch.dispatch1 but after placing markers it
741; unwinds to catch.dispatch0, which we fix. This additionally has a loop before
742; the real unwind destination (catch.dispatch1).
743
744; NOSORT-LABEL: unwind_mismatches_with_loop:
745; NOSORT: try
746; NOSORT:   try
747; NOSORT:     try
748; NOSORT:       call  foo
749; NOSORT:       try
750; NOSORT:         call  bar
751; NOSORT:       delegate    3                  # label/catch{{[0-9]+}}: down to catch[[C0:[0-9]+]]
752; NOSORT:     catch  $drop=, __cpp_exception
753; NOSORT:     end_try
754; NOSORT:   delegate    2                      # label/catch{{[0-9]+}}: to caller
755; NOSORT:   loop
756; NOSORT:     call  foo
757; NOSORT:   end_loop
758; NOSORT: catch  $drop=, __cpp_exception       # catch[[C0]]:
759; NOSORT:   return
760; NOSORT: end_try
761define void @unwind_mismatches_with_loop() personality ptr @__gxx_wasm_personality_v0 {
762bb0:
763  invoke void @foo()
764          to label %bb1 unwind label %catch.dispatch0
765
766bb1:                                              ; preds = %bb0
767  invoke void @bar()
768          to label %bb2 unwind label %catch.dispatch1
769
770catch.dispatch0:                                  ; preds = %bb0
771  %0 = catchswitch within none [label %catch.start0] unwind to caller
772
773catch.start0:                                     ; preds = %catch.dispatch0
774  %1 = catchpad within %0 [ptr null]
775  %2 = call ptr @llvm.wasm.get.exception(token %1)
776  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
777  catchret from %1 to label %bb2
778
779bb2:
780  invoke void @foo()
781          to label %bb3 unwind label %catch.dispatch1
782
783bb3:                                             ; preds = %bb14
784  br label %bb2
785
786catch.dispatch1:                                  ; preds = %bb1
787  %4 = catchswitch within none [label %catch.start1] unwind to caller
788
789catch.start1:                                     ; preds = %catch.dispatch1
790  %5 = catchpad within %4 [ptr null]
791  %6 = call ptr @llvm.wasm.get.exception(token %5)
792  %7 = call i32 @llvm.wasm.get.ehselector(token %5)
793  catchret from %5 to label %try.cont
794
795try.cont:                                         ; preds = %catch.start1, %catch.start0, %bb1
796  ret void
797}
798
799; Tests the case when TEE stackifies a register in RegStackify but it gets
800; unstackified in fixCallUnwindMismatches in CFGStackify.
801
802; NOSORT-LOCALS-LABEL: unstackify_when_fixing_unwind_mismatch:
803define void @unstackify_when_fixing_unwind_mismatch(i32 %x) personality ptr @__gxx_wasm_personality_v0 {
804bb0:
805  invoke void @foo()
806          to label %bb1 unwind label %catch.dispatch0
807
808bb1:                                              ; preds = %bb0
809  %t = add i32 %x, 4
810  ; This %addr is used in multiple places, so tee is introduced in RegStackify,
811  ; which stackifies the use of %addr in store instruction. A tee has two dest
812  ; registers, the first of which is stackified and the second is not.
813  ; But when we introduce a nested try-delegate in fixCallUnwindMismatches in
814  ; CFGStackify, we end up unstackifying the first dest register. In that case,
815  ; we convert that tee into a copy.
816  %addr = inttoptr i32 %t to ptr
817  %load = load i32, ptr %addr
818  %call = call i32 @baz()
819  %add = add i32 %load, %call
820  store i32 %add, ptr %addr
821  ret void
822; NOSORT-LOCALS:       i32.add
823; NOSORT-LOCALS-NOT:   local.tee
824; NOSORT-LOCALS-NEXT:  local.set
825
826catch.dispatch0:                                  ; preds = %bb0
827  %0 = catchswitch within none [label %catch.start0] unwind to caller
828
829catch.start0:                                     ; preds = %catch.dispatch0
830  %1 = catchpad within %0 [ptr null]
831  %2 = call ptr @llvm.wasm.get.exception(token %1)
832  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
833  catchret from %1 to label %try.cont
834
835try.cont:                                         ; preds = %catch.start0
836  ret void
837}
838
839; In CFGSort, EH pads should be sorted as soon as it is available and
840; 'Preferred' queue and should NOT be entered into 'Ready' queue unless we are
841; in the middle of sorting another region that does not contain the EH pad. In
842; this example, 'catch.start' should be sorted right after 'if.then' is sorted
843; (before 'cont' is sorted) and there should not be any unwind destination
844; mismatches in CFGStackify.
845
846; NOOPT-LABEL: cfg_sort_order:
847; NOOPT: block
848; NOOPT:   try
849; NOOPT:     call      foo
850; NOOPT:   catch
851; NOOPT:   end_try
852; NOOPT:   call      foo
853; NOOPT: end_block
854; NOOPT: return
855define void @cfg_sort_order(i32 %arg) personality ptr @__gxx_wasm_personality_v0 {
856entry:
857  %tobool = icmp ne i32 %arg, 0
858  br i1 %tobool, label %if.then, label %if.end
859
860catch.dispatch:                                   ; preds = %if.then
861  %0 = catchswitch within none [label %catch.start] unwind to caller
862
863catch.start:                                      ; preds = %catch.dispatch
864  %1 = catchpad within %0 [ptr null]
865  %2 = call ptr @llvm.wasm.get.exception(token %1)
866  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
867  %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
868  call void @__cxa_end_catch() [ "funclet"(token %1) ]
869  catchret from %1 to label %if.end
870
871if.then:                                          ; preds = %entry
872  invoke void @foo()
873          to label %cont unwind label %catch.dispatch
874
875cont:                                             ; preds = %if.then
876  call void @foo()
877  br label %if.end
878
879if.end:                                           ; preds = %cont, %catch.start, %entry
880  ret void
881}
882
883; Intrinsics like memcpy, memmove, and memset don't throw and are lowered into
884; calls to external symbols (not global addresses) in instruction selection,
885; which will be eventually lowered to library function calls.
886; Because this test runs with -wasm-disable-ehpad-sort, these library calls in
887; invoke.cont BB fall within try~end_try, but they shouldn't cause crashes or
888; unwinding destination mismatches in CFGStackify.
889
890; NOSORT-LABEL: mem_intrinsics:
891; NOSORT: try
892; NOSORT:   call  foo
893; NOSORT:   call {{.*}} memcpy
894; NOSORT:   call {{.*}} memmove
895; NOSORT:   call {{.*}} memset
896; NOSORT:   return
897; NOSORT: catch_all
898; NOSORT:   rethrow 0
899; NOSORT: end_try
900define void @mem_intrinsics(ptr %a, ptr %b) personality ptr @__gxx_wasm_personality_v0 {
901entry:
902  %o = alloca %class.Object, align 1
903  invoke void @foo()
904          to label %invoke.cont unwind label %ehcleanup
905
906invoke.cont:                                      ; preds = %entry
907  call void @llvm.memcpy.p0.p0.i32(ptr %a, ptr %b, i32 100, i1 false)
908  call void @llvm.memmove.p0.p0.i32(ptr %a, ptr %b, i32 100, i1 false)
909  call void @llvm.memset.p0.i32(ptr %a, i8 0, i32 100, i1 false)
910  %call = call ptr @_ZN6ObjectD2Ev(ptr %o)
911  ret void
912
913ehcleanup:                                        ; preds = %entry
914  %0 = cleanuppad within none []
915  %call2 = call ptr @_ZN6ObjectD2Ev(ptr %o) [ "funclet"(token %0) ]
916  cleanupret from %0 unwind to caller
917}
918
919; Tests if 'try' marker is placed correctly. In this test, 'try' should be
920; placed before the call to 'nothrow_i32' and not between the call to
921; 'nothrow_i32' and 'fun', because the return value of 'nothrow_i32' is
922; stackified and pushed onto the stack to be consumed by the call to 'fun'.
923
924; CHECK-LABEL: try_marker_with_stackified_input:
925; CHECK: try
926; CHECK: call      $push{{.*}}=, nothrow_i32
927; CHECK: call      fun, $pop{{.*}}
928define void @try_marker_with_stackified_input() personality ptr @__gxx_wasm_personality_v0 {
929entry:
930  %call = call i32 @nothrow_i32()
931  invoke void @fun(i32 %call)
932          to label %invoke.cont unwind label %terminate
933
934invoke.cont:                                      ; preds = %entry
935  ret void
936
937terminate:                                        ; preds = %entry
938  %0 = cleanuppad within none []
939  call void @_ZSt9terminatev() [ "funclet"(token %0) ]
940  unreachable
941}
942
943; This crashed on debug mode (= when NDEBUG is not defined) when the logic for
944; computing the innermost region was not correct, in which a loop region
945; contains an exception region. This should pass CFGSort without crashing.
946define void @loop_exception_region() personality ptr @__gxx_wasm_personality_v0 {
947entry:
948  %e = alloca %class.MyClass, align 4
949  br label %for.cond
950
951for.cond:                                         ; preds = %for.inc, %entry
952  %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.inc ]
953  %cmp = icmp slt i32 %i.0, 9
954  br i1 %cmp, label %for.body, label %for.end
955
956for.body:                                         ; preds = %for.cond
957  invoke void @quux(i32 %i.0)
958          to label %for.inc unwind label %catch.dispatch
959
960catch.dispatch:                                   ; preds = %for.body
961  %0 = catchswitch within none [label %catch.start] unwind to caller
962
963catch.start:                                      ; preds = %catch.dispatch
964  %1 = catchpad within %0 [ptr @_ZTI7MyClass]
965  %2 = call ptr @llvm.wasm.get.exception(token %1)
966  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
967  %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTI7MyClass)
968  %matches = icmp eq i32 %3, %4
969  br i1 %matches, label %catch, label %rethrow
970
971catch:                                            ; preds = %catch.start
972  %5 = call ptr @__cxa_get_exception_ptr(ptr %2) [ "funclet"(token %1) ]
973  %call = call ptr @_ZN7MyClassC2ERKS_(ptr %e, ptr dereferenceable(4) %5) [ "funclet"(token %1) ]
974  %6 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
975  %7 = load i32, ptr %e, align 4
976  invoke void @quux(i32 %7) [ "funclet"(token %1) ]
977          to label %invoke.cont2 unwind label %ehcleanup
978
979invoke.cont2:                                     ; preds = %catch
980  %call3 = call ptr @_ZN7MyClassD2Ev(ptr %e) [ "funclet"(token %1) ]
981  call void @__cxa_end_catch() [ "funclet"(token %1) ]
982  catchret from %1 to label %for.inc
983
984rethrow:                                          ; preds = %catch.start
985  call void @llvm.wasm.rethrow() [ "funclet"(token %1) ]
986  unreachable
987
988for.inc:                                          ; preds = %invoke.cont2, %for.body
989  %inc = add nsw i32 %i.0, 1
990  br label %for.cond
991
992ehcleanup:                                        ; preds = %catch
993  %8 = cleanuppad within %1 []
994  %call4 = call ptr @_ZN7MyClassD2Ev(ptr %e) [ "funclet"(token %8) ]
995  invoke void @__cxa_end_catch() [ "funclet"(token %8) ]
996          to label %invoke.cont6 unwind label %terminate7
997
998invoke.cont6:                                     ; preds = %ehcleanup
999  cleanupret from %8 unwind to caller
1000
1001for.end:                                          ; preds = %for.cond
1002  ret void
1003
1004terminate7:                                       ; preds = %ehcleanup
1005  %9 = cleanuppad within %8 []
1006  call void @_ZSt9terminatev() [ "funclet"(token %9) ]
1007  unreachable
1008}
1009
1010; Tests if CFGStackify's removeUnnecessaryInstrs() removes unnecessary branches
1011; correctly. The code is in the form below, where 'br' is unnecessary because
1012; after running the 'try' body the control flow will fall through to bb2 anyway.
1013
1014; bb0:
1015;   try
1016;     ...
1017;     br bb2      <- Not necessary
1018; bb1 (ehpad):
1019;   catch
1020;     ...
1021; bb2:            <- Continuation BB
1022;   end
1023; CHECK-LABEL: remove_unnecessary_instrs:
1024define void @remove_unnecessary_instrs(i32 %n) personality ptr @__gxx_wasm_personality_v0 {
1025entry:
1026  invoke void @foo()
1027          to label %for.body unwind label %catch.dispatch
1028
1029for.body:                                         ; preds = %for.end, %entry
1030  %i = phi i32 [ %inc, %for.end ], [ 0, %entry ]
1031  invoke void @foo()
1032          to label %for.end unwind label %catch.dispatch
1033
1034; Before going to CFGStackify, this BB will have a conditional branch followed
1035; by an unconditional branch. CFGStackify should remove only the unconditional
1036; one.
1037for.end:                                          ; preds = %for.body
1038  %inc = add nuw nsw i32 %i, 1
1039  %exitcond = icmp eq i32 %inc, %n
1040  br i1 %exitcond, label %try.cont, label %for.body
1041; CHECK: br_if
1042; CHECK-NOT: br
1043; CHECK: end_loop
1044; CHECK: catch
1045
1046catch.dispatch:                                   ; preds = %for.body, %entry
1047  %0 = catchswitch within none [label %catch.start] unwind to caller
1048
1049catch.start:                                      ; preds = %catch.dispatch
1050  %1 = catchpad within %0 [ptr null]
1051  %2 = call ptr @llvm.wasm.get.exception(token %1)
1052  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
1053  %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
1054  call void @__cxa_end_catch() [ "funclet"(token %1) ]
1055  catchret from %1 to label %try.cont
1056
1057try.cont:                                         ; preds = %catch.start, %for.end
1058  ret void
1059}
1060
1061; void foo();
1062; void remove_unnecessary_br() {
1063;   try {
1064;     foo();
1065;     try {
1066;       foo();
1067;     } catch (...) {
1068;     }
1069;   } catch (...) {
1070;   }
1071; }
1072;
1073; This tests whether the 'br' can be removed in code in the form as follows.
1074; Here 'br' is inside an inner try, whose 'end' is in another EH pad. In this
1075; case, after running an inner try body, the control flow should fall through to
1076; bb3, so the 'br' in the code is unnecessary.
1077
1078; bb0:
1079;   try
1080;     try
1081;       ...
1082;       br bb3      <- Not necessary
1083; bb1:
1084;     catch
1085; bb2:
1086;     end_try
1087;   catch
1088;     ...
1089; bb3:            <- Continuation BB
1090;   end
1091;
1092; CHECK-LABEL: remove_unnecessary_br:
1093define void @remove_unnecessary_br() personality ptr @__gxx_wasm_personality_v0 {
1094; CHECK: call foo
1095entry:
1096  invoke void @foo()
1097          to label %invoke.cont unwind label %catch.dispatch3
1098
1099; CHECK: call foo
1100; CHECK-NOT: br
1101invoke.cont:                                      ; preds = %entry
1102  invoke void @foo()
1103          to label %try.cont8 unwind label %catch.dispatch
1104
1105catch.dispatch:                                   ; preds = %invoke.cont
1106  %0 = catchswitch within none [label %catch.start] unwind label %catch.dispatch3
1107
1108; CHECK: catch
1109catch.start:                                      ; preds = %catch.dispatch
1110  %1 = catchpad within %0 [ptr null]
1111  %2 = call ptr @llvm.wasm.get.exception(token %1)
1112  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
1113  %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
1114  invoke void @__cxa_end_catch() [ "funclet"(token %1) ]
1115          to label %invoke.cont2 unwind label %catch.dispatch3
1116
1117catch.dispatch3:                                  ; preds = %catch.start, %catch.dispatch, %entry
1118  %5 = catchswitch within none [label %catch.start4] unwind to caller
1119
1120catch.start4:                                     ; preds = %catch.dispatch3
1121  %6 = catchpad within %5 [ptr null]
1122  %7 = call ptr @llvm.wasm.get.exception(token %6)
1123  %8 = call i32 @llvm.wasm.get.ehselector(token %6)
1124  %9 = call ptr @__cxa_begin_catch(ptr %7) [ "funclet"(token %6) ]
1125  call void @__cxa_end_catch() [ "funclet"(token %6) ]
1126  catchret from %6 to label %try.cont8
1127
1128try.cont8:                                        ; preds = %invoke.cont2, %catch.start4, %invoke.cont
1129  ret void
1130
1131invoke.cont2:                                     ; preds = %catch.start
1132  catchret from %1 to label %try.cont8
1133}
1134
1135; Here an exception is semantically contained in a loop. 'ehcleanup' BB belongs
1136; to the exception, but does not belong to the loop (because it does not have a
1137; path back to the loop header), and is placed after the loop latch block
1138; 'invoke.cont' intentionally. This tests if 'end_loop' marker is placed
1139; correctly not right after 'invoke.cont' part but after 'ehcleanup' part.
1140; NOSORT-LABEL: loop_contains_exception:
1141; NOSORT: loop
1142; NOSORT:   try
1143; NOSORT:     try
1144; NOSORT:     end_try
1145; NOSORT:   end_try
1146; NOSORT: end_loop
1147define void @loop_contains_exception(i32 %n) personality ptr @__gxx_wasm_personality_v0 {
1148entry:
1149  br label %while.cond
1150
1151while.cond:                                       ; preds = %invoke.cont, %entry
1152  %n.addr.0 = phi i32 [ %n, %entry ], [ %dec, %invoke.cont ]
1153  %tobool = icmp ne i32 %n.addr.0, 0
1154  br i1 %tobool, label %while.body, label %while.end
1155
1156while.body:                                       ; preds = %while.cond
1157  %dec = add nsw i32 %n.addr.0, -1
1158  invoke void @foo()
1159          to label %while.end unwind label %catch.dispatch
1160
1161catch.dispatch:                                   ; preds = %while.body
1162  %0 = catchswitch within none [label %catch.start] unwind to caller
1163
1164catch.start:                                      ; preds = %catch.dispatch
1165  %1 = catchpad within %0 [ptr null]
1166  %2 = call ptr @llvm.wasm.get.exception(token %1)
1167  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
1168  %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
1169  invoke void @__cxa_end_catch() [ "funclet"(token %1) ]
1170          to label %invoke.cont unwind label %ehcleanup
1171
1172invoke.cont:                                      ; preds = %catch.start
1173  catchret from %1 to label %while.cond
1174
1175ehcleanup:                                        ; preds = %catch.start
1176  %5 = cleanuppad within %1 []
1177  call void @_ZSt9terminatev() [ "funclet"(token %5) ]
1178  unreachable
1179
1180while.end:                                        ; preds = %while.body, %while.cond
1181  ret void
1182}
1183
1184; When the function return type is non-void and 'end' instructions are at the
1185; very end of a function, CFGStackify's fixEndsAtEndOfFunction function fixes
1186; the corresponding block/loop/try's type to match the function's return type.
1187; But when a `try`'s type is fixed, we should also check `end` instructions
1188; before its corresponding `catch_all`, because both `try` and `catch_all` body
1189; should satisfy the return type requirements.
1190
1191; NOSORT-LABEL: fix_function_end_return_type_with_try_catch:
1192; NOSORT: try i32
1193; NOSORT: loop i32
1194; NOSORT: end_loop
1195; NOSORT: catch_all
1196; NOSORT: end_try
1197; NOSORT-NEXT: end_function
1198define i32 @fix_function_end_return_type_with_try_catch(i32 %n) personality ptr @__gxx_wasm_personality_v0 {
1199entry:
1200  %t = alloca %class.Object, align 1
1201  br label %for.cond
1202
1203for.cond:                                         ; preds = %for.inc, %entry
1204  %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.inc ]
1205  %cmp = icmp slt i32 %i.0, %n
1206  br label %for.body
1207
1208for.body:                                         ; preds = %for.cond
1209  %div = sdiv i32 %n, 2
1210  %cmp1 = icmp eq i32 %i.0, %div
1211  br i1 %cmp1, label %if.then, label %for.inc
1212
1213if.then:                                          ; preds = %for.body
1214  %call = invoke i32 @baz()
1215          to label %invoke.cont unwind label %ehcleanup
1216
1217invoke.cont:                                      ; preds = %if.then
1218  %call2 = call ptr @_ZN6ObjectD2Ev(ptr %t)
1219  ret i32 %call
1220
1221for.inc:                                          ; preds = %for.body
1222  %inc = add nsw i32 %i.0, 1
1223  br label %for.cond
1224
1225ehcleanup:                                        ; preds = %if.then
1226  %0 = cleanuppad within none []
1227  %call3 = call ptr @_ZN6ObjectD2Ev(ptr %t) [ "funclet"(token %0) ]
1228  cleanupret from %0 unwind to caller
1229}
1230
1231; This tests if invalidated branch destinations after fixing catch unwind
1232; mismatches are correctly remapped. For example, we have this code and suppose
1233; we need to wrap this try-catch-end in this code with a try-delegate to fix a
1234; catch unwind mismatch:
1235  ; - Before:
1236; block
1237;   br (a)
1238;   try
1239;   catch
1240;   end_try
1241; end_block
1242;           <- (a)
1243;
1244; - After
1245; block
1246;   br (a)
1247;   try
1248;     try
1249;     catch
1250;     end_try
1251;           <- (a)
1252;   delegate
1253; end_block
1254;           <- (b)
1255; After adding a try-delegate, the 'br's destination BB, where (a) points,
1256; becomes invalid because it incorrectly branches into an inner scope. The
1257; destination should change to the BB where (b) points.
1258
1259; NOSORT-LABEL: branch_remapping_after_fixing_unwind_mismatches_0:
1260; NOSORT: try
1261; NOSORT:   br_if   0
1262define void @branch_remapping_after_fixing_unwind_mismatches_0(i1 %arg) personality ptr @__gxx_wasm_personality_v0 {
1263entry:
1264  br i1 %arg, label %bb0, label %dest
1265
1266bb0:                                              ; preds = %entry
1267  invoke void @foo()
1268          to label %bb1 unwind label %catch.dispatch0
1269
1270bb1:                                              ; preds = %bb0
1271  invoke void @bar()
1272          to label %try.cont unwind label %catch.dispatch1
1273
1274catch.dispatch0:                                  ; preds = %bb0
1275  %0 = catchswitch within none [label %catch.start0] unwind to caller
1276
1277catch.start0:                                     ; preds = %catch.dispatch0
1278  %1 = catchpad within %0 [ptr null]
1279  %2 = call ptr @llvm.wasm.get.exception(token %1)
1280  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
1281  catchret from %1 to label %try.cont
1282
1283dest:                                             ; preds = %entry
1284  ret void
1285
1286catch.dispatch1:                                  ; preds = %bb1
1287  %4 = catchswitch within none [label %catch.start1] unwind to caller
1288
1289catch.start1:                                     ; preds = %catch.dispatch1
1290  %5 = catchpad within %4 [ptr null]
1291  %6 = call ptr @llvm.wasm.get.exception(token %5)
1292  %7 = call i32 @llvm.wasm.get.ehselector(token %5)
1293  catchret from %5 to label %try.cont
1294
1295try.cont:                                         ; preds = %catch.start1, %catch.start0, %bb1
1296  ret void
1297}
1298
1299; The similar case with branch_remapping_after_fixing_unwind_mismatches_0, but
1300; multiple consecutive delegates are generated:
1301; - Before:
1302; block
1303;   br (a)
1304;   try
1305;   catch
1306;   end_try
1307; end_block
1308;           <- (a)
1309;
1310; - After
1311; block
1312;   br (a)
1313;   try
1314;     ...
1315;     try
1316;       try
1317;       catch
1318;       end_try
1319;             <- (a)
1320;     delegate
1321;   delegate
1322; end_block
1323;           <- (b) The br destination should be remapped to here
1324;
1325; The test was reduced by bugpoint and should not crash in CFGStackify.
1326define void @branch_remapping_after_fixing_unwind_mismatches_1() personality ptr @__gxx_wasm_personality_v0 {
1327entry:
1328  br i1 undef, label %if.then, label %if.end12
1329
1330if.then:                                          ; preds = %entry
1331  invoke void @__cxa_throw(ptr null, ptr null, ptr null) #1
1332          to label %unreachable unwind label %catch.dispatch
1333
1334catch.dispatch:                                   ; preds = %if.then
1335  %0 = catchswitch within none [label %catch.start] unwind to caller
1336
1337catch.start:                                      ; preds = %catch.dispatch
1338  %1 = catchpad within %0 [ptr @_ZTIi]
1339  %2 = call ptr @llvm.wasm.get.exception(token %1)
1340  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
1341  catchret from %1 to label %catchret.dest
1342
1343catchret.dest:                                    ; preds = %catch.start
1344  invoke void @foo()
1345          to label %invoke.cont unwind label %catch.dispatch4
1346
1347invoke.cont:                                      ; preds = %catchret.dest
1348  invoke void @__cxa_throw(ptr null, ptr null, ptr null) #1
1349          to label %unreachable unwind label %catch.dispatch4
1350
1351catch.dispatch4:                                  ; preds = %invoke.cont, %catchret.dest
1352  %4 = catchswitch within none [label %catch.start5] unwind to caller
1353
1354catch.start5:                                     ; preds = %catch.dispatch4
1355  %5 = catchpad within %4 [ptr @_ZTIi]
1356  %6 = call ptr @llvm.wasm.get.exception(token %5)
1357  %7 = call i32 @llvm.wasm.get.ehselector(token %5)
1358  unreachable
1359
1360if.end12:                                         ; preds = %entry
1361  invoke void @foo()
1362          to label %invoke.cont14 unwind label %catch.dispatch16
1363
1364catch.dispatch16:                                 ; preds = %if.end12
1365  %8 = catchswitch within none [label %catch.start17] unwind label %ehcleanup
1366
1367catch.start17:                                    ; preds = %catch.dispatch16
1368  %9 = catchpad within %8 [ptr @_ZTIi]
1369  %10 = call ptr @llvm.wasm.get.exception(token %9)
1370  %11 = call i32 @llvm.wasm.get.ehselector(token %9)
1371  br i1 undef, label %catch20, label %rethrow19
1372
1373catch20:                                          ; preds = %catch.start17
1374  catchret from %9 to label %catchret.dest22
1375
1376catchret.dest22:                                  ; preds = %catch20
1377  br label %try.cont23
1378
1379rethrow19:                                        ; preds = %catch.start17
1380  invoke void @llvm.wasm.rethrow() #1 [ "funclet"(token %9) ]
1381          to label %unreachable unwind label %ehcleanup
1382
1383try.cont23:                                       ; preds = %invoke.cont14, %catchret.dest22
1384  invoke void @foo()
1385          to label %invoke.cont24 unwind label %ehcleanup
1386
1387invoke.cont24:                                    ; preds = %try.cont23
1388  ret void
1389
1390invoke.cont14:                                    ; preds = %if.end12
1391  br label %try.cont23
1392
1393ehcleanup:                                        ; preds = %try.cont23, %rethrow19, %catch.dispatch16
1394  %12 = cleanuppad within none []
1395  cleanupret from %12 unwind to caller
1396
1397unreachable:                                      ; preds = %rethrow19, %invoke.cont, %if.then
1398  unreachable
1399}
1400
1401; Regression test for WasmEHFuncInfo's reverse mapping bug. 'UnwindDestToSrc'
1402; should return a vector and not a single BB, which was incorrect.
1403; This was reduced by bugpoint and should not crash in CFGStackify.
1404define void @wasm_eh_func_info_regression_test() personality ptr @__gxx_wasm_personality_v0 {
1405entry:
1406  invoke void @foo()
1407          to label %invoke.cont unwind label %catch.dispatch
1408
1409catch.dispatch:                                   ; preds = %entry
1410  %0 = catchswitch within none [label %catch.start] unwind label %ehcleanup22
1411
1412catch.start:                                      ; preds = %catch.dispatch
1413  %1 = catchpad within %0 [ptr @_ZTIi]
1414  %2 = call ptr @llvm.wasm.get.exception(token %1)
1415  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
1416  invoke void @__cxa_throw(ptr null, ptr null, ptr null) #1 [ "funclet"(token %1) ]
1417          to label %unreachable unwind label %catch.dispatch2
1418
1419catch.dispatch2:                                  ; preds = %catch.start
1420  %4 = catchswitch within %1 [label %catch.start3] unwind label %ehcleanup
1421
1422catch.start3:                                     ; preds = %catch.dispatch2
1423  %5 = catchpad within %4 [ptr @_ZTIi]
1424  %6 = call ptr @llvm.wasm.get.exception(token %5)
1425  %7 = call i32 @llvm.wasm.get.ehselector(token %5)
1426  catchret from %5 to label %try.cont
1427
1428try.cont:                                         ; preds = %catch.start3
1429  invoke void @foo() [ "funclet"(token %1) ]
1430          to label %invoke.cont8 unwind label %ehcleanup
1431
1432invoke.cont8:                                     ; preds = %try.cont
1433  invoke void @__cxa_throw(ptr null, ptr null, ptr null) #1 [ "funclet"(token %1) ]
1434          to label %unreachable unwind label %catch.dispatch11
1435
1436catch.dispatch11:                                 ; preds = %invoke.cont8
1437  %8 = catchswitch within %1 [label %catch.start12] unwind label %ehcleanup
1438
1439catch.start12:                                    ; preds = %catch.dispatch11
1440  %9 = catchpad within %8 [ptr @_ZTIi]
1441  %10 = call ptr @llvm.wasm.get.exception(token %9)
1442  %11 = call i32 @llvm.wasm.get.ehselector(token %9)
1443  unreachable
1444
1445invoke.cont:                                      ; preds = %entry
1446  unreachable
1447
1448ehcleanup:                                        ; preds = %catch.dispatch11, %try.cont, %catch.dispatch2
1449  %12 = cleanuppad within %1 []
1450  cleanupret from %12 unwind label %ehcleanup22
1451
1452ehcleanup22:                                      ; preds = %ehcleanup, %catch.dispatch
1453  %13 = cleanuppad within none []
1454  cleanupret from %13 unwind to caller
1455
1456unreachable:                                      ; preds = %invoke.cont8, %catch.start
1457  unreachable
1458}
1459
1460; void exception_grouping_0() {
1461;   try {
1462;     try {
1463;       throw 0;
1464;     } catch (int) {
1465;     }
1466;   } catch (int) {
1467;   }
1468; }
1469;
1470; Regression test for a WebAssemblyException grouping bug. After catchswitches
1471; are removed, EH pad catch.start2 is dominated by catch.start, but because
1472; catch.start2 is the unwind destination of catch.start, it should not be
1473; included in catch.start's exception. Also, after we take catch.start2's
1474; exception out of catch.start's exception, we have to take out try.cont8 out of
1475; catch.start's exception, because it has a predecessor in catch.start2.
1476define void @exception_grouping_0() personality ptr @__gxx_wasm_personality_v0 {
1477entry:
1478  %exception = call ptr @__cxa_allocate_exception(i32 4) #0
1479  store i32 0, ptr %exception, align 16
1480  invoke void @__cxa_throw(ptr %exception, ptr @_ZTIi, ptr null) #1
1481          to label %unreachable unwind label %catch.dispatch
1482
1483catch.dispatch:                                   ; preds = %entry
1484  %0 = catchswitch within none [label %catch.start] unwind label %catch.dispatch1
1485
1486catch.start:                                      ; preds = %catch.dispatch
1487  %1 = catchpad within %0 [ptr @_ZTIi]
1488  %2 = call ptr @llvm.wasm.get.exception(token %1)
1489  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
1490  %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #0
1491  %matches = icmp eq i32 %3, %4
1492  br i1 %matches, label %catch, label %rethrow
1493
1494catch:                                            ; preds = %catch.start
1495  %5 = call ptr @__cxa_begin_catch(ptr %2) #0 [ "funclet"(token %1) ]
1496  %6 = load i32, ptr %5, align 4
1497  call void @__cxa_end_catch() #0 [ "funclet"(token %1) ]
1498  catchret from %1 to label %catchret.dest
1499
1500catchret.dest:                                    ; preds = %catch
1501  br label %try.cont
1502
1503rethrow:                                          ; preds = %catch.start
1504  invoke void @llvm.wasm.rethrow() #1 [ "funclet"(token %1) ]
1505          to label %unreachable unwind label %catch.dispatch1
1506
1507catch.dispatch1:                                  ; preds = %rethrow, %catch.dispatch
1508  %7 = catchswitch within none [label %catch.start2] unwind to caller
1509
1510catch.start2:                                     ; preds = %catch.dispatch1
1511  %8 = catchpad within %7 [ptr @_ZTIi]
1512  %9 = call ptr @llvm.wasm.get.exception(token %8)
1513  %10 = call i32 @llvm.wasm.get.ehselector(token %8)
1514  %11 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #0
1515  %matches3 = icmp eq i32 %10, %11
1516  br i1 %matches3, label %catch5, label %rethrow4
1517
1518catch5:                                           ; preds = %catch.start2
1519  %12 = call ptr @__cxa_begin_catch(ptr %9) #0 [ "funclet"(token %8) ]
1520  %13 = load i32, ptr %12, align 4
1521  call void @__cxa_end_catch() #0 [ "funclet"(token %8) ]
1522  catchret from %8 to label %catchret.dest7
1523
1524catchret.dest7:                                   ; preds = %catch5
1525  br label %try.cont8
1526
1527rethrow4:                                         ; preds = %catch.start2
1528  call void @llvm.wasm.rethrow() #1 [ "funclet"(token %8) ]
1529  unreachable
1530
1531try.cont8:                                        ; preds = %try.cont, %catchret.dest7
1532  ret void
1533
1534try.cont:                                         ; preds = %catchret.dest
1535  br label %try.cont8
1536
1537unreachable:                                      ; preds = %rethrow, %entry
1538  unreachable
1539}
1540
1541; Test for WebAssemblyException grouping. This test is hand-modified to generate
1542; this structure:
1543; catch.start dominates catch.start4 and catch.start4 dominates catch.start12,
1544; so the after dominator-based grouping, we end up with:
1545; catch.start's exception > catch4.start's exception > catch12.start's exception
1546; (> here represents subexception relationship)
1547;
1548; But the unwind destination chain is catch.start -> catch.start4 ->
1549; catch.start12. So all these subexception relationship should be deconstructed.
1550; We have to make sure to take out catch.start4's exception out of catch.start's
1551; exception first, before taking out catch.start12's exception out of
1552; catch.start4's exception; otherwise we end up with an incorrect relationship
1553; of catch.start's exception > catch.start12's exception.
1554define void @exception_grouping_1() personality ptr @__gxx_wasm_personality_v0 {
1555entry:
1556  invoke void @foo()
1557          to label %invoke.cont unwind label %catch.dispatch
1558
1559invoke.cont:                                      ; preds = %entry
1560  invoke void @foo()
1561          to label %invoke.cont1 unwind label %catch.dispatch
1562
1563invoke.cont1:                                     ; preds = %invoke.cont
1564  invoke void @foo()
1565          to label %try.cont18 unwind label %catch.dispatch
1566
1567catch.dispatch11:                                 ; preds = %rethrow6, %catch.dispatch3
1568  %0 = catchswitch within none [label %catch.start12] unwind to caller
1569
1570catch.start12:                                    ; preds = %catch.dispatch11
1571  %1 = catchpad within %0 [ptr @_ZTIi]
1572  %2 = call ptr @llvm.wasm.get.exception(token %1)
1573  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
1574  %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #0
1575  %matches13 = icmp eq i32 %3, %4
1576  br i1 %matches13, label %catch15, label %rethrow14
1577
1578catch15:                                          ; preds = %catch.start12
1579  %5 = call ptr @__cxa_begin_catch(ptr %2) #0 [ "funclet"(token %1) ]
1580  %6 = load i32, ptr %5, align 4
1581  call void @__cxa_end_catch() #0 [ "funclet"(token %1) ]
1582  catchret from %1 to label %try.cont18
1583
1584rethrow14:                                        ; preds = %catch.start12
1585  call void @llvm.wasm.rethrow() #1 [ "funclet"(token %1) ]
1586  unreachable
1587
1588catch.dispatch3:                                  ; preds = %rethrow, %catch.dispatch
1589  %7 = catchswitch within none [label %catch.start4] unwind label %catch.dispatch11
1590
1591catch.start4:                                     ; preds = %catch.dispatch3
1592  %8 = catchpad within %7 [ptr @_ZTIi]
1593  %9 = call ptr @llvm.wasm.get.exception(token %8)
1594  %10 = call i32 @llvm.wasm.get.ehselector(token %8)
1595  %11 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #0
1596  %matches5 = icmp eq i32 %10, %11
1597  br i1 %matches5, label %catch7, label %rethrow6
1598
1599catch7:                                           ; preds = %catch.start4
1600  %12 = call ptr @__cxa_begin_catch(ptr %9) #0 [ "funclet"(token %8) ]
1601  %13 = load i32, ptr %12, align 4
1602  call void @__cxa_end_catch() #0 [ "funclet"(token %8) ]
1603  catchret from %8 to label %try.cont18
1604
1605rethrow6:                                         ; preds = %catch.start4
1606  invoke void @llvm.wasm.rethrow() #1 [ "funclet"(token %8) ]
1607          to label %unreachable unwind label %catch.dispatch11
1608
1609catch.dispatch:                                   ; preds = %invoke.cont1, %invoke.cont, %entry
1610  %14 = catchswitch within none [label %catch.start] unwind label %catch.dispatch3
1611
1612catch.start:                                      ; preds = %catch.dispatch
1613  %15 = catchpad within %14 [ptr @_ZTIi]
1614  %16 = call ptr @llvm.wasm.get.exception(token %15)
1615  %17 = call i32 @llvm.wasm.get.ehselector(token %15)
1616  %18 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #0
1617  %matches = icmp eq i32 %17, %18
1618  br i1 %matches, label %catch, label %rethrow
1619
1620catch:                                            ; preds = %catch.start
1621  %19 = call ptr @__cxa_begin_catch(ptr %16) #0 [ "funclet"(token %15) ]
1622  %20 = load i32, ptr %19, align 4
1623  call void @__cxa_end_catch() #0 [ "funclet"(token %15) ]
1624  catchret from %15 to label %try.cont18
1625
1626rethrow:                                          ; preds = %catch.start
1627  invoke void @llvm.wasm.rethrow() #1 [ "funclet"(token %15) ]
1628          to label %unreachable unwind label %catch.dispatch3
1629
1630try.cont18:                                       ; preds = %catch, %catch7, %catch15, %invoke.cont1
1631  ret void
1632
1633unreachable:                                      ; preds = %rethrow, %rethrow6
1634  unreachable
1635}
1636
1637; void exception_grouping_2() {
1638;   try {
1639;     try {
1640;       throw 0;
1641;     } catch (int) { // (a)
1642;     }
1643;   } catch (int) {   // (b)
1644;   }
1645;   try {
1646;     foo();
1647;   } catch (int) {   // (c)
1648;   }
1649; }
1650;
1651; Regression test for an ExceptionInfo grouping bug. Because the first (inner)
1652; try always throws, both EH pads (b) (catch.start2) and (c) (catch.start10) are
1653; dominated by EH pad (a) (catch.start), even though they are not semantically
1654; contained in (a)'s exception. Because (a)'s unwind destination is (b), (b)'s
1655; exception is taken out of (a)'s. But because (c) is reachable from (b), we
1656; should make sure to take out (c)'s exception out of (a)'s exception too.
1657define void @exception_grouping_2() personality ptr @__gxx_wasm_personality_v0 {
1658entry:
1659  %exception = call ptr @__cxa_allocate_exception(i32 4) #1
1660  store i32 0, ptr %exception, align 16
1661  invoke void @__cxa_throw(ptr %exception, ptr @_ZTIi, ptr null) #3
1662          to label %unreachable unwind label %catch.dispatch
1663
1664catch.dispatch:                                   ; preds = %entry
1665  %0 = catchswitch within none [label %catch.start] unwind label %catch.dispatch1
1666
1667catch.start:                                      ; preds = %catch.dispatch
1668  %1 = catchpad within %0 [ptr @_ZTIi]
1669  %2 = call ptr @llvm.wasm.get.exception(token %1)
1670  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
1671  %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #1
1672  %matches = icmp eq i32 %3, %4
1673  br i1 %matches, label %catch, label %rethrow
1674
1675catch:                                            ; preds = %catch.start
1676  %5 = call ptr @__cxa_begin_catch(ptr %2) #1 [ "funclet"(token %1) ]
1677  %6 = load i32, ptr %5, align 4
1678  call void @__cxa_end_catch() #1 [ "funclet"(token %1) ]
1679  catchret from %1 to label %try.cont8
1680
1681rethrow:                                          ; preds = %catch.start
1682  invoke void @llvm.wasm.rethrow() #3 [ "funclet"(token %1) ]
1683          to label %unreachable unwind label %catch.dispatch1
1684
1685catch.dispatch1:                                  ; preds = %rethrow, %catch.dispatch
1686  %7 = catchswitch within none [label %catch.start2] unwind to caller
1687
1688catch.start2:                                     ; preds = %catch.dispatch1
1689  %8 = catchpad within %7 [ptr @_ZTIi]
1690  %9 = call ptr @llvm.wasm.get.exception(token %8)
1691  %10 = call i32 @llvm.wasm.get.ehselector(token %8)
1692  %11 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #1
1693  %matches3 = icmp eq i32 %10, %11
1694  br i1 %matches3, label %catch5, label %rethrow4
1695
1696catch5:                                           ; preds = %catch.start2
1697  %12 = call ptr @__cxa_begin_catch(ptr %9) #1 [ "funclet"(token %8) ]
1698  %13 = load i32, ptr %12, align 4
1699  call void @__cxa_end_catch() #1 [ "funclet"(token %8) ]
1700  catchret from %8 to label %try.cont8
1701
1702rethrow4:                                         ; preds = %catch.start2
1703  call void @llvm.wasm.rethrow() #3 [ "funclet"(token %8) ]
1704  unreachable
1705
1706try.cont8:                                        ; preds = %catch, %catch5
1707  invoke void @foo()
1708          to label %try.cont16 unwind label %catch.dispatch9
1709
1710catch.dispatch9:                                  ; preds = %try.cont8
1711  %14 = catchswitch within none [label %catch.start10] unwind to caller
1712
1713catch.start10:                                    ; preds = %catch.dispatch9
1714  %15 = catchpad within %14 [ptr @_ZTIi]
1715  %16 = call ptr @llvm.wasm.get.exception(token %15)
1716  %17 = call i32 @llvm.wasm.get.ehselector(token %15)
1717  %18 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #1
1718  %matches11 = icmp eq i32 %17, %18
1719  br i1 %matches11, label %catch13, label %rethrow12
1720
1721catch13:                                          ; preds = %catch.start10
1722  %19 = call ptr @__cxa_begin_catch(ptr %16) #1 [ "funclet"(token %15) ]
1723  %20 = load i32, ptr %19, align 4
1724  call void @__cxa_end_catch() #1 [ "funclet"(token %15) ]
1725  catchret from %15 to label %try.cont16
1726
1727rethrow12:                                        ; preds = %catch.start10
1728  call void @llvm.wasm.rethrow() #3 [ "funclet"(token %15) ]
1729  unreachable
1730
1731try.cont16:                                       ; preds = %try.cont8, %catch13
1732  ret void
1733
1734unreachable:                                      ; preds = %rethrow, %entry
1735  unreachable
1736}
1737
1738; Check if the unwind destination mismatch stats are correct
1739; NOSORT: 24 wasm-cfg-stackify    - Number of call unwind mismatches found
1740; NOSORT:  5 wasm-cfg-stackify    - Number of catch unwind mismatches found
1741
1742declare void @foo()
1743declare void @bar()
1744declare i32 @baz()
1745declare i32 @qux(i32)
1746declare void @quux(i32)
1747declare void @fun(i32)
1748; Function Attrs: nounwind
1749declare void @nothrow(i32) #0
1750; Function Attrs: nounwind
1751declare i32 @nothrow_i32() #0
1752
1753; Function Attrs: nounwind
1754declare ptr @_ZN6ObjectD2Ev(ptr returned) #0
1755@_ZTI7MyClass = external constant { ptr, ptr }, align 4
1756; Function Attrs: nounwind
1757declare ptr @_ZN7MyClassD2Ev(ptr returned) #0
1758; Function Attrs: nounwind
1759declare ptr @_ZN7MyClassC2ERKS_(ptr returned, ptr dereferenceable(4)) #0
1760
1761declare i32 @__gxx_wasm_personality_v0(...)
1762; Function Attrs: nounwind
1763declare ptr @llvm.wasm.get.exception(token) #0
1764; Function Attrs: nounwind
1765declare i32 @llvm.wasm.get.ehselector(token) #0
1766declare ptr @__cxa_allocate_exception(i32) #0
1767declare void @__cxa_throw(ptr, ptr, ptr)
1768; Function Attrs: noreturn
1769declare void @llvm.wasm.rethrow() #1
1770; Function Attrs: nounwind
1771declare i32 @llvm.eh.typeid.for(ptr) #0
1772
1773declare ptr @__cxa_begin_catch(ptr)
1774declare void @__cxa_end_catch()
1775declare ptr @__cxa_get_exception_ptr(ptr)
1776declare void @_ZSt9terminatev()
1777; Function Attrs: nounwind
1778declare void @llvm.memcpy.p0.p0.i32(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i32, i1 immarg) #0
1779; Function Attrs: nounwind
1780declare void @llvm.memmove.p0.p0.i32(ptr nocapture, ptr nocapture readonly, i32, i1 immarg) #0
1781; Function Attrs: nounwind
1782declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1 immarg) #0
1783
1784attributes #0 = { nounwind }
1785attributes #1 = { noreturn }
1786