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

(module
  ;; CHECK:      (import "binaryen-intrinsics" "call.without.effects" (func $call.without.effects (type $2) (param i32 funcref) (result i32)))
  (import "binaryen-intrinsics" "call.without.effects" (func $call.without.effects (param i32 funcref) (result i32)))

  ;; CHECK:      (func $if-nop (type $0) (param $p i32)
  ;; CHECK-NEXT:  (local $x i32)
  ;; CHECK-NEXT:  (local.set $x
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (local.get $p)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (nop)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $if-nop (param $p i32)
    (local $x i32)
    ;; The set local is not used in any if arm; do nothing.
    (local.set $x (i32.const 1))
    (if
      (local.get $p)
      (then
        (nop)
      )
    )
  )

  ;; CHECK:      (func $if-nop-nop (type $0) (param $p i32)
  ;; CHECK-NEXT:  (local $x i32)
  ;; CHECK-NEXT:  (local.set $x
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (local.get $p)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (nop)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (else
  ;; CHECK-NEXT:    (nop)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $if-nop-nop (param $p i32)
    (local $x i32)
    (local.set $x (i32.const 1))
    (if
      (local.get $p)
      (then
        (nop)
      )
      (else
        (nop) ;; add a nop here compared to the last testcase (no output change)
      )
    )
  )

  ;; CHECK:      (func $if-use (type $0) (param $p i32)
  ;; CHECK-NEXT:  (local $x i32)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (local.get $p)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (local.set $x
  ;; CHECK-NEXT:     (i32.const 1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $if-use (param $p i32)
    (local $x i32)
    ;; The set local is used in one arm and nowhere else; push it there.
    (local.set $x (i32.const 1))
    (if
      (local.get $p)
      (then
        (drop (local.get $x))
      )
    )
  )

  ;; CHECK:      (func $if-use-nop (type $0) (param $p i32)
  ;; CHECK-NEXT:  (local $x i32)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (local.get $p)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (local.set $x
  ;; CHECK-NEXT:     (i32.const 1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (else
  ;; CHECK-NEXT:    (nop)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $if-use-nop (param $p i32)
    (local $x i32)
    (local.set $x (i32.const 1))
    (if
      (local.get $p)
      (then
        (drop (local.get $x))
      )
      (else
        (nop) ;; add a nop here compared to the last testcase (no output change)
      )
    )
  )

  ;; CHECK:      (func $if-else-use (type $0) (param $p i32)
  ;; CHECK-NEXT:  (local $x i32)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (local.get $p)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (nop)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (else
  ;; CHECK-NEXT:    (local.set $x
  ;; CHECK-NEXT:     (i32.const 1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $if-else-use (param $p i32)
    (local $x i32)
    ;; The set local is used in one arm and nowhere else; push it there.
    (local.set $x (i32.const 1))
    (if
      (local.get $p)
      (then
        (nop)
      )
      (else
        (drop (local.get $x))
      )
    )
  )

  ;; CHECK:      (func $unpushed-interference (type $0) (param $p i32)
  ;; CHECK-NEXT:  (local $x i32)
  ;; CHECK-NEXT:  (local $y i32)
  ;; CHECK-NEXT:  (local.set $x
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (local.set $y
  ;; CHECK-NEXT:   (local.get $x)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (local.get $p)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $unpushed-interference (param $p i32)
    (local $x i32)
    (local $y i32)
    (local.set $x (i32.const 1))
    ;; This set is not pushed (as it is not used in the if) and it will then
    ;; prevent the previous set of $x from being pushed, since we can't push a
    ;; set of $x past a get of it.
    (local.set $y (local.get $x))
    (if
      (local.get $p)
      (then
        (drop (local.get $x))
      )
    )
  )

  ;; CHECK:      (func $if-use-use (type $0) (param $p i32)
  ;; CHECK-NEXT:  (local $x i32)
  ;; CHECK-NEXT:  (local.set $x
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (local.get $p)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (else
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $if-use-use (param $p i32)
    (local $x i32)
    ;; The set local is used in both arms, so we can't do anything.
    (local.set $x (i32.const 1))
    (if
      (local.get $p)
      (then
        (drop (local.get $x))
      )
      (else
        (drop (local.get $x))
      )
    )
  )

  ;; CHECK:      (func $if-use-after (type $0) (param $p i32)
  ;; CHECK-NEXT:  (local $x i32)
  ;; CHECK-NEXT:  (local.set $x
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (local.get $p)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $x)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $if-use-after (param $p i32)
    (local $x i32)
    ;; The use after the if prevents optimization.
    (local.set $x (i32.const 1))
    (if
      (local.get $p)
      (then
        (drop (local.get $x))
      )
    )
    (drop (local.get $x))
  )

  ;; CHECK:      (func $if-use-after-nop (type $0) (param $p i32)
  ;; CHECK-NEXT:  (local $x i32)
  ;; CHECK-NEXT:  (local.set $x
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (local.get $p)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (else
  ;; CHECK-NEXT:    (nop)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $x)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $if-use-after-nop (param $p i32)
    (local $x i32)
    (local.set $x (i32.const 1))
    (if
      (local.get $p)
      (then
        (drop (local.get $x))
      )
      (else
        (nop) ;; add a nop here compared to the last testcase (no output change)
      )
    )
    (drop (local.get $x))
  )

  ;; CHECK:      (func $if-else-use-after (type $0) (param $p i32)
  ;; CHECK-NEXT:  (local $x i32)
  ;; CHECK-NEXT:  (local.set $x
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (local.get $p)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (nop)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (else
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $x)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $if-else-use-after (param $p i32)
    (local $x i32)
    (local.set $x (i32.const 1))
    (if
      (local.get $p)
      (then
        (nop)
      )
      (else
        (drop (local.get $x)) ;; now the use in the if is in the else arm
      )
    )
    (drop (local.get $x))
  )

  ;; CHECK:      (func $if-use-after-unreachable (type $0) (param $p i32)
  ;; CHECK-NEXT:  (local $x i32)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (local.get $p)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (local.set $x
  ;; CHECK-NEXT:     (i32.const 1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (else
  ;; CHECK-NEXT:    (return)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $x)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $if-use-after-unreachable (param $p i32)
    (local $x i32)
    ;; A use after the if is ok as the other arm is unreachable.
    (local.set $x (i32.const 1))
    (if
      (local.get $p)
      (then
        (drop (local.get $x))
      )
      (else
        (return)
      )
    )
    (drop (local.get $x))
  )

  ;; CHECK:      (func $if-use-after-unreachable-else (type $0) (param $p i32)
  ;; CHECK-NEXT:  (local $x i32)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (local.get $p)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (return)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (else
  ;; CHECK-NEXT:    (local.set $x
  ;; CHECK-NEXT:     (i32.const 1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $x)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $if-use-after-unreachable-else (param $p i32)
    (local $x i32)
    (local.set $x (i32.const 1))
    (if
      (local.get $p)
      (then
        (return) ;; as above, but with arms flipped
      )
      (else
        (drop (local.get $x))
      )
    )
    (drop (local.get $x))
  )

  ;; CHECK:      (func $optimize-many (type $0) (param $p i32)
  ;; CHECK-NEXT:  (local $x i32)
  ;; CHECK-NEXT:  (local $y i32)
  ;; CHECK-NEXT:  (local $z i32)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (local.get $p)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (local.set $x
  ;; CHECK-NEXT:     (i32.const 1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (local.set $z
  ;; CHECK-NEXT:     (i32.const 3)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (local.get $z)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (else
  ;; CHECK-NEXT:    (local.set $y
  ;; CHECK-NEXT:     (i32.const 2)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (local.get $y)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $optimize-many (param $p i32)
    (local $x i32)
    (local $y i32)
    (local $z i32)
    ;; Multiple things we can push, to various arms.
    (local.set $x (i32.const 1))
    (local.set $y (i32.const 2))
    (local.set $z (i32.const 3))
    (if
      (local.get $p)
      (then
        (block
          (drop (local.get $x))
          (drop (local.get $z))
        )
      )
      (else
        (drop (local.get $y))
      )
    )
  )

  ;; CHECK:      (func $past-other (type $0) (param $p i32)
  ;; CHECK-NEXT:  (local $x i32)
  ;; CHECK-NEXT:  (local $t i32)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (i32.const 2)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (local.get $p)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (local.set $x
  ;; CHECK-NEXT:     (local.get $t)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $past-other (param $p i32)
    (local $x i32)
    (local $t i32)
    ;; We can push this past the drop after it.
    (local.set $x (local.get $t))
    (drop (i32.const 2))
    (if
      (local.get $p)
      (then
        (drop (local.get $x))
      )
    )
  )

  ;; CHECK:      (func $past-other-no (type $0) (param $p i32)
  ;; CHECK-NEXT:  (local $x i32)
  ;; CHECK-NEXT:  (local $t i32)
  ;; CHECK-NEXT:  (local.set $x
  ;; CHECK-NEXT:   (local.get $t)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.tee $t
  ;; CHECK-NEXT:    (i32.const 2)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (local.get $p)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $past-other-no (param $p i32)
    (local $x i32)
    (local $t i32)
    ;; We cannot push this due to the tee, which interferes with us.
    (local.set $x (local.get $t))
    (drop (local.tee $t (i32.const 2)))
    (if
      (local.get $p)
      (then
        (drop (local.get $x))
      )
    )
  )

  ;; CHECK:      (func $past-condition-no (type $0) (param $p i32)
  ;; CHECK-NEXT:  (local $x i32)
  ;; CHECK-NEXT:  (local $t i32)
  ;; CHECK-NEXT:  (local.set $x
  ;; CHECK-NEXT:   (local.get $t)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (local.tee $t
  ;; CHECK-NEXT:    (local.get $p)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $past-condition-no (param $p i32)
    (local $x i32)
    (local $t i32)
    ;; We cannot push this due to the tee in the if condition.
    (local.set $x (local.get $t))
    (if
      (local.tee $t (local.get $p))
      (then
        (drop (local.get $x))
      )
    )
  )

  ;; CHECK:      (func $past-condition-no-2 (type $3)
  ;; CHECK-NEXT:  (local $x i32)
  ;; CHECK-NEXT:  (local $t i32)
  ;; CHECK-NEXT:  (local.set $x
  ;; CHECK-NEXT:   (local.get $t)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (local.get $x)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $past-condition-no-2
    (local $x i32)
    (local $t i32)
    ;; We cannot push this due to the read of $x in the if condition.
    (local.set $x (local.get $t))
    (if
      (local.get $x)
      (then
        (drop (local.get $x))
      )
    )
  )

  ;; CHECK:      (func $past-condition-no-3 (type $0) (param $p i32)
  ;; CHECK-NEXT:  (local $x i32)
  ;; CHECK-NEXT:  (local $t i32)
  ;; CHECK-NEXT:  (local.set $x
  ;; CHECK-NEXT:   (local.get $t)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (local.tee $x
  ;; CHECK-NEXT:    (local.get $p)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $past-condition-no-3 (param $p i32)
    (local $x i32)
    (local $t i32)
    ;; We cannot push this due to the write of $x in the if condition.
    (local.set $x (local.get $t))
    (if
      (local.tee $x (local.get $p))
      (then
        (drop (local.get $x))
      )
    )
  )

  ;; CHECK:      (func $if-condition-return (type $0) (param $p i32)
  ;; CHECK-NEXT:  (local $x i32)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (block (result i32)
  ;; CHECK-NEXT:    (return)
  ;; CHECK-NEXT:    (local.get $p)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (local.set $x
  ;; CHECK-NEXT:     (i32.const 1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $if-condition-return (param $p i32)
    (local $x i32)
    (local.set $x (i32.const 1))
    (if
      (block (result i32)
        (return) ;; This return does not prevent us from optimizing; if it
                 ;; happens then we don't need the local.set to execute
                 ;; anyhow.
        (local.get $p)
      )
      (then
        (drop (local.get $x))
      )
    )
  )

  ;; CHECK:      (func $if-condition-break-used (type $0) (param $p i32)
  ;; CHECK-NEXT:  (local $x i32)
  ;; CHECK-NEXT:  (local.set $x
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (block $out
  ;; CHECK-NEXT:   (if
  ;; CHECK-NEXT:    (block (result i32)
  ;; CHECK-NEXT:     (br $out)
  ;; CHECK-NEXT:     (local.get $p)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (then
  ;; CHECK-NEXT:     (drop
  ;; CHECK-NEXT:      (local.get $x)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (return)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $x)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $if-condition-break-used (param $p i32)
    (local $x i32)
    (local.set $x (i32.const 1))
    ;; As above, but the return is replaced with a break. The break goes to a
    ;; location with a use of the local, which prevents optimization.
    (block $out
      (if
        (block (result i32)
          (br $out)
          (local.get $p)
        )
        (then
          (drop (local.get $x))
        )
      )
      (return)
    )
    (drop (local.get $x))
  )

  ;; CHECK:      (func $one-push-prevents-another (type $0) (param $p i32)
  ;; CHECK-NEXT:  (local $x i32)
  ;; CHECK-NEXT:  (local $y i32)
  ;; CHECK-NEXT:  (local.set $x
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (local.get $p)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (else
  ;; CHECK-NEXT:    (local.set $y
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (local.get $y)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $one-push-prevents-another (param $p i32)
    (local $x i32)
    (local $y i32)
    ;; We will push $y into one arm, and as a result both arms will have a get
    ;; of $x, which prevents pushing $x.
    (local.set $x (i32.const 1))
    (local.set $y (local.get $x))
    (if
      (local.get $p)
      (then
        (drop (local.get $x))
      )
      (else
        (drop (local.get $y))
      )
    )
  )

  ;; CHECK:      (func $one-push-prevents-another-flipped (type $0) (param $p i32)
  ;; CHECK-NEXT:  (local $x i32)
  ;; CHECK-NEXT:  (local $y i32)
  ;; CHECK-NEXT:  (local.set $x
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (local.get $p)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (local.set $y
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (local.get $y)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (else
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $one-push-prevents-another-flipped (param $p i32)
    (local $x i32)
    (local $y i32)
    ;; As above but with if arms flipped. The result should be similar, with
    ;; only $y pushed.
    (local.set $x (i32.const 1))
    (local.set $y (local.get $x))
    (if
      (local.get $p)
      (then
        (drop (local.get $y))
      )
      (else
        (drop (local.get $x))
      )
    )
  )

  ;; CHECK:      (func $sink-call (type $1) (param $p i32) (result i32)
  ;; CHECK-NEXT:  (local $temp i32)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (local.get $p)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (local.set $temp
  ;; CHECK-NEXT:     (call $call.without.effects
  ;; CHECK-NEXT:      (i32.const 1234)
  ;; CHECK-NEXT:      (ref.func $sink-call)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (return
  ;; CHECK-NEXT:     (local.get $temp)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (i32.const 0)
  ;; CHECK-NEXT: )
  (func $sink-call (param $p i32) (result i32)
    (local $temp i32)

    ;; This local has a call, but the call is an intrinsic indicating no
    ;; effects, so it is safe to sink into the if.
    (local.set $temp
      (call $call.without.effects
        (i32.const 1234)
        (ref.func $sink-call)
      )
    )
    (if
      (local.get $p)
      (then
        (return
          (local.get $temp)
        )
      )
    )
    (i32.const 0)
  )

  ;; CHECK:      (func $no-sink-call (type $1) (param $p i32) (result i32)
  ;; CHECK-NEXT:  (local $temp i32)
  ;; CHECK-NEXT:  (local.set $temp
  ;; CHECK-NEXT:   (call $call.without.effects
  ;; CHECK-NEXT:    (i32.const 1234)
  ;; CHECK-NEXT:    (ref.func $no-sink-call)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (local.get $p)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (return
  ;; CHECK-NEXT:     (local.get $temp)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (local.get $temp)
  ;; CHECK-NEXT: )
  (func $no-sink-call (param $p i32) (result i32)
    (local $temp i32)

    ;; As above, but now after the if we have a get of the temp local, so we
    ;; cannot sink. This + the previous testcase show we scan for such local
    ;; uses in exactly the right places.
    (local.set $temp
      (call $call.without.effects
        (i32.const 1234)
        (ref.func $no-sink-call)
      )
    )
    (if
      (local.get $p)
      (then
        (return
          (local.get $temp)
        )
      )
    )
    (local.get $temp) ;; this line changed.
  )

  ;; CHECK:      (func $no-sink-call-2 (type $1) (param $p i32) (result i32)
  ;; CHECK-NEXT:  (local $temp i32)
  ;; CHECK-NEXT:  (local.set $temp
  ;; CHECK-NEXT:   (call $call.without.effects
  ;; CHECK-NEXT:    (i32.const 1234)
  ;; CHECK-NEXT:    (ref.func $no-sink-call-2)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (local.get $p)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (return
  ;; CHECK-NEXT:     (local.get $temp)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (local.get $temp)
  ;; CHECK-NEXT: )
  (func $no-sink-call-2 (param $p i32) (result i32)
    (local $temp i32)

    ;; As above, but add a nop before the final value. We still should not
    ;; optimize.
    (local.set $temp
      (call $call.without.effects
        (i32.const 1234)
        (ref.func $no-sink-call-2)
      )
    )
    (if
      (local.get $p)
      (then
        (return
          (local.get $temp)
        )
      )
    )
    (nop) ;; this line was added.
    (local.get $temp)
  )

  ;; CHECK:      (func $no-sink-call-3 (type $1) (param $p i32) (result i32)
  ;; CHECK-NEXT:  (local $temp i32)
  ;; CHECK-NEXT:  (local.set $temp
  ;; CHECK-NEXT:   (call $call.without.effects
  ;; CHECK-NEXT:    (i32.const 1234)
  ;; CHECK-NEXT:    (ref.func $no-sink-call-3)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (local.get $p)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (return
  ;; CHECK-NEXT:     (local.get $temp)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $temp)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (i32.const 0)
  ;; CHECK-NEXT: )
  (func $no-sink-call-3 (param $p i32) (result i32)
    (local $temp i32)

    ;; As above, but add a nop after the get of the local after the if. We still
    ;; should not optimize.
    (local.set $temp
      (call $call.without.effects
        (i32.const 1234)
        (ref.func $no-sink-call-3)
      )
    )
    (if
      (local.get $p)
      (then
        (return
          (local.get $temp)
        )
      )
    )
    (nop)
    (drop
      (local.get $temp) ;; this get is now dropped.
    )
    (nop) ;; this line was added;
    (i32.const 0) ;; this line was added.
  )

  ;; CHECK:      (func $sink-call-3 (type $1) (param $p i32) (result i32)
  ;; CHECK-NEXT:  (local $temp i32)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (local.get $p)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (local.set $temp
  ;; CHECK-NEXT:     (call $call.without.effects
  ;; CHECK-NEXT:      (i32.const 1234)
  ;; CHECK-NEXT:      (ref.func $no-sink-call-3)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (return
  ;; CHECK-NEXT:     (local.get $temp)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $p)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (i32.const 0)
  ;; CHECK-NEXT: )
  (func $sink-call-3 (param $p i32) (result i32)
    (local $temp i32)

    ;; As above, but stop reading the relevant local after the if, keeping the
    ;; number of other items unchanged. This verifies the presence of multiple
    ;; items is not a problem and we can optimize.
    (local.set $temp
      (call $call.without.effects
        (i32.const 1234)
        (ref.func $no-sink-call-3)
      )
    )
    (if
      (local.get $p)
      (then
        (return
          (local.get $temp)
        )
      )
    )
    (nop)
    (drop
      (local.get $p) ;; this get now reads $p
    )
    (nop)
    (i32.const 0)
  )

  ;; CHECK:      (func $no-sink-call-sub (type $1) (param $p i32) (result i32)
  ;; CHECK-NEXT:  (local $temp i32)
  ;; CHECK-NEXT:  (local $other i32)
  ;; CHECK-NEXT:  (local.set $temp
  ;; CHECK-NEXT:   (call $call.without.effects
  ;; CHECK-NEXT:    (local.tee $other
  ;; CHECK-NEXT:     (i32.const 1234)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (ref.func $no-sink-call)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (local.get $p)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (return
  ;; CHECK-NEXT:     (local.get $temp)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (i32.const 0)
  ;; CHECK-NEXT: )
  (func $no-sink-call-sub (param $p i32) (result i32)
    (local $temp i32)
    (local $other i32)

    ;; The call has no effects, but one of the arguments to it does, so we
    ;; cannot optimize.
    (local.set $temp
      (call $call.without.effects
        (local.tee $other ;; an effect
          (i32.const 1234)
        )
        (ref.func $no-sink-call)
      )
    )
    (if
      (local.get $p)
      (then
        (return
          (local.get $temp)
        )
      )
    )
    (i32.const 0)
  )

  ;; CHECK:      (func $ref-into-if (type $4) (param $0 (ref any))
  ;; CHECK-NEXT:  (local $1 anyref)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (unreachable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (else
  ;; CHECK-NEXT:    (block $label$3
  ;; CHECK-NEXT:     (local.set $1
  ;; CHECK-NEXT:      (local.get $0)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (drop
  ;; CHECK-NEXT:      (ref.as_non_null
  ;; CHECK-NEXT:       (local.get $1)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.as_non_null
  ;; CHECK-NEXT:    (local.get $1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $ref-into-if (param $0 (ref any))
    (local $1 (ref any))
    ;; This can be pushed into the reachable if arm. After doing so, however,
    ;; the local $1 no longer has a single set that dominates it in the sense of
    ;; the wasm validation rules for non-nullable locals, so the local must be
    ;; turned into a nullable one.
    (local.set $1
      (local.get $0)
    )
    (if
      (i32.const 1)
      (then
        (unreachable)
      )
      (else
        (block $label$3
          (drop
            (local.get $1)
          )
        )
      )
    )
    (drop
      (local.get $1)
    )
  )

)