llvm/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh-legacy.ll

; REQUIRES: asserts
; RUN: llc < %s -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -disable-block-placement -verify-machineinstrs -fast-isel=false -machine-sink-split-probability-threshold=0 -cgp-freq-ratio-to-skip-merge=1000 -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling | FileCheck %s
; RUN: llc < %s -disable-wasm-fallthrough-return-opt -disable-block-placement -verify-machineinstrs -fast-isel=false -machine-sink-split-probability-threshold=0 -cgp-freq-ratio-to-skip-merge=1000 -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling
; RUN: llc < %s -O0 -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -verify-machineinstrs -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling | FileCheck %s --check-prefix=NOOPT
; RUN: llc < %s -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -disable-block-placement -verify-machineinstrs -fast-isel=false -machine-sink-split-probability-threshold=0 -cgp-freq-ratio-to-skip-merge=1000 -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling -wasm-disable-ehpad-sort -stats 2>&1 | FileCheck %s --check-prefix=NOSORT
; RUN: llc < %s -disable-wasm-fallthrough-return-opt -disable-block-placement -verify-machineinstrs -fast-isel=false -machine-sink-split-probability-threshold=0 -cgp-freq-ratio-to-skip-merge=1000 -wasm-enable-eh -exception-model=wasm -mattr=+exception-handling -wasm-disable-ehpad-sort | FileCheck %s --check-prefix=NOSORT-LOCALS

target triple = "wasm32-unknown-unknown"

@_ZTIi = external constant ptr
@_ZTId = external constant ptr

%class.Object = type { i8 }
%class.MyClass = type { i32 }

; Simple test case with two catch clauses
;
; void foo();
; void two_catches() {
;   try {
;     foo();
;   } catch (int) {
;   } catch (double) {
;   }
; }

; CHECK-LABEL: two_catches:
; CHECK: try
; CHECK:   call      foo
; CHECK: catch
; CHECK:   block
; CHECK:     br_if     0, {{.*}}                       # 0: down to label[[L0:[0-9]+]]
; CHECK:     call      $drop=, __cxa_begin_catch
; CHECK:     call      __cxa_end_catch
; CHECK:     br        1                               # 1: down to label[[L1:[0-9]+]]
; CHECK:   end_block                                   # label[[L0]]:
; CHECK:   block
; CHECK:     br_if     0, {{.*}}                       # 0: down to label[[L2:[0-9]+]]
; CHECK:     call      $drop=, __cxa_begin_catch
; CHECK:     call      __cxa_end_catch
; CHECK:     br        1                               # 1: down to label[[L1]]
; CHECK:   end_block                                   # label[[L2]]:
; CHECK:   rethrow   0                                 # to caller
; CHECK: end_try                                       # label[[L1]]:
define void @two_catches() personality ptr @__gxx_wasm_personality_v0 {
entry:
  invoke void @foo()
          to label %try.cont unwind label %catch.dispatch

catch.dispatch:                                   ; preds = %entry
  %0 = catchswitch within none [label %catch.start] unwind to caller

catch.start:                                      ; preds = %catch.dispatch
  %1 = catchpad within %0 [ptr @_ZTIi, ptr @_ZTId]
  %2 = call ptr @llvm.wasm.get.exception(token %1)
  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
  %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi)
  %matches = icmp eq i32 %3, %4
  br i1 %matches, label %catch2, label %catch.fallthrough

catch2:                                           ; preds = %catch.start
  %5 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
  call void @__cxa_end_catch() [ "funclet"(token %1) ]
  catchret from %1 to label %try.cont

catch.fallthrough:                                ; preds = %catch.start
  %6 = call i32 @llvm.eh.typeid.for(ptr @_ZTId)
  %matches1 = icmp eq i32 %3, %6
  br i1 %matches1, label %catch, label %rethrow

catch:                                            ; preds = %catch.fallthrough
  %7 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
  call void @__cxa_end_catch() [ "funclet"(token %1) ]
  catchret from %1 to label %try.cont

rethrow:                                          ; preds = %catch.fallthrough
  call void @llvm.wasm.rethrow() [ "funclet"(token %1) ]
  unreachable

try.cont:                                         ; preds = %catch, %catch2, %entry
  ret void
}

; Nested try-catches within a catch
; void nested_catch() {
;   try {
;     foo();
;   } catch (int) {
;     try {
;       foo();
;     } catch (int) {
;       foo();
;     }
;   }
; }

; CHECK-LABEL: nested_catch:
; CHECK: try
; CHECK:   call  foo
; CHECK: catch
; CHECK:   block
; CHECK:     block
; CHECK:       br_if     0, {{.*}}                     # 0: down to label[[L0:[0-9+]]]
; CHECK:       call  $drop=, __cxa_begin_catch, $0
; CHECK:       try
; CHECK:         try
; CHECK:           call  foo
; CHECK:           br        3                         # 3: down to label[[L1:[0-9+]]]
; CHECK:         catch
; CHECK:           block
; CHECK:             block
; CHECK:               br_if     0, {{.*}}             # 0: down to label[[L2:[0-9+]]]
; CHECK:               call  $drop=, __cxa_begin_catch
; CHECK:               try
; CHECK:                 call  foo
; CHECK:                 br        2                   # 2: down to label[[L3:[0-9+]]]
; CHECK:               catch_all
; CHECK:                 call  __cxa_end_catch
; CHECK:                 rethrow   0                   # down to catch[[L4:[0-9+]]]
; CHECK:               end_try
; CHECK:             end_block                         # label[[L2]]:
; CHECK:             rethrow   1                       # down to catch[[L4]]
; CHECK:           end_block                           # label[[L3]]:
; CHECK:           call  __cxa_end_catch
; CHECK:           br        3                         # 3: down to label[[L1]]
; CHECK:         end_try
; CHECK:       catch_all                               # catch[[L4]]:
; CHECK:         call  __cxa_end_catch
; CHECK:         rethrow   0                           # to caller
; CHECK:       end_try
; CHECK:     end_block                                 # label[[L0]]:
; CHECK:     rethrow   1                               # to caller
; CHECK:   end_block                                   # label[[L1]]:
; CHECK:   call  __cxa_end_catch
; CHECK: end_try
define void @nested_catch() personality ptr @__gxx_wasm_personality_v0 {
entry:
  invoke void @foo()
          to label %try.cont11 unwind label %catch.dispatch

catch.dispatch:                                   ; preds = %entry
  %0 = catchswitch within none [label %catch.start] unwind to caller

catch.start:                                      ; preds = %catch.dispatch
  %1 = catchpad within %0 [ptr @_ZTIi]
  %2 = call ptr @llvm.wasm.get.exception(token %1)
  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
  %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi)
  %matches = icmp eq i32 %3, %4
  br i1 %matches, label %catch, label %rethrow

catch:                                            ; preds = %catch.start
  %5 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
  %6 = load i32, ptr %5, align 4
  invoke void @foo() [ "funclet"(token %1) ]
          to label %try.cont unwind label %catch.dispatch2

catch.dispatch2:                                  ; preds = %catch
  %7 = catchswitch within %1 [label %catch.start3] unwind label %ehcleanup9

catch.start3:                                     ; preds = %catch.dispatch2
  %8 = catchpad within %7 [ptr @_ZTIi]
  %9 = call ptr @llvm.wasm.get.exception(token %8)
  %10 = call i32 @llvm.wasm.get.ehselector(token %8)
  %11 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi)
  %matches4 = icmp eq i32 %10, %11
  br i1 %matches4, label %catch6, label %rethrow5

catch6:                                           ; preds = %catch.start3
  %12 = call ptr @__cxa_begin_catch(ptr %9) [ "funclet"(token %8) ]
  %13 = load i32, ptr %12, align 4
  invoke void @foo() [ "funclet"(token %8) ]
          to label %invoke.cont8 unwind label %ehcleanup

invoke.cont8:                                     ; preds = %catch6
  call void @__cxa_end_catch() [ "funclet"(token %8) ]
  catchret from %8 to label %try.cont

rethrow5:                                         ; preds = %catch.start3
  invoke void @llvm.wasm.rethrow() [ "funclet"(token %8) ]
          to label %unreachable unwind label %ehcleanup9

try.cont:                                         ; preds = %invoke.cont8, %catch
  call void @__cxa_end_catch() [ "funclet"(token %1) ]
  catchret from %1 to label %try.cont11

rethrow:                                          ; preds = %catch.start
  call void @llvm.wasm.rethrow() [ "funclet"(token %1) ]
  unreachable

