;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.

;; Run in both TNH and non-TNH mode.

;; RUN: wasm-opt %s --vacuum --traps-never-happen -all -S -o - | filecheck %s --check-prefix=YESTNH
;; RUN: wasm-opt %s --vacuum                      -all -S -o - | filecheck %s --check-prefix=NO_TNH

(module
  ;; YESTNH:      (type $struct (struct (field (mut i32))))

  ;; YESTNH:      (tag $tag (type $1) (param i32))
  ;; NO_TNH:      (type $struct (struct (field (mut i32))))

  ;; NO_TNH:      (tag $tag (type $2) (param i32))
  (tag $tag (param i32))

  (memory 1 1 shared)

  (type $struct (struct (field (mut i32))))

  ;; YESTNH:      (func $drop (type $4) (param $x i32) (param $y anyref)
  ;; YESTNH-NEXT:  (drop
  ;; YESTNH-NEXT:   (unreachable)
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $drop (type $4) (param $x i32) (param $y anyref)
  ;; NO_TNH-NEXT:  (drop
  ;; NO_TNH-NEXT:   (i32.load
  ;; NO_TNH-NEXT:    (local.get $x)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (drop
  ;; NO_TNH-NEXT:   (ref.as_non_null
  ;; NO_TNH-NEXT:    (local.get $y)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (drop
  ;; NO_TNH-NEXT:   (ref.cast i31ref
  ;; NO_TNH-NEXT:    (local.get $y)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (drop
  ;; NO_TNH-NEXT:   (unreachable)
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $drop (param $x i32) (param $y anyref)
    ;; A load might trap, normally, but if traps never happen then we can
    ;; remove it.
    (drop
      (i32.load (local.get $x))
    )

    ;; A trap on a null value can also be ignored.
    (drop
      (ref.as_non_null
        (local.get $y)
      )
    )

    ;; Other casts as well.
    (drop
      (ref.cast i31ref
        (local.get $y)
      )
    )

    ;; Ignore unreachable code. Note that we leave this dropped unreachable in
    ;; the final output - in TNH it is valid to turn it into a nop, but we do
    ;; not remove unreachables, to let them propagate to callers.
    (drop
      (unreachable)
    )
  )

  ;; Other side effects prevent us making any changes.
  ;; YESTNH:      (func $other-side-effects (type $3) (param $x i32) (result i32)
  ;; YESTNH-NEXT:  (drop
  ;; YESTNH-NEXT:   (call $other-side-effects
  ;; YESTNH-NEXT:    (i32.const 1)
  ;; YESTNH-NEXT:   )
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT:  (local.set $x
  ;; YESTNH-NEXT:   (i32.const 2)
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT:  (i32.const 1)
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $other-side-effects (type $3) (param $x i32) (result i32)
  ;; NO_TNH-NEXT:  (drop
  ;; NO_TNH-NEXT:   (call $other-side-effects
  ;; NO_TNH-NEXT:    (i32.const 1)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (drop
  ;; NO_TNH-NEXT:   (block (result i32)
  ;; NO_TNH-NEXT:    (local.set $x
  ;; NO_TNH-NEXT:     (i32.const 2)
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:    (i32.load
  ;; NO_TNH-NEXT:     (local.get $x)
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (i32.const 1)
  ;; NO_TNH-NEXT: )
  (func $other-side-effects (param $x i32) (result i32)
    ;; A call has all manner of other side effects.
    (drop
      (call $other-side-effects (i32.const 1))
    )

    ;; Add to the load an additional specific side effect, of writing to a
    ;; local. We can remove the load, but not the write to a local.
    (drop
      (block (result i32)
        (local.set $x (i32.const 2))
        (i32.load (local.get $x))
      )
    )

    (i32.const 1)
  )

  ;; A helper function for the above, that returns nothing.
  ;; YESTNH:      (func $return-nothing (type $0)
  ;; YESTNH-NEXT:  (nop)
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $return-nothing (type $0)
  ;; NO_TNH-NEXT:  (nop)
  ;; NO_TNH-NEXT: )
  (func $return-nothing)

  ;; YESTNH:      (func $partial (type $5) (param $x (ref $struct)) (result (ref null $struct))
  ;; YESTNH-NEXT:  (local $y (ref null $struct))
  ;; YESTNH-NEXT:  (local.set $y
  ;; YESTNH-NEXT:   (local.get $x)
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT:  (local.set $y
  ;; YESTNH-NEXT:   (local.get $x)
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT:  (local.get $y)
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $partial (type $5) (param $x (ref $struct)) (result (ref null $struct))
  ;; NO_TNH-NEXT:  (local $y (ref null $struct))
  ;; NO_TNH-NEXT:  (drop
  ;; NO_TNH-NEXT:   (struct.get $struct 0
  ;; NO_TNH-NEXT:    (local.tee $y
  ;; NO_TNH-NEXT:     (local.get $x)
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (drop
  ;; NO_TNH-NEXT:   (struct.get $struct 0
  ;; NO_TNH-NEXT:    (local.tee $y
  ;; NO_TNH-NEXT:     (local.get $x)
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (local.get $y)
  ;; NO_TNH-NEXT: )
  (func $partial (param $x (ref $struct)) (result (ref null $struct))
    (local $y (ref null $struct))
    ;; The struct.get's side effect can be ignored due to tnh, and the value is
    ;; dropped anyhow, so we can remove it. We cannot remove the local.tee
    ;; inside it, however, so we must only vacuum out the struct.get and
    ;; nothing more. (In addition, a drop of a tee will become a set.)
    (drop
      (struct.get $struct 0
        (local.tee $y
          (local.get $x)
        )
      )
    )
    ;; Similar, but with an eqz on the outside, which can also be removed.
    (drop
      (i32.eqz
        (struct.get $struct 0
          (local.tee $y
            (local.get $x)
          )
        )
      )
    )
    (local.get $y)
  )

  ;; YESTNH:      (func $toplevel (type $0)
  ;; YESTNH-NEXT:  (unreachable)
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $toplevel (type $0)
  ;; NO_TNH-NEXT:  (unreachable)
  ;; NO_TNH-NEXT: )
  (func $toplevel
    ;; A removable side effect at the top level of a function. We can turn this
    ;; into a nop, but leave it as unreachable even in TNH, so that it can
    ;; propagate to callers.
    (unreachable)
  )

  ;; YESTNH:      (func $toplevel-might-trap (type $0)
  ;; YESTNH-NEXT:  (local $0 i32)
  ;; YESTNH-NEXT:  (nop)
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $toplevel-might-trap (type $0)
  ;; NO_TNH-NEXT:  (local $0 i32)
  ;; NO_TNH-NEXT:  (local.set $0
  ;; NO_TNH-NEXT:   (i32.load
  ;; NO_TNH-NEXT:    (i32.const 0)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $toplevel-might-trap
    ;; This might trap, but we can still remove it all in TNH mode: the implicit
    ;; trap does not inhibit us from removing this code. (If we saw an explicit
    ;; unreachable, we would not remove it, as tested above.)
    (local $0 i32)
    (local.set $0
      (i32.load
        (i32.const 0)
      )
    )
  )


  ;; YESTNH:      (func $drop-loop (type $0)
  ;; YESTNH-NEXT:  (drop
  ;; YESTNH-NEXT:   (loop $loop (result i32)
  ;; YESTNH-NEXT:    (br_if $loop
  ;; YESTNH-NEXT:     (i32.const 1)
  ;; YESTNH-NEXT:    )
  ;; YESTNH-NEXT:    (i32.const 10)
  ;; YESTNH-NEXT:   )
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $drop-loop (type $0)
  ;; NO_TNH-NEXT:  (drop
  ;; NO_TNH-NEXT:   (loop $loop (result i32)
  ;; NO_TNH-NEXT:    (br_if $loop
  ;; NO_TNH-NEXT:     (i32.const 1)
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:    (i32.const 10)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $drop-loop
    ;; A loop has the effect of potentially being infinite. Even in TNH mode we
    ;; do not optimize out such loops.
    (drop
      (loop $loop (result i32)
        (br_if $loop
          (i32.const 1)
        )
        (i32.const 10)
      )
    )
  )

  ;; YESTNH:      (func $loop-effects (type $0)
  ;; YESTNH-NEXT:  (drop
  ;; YESTNH-NEXT:   (loop $loop (result i32)
  ;; YESTNH-NEXT:    (drop
  ;; YESTNH-NEXT:     (i32.atomic.load
  ;; YESTNH-NEXT:      (i32.const 0)
  ;; YESTNH-NEXT:     )
  ;; YESTNH-NEXT:    )
  ;; YESTNH-NEXT:    (br_if $loop
  ;; YESTNH-NEXT:     (i32.const 1)
  ;; YESTNH-NEXT:    )
  ;; YESTNH-NEXT:    (i32.const 10)
  ;; YESTNH-NEXT:   )
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $loop-effects (type $0)
  ;; NO_TNH-NEXT:  (drop
  ;; NO_TNH-NEXT:   (loop $loop (result i32)
  ;; NO_TNH-NEXT:    (drop
  ;; NO_TNH-NEXT:     (i32.atomic.load
  ;; NO_TNH-NEXT:      (i32.const 0)
  ;; NO_TNH-NEXT:     )
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:    (br_if $loop
  ;; NO_TNH-NEXT:     (i32.const 1)
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:    (i32.const 10)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $loop-effects
    ;; As above, but the loop also has an atomic load effect. That prevents
    ;; optimization.
    (drop
      (loop $loop (result i32)
        (drop
          (i32.atomic.load
            (i32.const 0)
          )
        )
        (br_if $loop
          (i32.const 1)
        )
        (i32.const 10)
      )
    )
  )

  ;; YESTNH:      (func $if-unreachable (type $1) (param $p i32)
  ;; YESTNH-NEXT:  (drop
  ;; YESTNH-NEXT:   (local.get $p)
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT:  (block
  ;; YESTNH-NEXT:   (drop
  ;; YESTNH-NEXT:    (local.get $p)
  ;; YESTNH-NEXT:   )
  ;; YESTNH-NEXT:   (call $if-unreachable
  ;; YESTNH-NEXT:    (i32.const 0)
  ;; YESTNH-NEXT:   )
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT:  (if
  ;; YESTNH-NEXT:   (local.get $p)
  ;; YESTNH-NEXT:   (then
  ;; YESTNH-NEXT:    (unreachable)
  ;; YESTNH-NEXT:   )
  ;; YESTNH-NEXT:   (else
  ;; YESTNH-NEXT:    (unreachable)
  ;; YESTNH-NEXT:   )
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $if-unreachable (type $2) (param $p i32)
  ;; NO_TNH-NEXT:  (if
  ;; NO_TNH-NEXT:   (local.get $p)
  ;; NO_TNH-NEXT:   (then
  ;; NO_TNH-NEXT:    (unreachable)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (if
  ;; NO_TNH-NEXT:   (local.get $p)
  ;; NO_TNH-NEXT:   (then
  ;; NO_TNH-NEXT:    (call $if-unreachable
  ;; NO_TNH-NEXT:     (i32.const 0)
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:   (else
  ;; NO_TNH-NEXT:    (unreachable)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (if
  ;; NO_TNH-NEXT:   (local.get $p)
  ;; NO_TNH-NEXT:   (then
  ;; NO_TNH-NEXT:    (unreachable)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:   (else
  ;; NO_TNH-NEXT:    (unreachable)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $if-unreachable (param $p i32)
    ;; The if arm can be nopped, as in tnh we assume we never reach it.
    (if
      (local.get $p)
      (then
        (unreachable)
      )
    )
    ;; This else arm can be removed.
    (if
      (local.get $p)
      (then
        (call $if-unreachable
          (i32.const 0)
        )
      )
      (else
        (unreachable)
      )
    )
    ;; Both of these can be removed, but we leave this for DCE to handle.
    (if
      (local.get $p)
      (then
        (unreachable)
      )
      (else
        (unreachable)
      )
    )
  )

  ;; YESTNH:      (func $if-unreachable-value (type $3) (param $p i32) (result i32)
  ;; YESTNH-NEXT:  (drop
  ;; YESTNH-NEXT:   (local.get $p)
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT:  (i32.const 1)
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $if-unreachable-value (type $3) (param $p i32) (result i32)
  ;; NO_TNH-NEXT:  (if (result i32)
  ;; NO_TNH-NEXT:   (local.get $p)
  ;; NO_TNH-NEXT:   (then
  ;; NO_TNH-NEXT:    (unreachable)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:   (else
  ;; NO_TNH-NEXT:    (i32.const 1)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $if-unreachable-value (param $p i32) (result i32)
    ;; When removing the unreachable arm we must update the IR properly, as it
    ;; cannot have a nop there.
    (if (result i32)
      (local.get $p)
      (then
        (unreachable)
      )
      (else
        (i32.const 1)
      )
    )
  )

  ;; YESTNH:      (func $if-unreachable-value-2 (type $3) (param $p i32) (result i32)
  ;; YESTNH-NEXT:  (drop
  ;; YESTNH-NEXT:   (local.get $p)
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT:  (i32.const 1)
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $if-unreachable-value-2 (type $3) (param $p i32) (result i32)
  ;; NO_TNH-NEXT:  (if (result i32)
  ;; NO_TNH-NEXT:   (local.get $p)
  ;; NO_TNH-NEXT:   (then
  ;; NO_TNH-NEXT:    (i32.const 1)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:   (else
  ;; NO_TNH-NEXT:    (unreachable)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $if-unreachable-value-2 (param $p i32) (result i32)
    ;; As above but in the other arm.
    (if (result i32)
      (local.get $p)
      (then
        (i32.const 1)
      )
      (else
        (unreachable)
      )
    )
  )

  ;; YESTNH:      (func $block-unreachable (type $1) (param $p i32)
  ;; YESTNH-NEXT:  (if
  ;; YESTNH-NEXT:   (local.get $p)
  ;; YESTNH-NEXT:   (then
  ;; YESTNH-NEXT:    (i32.store
  ;; YESTNH-NEXT:     (i32.const 0)
  ;; YESTNH-NEXT:     (i32.const 1)
  ;; YESTNH-NEXT:    )
  ;; YESTNH-NEXT:    (if
  ;; YESTNH-NEXT:     (local.get $p)
  ;; YESTNH-NEXT:     (then
  ;; YESTNH-NEXT:      (return)
  ;; YESTNH-NEXT:     )
  ;; YESTNH-NEXT:    )
  ;; YESTNH-NEXT:    (unreachable)
  ;; YESTNH-NEXT:   )
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $block-unreachable (type $2) (param $p i32)
  ;; NO_TNH-NEXT:  (if
  ;; NO_TNH-NEXT:   (local.get $p)
  ;; NO_TNH-NEXT:   (then
  ;; NO_TNH-NEXT:    (i32.store
  ;; NO_TNH-NEXT:     (i32.const 0)
  ;; NO_TNH-NEXT:     (i32.const 1)
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:    (if
  ;; NO_TNH-NEXT:     (local.get $p)
  ;; NO_TNH-NEXT:     (then
  ;; NO_TNH-NEXT:      (return)
  ;; NO_TNH-NEXT:     )
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:    (i32.store
  ;; NO_TNH-NEXT:     (i32.const 2)
  ;; NO_TNH-NEXT:     (i32.const 3)
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:    (unreachable)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $block-unreachable (param $p i32)
    (if
      (local.get $p)
      (then
        (block
          (i32.store
            (i32.const 0)
            (i32.const 1)
          )
          (if
            (local.get $p)
            (then
              (return)
            )
          )
          ;; This store can be removed as it leads up to an unreachable which we
          ;; assume is never reached.
          (i32.store
            (i32.const 2)
            (i32.const 3)
          )
          (unreachable)
        )
      )
    )
  )

  ;; YESTNH:      (func $block-unreachable-named (type $1) (param $p i32)
  ;; YESTNH-NEXT:  (if
  ;; YESTNH-NEXT:   (local.get $p)
  ;; YESTNH-NEXT:   (then
  ;; YESTNH-NEXT:    (block $named
  ;; YESTNH-NEXT:     (i32.store
  ;; YESTNH-NEXT:      (i32.const 0)
  ;; YESTNH-NEXT:      (i32.const 1)
  ;; YESTNH-NEXT:     )
  ;; YESTNH-NEXT:     (br_if $named
  ;; YESTNH-NEXT:      (local.get $p)
  ;; YESTNH-NEXT:     )
  ;; YESTNH-NEXT:     (unreachable)
  ;; YESTNH-NEXT:    )
  ;; YESTNH-NEXT:   )
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $block-unreachable-named (type $2) (param $p i32)
  ;; NO_TNH-NEXT:  (if
  ;; NO_TNH-NEXT:   (local.get $p)
  ;; NO_TNH-NEXT:   (then
  ;; NO_TNH-NEXT:    (block $named
  ;; NO_TNH-NEXT:     (i32.store
  ;; NO_TNH-NEXT:      (i32.const 0)
  ;; NO_TNH-NEXT:      (i32.const 1)
  ;; NO_TNH-NEXT:     )
  ;; NO_TNH-NEXT:     (br_if $named
  ;; NO_TNH-NEXT:      (local.get $p)
  ;; NO_TNH-NEXT:     )
  ;; NO_TNH-NEXT:     (i32.store
  ;; NO_TNH-NEXT:      (i32.const 2)
  ;; NO_TNH-NEXT:      (i32.const 3)
  ;; NO_TNH-NEXT:     )
  ;; NO_TNH-NEXT:     (unreachable)
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $block-unreachable-named (param $p i32)
    (if
      (local.get $p)
      (then
        (block $named
          (i32.store
            (i32.const 0)
            (i32.const 1)
          )
          ;; As above, but now the block is named and we use a br_if. We should
          ;; again only remove the last store.
          (br_if $named
            (local.get $p)
          )
          (i32.store
            (i32.const 2)
            (i32.const 3)
          )
          (unreachable)
        )
      )
    )
  )

  ;; YESTNH:      (func $block-unreachable-all (type $1) (param $p i32)
  ;; YESTNH-NEXT:  (nop)
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $block-unreachable-all (type $2) (param $p i32)
  ;; NO_TNH-NEXT:  (if
  ;; NO_TNH-NEXT:   (local.get $p)
  ;; NO_TNH-NEXT:   (then
  ;; NO_TNH-NEXT:    (i32.store
  ;; NO_TNH-NEXT:     (i32.const 0)
  ;; NO_TNH-NEXT:     (i32.const 1)
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:    (i32.store
  ;; NO_TNH-NEXT:     (i32.const 2)
  ;; NO_TNH-NEXT:     (i32.const 3)
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:    (unreachable)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $block-unreachable-all (param $p i32)
    (if
      (local.get $p)
      (then
        (block
          ;; Both stores can be removed, and even the entire if arm and then the
          ;; entire if.
          (i32.store
            (i32.const 0)
            (i32.const 1)
          )
          (i32.store
            (i32.const 2)
            (i32.const 3)
          )
          (unreachable)
        )
      )
    )
  )

  ;; YESTNH:      (func $block-unreachable-but-call (type $0)
  ;; YESTNH-NEXT:  (i32.store
  ;; YESTNH-NEXT:   (i32.const 0)
  ;; YESTNH-NEXT:   (i32.const 1)
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT:  (call $block-unreachable-but-call)
  ;; YESTNH-NEXT:  (unreachable)
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $block-unreachable-but-call (type $0)
  ;; NO_TNH-NEXT:  (i32.store
  ;; NO_TNH-NEXT:   (i32.const 0)
  ;; NO_TNH-NEXT:   (i32.const 1)
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (call $block-unreachable-but-call)
  ;; NO_TNH-NEXT:  (i32.store
  ;; NO_TNH-NEXT:   (i32.const 2)
  ;; NO_TNH-NEXT:   (i32.const 3)
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (unreachable)
  ;; NO_TNH-NEXT: )
  (func $block-unreachable-but-call
    ;; A call cannot be removed, even if it leads to a trap, since it might have
    ;; non-trap effects (like mayNotReturn). We can remove the store after it,
    ;; though.
    (i32.store
      (i32.const 0)
      (i32.const 1)
    )
    (call $block-unreachable-but-call)
    (i32.store
      (i32.const 2)
      (i32.const 3)
    )
    (unreachable)
  )

  ;; YESTNH:      (func $catch-pop (type $0)
  ;; YESTNH-NEXT:  (try $try
  ;; YESTNH-NEXT:   (do
  ;; YESTNH-NEXT:    (call $catch-pop)
  ;; YESTNH-NEXT:   )
  ;; YESTNH-NEXT:   (catch $tag
  ;; YESTNH-NEXT:    (drop
  ;; YESTNH-NEXT:     (pop i32)
  ;; YESTNH-NEXT:    )
  ;; YESTNH-NEXT:    (unreachable)
  ;; YESTNH-NEXT:   )
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $catch-pop (type $0)
  ;; NO_TNH-NEXT:  (try $try
  ;; NO_TNH-NEXT:   (do
  ;; NO_TNH-NEXT:    (call $catch-pop)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:   (catch $tag
  ;; NO_TNH-NEXT:    (drop
  ;; NO_TNH-NEXT:     (pop i32)
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:    (i32.store
  ;; NO_TNH-NEXT:     (i32.const 0)
  ;; NO_TNH-NEXT:     (i32.const 1)
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:    (unreachable)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $catch-pop
    (try $try
      (do
        ;; Put a call here so the entire try-catch is not removed as trivial.
        (call $catch-pop)
      )
      (catch $tag
        ;; A pop on the way to a trap cannot be removed. But the store can.
        ;; TODO: The pop can actually be removed since it is valid per the spec
        ;;       because of the unreachable afterwards. We need to fix our
        ;;       validation rules to handle that though.
        (drop
          (pop i32)
        )
        (i32.store
          (i32.const 0)
          (i32.const 1)
        )
        (unreachable)
      )
    )
  )

  ;; YESTNH:      (func $loop-unreachable (type $1) (param $p i32)
  ;; YESTNH-NEXT:  (loop $loop
  ;; YESTNH-NEXT:   (i32.store
  ;; YESTNH-NEXT:    (i32.const 0)
  ;; YESTNH-NEXT:    (i32.const 1)
  ;; YESTNH-NEXT:   )
  ;; YESTNH-NEXT:   (if
  ;; YESTNH-NEXT:    (local.get $p)
  ;; YESTNH-NEXT:    (then
  ;; YESTNH-NEXT:     (br $loop)
  ;; YESTNH-NEXT:    )
  ;; YESTNH-NEXT:   )
  ;; YESTNH-NEXT:   (unreachable)
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $loop-unreachable (type $2) (param $p i32)
  ;; NO_TNH-NEXT:  (loop $loop
  ;; NO_TNH-NEXT:   (i32.store
  ;; NO_TNH-NEXT:    (i32.const 0)
  ;; NO_TNH-NEXT:    (i32.const 1)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:   (if
  ;; NO_TNH-NEXT:    (local.get $p)
  ;; NO_TNH-NEXT:    (then
  ;; NO_TNH-NEXT:     (br $loop)
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:   (i32.store
  ;; NO_TNH-NEXT:    (i32.const 2)
  ;; NO_TNH-NEXT:    (i32.const 3)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:   (unreachable)
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $loop-unreachable (param $p i32)
    (loop $loop
      (i32.store
        (i32.const 0)
        (i32.const 1)
      )
      (if
        (local.get $p)
        (then
          (br $loop)
        )
      )
      ;; This store can be removed as it leads up to an unreachable which we
      ;; assume is never reached.
      (i32.store
        (i32.const 2)
        (i32.const 3)
      )
      (unreachable)
    )
  )

  ;; YESTNH:      (func $unreached-infinite-loop (type $0)
  ;; YESTNH-NEXT:  (loop $label$1
  ;; YESTNH-NEXT:   (br $label$1)
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $unreached-infinite-loop (type $0)
  ;; NO_TNH-NEXT:  (loop $label$1
  ;; NO_TNH-NEXT:   (br $label$1)
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $unreached-infinite-loop
    ;; Code that reaches an unreachable can be removed in TNH mode, but an
    ;; infinite loop may not reach it, so nothing can be removed here.
    (loop $label$1
      (br $label$1)
    )
    (unreachable)
  )
)
