;; 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 $pop-test (type $1)
  ;; CHECK-NEXT:  (block $folding-inner0
  ;; CHECK-NEXT:   (try
  ;; CHECK-NEXT:    (do
  ;; CHECK-NEXT:     (try
  ;; CHECK-NEXT:      (do
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:      (catch $e-i32
  ;; CHECK-NEXT:       (drop
  ;; CHECK-NEXT:        (pop i32)
  ;; CHECK-NEXT:       )
  ;; CHECK-NEXT:       (br $folding-inner0)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (catch $e-i32
  ;; CHECK-NEXT:     (drop
  ;; CHECK-NEXT:      (pop i32)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (br $folding-inner0)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (return)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (i32.const 111)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (i32.const 222)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (i32.const 333)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (unreachable)
  ;; CHECK-NEXT: )
  (func $pop-test
    (try
      (do
        (try
          (do)
          (catch $e-i32
            ;; Expressions containing a pop should NOT be taken out and folded.
            (drop (pop i32))
            (drop (i32.const 111))
            (drop (i32.const 222))
            (drop (i32.const 333))
            (unreachable)
          )
        )
      )
      (catch $e-i32
        (drop (pop i32))
        (drop (i32.const 111))
        (drop (i32.const 222))
        (drop (i32.const 333))
        (unreachable)
      )
    )
  )

  ;; CHECK:      (func $try-call-optimize-terminating-tails-success (type $0) (result i32)
  ;; CHECK-NEXT:  (block $folding-inner0
  ;; CHECK-NEXT:   (return
  ;; CHECK-NEXT:    (block (result i32)
  ;; CHECK-NEXT:     (try
  ;; CHECK-NEXT:      (do
  ;; CHECK-NEXT:       (br $folding-inner0)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:      (catch_all
  ;; CHECK-NEXT:       (br $folding-inner0)
  ;; CHECK-NEXT:      )
  ;; 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-call-optimize-terminating-tails-success (result i32)
    (try
      (do
        ;; Expressions that cannot throw can be taken out of 'try' scope.
        (drop (i32.const 1))
        (drop (i32.const 1))
        (return (i32.const 0))
      )
      (catch_all
        (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-call-optimize-terminating-tails (type $0) (result i32)
  ;; CHECK-NEXT:  (try
  ;; CHECK-NEXT:   (do
  ;; 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:   (catch_all
  ;; 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:  )
  ;; CHECK-NEXT:  (i32.const 0)
  ;; CHECK-NEXT: )
  (func $try-call-optimize-terminating-tails (result i32)
    (try
      (do
        ;; Expressions that can throw should NOT be taken out of 'try' scope.
        (call $foo)
        (call $foo)
        (call $foo)
        (call $foo)
        (return (i32.const 0))
      )
      (catch_all
        (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-call-optimize-terminating-tails-call-return (type $0) (result i32)
  ;; CHECK-NEXT:  (try
  ;; CHECK-NEXT:   (do
  ;; 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:   (catch_all
  ;; 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:  )
  ;; CHECK-NEXT:  (i32.const 0)
  ;; CHECK-NEXT: )
  (func $try-call-optimize-terminating-tails-call-return (result i32)
    (try
      (do
        (drop (i32.const 1))
        (drop (i32.const 1))
        ;; Cannot be folded out of the try because it might throw.
        (return (call $foo-i32))
      )
      (catch_all
        (drop (i32.const 1))
        (drop (i32.const 1))
        (return (call $foo-i32))
      )
    )
    (i32.const 0)
  )

  ;; CHECK:      (func $try-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:     (try
  ;; CHECK-NEXT:      (do
  ;; CHECK-NEXT:       (br $folding-inner0)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:      (catch_all
  ;; CHECK-NEXT:       (br $folding-inner0)
  ;; CHECK-NEXT:      )
  ;; 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-call-optimize-terminating-tails-return-call (result i32)
    (try
      (do
        (drop (i32.const 1))
        (drop (i32.const 1))
        (drop (i32.const 1))
        ;; return_call executes the call after returning from this function.
        ;; This try cannot catch exceptions it throws, so we can fold it out of
        ;; the try.
        (return_call $foo-i32)
      )
      (catch_all
        (drop (i32.const 1))
        (drop (i32.const 1))
        (drop (i32.const 1))
        (return_call $foo-i32)
      )
    )
    (i32.const 0)
  )

  ;; CHECK:      (func $try-call-optimize-expression-tails-success (type $1)
  ;; CHECK-NEXT:  (block $x
  ;; CHECK-NEXT:   (try
  ;; CHECK-NEXT:    (do
  ;; CHECK-NEXT:     (br $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (catch_all
  ;; CHECK-NEXT:     (br $x)
  ;; CHECK-NEXT:    )
  ;; 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-call-optimize-expression-tails-success
    (block $x
      (try
        (do
          ;; Expressions that cannot throw can be taken out of 'try' scope.
          (drop (i32.const 1))
          (drop (i32.const 1))
          (drop (i32.const 1))
          (br $x)
        )
        (catch_all
          (drop (i32.const 1))
          (drop (i32.const 1))
          (drop (i32.const 1))
          (br $x)
        )
      )
      (unreachable)
    )
  )

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

  ;; CHECK:      (func $if-arms-in-catch (type $0) (result i32)
  ;; CHECK-NEXT:  (local $0 i32)
  ;; CHECK-NEXT:  (try
  ;; CHECK-NEXT:   (do
  ;; CHECK-NEXT:    (unreachable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (catch $e-i32
  ;; CHECK-NEXT:    (local.set $0
  ;; CHECK-NEXT:     (pop i32)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (block
  ;; CHECK-NEXT:     (block
  ;; CHECK-NEXT:      (drop
  ;; CHECK-NEXT:       (local.get $0)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:      (drop
  ;; CHECK-NEXT:       (i32.eqz
  ;; CHECK-NEXT:        (i32.const 1)
  ;; CHECK-NEXT:       )
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (unreachable)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $if-arms-in-catch (result i32)
    (try
      (do
        (unreachable)
      )
      (catch $e-i32
        ;; These if arms can be folded, after which the if is replaced by a
        ;; block, so we need a fixup for the pop.
        (if
          (pop i32)
          (then
            (drop
              (i32.eqz
                (i32.const 1)
              )
            )
          )
          (else
            (drop
              (i32.eqz
                (i32.const 1)
              )
            )
          )
        )
        (unreachable)
      )
    )
  )
)