try.cont11:                                       ; preds = %try.cont, %entry
  ret void

ehcleanup:                                        ; preds = %catch6
  %14 = cleanuppad within %8 []
  call void @__cxa_end_catch() [ "funclet"(token %14) ]
  cleanupret from %14 unwind label %ehcleanup9

ehcleanup9:                                       ; preds = %ehcleanup, %rethrow5, %catch.dispatch2
  %15 = cleanuppad within %1 []
  call void @__cxa_end_catch() [ "funclet"(token %15) ]
  cleanupret from %15 unwind to caller

unreachable:                                      ; preds = %rethrow5
  unreachable
}

; Nested loop within a catch clause
; void loop_within_catch() {
;   try {
;     foo();
;   } catch (...) {
;     for (int i = 0; i < 50; i++)
;       foo();
;   }
; }

; CHECK-LABEL: loop_within_catch:
; CHECK: try
; CHECK:   call      foo
; CHECK: catch
; CHECK:   call      $drop=, __cxa_begin_catch
; CHECK:   loop                                        # label[[L0:[0-9]+]]:
; CHECK:     block
; CHECK:       block
; CHECK:         br_if     0, {{.*}}                   # 0: down to label[[L1:[0-9]+]]
; CHECK:         try
; CHECK:           call      foo
; CHECK:           br        2                         # 2: down to label[[L2:[0-9]+]]
; CHECK:         catch
; CHECK:           try
; CHECK:             call      __cxa_end_catch
; CHECK:           catch_all
; CHECK:             call      _ZSt9terminatev
; CHECK:             unreachable
; CHECK:           end_try
; CHECK:           rethrow   0                         # to caller
; CHECK:         end_try
; CHECK:       end_block                               # label[[L1]]:
; CHECK:       call      __cxa_end_catch
; CHECK:       br        2                             # 2: down to label[[L3:[0-9]+]]
; CHECK:     end_block                                 # label[[L2]]:
; CHECK:     br        0                               # 0: up to label[[L0]]
; CHECK:   end_loop
; CHECK: end_try                                       # label[[L3]]:
define void @loop_within_catch() personality ptr @__gxx_wasm_personality_v0 {
entry:
  invoke void @foo()
          to label %try.cont unwind label %catch.dispatch

catch.dispatch:                                   ; preds = %entry
  %0 = catchswitch within none [label %catch.start] unwind to caller

catch.start:                                      ; preds = %catch.dispatch
  %1 = catchpad within %0 [ptr null]
  %2 = call ptr @llvm.wasm.get.exception(token %1)
  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
  %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
  br label %for.cond

for.cond:                                         ; preds = %for.inc, %catch.start
  %i.0 = phi i32 [ 0, %catch.start ], [ %inc, %for.inc ]
  %cmp = icmp slt i32 %i.0, 50
  br i1 %cmp, label %for.body, label %for.end

for.body:                                         ; preds = %for.cond
  invoke void @foo() [ "funclet"(token %1) ]
          to label %for.inc unwind label %ehcleanup

for.inc:                                          ; preds = %for.body
  %inc = add nsw i32 %i.0, 1
  br label %for.cond

for.end:                                          ; preds = %for.cond
  call void @__cxa_end_catch() [ "funclet"(token %1) ]
  catchret from %1 to label %try.cont

try.cont:                                         ; preds = %for.end, %entry
  ret void

ehcleanup:                                        ; preds = %for.body
  %5 = cleanuppad within %1 []
  invoke void @__cxa_end_catch() [ "funclet"(token %5) ]
          to label %invoke.cont2 unwind label %terminate

invoke.cont2:                                     ; preds = %ehcleanup
  cleanupret from %5 unwind to caller

terminate:                                        ; preds = %ehcleanup
  %6 = cleanuppad within %5 []
  call void @_ZSt9terminatev() [ "funclet"(token %6) ]
  unreachable
}

; Tests if block and try markers are correctly placed. Even if two predecessors
; of the EH pad are bb2 and bb3 and their nearest common dominator is bb1, the
; TRY marker should be placed at bb0 because there's a branch from bb0 to bb2,
; and scopes cannot be interleaved.

; NOOPT-LABEL: block_try_markers:
; NOOPT: try
; NOOPT:   block
; NOOPT:     block
; NOOPT:       block
; NOOPT:       end_block
; NOOPT:     end_block
; NOOPT:     call      foo
; NOOPT:   end_block
; NOOPT:   call      bar
; NOOPT: catch     {{.*}}
; NOOPT: end_try
define void @block_try_markers() personality ptr @__gxx_wasm_personality_v0 {
bb0:
  br i1 undef, label %bb1, label %bb2

bb1:                                              ; preds = %bb0
  br i1 undef, label %bb3, label %bb4

bb2:                                              ; preds = %bb0
  br label %try.cont

bb3:                                              ; preds = %bb1
  invoke void @foo()
          to label %try.cont unwind label %catch.dispatch

bb4:                                              ; preds = %bb1
  invoke void @bar()
          to label %try.cont unwind label %catch.dispatch

catch.dispatch:                                   ; preds = %bb4, %bb3
  %0 = catchswitch within none [label %catch.start] unwind to caller

catch.start:                                      ; preds = %catch.dispatch
  %1 = catchpad within %0 [ptr null]
  %2 = call ptr @llvm.wasm.get.exception(token %1)
  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
  catchret from %1 to label %try.cont

try.cont:                                         ; preds = %catch.start, %bb4, %bb3, %bb2
  ret void
}

; Tests if try/end_try markers are placed correctly wrt loop/end_loop markers,
; when try and loop markers are in the same BB and end_try and end_loop are in
; another BB.
; CHECK-LABEL: loop_try_markers:
; CHECK: loop
; CHECK:   try
; CHECK:     call      foo
; CHECK:   catch
; CHECK:   end_try
; CHECK: end_loop
define void @loop_try_markers(ptr %p) personality ptr @__gxx_wasm_personality_v0 {
entry:
  store volatile i32 0, ptr %p
  br label %loop

loop:                                             ; preds = %try.cont, %entry
  store volatile i32 1, ptr %p
  invoke void @foo()
          to label %try.cont unwind label %catch.dispatch

catch.dispatch:                                   ; preds = %loop
  %0 = catchswitch within none [label %catch.start] unwind to caller

catch.start:                                      ; preds = %catch.dispatch
  %1 = catchpad within %0 [ptr null]
  %2 = call ptr @llvm.wasm.get.exception(token %1)
  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
  catchret from %1 to label %try.cont

try.cont:                                         ; preds = %catch.start, %loop
  br label %loop
}

; Some of test cases below are hand-tweaked by deleting some library calls to
; simplify tests and changing the order of basic blocks to cause unwind
; destination mismatches. And we use -wasm-disable-ehpad-sort to create maximum
; number of mismatches in several tests below.

; - Call unwind mismatch
; 'call bar''s original unwind destination was 'C0', but after control flow
; linearization, its unwind destination incorrectly becomes 'C1'. We fix this by
; wrapping the call with a nested try-delegate that targets 'C0'.
; - Catch unwind mismatch
; If 'call foo' throws a foreign exception, it will not be caught by C1, and
; should be rethrown to the caller. But after control flow linearization, it
; will instead unwind to C0, an incorrect next EH pad. We wrap the whole
; try-catch with try-delegate that rethrows an exception to the caller to fix
; this.

; NOSORT-LABEL: unwind_mismatches_0:
; NOSORT: try
; --- try-delegate starts (catch unwind mismatch)
; NOSORT    try
; NOSORT:     try
; NOSORT:       call  foo
; --- try-delegate starts (call unwind mismatch)
; NOSORT:       try
; NOSORT:         call  bar
; NOSORT:       delegate    2     # label/catch{{[0-9]+}}: down to catch[[C0:[0-9]+]]
; --- try-delegate ends (call unwind mismatch)
; NOSORT:     catch   {{.*}}      # catch[[C1:[0-9]+]]:
; NOSORT:     end_try
; NOSORT:   delegate    1         # label/catch{{[0-9]+}}: to caller
; --- try-delegate ends (catch unwind mismatch)
; NOSORT: catch   {{.*}}          # catch[[C0]]:
; NOSORT: end_try
; NOSORT: return

