;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
;; RUN: wasm-opt %s --remove-unused-names --code-folding -all -S -o - \
;; RUN:   | filecheck %s

(module
  ;; CHECK:      (tag $e-i32 (param i32))
  (tag $e-i32 (param i32))

  ;; CHECK:      (func $try_table-call-optimize-terminating-tails-success (type $0) (result i32)
  ;; CHECK-NEXT:  (block $folding-inner0
  ;; CHECK-NEXT:   (return
  ;; CHECK-NEXT:    (block (result i32)
  ;; CHECK-NEXT:     (block $tryend
  ;; CHECK-NEXT:      (block $catch
  ;; CHECK-NEXT:       (try_table (catch_all $catch)
  ;; CHECK-NEXT:        (br $folding-inner0)
  ;; CHECK-NEXT:       )
  ;; CHECK-NEXT:       (br $tryend)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:      (br $folding-inner0)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (i32.const 0)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (return
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $try_table-call-optimize-terminating-tails-success (result i32)
    (block $tryend
      (block $catch
        (try_table (catch_all $catch)
          ;; Expressions that cannot throw can be taken out of 'try' scope.
          (drop (i32.const 1))
          (drop (i32.const 1))
          (return (i32.const 0))
        )
        (br $tryend)
      )
      (drop (i32.const 1))
      (drop (i32.const 1))
      (return (i32.const 0))
    )
    (i32.const 0)
  )


  ;; CHECK:      (func $foo (type $1)
  ;; CHECK-NEXT: )
  (func $foo)

  ;; CHECK:      (func $try_table-call-optimize-terminating-tails (type $0) (result i32)
  ;; CHECK-NEXT:  (block $tryend
  ;; CHECK-NEXT:   (block $catch
  ;; CHECK-NEXT:    (try_table (catch_all $catch)
  ;; CHECK-NEXT:     (call $foo)
  ;; CHECK-NEXT:     (call $foo)
  ;; CHECK-NEXT:     (call $foo)
  ;; CHECK-NEXT:     (call $foo)
  ;; CHECK-NEXT:     (return
  ;; CHECK-NEXT:      (i32.const 0)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (br $tryend)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (call $foo)
  ;; CHECK-NEXT:   (call $foo)
  ;; CHECK-NEXT:   (call $foo)
  ;; CHECK-NEXT:   (call $foo)
  ;; CHECK-NEXT:   (return
  ;; CHECK-NEXT:    (i32.const 0)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (i32.const 0)
  ;; CHECK-NEXT: )
  (func $try_table-call-optimize-terminating-tails (result i32)
    (block $tryend
      (block $catch
        (try_table (catch_all $catch)
          ;; Expressions that can throw should NOT be taken out of 'try' scope.
          (call $foo)
          (call $foo)
          (call $foo)
          (call $foo)
          (return (i32.const 0))
        )
        (br $tryend)
      )
      (call $foo)
      (call $foo)
      (call $foo)
      (call $foo)
      (return (i32.const 0))
    )
    (i32.const 0)
  )

  ;; CHECK:      (func $foo-i32 (type $0) (result i32)
  ;; CHECK-NEXT:  (i32.const 0)
  ;; CHECK-NEXT: )
  (func $foo-i32 (result i32)
    (i32.const 0)
  )

  ;; CHECK:      (func $try_table-call-optimize-terminating-tails-call-return (type $0) (result i32)
  ;; CHECK-NEXT:  (block $tryend
  ;; CHECK-NEXT:   (block $catch
  ;; CHECK-NEXT:    (try_table (catch_all $catch)
  ;; CHECK-NEXT:     (drop
  ;; CHECK-NEXT:      (i32.const 1)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (drop
  ;; CHECK-NEXT:      (i32.const 1)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (return
  ;; CHECK-NEXT:      (call $foo-i32)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (br $tryend)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (drop
  ;; CHECK-NEXT:    (i32.const 1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (drop
  ;; CHECK-NEXT:    (i32.const 1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (return
  ;; CHECK-NEXT:    (call $foo-i32)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (i32.const 0)
  ;; CHECK-NEXT: )
  (func $try_table-call-optimize-terminating-tails-call-return (result i32)
    (block $tryend
      (block $catch
        (try_table (catch_all $catch)
          (drop (i32.const 1))
          (drop (i32.const 1))
          ;; Cannot be folded out of the try because it might throw.
          (return (call $foo-i32))
        )
        (br $tryend)
      )
      (drop (i32.const 1))
      (drop (i32.const 1))
      (return (call $foo-i32))
    )
    (i32.const 0)
  )

  ;; CHECK:      (func $try_table-call-optimize-terminating-tails-return-call (type $0) (result i32)
  ;; CHECK-NEXT:  (block $folding-inner0
  ;; CHECK-NEXT:   (return
  ;; CHECK-NEXT:    (block (result i32)
  ;; CHECK-NEXT:     (block $tryend
  ;; CHECK-NEXT:      (block $catch
  ;; CHECK-NEXT:       (try_table (catch_all $catch)
  ;; CHECK-NEXT:        (br $folding-inner0)
  ;; CHECK-NEXT:       )
  ;; CHECK-NEXT:       (br $tryend)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:      (br $folding-inner0)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (i32.const 0)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (return_call $foo-i32)
  ;; CHECK-NEXT: )
  (func $try_table-call-optimize-terminating-tails-return-call (result i32)
    (block $tryend
      (block $catch
        (try_table (catch_all $catch)
          (drop (i32.const 1))
          (drop (i32.const 1))
          (drop (i32.const 1))
          ;; return_call executes the call after returning from this function.
          ;; This try_table cannot catch exceptions it throws, so we can fold it
          ;; out of the try_table.
          (return_call $foo-i32)
        )
        (br $tryend)
      )
      (drop (i32.const 1))
      (drop (i32.const 1))
      (drop (i32.const 1))
      (return_call $foo-i32)
    )
    (i32.const 0)
  )

  ;; CHECK:      (func $try_table-call-optimize-expression-tails-success (type $1)
  ;; CHECK-NEXT:  (block $x
  ;; CHECK-NEXT:   (block $tryend
  ;; CHECK-NEXT:    (block $catch
  ;; CHECK-NEXT:     (try_table (catch_all $catch)
  ;; CHECK-NEXT:      (br $x)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (br $tryend)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (br $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (unreachable)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $try_table-call-optimize-expression-tails-success
    (block $x
      (block $tryend
        (block $catch
          (try_table (catch_all $catch)
            ;; Expressions that cannot throw can be taken out of 'try_table'
            ;; scope.
            (drop (i32.const 1))
            (drop (i32.const 1))
            (drop (i32.const 1))
            (br $x)
          )
          (br $tryend)
        )
        (drop (i32.const 1))
        (drop (i32.const 1))
        (drop (i32.const 1))
        (br $x)
      )
      (unreachable)
    )
  )

  ;; CHECK:      (func $try_table-call-optimize-expression-tails (type $1)
  ;; CHECK-NEXT:  (block $x
  ;; CHECK-NEXT:   (block $tryend
  ;; CHECK-NEXT:    (block $catch
  ;; CHECK-NEXT:     (try_table (catch_all $catch)
  ;; CHECK-NEXT:      (call $foo)
  ;; CHECK-NEXT:      (call $foo)
  ;; CHECK-NEXT:      (call $foo)
  ;; CHECK-NEXT:      (br $x)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (br $tryend)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (call $foo)
  ;; CHECK-NEXT:    (call $foo)
  ;; CHECK-NEXT:    (call $foo)
  ;; CHECK-NEXT:    (br $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (unreachable)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $try_table-call-optimize-expression-tails
    (block $x
      (block $tryend
        (block $catch
          (try_table (catch_all $catch)
            ;; Expressions that can throw should NOT be taken out of 'try' scope.
            (call $foo)
            (call $foo)
            (call $foo)
            (br $x)
          )
          (br $tryend)
        )
        (call $foo)
        (call $foo)
        (call $foo)
        (br $x)
      )
      (unreachable)
    )
  )
)