define void @unwind_mismatches_0() personality ptr @__gxx_wasm_personality_v0 {
bb0:
  invoke void @foo()
          to label %bb1 unwind label %catch.dispatch0

bb1:                                              ; preds = %bb0
  invoke void @bar()
          to label %try.cont unwind label %catch.dispatch1

catch.dispatch0:                                  ; preds = %bb0
  %0 = catchswitch within none [label %catch.start0] unwind to caller

catch.start0:                                     ; preds = %catch.dispatch0
  %1 = catchpad within %0 [ptr null]
  %2 = call ptr @llvm.wasm.get.exception(token %1)
  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
  catchret from %1 to label %try.cont

catch.dispatch1:                                  ; preds = %bb1
  %4 = catchswitch within none [label %catch.start1] unwind to caller

catch.start1:                                     ; preds = %catch.dispatch1
  %5 = catchpad within %4 [ptr null]
  %6 = call ptr @llvm.wasm.get.exception(token %5)
  %7 = call i32 @llvm.wasm.get.ehselector(token %5)
  catchret from %5 to label %try.cont

try.cont:                                         ; preds = %catch.start1, %catch.start0, %bb1
  ret void
}

; 'call bar' and 'call baz''s original unwind destination was the caller, but
; after control flow linearization, their unwind destination incorrectly becomes
; 'C0'. We fix this by wrapping the calls with a nested try-delegate that
; rethrows exceptions to the caller.

; And the return value of 'baz' should NOT be stackified because the BB is split
; during fixing unwind mismatches.

; NOSORT-LABEL: unwind_mismatches_1:
; NOSORT: try
; NOSORT:   call  foo
; --- try-delegate starts (call unwind mismatch)
; NOSORT:   try
; NOSORT:     call  bar
; NOSORT:     call  $[[RET:[0-9]+]]=, baz
; NOSORT-NOT: call  $push{{.*}}=, baz
; NOSORT:   delegate    1                     # label/catch{{[0-9]+}}: to caller
; --- try-delegate ends (call unwind mismatch)
; NOSORT:   call  nothrow, $[[RET]]
; NOSORT:   return
; NOSORT: catch   {{.*}}                      # catch[[C0:[0-9]+]]:
; NOSORT:   return
; NOSORT: end_try

define void @unwind_mismatches_1() personality ptr @__gxx_wasm_personality_v0 {
bb0:
  invoke void @foo()
          to label %bb1 unwind label %catch.dispatch0

bb1:                                              ; preds = %bb0
  call void @bar()
  %call = call i32 @baz()
  call void @nothrow(i32 %call) #0
  ret void

catch.dispatch0:                                  ; preds = %bb0
  %0 = catchswitch within none [label %catch.start0] unwind to caller

catch.start0:                                     ; preds = %catch.dispatch0
  %1 = catchpad within %0 [ptr null]
  %2 = call ptr @llvm.wasm.get.exception(token %1)
  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
  catchret from %1 to label %try.cont

try.cont:                                         ; preds = %catch.start0
  ret void
}

; The same as unwind_mismatches_0, but we have one more call 'call @foo' in bb1
; which unwinds to the caller. IN this case bb1 has two call unwind mismatches:
  ; 'call @foo' unwinds to the caller and 'call @bar' unwinds to catch C0.

; NOSORT-LABEL: unwind_mismatches_2:
; NOSORT: try
; --- try-delegate starts (catch unwind mismatch)
; NOSORT    try
; NOSORT:     try
; NOSORT:       call  foo
; --- try-delegate starts (call unwind mismatch)
; NOSORT:       try
; NOSORT:         call  foo
; NOSORT:       delegate    3     # label/catch{{[0-9]+}}: to caller
; --- try-delegate ends (call unwind mismatch)
; --- try-delegate starts (call unwind mismatch)
; NOSORT:       try
; NOSORT:         call  bar
; NOSORT:       delegate    2     # label/catch{{[0-9]+}}: down to catch[[C0:[0-9]+]]
; --- try-delegate ends (call unwind mismatch)
; NOSORT:     catch   {{.*}}      # catch[[C1:[0-9]+]]:
; NOSORT:     end_try
; NOSORT:   delegate    1         # label/catch{{[0-9]+}}: to caller
; --- try-delegate ends (catch unwind mismatch)
; NOSORT: catch   {{.*}}        # catch[[C0]]:
; NOSORT: end_try
; NOSORT: return

define void @unwind_mismatches_2() personality ptr @__gxx_wasm_personality_v0 {
bb0:
  invoke void @foo()
          to label %bb1 unwind label %catch.dispatch0

bb1:                                              ; preds = %bb0
  call void @foo()
  invoke void @bar()
          to label %try.cont unwind label %catch.dispatch1

catch.dispatch0:                                  ; preds = %bb0
  %0 = catchswitch within none [label %catch.start0] unwind to caller

catch.start0:                                     ; preds = %catch.dispatch0
  %1 = catchpad within %0 [ptr null]
  %2 = call ptr @llvm.wasm.get.exception(token %1)
  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
  catchret from %1 to label %try.cont

catch.dispatch1:                                  ; preds = %bb1
  %4 = catchswitch within none [label %catch.start1] unwind to caller

catch.start1:                                     ; preds = %catch.dispatch1
  %5 = catchpad within %4 [ptr null]
  %6 = call ptr @llvm.wasm.get.exception(token %5)
  %7 = call i32 @llvm.wasm.get.ehselector(token %5)
  catchret from %5 to label %try.cont

try.cont:                                         ; preds = %catch.start1, %catch.start0, %bb1
  ret void
}

; Similar situation as @unwind_mismatches_1. Here 'call @qux''s original unwind
; destination was the caller, but after control flow linearization, their unwind
; destination incorrectly becomes 'C0' within the function. We fix this by
; wrapping the call with a nested try-delegate that rethrows the exception to
; the caller.

; Because 'call @qux' pops an argument pushed by 'i32.const 5' from stack, the
; nested 'try' should be placed before `i32.const 5', not between 'i32.const 5'
; and 'call @qux'.

; NOSORT-LABEL: unwind_mismatches_3:
; NOSORT: try       i32
; NOSORT:   call  foo
; --- try-delegate starts (call unwind mismatch)
; NOSORT:   try
; NOSORT:     i32.const  $push{{[0-9]+}}=, 5
; NOSORT:     call  ${{[0-9]+}}=, qux
; NOSORT:   delegate    1                     # label/catch{{[0-9]+}}: to caller
; --- try-delegate ends (call unwind mismatch)
; NOSORT:   return
; NOSORT: catch   {{.*}}                      # catch[[C0:[0-9]+]]:
; NOSORT:   return
; NOSORT: end_try

define i32 @unwind_mismatches_3() personality ptr @__gxx_wasm_personality_v0 {
bb0:
  invoke void @foo()
          to label %bb1 unwind label %catch.dispatch0

bb1:                                              ; preds = %bb0
  %0 = call i32 @qux(i32 5)
  ret i32 %0

catch.dispatch0:                                  ; preds = %bb0
  %1 = catchswitch within none [label %catch.start0] unwind to caller

catch.start0:                                     ; preds = %catch.dispatch0
  %2 = catchpad within %1 [ptr null]
  %3 = call ptr @llvm.wasm.get.exception(token %2)
  %j = call i32 @llvm.wasm.get.ehselector(token %2)
  catchret from %2 to label %try.cont

try.cont:                                         ; preds = %catch.start0
  ret i32 0
}

; Tests the case when TEE stackifies a register in RegStackify but it gets
; unstackified in fixCallUnwindMismatches in CFGStackify.

; NOSORT-LOCALS-LABEL: unstackify_when_fixing_unwind_mismatch:
define void @unstackify_when_fixing_unwind_mismatch(i32 %x) personality ptr @__gxx_wasm_personality_v0 {
bb0:
  invoke void @foo()
          to label %bb1 unwind label %catch.dispatch0

bb1:                                              ; preds = %bb0
  %t = add i32 %x, 4
  ; This %addr is used in multiple places, so tee is introduced in RegStackify,
  ; which stackifies the use of %addr in store instruction. A tee has two dest
  ; registers, the first of which is stackified and the second is not.
  ; But when we introduce a nested try-delegate in fixCallUnwindMismatches in
  ; CFGStackify, it is possible that we end up unstackifying the first dest
  ; register. In that case, we convert that tee into a copy.
  %addr = inttoptr i32 %t to ptr
  %load = load i32, ptr %addr
  %call = call i32 @baz()
  %add = add i32 %load, %call
  store i32 %add, ptr %addr
  ret void
; NOSORT-LOCALS:       i32.add
; NOSORT-LOCALS-NOT:   local.tee
; NOSORT-LOCALS-NEXT:  local.set

catch.dispatch0:                                  ; preds = %bb0
  %0 = catchswitch within none [label %catch.start0] unwind to caller

catch.start0:                                     ; preds = %catch.dispatch0
  %1 = catchpad within %0 [ptr null]
  %2 = call ptr @llvm.wasm.get.exception(token %1)
  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
  catchret from %1 to label %try.cont

try.cont:                                         ; preds = %catch.start0
  ret void
}

; We have two call unwind unwind mismatches:
; - A may-throw instruction unwinds to an incorrect EH pad after linearizing the
;   CFG, when it is supposed to unwind to another EH pad.
; - A may-throw instruction unwinds to an incorrect EH pad after linearizing the
;   CFG, when it is supposed to unwind to the caller.
; We also have a catch unwind mismatch: If an exception is not caught by the
; first catch because it is a non-C++ exception, it shouldn't unwind to the next
; catch, but it should unwind to the caller.

; NOSORT-LABEL: unwind_mismatches_4:
; NOSORT: try
; --- try-delegate starts (catch unwind mismatch)
; NOSORT:   try
; NOSORT:     try
; NOSORT:       call  foo
; --- try-delegate starts (call unwind mismatch)
; NOSORT:       try
; NOSORT:         call  bar
; NOSORT:       delegate    2            # label/catch{{[0-9]+}}: down to catch[[C0:[0-9]+]]
; --- try-delegate ends (call unwind mismatch)
; NOSORT:     catch
; NOSORT:       call  {{.*}} __cxa_begin_catch
; --- try-delegate starts (call unwind mismatch)
; NOSORT:       try
; NOSORT:         call  __cxa_end_catch
; NOSORT:       delegate    3            # label/catch{{[0-9]+}}: to caller
; --- try-delegate ends (call unwind mismatch)
; NOSORT:     end_try
; NOSORT:   delegate    1                # label/catch{{[0-9]+}}: to caller
; --- try-delegate ends (catch unwind mismatch)
; NOSORT: catch  {{.*}}                  # catch[[C0]]:
; NOSORT:   call  {{.*}} __cxa_begin_catch
; NOSORT:   call  __cxa_end_catch
; NOSORT: end_try
; NOSORT: return

define void @unwind_mismatches_4() personality ptr @__gxx_wasm_personality_v0 {
bb0:
  invoke void @foo()
          to label %bb1 unwind label %catch.dispatch0

bb1:                                              ; preds = %bb0
  invoke void @bar()
          to label %try.cont unwind label %catch.dispatch1

catch.dispatch0:                                  ; preds = %bb0
  %0 = catchswitch within none [label %catch.start0] unwind to caller

catch.start0:                                     ; preds = %catch.dispatch0
  %1 = catchpad within %0 [ptr null]
  %2 = call ptr @llvm.wasm.get.exception(token %1)
  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
  %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
  call void @__cxa_end_catch() [ "funclet"(token %1) ]
  catchret from %1 to label %try.cont

catch.dispatch1:                                  ; preds = %bb1
  %5 = catchswitch within none [label %catch.start1] unwind to caller

catch.start1:                                     ; preds = %catch.dispatch1
  %6 = catchpad within %5 [ptr null]
  %7 = call ptr @llvm.wasm.get.exception(token %6)
  %8 = call i32 @llvm.wasm.get.ehselector(token %6)
  %9 = call ptr @__cxa_begin_catch(ptr %7) [ "funclet"(token %6) ]
  call void @__cxa_end_catch() [ "funclet"(token %6) ]
  catchret from %6 to label %try.cont

try.cont:                                         ; preds = %catch.start1, %catch.start0, %bb1
  ret void
}

; In CFGSort, EH pads should be sorted as soon as it is available and
; 'Preferred' queue and should NOT be entered into 'Ready' queue unless we are
; in the middle of sorting another region that does not contain the EH pad. In
; this example, 'catch.start' should be sorted right after 'if.then' is sorted
; (before 'cont' is sorted) and there should not be any unwind destination
; mismatches in CFGStackify.

; NOOPT-LABEL: cfg_sort_order:
; NOOPT: block
; NOOPT:   try
; NOOPT:     call      foo
; NOOPT:   catch
; NOOPT:   end_try
; NOOPT:   call      foo
; NOOPT: end_block
; NOOPT: return
define void @cfg_sort_order(i32 %arg) personality ptr @__gxx_wasm_personality_v0 {
entry:
  %tobool = icmp ne i32 %arg, 0
  br i1 %tobool, label %if.then, label %if.end

catch.dispatch:                                   ; preds = %if.then
  %0 = catchswitch within none [label %catch.start] unwind to caller

catch.start:                                      ; preds = %catch.dispatch
  %1 = catchpad within %0 [ptr null]
  %2 = call ptr @llvm.wasm.get.exception(token %1)
  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
  %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
  call void @__cxa_end_catch() [ "funclet"(token %1) ]
  catchret from %1 to label %if.end

if.then:                                          ; preds = %entry
  invoke void @foo()
          to label %cont unwind label %catch.dispatch

cont:                                             ; preds = %if.then
  call void @foo()
  br label %if.end

if.end:                                           ; preds = %cont, %catch.start, %entry
  ret void
}

; Intrinsics like memcpy, memmove, and memset don't throw and are lowered into
; calls to external symbols (not global addresses) in instruction selection,
; which will be eventually lowered to library function calls.
; Because this test runs with -wasm-disable-ehpad-sort, these library calls in
; invoke.cont BB fall within try~end_try, but they shouldn't cause crashes or
; unwinding destination mismatches in CFGStackify.

; NOSORT-LABEL: mem_intrinsics:
; NOSORT: try
; NOSORT:   call  foo
; NOSORT:   call {{.*}} memcpy
; NOSORT:   call {{.*}} memmove
; NOSORT:   call {{.*}} memset
; NOSORT:   return
; NOSORT: catch_all
; NOSORT:   rethrow 0
; NOSORT: end_try
define void @mem_intrinsics(ptr %a, ptr %b) personality ptr @__gxx_wasm_personality_v0 {
entry:
  %o = alloca %class.Object, align 1
  invoke void @foo()
          to label %invoke.cont unwind label %ehcleanup

invoke.cont:                                      ; preds = %entry
  call void @llvm.memcpy.p0.p0.i32(ptr %a, ptr %b, i32 100, i1 false)
  call void @llvm.memmove.p0.p0.i32(ptr %a, ptr %b, i32 100, i1 false)
  call void @llvm.memset.p0.i32(ptr %a, i8 0, i32 100, i1 false)
  %call = call ptr @_ZN6ObjectD2Ev(ptr %o)
  ret void

ehcleanup:                                        ; preds = %entry
  %0 = cleanuppad within none []
  %call2 = call ptr @_ZN6ObjectD2Ev(ptr %o) [ "funclet"(token %0) ]
  cleanupret from %0 unwind to caller
}

; Tests if 'try' marker is placed correctly. In this test, 'try' should be
; placed before the call to 'nothrow_i32' and not between the call to
; 'nothrow_i32' and 'fun', because the return value of 'nothrow_i32' is
; stackified and pushed onto the stack to be consumed by the call to 'fun'.

; CHECK-LABEL: try_marker_with_stackified_input:
; CHECK: try
; CHECK: call      $push{{.*}}=, nothrow_i32
; CHECK: call      fun, $pop{{.*}}
define void @try_marker_with_stackified_input() personality ptr @__gxx_wasm_personality_v0 {
entry:
  %call = call i32 @nothrow_i32()
  invoke void @fun(i32 %call)
          to label %invoke.cont unwind label %terminate

invoke.cont:                                      ; preds = %entry
  ret void

terminate:                                        ; preds = %entry
  %0 = cleanuppad within none []
  call void @_ZSt9terminatev() [ "funclet"(token %0) ]
  unreachable
}

; This crashed on debug mode (= when NDEBUG is not defined) when the logic for
; computing the innermost region was not correct, in which a loop region
; contains an exception region. This should pass CFGSort without crashing.
define void @loop_exception_region() personality ptr @__gxx_wasm_personality_v0 {
entry:
  %e = alloca %class.MyClass, align 4
  br label %for.cond

for.cond:                                         ; preds = %for.inc, %entry
  %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.inc ]
  %cmp = icmp slt i32 %i.0, 9
  br i1 %cmp, label %for.body, label %for.end

for.body:                                         ; preds = %for.cond
  invoke void @quux(i32 %i.0)
          to label %for.inc unwind label %catch.dispatch

catch.dispatch:                                   ; preds = %for.body
  %0 = catchswitch within none [label %catch.start] unwind to caller

catch.start:                                      ; preds = %catch.dispatch
  %1 = catchpad within %0 [ptr @_ZTI7MyClass]
  %2 = call ptr @llvm.wasm.get.exception(token %1)
  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
  %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTI7MyClass)
  %matches = icmp eq i32 %3, %4
  br i1 %matches, label %catch, label %rethrow

catch:                                            ; preds = %catch.start
  %5 = call ptr @__cxa_get_exception_ptr(ptr %2) [ "funclet"(token %1) ]
  %call = call ptr @_ZN7MyClassC2ERKS_(ptr %e, ptr dereferenceable(4) %5) [ "funclet"(token %1) ]
  %6 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
  %7 = load i32, ptr %e, align 4
  invoke void @quux(i32 %7) [ "funclet"(token %1) ]
          to label %invoke.cont2 unwind label %ehcleanup

invoke.cont2:                                     ; preds = %catch
  %call3 = call ptr @_ZN7MyClassD2Ev(ptr %e) [ "funclet"(token %1) ]
  call void @__cxa_end_catch() [ "funclet"(token %1) ]
  catchret from %1 to label %for.inc

rethrow:                                          ; preds = %catch.start
  call void @llvm.wasm.rethrow() [ "funclet"(token %1) ]
  unreachable

for.inc:                                          ; preds = %invoke.cont2, %for.body
  %inc = add nsw i32 %i.0, 1
  br label %for.cond

ehcleanup:                                        ; preds = %catch
  %8 = cleanuppad within %1 []
  %call4 = call ptr @_ZN7MyClassD2Ev(ptr %e) [ "funclet"(token %8) ]
  invoke void @__cxa_end_catch() [ "funclet"(token %8) ]
          to label %invoke.cont6 unwind label %terminate7

invoke.cont6:                                     ; preds = %ehcleanup
  cleanupret from %8 unwind to caller

for.end:                                          ; preds = %for.cond
  ret void

terminate7:                                       ; preds = %ehcleanup
  %9 = cleanuppad within %8 []
  call void @_ZSt9terminatev() [ "funclet"(token %9) ]
  unreachable
}

; Tests if CFGStackify's removeUnnecessaryInstrs() removes unnecessary branches
; correctly. The code is in the form below, where 'br' is unnecessary because
; after running the 'try' body the control flow will fall through to bb2 anyway.

; bb0:
;   try
;     ...
;     br bb2      <- Not necessary
; bb1 (ehpad):
;   catch
;     ...
; bb2:            <- Continuation BB
;   end
; CHECK-LABEL: remove_unnecessary_instrs:
define void @remove_unnecessary_instrs(i32 %n) personality ptr @__gxx_wasm_personality_v0 {
entry:
  invoke void @foo()
          to label %for.body unwind label %catch.dispatch

for.body:                                         ; preds = %for.end, %entry
  %i = phi i32 [ %inc, %for.end ], [ 0, %entry ]
  invoke void @foo()
          to label %for.end unwind label %catch.dispatch

; Before going to CFGStackify, this BB will have a conditional branch followed
; by an unconditional branch. CFGStackify should remove only the unconditional
; one.
for.end:                                          ; preds = %for.body
  %inc = add nuw nsw i32 %i, 1
  %exitcond = icmp eq i32 %inc, %n
  br i1 %exitcond, label %try.cont, label %for.body
; CHECK: br_if
; CHECK-NOT: br
; CHECK: end_loop
; CHECK: catch

catch.dispatch:                                   ; preds = %for.body, %entry
  %0 = catchswitch within none [label %catch.start] unwind to caller

catch.start:                                      ; preds = %catch.dispatch
  %1 = catchpad within %0 [ptr null]
  %2 = call ptr @llvm.wasm.get.exception(token %1)
  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
  %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
  call void @__cxa_end_catch() [ "funclet"(token %1) ]
  catchret from %1 to label %try.cont

try.cont:                                         ; preds = %catch.start, %for.end
  ret void
}

; void foo();
; void remove_unnecessary_br() {
;   try {
;     foo();
;     try {
;       foo();
;     } catch (...) {
;     }
;   } catch (...) {
;   }
; }
;
; This tests whether the 'br' can be removed in code in the form as follows.
; Here 'br' is inside an inner try, whose 'end' is in another EH pad. In this
; case, after running an inner try body, the control flow should fall through to
; bb3, so the 'br' in the code is unnecessary.

; bb0:
;   try
;     try
;       ...
;       br bb3      <- Not necessary
; bb1:
;     catch
; bb2:
;     end_try
;   catch
;     ...
; bb3:            <- Continuation BB
;   end
;
; CHECK-LABEL: remove_unnecessary_br:
define void @remove_unnecessary_br() personality ptr @__gxx_wasm_personality_v0 {
; CHECK: call foo
entry:
  invoke void @foo()
          to label %invoke.cont unwind label %catch.dispatch3

; CHECK: call foo
; CHECK-NOT: br
invoke.cont:                                      ; preds = %entry
  invoke void @foo()
          to label %try.cont8 unwind label %catch.dispatch

catch.dispatch:                                   ; preds = %invoke.cont
  %0 = catchswitch within none [label %catch.start] unwind label %catch.dispatch3

; CHECK: catch
catch.start:                                      ; preds = %catch.dispatch
  %1 = catchpad within %0 [ptr null]
  %2 = call ptr @llvm.wasm.get.exception(token %1)
  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
  %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
  invoke void @__cxa_end_catch() [ "funclet"(token %1) ]
          to label %invoke.cont2 unwind label %catch.dispatch3

catch.dispatch3:                                  ; preds = %catch.start, %catch.dispatch, %entry
  %5 = catchswitch within none [label %catch.start4] unwind to caller

catch.start4:                                     ; preds = %catch.dispatch3
  %6 = catchpad within %5 [ptr null]
  %7 = call ptr @llvm.wasm.get.exception(token %6)
  %8 = call i32 @llvm.wasm.get.ehselector(token %6)
  %9 = call ptr @__cxa_begin_catch(ptr %7) [ "funclet"(token %6) ]
  call void @__cxa_end_catch() [ "funclet"(token %6) ]
  catchret from %6 to label %try.cont8

try.cont8:                                        ; preds = %invoke.cont2, %catch.start4, %invoke.cont
  ret void

invoke.cont2:                                     ; preds = %catch.start
  catchret from %1 to label %try.cont8
}

; Here an exception is semantically contained in a loop. 'ehcleanup' BB belongs
; to the exception, but does not belong to the loop (because it does not have a
; path back to the loop header), and is placed after the loop latch block
; 'invoke.cont' intentionally. This tests if 'end_loop' marker is placed
; correctly not right after 'invoke.cont' part but after 'ehcleanup' part,
; NOSORT-LABEL: loop_contains_exception:
; NOSORT: loop
; NOSORT: try
; NOSORT: end_try
; NOSORT: end_loop
define void @loop_contains_exception(i32 %n) personality ptr @__gxx_wasm_personality_v0 {
entry:
  br label %while.cond

while.cond:                                       ; preds = %invoke.cont, %entry
  %n.addr.0 = phi i32 [ %n, %entry ], [ %dec, %invoke.cont ]
  %tobool = icmp ne i32 %n.addr.0, 0
  br i1 %tobool, label %while.body, label %while.end

while.body:                                       ; preds = %while.cond
  %dec = add nsw i32 %n.addr.0, -1
  invoke void @foo()
          to label %while.end unwind label %catch.dispatch

catch.dispatch:                                   ; preds = %while.body
  %0 = catchswitch within none [label %catch.start] unwind to caller

catch.start:                                      ; preds = %catch.dispatch
  %1 = catchpad within %0 [ptr null]
  %2 = call ptr @llvm.wasm.get.exception(token %1)
  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
  %4 = call ptr @__cxa_begin_catch(ptr %2) [ "funclet"(token %1) ]
  invoke void @__cxa_end_catch() [ "funclet"(token %1) ]
          to label %invoke.cont unwind label %ehcleanup

invoke.cont:                                      ; preds = %catch.start
  catchret from %1 to label %while.cond

ehcleanup:                                        ; preds = %catch.start
  %5 = cleanuppad within %1 []
  call void @_ZSt9terminatev() [ "funclet"(token %5) ]
  unreachable

while.end:                                        ; preds = %while.body, %while.cond
  ret void
}

; When the function return type is non-void and 'end' instructions are at the
; very end of a function, CFGStackify's fixEndsAtEndOfFunction function fixes
; the corresponding block/loop/try's type to match the function's return type.
; But when a `try`'s type is fixed, we should also check `end` instructions
; before its corresponding `catch_all`, because both `try` and `catch_all` body
; should satisfy the return type requirements.

; NOSORT-LABEL: fix_function_end_return_type_with_try_catch:
; NOSORT: try i32
; NOSORT: loop i32
; NOSORT: end_loop
; NOSORT: catch_all
; NOSORT: end_try
; NOSORT-NEXT: end_function
define i32 @fix_function_end_return_type_with_try_catch(i32 %n) personality ptr @__gxx_wasm_personality_v0 {
entry:
  %t = alloca %class.Object, align 1
  br label %for.cond

for.cond:                                         ; preds = %for.inc, %entry
  %i.0 = phi i32 [ 0, %entry ], [ %inc, %for.inc ]
  %cmp = icmp slt i32 %i.0, %n
  br label %for.body

for.body:                                         ; preds = %for.cond
  %div = sdiv i32 %n, 2
  %cmp1 = icmp eq i32 %i.0, %div
  br i1 %cmp1, label %if.then, label %for.inc

if.then:                                          ; preds = %for.body
  %call = invoke i32 @baz()
          to label %invoke.cont unwind label %ehcleanup

invoke.cont:                                      ; preds = %if.then
  %call2 = call ptr @_ZN6ObjectD2Ev(ptr %t)
  ret i32 %call

for.inc:                                          ; preds = %for.body
  %inc = add nsw i32 %i.0, 1
  br label %for.cond

ehcleanup:                                        ; preds = %if.then
  %0 = cleanuppad within none []
  %call3 = call ptr @_ZN6ObjectD2Ev(ptr %t) [ "funclet"(token %0) ]
  cleanupret from %0 unwind to caller
}

; This crashed when updating EHPadStack within fixCallUniwindMismatch had a bug.
; This should not crash and try-delegate has to be created around 'call @baz',
; because the initial TRY placement for 'call @quux' was done before 'call @baz'
; because 'call @baz''s return value is stackified.

; CHECK-LABEL: unwind_mismatches_5:
; CHECK: try
; CHECK:   try
; CHECK:     call $[[RET:[0-9]+]]=, baz
; CHECK:   delegate  1
; CHECK:    call  quux, $[[RET]]
; CHECK: catch_all
; CHECK: end_try
define void @unwind_mismatches_5() personality ptr @__gxx_wasm_personality_v0 {
entry:
  %call = call i32 @baz()
  invoke void @quux(i32 %call)
          to label %invoke.cont unwind label %ehcleanup

ehcleanup:                                        ; preds = %entry
  %0 = cleanuppad within none []
  cleanupret from %0 unwind to caller

invoke.cont:                                      ; preds = %entry
  unreachable
}

; This tests if invalidated branch destinations after fixing catch unwind
; mismatches are correctly remapped. For example, we have this code and suppose
; we need to wrap this try-catch-end in this code with a try-delegate to fix a
; catch unwind mismatch:
  ; - Before:
; block
;   br (a)
;   try
;   catch
;   end_try
; end_block
;           <- (a)
;
; - After
; block
;   br (a)
;   try
;     try
;     catch
;     end_try
;           <- (a)
;   delegate
; end_block
;           <- (b)
; After adding a try-delegate, the 'br's destination BB, where (a) points,
; becomes invalid because it incorrectly branches into an inner scope. The
; destination should change to the BB where (b) points.

; NOSORT-LABEL: branch_remapping_after_fixing_unwind_mismatches_0:
; NOSORT: try
; NOSORT:   br_if   0
define void @branch_remapping_after_fixing_unwind_mismatches_0(i1 %arg) personality ptr @__gxx_wasm_personality_v0 {
entry:
  br i1 %arg, label %bb0, label %dest

bb0:                                              ; preds = %entry
  invoke void @foo()
          to label %bb1 unwind label %catch.dispatch0

bb1:                                              ; preds = %bb0
  invoke void @bar()
          to label %try.cont unwind label %catch.dispatch1

catch.dispatch0:                                  ; preds = %bb0
  %0 = catchswitch within none [label %catch.start0] unwind to caller

catch.start0:                                     ; preds = %catch.dispatch0
  %1 = catchpad within %0 [ptr null]
  %2 = call ptr @llvm.wasm.get.exception(token %1)
  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
  catchret from %1 to label %try.cont

dest:                                             ; preds = %entry
  ret void

catch.dispatch1:                                  ; preds = %bb1
  %4 = catchswitch within none [label %catch.start1] unwind to caller

catch.start1:                                     ; preds = %catch.dispatch1
  %5 = catchpad within %4 [ptr null]
  %6 = call ptr @llvm.wasm.get.exception(token %5)
  %7 = call i32 @llvm.wasm.get.ehselector(token %5)
  catchret from %5 to label %try.cont

try.cont:                                         ; preds = %catch.start1, %catch.start0, %bb1
  ret void
}

; The similar case with branch_remapping_after_fixing_unwind_mismatches_0, but
; multiple consecutive delegates are generated:
; - Before:
; block
;   br (a)
;   try
;   catch
;   end_try
; end_block
;           <- (a)
;
; - After
; block
;   br (a)
;   try
;     ...
;     try
;       try
;       catch
;       end_try
;             <- (a)
;     delegate
;   delegate
; end_block
;           <- (b) The br destination should be remapped to here
;
; The test was reduced by bugpoint and should not crash in CFGStackify.
define void @branch_remapping_after_fixing_unwind_mismatches_1() personality ptr @__gxx_wasm_personality_v0 {
entry:
  br i1 undef, label %if.then, label %if.end12

if.then:                                          ; preds = %entry
  invoke void @__cxa_throw(ptr null, ptr null, ptr null) #1
          to label %unreachable unwind label %catch.dispatch

catch.dispatch:                                   ; preds = %if.then
  %0 = catchswitch within none [label %catch.start] unwind to caller

catch.start:                                      ; preds = %catch.dispatch
  %1 = catchpad within %0 [ptr @_ZTIi]
  %2 = call ptr @llvm.wasm.get.exception(token %1)
  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
  catchret from %1 to label %catchret.dest

catchret.dest:                                    ; preds = %catch.start
  invoke void @foo()
          to label %invoke.cont unwind label %catch.dispatch4

invoke.cont:                                      ; preds = %catchret.dest
  invoke void @__cxa_throw(ptr null, ptr null, ptr null) #1
          to label %unreachable unwind label %catch.dispatch4

catch.dispatch4:                                  ; preds = %invoke.cont, %catchret.dest
  %4 = catchswitch within none [label %catch.start5] unwind to caller

catch.start5:                                     ; preds = %catch.dispatch4
  %5 = catchpad within %4 [ptr @_ZTIi]
  %6 = call ptr @llvm.wasm.get.exception(token %5)
  %7 = call i32 @llvm.wasm.get.ehselector(token %5)
  unreachable

if.end12:                                         ; preds = %entry
  invoke void @foo()
          to label %invoke.cont14 unwind label %catch.dispatch16

catch.dispatch16:                                 ; preds = %if.end12
  %8 = catchswitch within none [label %catch.start17] unwind label %ehcleanup

catch.start17:                                    ; preds = %catch.dispatch16
  %9 = catchpad within %8 [ptr @_ZTIi]
  %10 = call ptr @llvm.wasm.get.exception(token %9)
  %11 = call i32 @llvm.wasm.get.ehselector(token %9)
  br i1 undef, label %catch20, label %rethrow19

catch20:                                          ; preds = %catch.start17
  catchret from %9 to label %catchret.dest22

catchret.dest22:                                  ; preds = %catch20
  br label %try.cont23

rethrow19:                                        ; preds = %catch.start17
  invoke void @llvm.wasm.rethrow() #1 [ "funclet"(token %9) ]
          to label %unreachable unwind label %ehcleanup

try.cont23:                                       ; preds = %invoke.cont14, %catchret.dest22
  invoke void @foo()
          to label %invoke.cont24 unwind label %ehcleanup

invoke.cont24:                                    ; preds = %try.cont23
  ret void

invoke.cont14:                                    ; preds = %if.end12
  br label %try.cont23

ehcleanup:                                        ; preds = %try.cont23, %rethrow19, %catch.dispatch16
  %12 = cleanuppad within none []
  cleanupret from %12 unwind to caller

unreachable:                                      ; preds = %rethrow19, %invoke.cont, %if.then
  unreachable
}

; Regression test for WasmEHFuncInfo's reverse mapping bug. 'UnwindDestToSrc'
; should return a vector and not a single BB, which was incorrect.
; This was reduced by bugpoint and should not crash in CFGStackify.
define void @wasm_eh_func_info_regression_test() personality ptr @__gxx_wasm_personality_v0 {
entry:
  invoke void @foo()
          to label %invoke.cont unwind label %catch.dispatch

catch.dispatch:                                   ; preds = %entry
  %0 = catchswitch within none [label %catch.start] unwind label %ehcleanup22

catch.start:                                      ; preds = %catch.dispatch
  %1 = catchpad within %0 [ptr @_ZTIi]
  %2 = call ptr @llvm.wasm.get.exception(token %1)
  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
  invoke void @__cxa_throw(ptr null, ptr null, ptr null) #1 [ "funclet"(token %1) ]
          to label %unreachable unwind label %catch.dispatch2

catch.dispatch2:                                  ; preds = %catch.start
  %4 = catchswitch within %1 [label %catch.start3] unwind label %ehcleanup

catch.start3:                                     ; preds = %catch.dispatch2
  %5 = catchpad within %4 [ptr @_ZTIi]
  %6 = call ptr @llvm.wasm.get.exception(token %5)
  %7 = call i32 @llvm.wasm.get.ehselector(token %5)
  catchret from %5 to label %try.cont

try.cont:                                         ; preds = %catch.start3
  invoke void @foo() [ "funclet"(token %1) ]
          to label %invoke.cont8 unwind label %ehcleanup

invoke.cont8:                                     ; preds = %try.cont
  invoke void @__cxa_throw(ptr null, ptr null, ptr null) #1 [ "funclet"(token %1) ]
          to label %unreachable unwind label %catch.dispatch11

catch.dispatch11:                                 ; preds = %invoke.cont8
  %8 = catchswitch within %1 [label %catch.start12] unwind label %ehcleanup

catch.start12:                                    ; preds = %catch.dispatch11
  %9 = catchpad within %8 [ptr @_ZTIi]
  %10 = call ptr @llvm.wasm.get.exception(token %9)
  %11 = call i32 @llvm.wasm.get.ehselector(token %9)
  unreachable

invoke.cont:                                      ; preds = %entry
  unreachable

ehcleanup:                                        ; preds = %catch.dispatch11, %try.cont, %catch.dispatch2
  %12 = cleanuppad within %1 []
  cleanupret from %12 unwind label %ehcleanup22

ehcleanup22:                                      ; preds = %ehcleanup, %catch.dispatch
  %13 = cleanuppad within none []
  cleanupret from %13 unwind to caller

unreachable:                                      ; preds = %invoke.cont8, %catch.start
  unreachable
}

; void exception_grouping_0() {
;   try {
;     try {
;       throw 0;
;     } catch (int) {
;     }
;   } catch (int) {
;   }
; }
;
; Regression test for a WebAssemblyException grouping bug. After catchswitches
; are removed, EH pad catch.start2 is dominated by catch.start, but because
; catch.start2 is the unwind destination of catch.start, it should not be
; included in catch.start's exception. Also, after we take catch.start2's
; exception out of catch.start's exception, we have to take out try.cont8 out of
; catch.start's exception, because it has a predecessor in catch.start2.
define void @exception_grouping_0() personality ptr @__gxx_wasm_personality_v0 {
entry:
  %exception = call ptr @__cxa_allocate_exception(i32 4) #0
  store i32 0, ptr %exception, align 16
  invoke void @__cxa_throw(ptr %exception, ptr @_ZTIi, ptr null) #1
          to label %unreachable unwind label %catch.dispatch

catch.dispatch:                                   ; preds = %entry
  %0 = catchswitch within none [label %catch.start] unwind label %catch.dispatch1

catch.start:                                      ; preds = %catch.dispatch
  %1 = catchpad within %0 [ptr @_ZTIi]
  %2 = call ptr @llvm.wasm.get.exception(token %1)
  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
  %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #0
  %matches = icmp eq i32 %3, %4
  br i1 %matches, label %catch, label %rethrow

catch:                                            ; preds = %catch.start
  %5 = call ptr @__cxa_begin_catch(ptr %2) #0 [ "funclet"(token %1) ]
  %6 = load i32, ptr %5, align 4
  call void @__cxa_end_catch() #0 [ "funclet"(token %1) ]
  catchret from %1 to label %catchret.dest

catchret.dest:                                    ; preds = %catch
  br label %try.cont

rethrow:                                          ; preds = %catch.start
  invoke void @llvm.wasm.rethrow() #1 [ "funclet"(token %1) ]
          to label %unreachable unwind label %catch.dispatch1

catch.dispatch1:                                  ; preds = %rethrow, %catch.dispatch
  %7 = catchswitch within none [label %catch.start2] unwind to caller

catch.start2:                                     ; preds = %catch.dispatch1
  %8 = catchpad within %7 [ptr @_ZTIi]
  %9 = call ptr @llvm.wasm.get.exception(token %8)
  %10 = call i32 @llvm.wasm.get.ehselector(token %8)
  %11 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #0
  %matches3 = icmp eq i32 %10, %11
  br i1 %matches3, label %catch5, label %rethrow4

catch5:                                           ; preds = %catch.start2
  %12 = call ptr @__cxa_begin_catch(ptr %9) #0 [ "funclet"(token %8) ]
  %13 = load i32, ptr %12, align 4
  call void @__cxa_end_catch() #0 [ "funclet"(token %8) ]
  catchret from %8 to label %catchret.dest7

catchret.dest7:                                   ; preds = %catch5
  br label %try.cont8

rethrow4:                                         ; preds = %catch.start2
  call void @llvm.wasm.rethrow() #1 [ "funclet"(token %8) ]
  unreachable

try.cont8:                                        ; preds = %try.cont, %catchret.dest7
  ret void

try.cont:                                         ; preds = %catchret.dest
  br label %try.cont8

unreachable:                                      ; preds = %rethrow, %entry
  unreachable
}

; Test for WebAssemblyException grouping. This test is hand-modified to generate
; this structure:
; catch.start dominates catch.start4 and catch.start4 dominates catch.start12,
; so the after dominator-based grouping, we end up with:
; catch.start's exception > catch4.start's exception > catch12.start's exception
; (> here represents subexception relationship)
;
; But the unwind destination chain is catch.start -> catch.start4 ->
; catch.start12. So all these subexception relationship should be deconstructed.
; We have to make sure to take out catch.start4's exception out of catch.start's
; exception first, before taking out catch.start12's exception out of
; catch.start4's exception; otherwise we end up with an incorrect relationship
; of catch.start's exception > catch.start12's exception.
define void @exception_grouping_1() personality ptr @__gxx_wasm_personality_v0 {
entry:
  invoke void @foo()
          to label %invoke.cont unwind label %catch.dispatch

invoke.cont:                                      ; preds = %entry
  invoke void @foo()
          to label %invoke.cont1 unwind label %catch.dispatch

invoke.cont1:                                     ; preds = %invoke.cont
  invoke void @foo()
          to label %try.cont18 unwind label %catch.dispatch

catch.dispatch11:                                 ; preds = %rethrow6, %catch.dispatch3
  %0 = catchswitch within none [label %catch.start12] unwind to caller

catch.start12:                                    ; preds = %catch.dispatch11
  %1 = catchpad within %0 [ptr @_ZTIi]
  %2 = call ptr @llvm.wasm.get.exception(token %1)
  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
  %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #0
  %matches13 = icmp eq i32 %3, %4
  br i1 %matches13, label %catch15, label %rethrow14

catch15:                                          ; preds = %catch.start12
  %5 = call ptr @__cxa_begin_catch(ptr %2) #0 [ "funclet"(token %1) ]
  %6 = load i32, ptr %5, align 4
  call void @__cxa_end_catch() #0 [ "funclet"(token %1) ]
  catchret from %1 to label %try.cont18

rethrow14:                                        ; preds = %catch.start12
  call void @llvm.wasm.rethrow() #1 [ "funclet"(token %1) ]
  unreachable

catch.dispatch3:                                  ; preds = %rethrow, %catch.dispatch
  %7 = catchswitch within none [label %catch.start4] unwind label %catch.dispatch11

catch.start4:                                     ; preds = %catch.dispatch3
  %8 = catchpad within %7 [ptr @_ZTIi]
  %9 = call ptr @llvm.wasm.get.exception(token %8)
  %10 = call i32 @llvm.wasm.get.ehselector(token %8)
  %11 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #0
  %matches5 = icmp eq i32 %10, %11
  br i1 %matches5, label %catch7, label %rethrow6

catch7:                                           ; preds = %catch.start4
  %12 = call ptr @__cxa_begin_catch(ptr %9) #0 [ "funclet"(token %8) ]
  %13 = load i32, ptr %12, align 4
  call void @__cxa_end_catch() #0 [ "funclet"(token %8) ]
  catchret from %8 to label %try.cont18

rethrow6:                                         ; preds = %catch.start4
  invoke void @llvm.wasm.rethrow() #1 [ "funclet"(token %8) ]
          to label %unreachable unwind label %catch.dispatch11

catch.dispatch:                                   ; preds = %invoke.cont1, %invoke.cont, %entry
  %14 = catchswitch within none [label %catch.start] unwind label %catch.dispatch3

catch.start:                                      ; preds = %catch.dispatch
  %15 = catchpad within %14 [ptr @_ZTIi]
  %16 = call ptr @llvm.wasm.get.exception(token %15)
  %17 = call i32 @llvm.wasm.get.ehselector(token %15)
  %18 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #0
  %matches = icmp eq i32 %17, %18
  br i1 %matches, label %catch, label %rethrow

catch:                                            ; preds = %catch.start
  %19 = call ptr @__cxa_begin_catch(ptr %16) #0 [ "funclet"(token %15) ]
  %20 = load i32, ptr %19, align 4
  call void @__cxa_end_catch() #0 [ "funclet"(token %15) ]
  catchret from %15 to label %try.cont18

rethrow:                                          ; preds = %catch.start
  invoke void @llvm.wasm.rethrow() #1 [ "funclet"(token %15) ]
          to label %unreachable unwind label %catch.dispatch3

try.cont18:                                       ; preds = %catch, %catch7, %catch15, %invoke.cont1
  ret void

unreachable:                                      ; preds = %rethrow, %rethrow6
  unreachable
}

; void exception_grouping_2() {
;   try {
;     try {
;       throw 0;
;     } catch (int) { // (a)
;     }
;   } catch (int) {   // (b)
;   }
;   try {
;     foo();
;   } catch (int) {   // (c)
;   }
; }
;
; Regression test for an ExceptionInfo grouping bug. Because the first (inner)
; try always throws, both EH pads (b) (catch.start2) and (c) (catch.start10) are
; dominated by EH pad (a) (catch.start), even though they are not semantically
; contained in (a)'s exception. Because (a)'s unwind destination is (b), (b)'s
; exception is taken out of (a)'s. But because (c) is reachable from (b), we
; should make sure to take out (c)'s exception out of (a)'s exception too.
define void @exception_grouping_2() personality ptr @__gxx_wasm_personality_v0 {
entry:
  %exception = call ptr @__cxa_allocate_exception(i32 4) #1
  store i32 0, ptr %exception, align 16
  invoke void @__cxa_throw(ptr %exception, ptr @_ZTIi, ptr null) #3
          to label %unreachable unwind label %catch.dispatch

catch.dispatch:                                   ; preds = %entry
  %0 = catchswitch within none [label %catch.start] unwind label %catch.dispatch1

catch.start:                                      ; preds = %catch.dispatch
  %1 = catchpad within %0 [ptr @_ZTIi]
  %2 = call ptr @llvm.wasm.get.exception(token %1)
  %3 = call i32 @llvm.wasm.get.ehselector(token %1)
  %4 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #1
  %matches = icmp eq i32 %3, %4
  br i1 %matches, label %catch, label %rethrow

catch:                                            ; preds = %catch.start
  %5 = call ptr @__cxa_begin_catch(ptr %2) #1 [ "funclet"(token %1) ]
  %6 = load i32, ptr %5, align 4
  call void @__cxa_end_catch() #1 [ "funclet"(token %1) ]
  catchret from %1 to label %try.cont8

rethrow:                                          ; preds = %catch.start
  invoke void @llvm.wasm.rethrow() #3 [ "funclet"(token %1) ]
          to label %unreachable unwind label %catch.dispatch1

catch.dispatch1:                                  ; preds = %rethrow, %catch.dispatch
  %7 = catchswitch within none [label %catch.start2] unwind to caller

catch.start2:                                     ; preds = %catch.dispatch1
  %8 = catchpad within %7 [ptr @_ZTIi]
  %9 = call ptr @llvm.wasm.get.exception(token %8)
  %10 = call i32 @llvm.wasm.get.ehselector(token %8)
  %11 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #1
  %matches3 = icmp eq i32 %10, %11
  br i1 %matches3, label %catch5, label %rethrow4

catch5:                                           ; preds = %catch.start2
  %12 = call ptr @__cxa_begin_catch(ptr %9) #1 [ "funclet"(token %8) ]
  %13 = load i32, ptr %12, align 4
  call void @__cxa_end_catch() #1 [ "funclet"(token %8) ]
  catchret from %8 to label %try.cont8

rethrow4:                                         ; preds = %catch.start2
  call void @llvm.wasm.rethrow() #3 [ "funclet"(token %8) ]
  unreachable

try.cont8:                                        ; preds = %catch, %catch5
  invoke void @foo()
          to label %try.cont16 unwind label %catch.dispatch9

catch.dispatch9:                                  ; preds = %try.cont8
  %14 = catchswitch within none [label %catch.start10] unwind to caller

catch.start10:                                    ; preds = %catch.dispatch9
  %15 = catchpad within %14 [ptr @_ZTIi]
  %16 = call ptr @llvm.wasm.get.exception(token %15)
  %17 = call i32 @llvm.wasm.get.ehselector(token %15)
  %18 = call i32 @llvm.eh.typeid.for(ptr @_ZTIi) #1
  %matches11 = icmp eq i32 %17, %18
  br i1 %matches11, label %catch13, label %rethrow12

catch13:                                          ; preds = %catch.start10
  %19 = call ptr @__cxa_begin_catch(ptr %16) #1 [ "funclet"(token %15) ]
  %20 = load i32, ptr %19, align 4
  call void @__cxa_end_catch() #1 [ "funclet"(token %15) ]
  catchret from %15 to label %try.cont16

rethrow12:                                        ; preds = %catch.start10
  call void @llvm.wasm.rethrow() #3 [ "funclet"(token %15) ]
  unreachable

try.cont16:                                       ; preds = %try.cont8, %catch13
  ret void

unreachable:                                      ; preds = %rethrow, %entry
  unreachable
}

; Check if the unwind destination mismatch stats are correct
; NOSORT: 23 wasm-cfg-stackify    - Number of call unwind mismatches found
; NOSORT:  4 wasm-cfg-stackify    - Number of catch unwind mismatches found

declare void @foo()
declare void @bar()
declare i32 @baz()
declare i32 @qux(i32)
declare void @quux(i32)
declare void @fun(i32)
; Function Attrs: nounwind
declare void @nothrow(i32) #0
; Function Attrs: nounwind
declare i32 @nothrow_i32() #0

; Function Attrs: nounwind
declare ptr @_ZN6ObjectD2Ev(ptr returned) #0
@_ZTI7MyClass = external constant { ptr, ptr }, align 4
; Function Attrs: nounwind
declare ptr @_ZN7MyClassD2Ev(ptr returned) #0
; Function Attrs: nounwind
declare ptr @_ZN7MyClassC2ERKS_(ptr returned, ptr dereferenceable(4)) #0

declare i32 @__gxx_wasm_personality_v0(...)
; Function Attrs: nounwind
declare ptr @llvm.wasm.get.exception(token) #0
; Function Attrs: nounwind
declare i32 @llvm.wasm.get.ehselector(token) #0
declare ptr @__cxa_allocate_exception(i32) #0
declare void @__cxa_throw(ptr, ptr, ptr)
; Function Attrs: noreturn
declare void @llvm.wasm.rethrow() #1
; Function Attrs: nounwind
declare i32 @llvm.eh.typeid.for(ptr) #0

declare ptr @__cxa_begin_catch(ptr)
declare void @__cxa_end_catch()
declare ptr @__cxa_get_exception_ptr(ptr)
declare void @_ZSt9terminatev()
; Function Attrs: nounwind
declare void @llvm.memcpy.p0.p0.i32(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i32, i1 immarg) #0
; Function Attrs: nounwind
declare void @llvm.memmove.p0.p0.i32(ptr nocapture, ptr nocapture readonly, i32, i1 immarg) #0
; Function Attrs: nounwind
declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1 immarg) #0

attributes #0 = { nounwind }
attributes #1 = { noreturn }