;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
;; RUN: wasm-opt %s --generate-stack-ir --optimize-stack-ir --shrink-level=1 \
;; RUN:   -all --print-stack-ir | filecheck %s

;; Shrink level is set to 1 to enable local2stack in StackIR opts.

(module
 ;; CHECK:      (func $if (type $1) (param $param (ref eq)) (result (ref eq))
 ;; CHECK-NEXT:  (local $temp (ref eq))
 ;; CHECK-NEXT:  local.get $param
 ;; CHECK-NEXT:  local.set $temp
 ;; CHECK-NEXT:  local.get $temp
 ;; CHECK-NEXT:  i32.const 0
 ;; CHECK-NEXT:  ref.i31
 ;; CHECK-NEXT:  ref.eq
 ;; CHECK-NEXT:  if
 ;; CHECK-NEXT:   i32.const 1
 ;; CHECK-NEXT:   ref.i31
 ;; CHECK-NEXT:   local.set $temp
 ;; CHECK-NEXT:  else
 ;; CHECK-NEXT:   i32.const 2
 ;; CHECK-NEXT:   ref.i31
 ;; CHECK-NEXT:   local.set $temp
 ;; CHECK-NEXT:  end
 ;; CHECK-NEXT:  local.get $temp
 ;; CHECK-NEXT: )
 (func $if (param $param (ref eq)) (result (ref eq))
  (local $temp (ref eq))
  ;; Copy the param into $temp. $temp is then set in both arms of the if, so
  ;; it is set before the get at the end of the function, but we still need to
  ;; keep this set for validation purposes. Specifically, there is a set of
  ;; $temp followed by a get of it in the if condition, which local2stack could
  ;; remove in principle, if not for that final get at the function end.
  (local.set $temp
   (local.get $param)
  )
  (if
   (ref.eq
    (local.get $temp)
    (ref.i31
     (i32.const 0)
    )
   )
   (then
    (local.set $temp
     (ref.i31
      (i32.const 1)
     )
    )
   )
   (else
    (local.set $temp
     (ref.i31
      (i32.const 2)
     )
    )
   )
  )
  (local.get $temp)
 )

 ;; CHECK:      (func $if-no-last-get (type $1) (param $param (ref eq)) (result (ref eq))
 ;; CHECK-NEXT:  (local $temp (ref eq))
 ;; CHECK-NEXT:  local.get $param
 ;; CHECK-NEXT:  i32.const 0
 ;; CHECK-NEXT:  ref.i31
 ;; CHECK-NEXT:  ref.eq
 ;; CHECK-NEXT:  if
 ;; CHECK-NEXT:   i32.const 1
 ;; CHECK-NEXT:   ref.i31
 ;; CHECK-NEXT:   local.set $temp
 ;; CHECK-NEXT:  else
 ;; CHECK-NEXT:   i32.const 2
 ;; CHECK-NEXT:   ref.i31
 ;; CHECK-NEXT:   local.set $temp
 ;; CHECK-NEXT:  end
 ;; CHECK-NEXT:  local.get $param
 ;; CHECK-NEXT: )
 (func $if-no-last-get (param $param (ref eq)) (result (ref eq))
  ;; As the original, but now there is no final get, so we can remove the set-
  ;; get pair of $temp before the if.
  (local $temp (ref eq))
  (local.set $temp
   (local.get $param)
  )
  (if
   (ref.eq
    (local.get $temp)
    (ref.i31
     (i32.const 0)
    )
   )
   (then
    (local.set $temp
     (ref.i31
      (i32.const 1)
     )
    )
   )
   (else
    (local.set $temp
     (ref.i31
      (i32.const 2)
     )
    )
   )
  )
  (local.get $param) ;; this changed from $temp to $param
 )

 ;; CHECK:      (func $if-extra-set (type $1) (param $param (ref eq)) (result (ref eq))
 ;; CHECK-NEXT:  (local $temp (ref eq))
 ;; CHECK-NEXT:  local.get $param
 ;; CHECK-NEXT:  i32.const 0
 ;; CHECK-NEXT:  ref.i31
 ;; CHECK-NEXT:  ref.eq
 ;; CHECK-NEXT:  if
 ;; CHECK-NEXT:   i32.const 1
 ;; CHECK-NEXT:   ref.i31
 ;; CHECK-NEXT:   local.set $temp
 ;; CHECK-NEXT:  else
 ;; CHECK-NEXT:   i32.const 2
 ;; CHECK-NEXT:   ref.i31
 ;; CHECK-NEXT:   local.set $temp
 ;; CHECK-NEXT:  end
 ;; CHECK-NEXT:  local.get $param
 ;; CHECK-NEXT: )
 (func $if-extra-set (param $param (ref eq)) (result (ref eq))
  ;; As the original, but now there is an extra set before the final get, so
  ;; we can optimize - the extra set ensures validation.
  (local $temp (ref eq))
  (local.set $temp
   (local.get $param)
  )
  (if
   (ref.eq
    (local.get $temp)
    (ref.i31
     (i32.const 0)
    )
   )
   (then
    (local.set $temp
     (ref.i31
      (i32.const 1)
     )
    )
   )
   (else
    (local.set $temp
     (ref.i31
      (i32.const 2)
     )
    )
   )
  )
  (local.set $temp    ;; This set is new.
   (local.get $param)
  )
  (local.get $temp)
 )

 ;; CHECK:      (func $if-wrong-extra-set (type $1) (param $param (ref eq)) (result (ref eq))
 ;; CHECK-NEXT:  (local $temp (ref eq))
 ;; CHECK-NEXT:  local.get $param
 ;; CHECK-NEXT:  local.set $temp
 ;; CHECK-NEXT:  local.get $temp
 ;; CHECK-NEXT:  i32.const 0
 ;; CHECK-NEXT:  ref.i31
 ;; CHECK-NEXT:  ref.eq
 ;; CHECK-NEXT:  if
 ;; CHECK-NEXT:   i32.const 1
 ;; CHECK-NEXT:   ref.i31
 ;; CHECK-NEXT:   local.set $temp
 ;; CHECK-NEXT:  else
 ;; CHECK-NEXT:   i32.const 2
 ;; CHECK-NEXT:   ref.i31
 ;; CHECK-NEXT:   local.set $temp
 ;; CHECK-NEXT:  end
 ;; CHECK-NEXT:  local.get $param
 ;; CHECK-NEXT:  local.set $param
 ;; CHECK-NEXT:  local.get $temp
 ;; CHECK-NEXT: )
 (func $if-wrong-extra-set (param $param (ref eq)) (result (ref eq))
  ;; As the last testcase, but the extra set's index is wrong, so we cannot
  ;; optimize.
  (local $temp (ref eq))
  (local.set $temp
   (local.get $param)
  )
  (if
   (ref.eq
    (local.get $temp)
    (ref.i31
     (i32.const 0)
    )
   )
   (then
    (local.set $temp
     (ref.i31
      (i32.const 1)
     )
    )
   )
   (else
    (local.set $temp
     (ref.i31
      (i32.const 2)
     )
    )
   )
  )
  (local.set $param    ;; This set now writes to $param.
   (local.get $param)
  )
  (local.get $temp)
 )

 ;; CHECK:      (func $if-wrong-extra-get (type $1) (param $param (ref eq)) (result (ref eq))
 ;; CHECK-NEXT:  (local $temp (ref eq))
 ;; CHECK-NEXT:  local.get $param
 ;; CHECK-NEXT:  i32.const 0
 ;; CHECK-NEXT:  ref.i31
 ;; CHECK-NEXT:  ref.eq
 ;; CHECK-NEXT:  if
 ;; CHECK-NEXT:   i32.const 1
 ;; CHECK-NEXT:   ref.i31
 ;; CHECK-NEXT:   local.set $temp
 ;; CHECK-NEXT:  else
 ;; CHECK-NEXT:   i32.const 2
 ;; CHECK-NEXT:   ref.i31
 ;; CHECK-NEXT:   local.set $temp
 ;; CHECK-NEXT:  end
 ;; CHECK-NEXT:  local.get $param
 ;; CHECK-NEXT:  local.set $temp
 ;; CHECK-NEXT:  local.get $param
 ;; CHECK-NEXT: )
 (func $if-wrong-extra-get (param $param (ref eq)) (result (ref eq))
  ;; As the last testcase, but now it is the get that has the wrong index to
  ;; stop us, so we can optimize.
  (local $temp (ref eq))
  (local.set $temp
   (local.get $param)
  )
  (if
   (ref.eq
    (local.get $temp)
    (ref.i31
     (i32.const 0)
    )
   )
   (then
    (local.set $temp
     (ref.i31
      (i32.const 1)
     )
    )
   )
   (else
    (local.set $temp
     (ref.i31
      (i32.const 2)
     )
    )
   )
  )
  (local.set $temp
   (local.get $param)
  )
  (local.get $param) ;; This get does not affect optimizing the pair before the
                     ;; if, because it is of another local.
 )

 ;; CHECK:      (func $if-param (type $2) (param $param (ref eq)) (param $temp (ref eq)) (result (ref eq))
 ;; CHECK-NEXT:  local.get $param
 ;; CHECK-NEXT:  i32.const 0
 ;; CHECK-NEXT:  ref.i31
 ;; CHECK-NEXT:  ref.eq
 ;; CHECK-NEXT:  if
 ;; CHECK-NEXT:   i32.const 1
 ;; CHECK-NEXT:   ref.i31
 ;; CHECK-NEXT:   local.set $temp
 ;; CHECK-NEXT:  else
 ;; CHECK-NEXT:   i32.const 2
 ;; CHECK-NEXT:   ref.i31
 ;; CHECK-NEXT:   local.set $temp
 ;; CHECK-NEXT:  end
 ;; CHECK-NEXT:  local.get $temp
 ;; CHECK-NEXT: )
 (func $if-param (param $param (ref eq)) (param $temp (ref eq)) (result (ref eq))
  ;; As the original testcase, but now $temp is a param. Validation is no
  ;; longer an issue, so we can optimize away the pair.
  (local.set $temp
   (local.get $param)
  )
  (if
   (ref.eq
    (local.get $temp)
    (ref.i31
     (i32.const 0)
    )
   )
   (then
    (local.set $temp
     (ref.i31
      (i32.const 1)
     )
    )
   )
   (else
    (local.set $temp
     (ref.i31
      (i32.const 2)
     )
    )
   )
  )
  (local.get $temp)
 )

 ;; CHECK:      (func $if-nullable (type $3) (param $param (ref eq)) (result eqref)
 ;; CHECK-NEXT:  (local $temp eqref)
 ;; CHECK-NEXT:  local.get $param
 ;; CHECK-NEXT:  i32.const 0
 ;; CHECK-NEXT:  ref.i31
 ;; CHECK-NEXT:  ref.eq
 ;; CHECK-NEXT:  if
 ;; CHECK-NEXT:   i32.const 1
 ;; CHECK-NEXT:   ref.i31
 ;; CHECK-NEXT:   local.set $temp
 ;; CHECK-NEXT:  else
 ;; CHECK-NEXT:   i32.const 2
 ;; CHECK-NEXT:   ref.i31
 ;; CHECK-NEXT:   local.set $temp
 ;; CHECK-NEXT:  end
 ;; CHECK-NEXT:  local.get $temp
 ;; CHECK-NEXT: )
 (func $if-nullable (param $param (ref eq)) (result (ref null eq))
  (local $temp (ref null eq)) ;; this changed
  ;; As the original testcase, but now $temp is a nullable. Validation is no
  ;; longer an issue, so we can optimize away the pair.
  (local.set $temp
   (local.get $param)
  )
  (if
   (ref.eq
    (local.get $temp)
    (ref.i31
     (i32.const 0)
    )
   )
   (then
    (local.set $temp
     (ref.i31
      (i32.const 1)
     )
    )
   )
   (else
    (local.set $temp
     (ref.i31
      (i32.const 2)
     )
    )
   )
  )
  (local.get $temp)
 )

 ;; CHECK:      (func $if-nondefaultable (type $1) (param $param (ref eq)) (result (ref eq))
 ;; CHECK-NEXT:  (local $temp (tuple i32 (ref eq)))
 ;; CHECK-NEXT:  i32.const 0
 ;; CHECK-NEXT:  local.get $param
 ;; CHECK-NEXT:  tuple.make 2
 ;; CHECK-NEXT:  local.set $temp
 ;; CHECK-NEXT:  local.get $temp
 ;; CHECK-NEXT:  tuple.extract 2 1
 ;; CHECK-NEXT:  i32.const 1
 ;; CHECK-NEXT:  ref.i31
 ;; CHECK-NEXT:  ref.eq
 ;; CHECK-NEXT:  if
 ;; CHECK-NEXT:   i32.const 2
 ;; CHECK-NEXT:   i32.const 3
 ;; CHECK-NEXT:   ref.i31
 ;; CHECK-NEXT:   tuple.make 2
 ;; CHECK-NEXT:   local.set $temp
 ;; CHECK-NEXT:  else
 ;; CHECK-NEXT:   i32.const 4
 ;; CHECK-NEXT:   i32.const 5
 ;; CHECK-NEXT:   ref.i31
 ;; CHECK-NEXT:   tuple.make 2
 ;; CHECK-NEXT:   local.set $temp
 ;; CHECK-NEXT:  end
 ;; CHECK-NEXT:  local.get $temp
 ;; CHECK-NEXT:  tuple.extract 2 1
 ;; CHECK-NEXT: )
 (func $if-nondefaultable (param $param (ref eq)) (result (ref eq))
  (local $temp (tuple i32 (ref eq)))
  ;; As the original testcase, but now $temp is a nondefaultable tuple rather
  ;; than a non-nullable reference by itself. We cannot remove the first set
  ;; here.
  (local.set $temp
   (tuple.make 2
    (i32.const 0)
    (local.get $param)
   )
  )
  (if
   (ref.eq
    (tuple.extract 2 1
     (local.get $temp)
    )
    (ref.i31
     (i32.const 1)
    )
   )
   (then
    (local.set $temp
     (tuple.make 2
      (i32.const 2)
      (ref.i31
       (i32.const 3)
      )
     )
    )
   )
   (else
    (local.set $temp
     (tuple.make 2
      (i32.const 4)
      (ref.i31
       (i32.const 5)
      )
     )
    )
   )
  )
  (tuple.extract 2 1
   (local.get $temp)
  )
 )

 ;; CHECK:      (func $if-defaultable (type $4) (param $param eqref) (result eqref)
 ;; CHECK-NEXT:  (local $temp (tuple i32 eqref))
 ;; CHECK-NEXT:  i32.const 0
 ;; CHECK-NEXT:  local.get $param
 ;; CHECK-NEXT:  tuple.make 2
 ;; CHECK-NEXT:  local.set $temp
 ;; CHECK-NEXT:  local.get $temp
 ;; CHECK-NEXT:  tuple.extract 2 1
 ;; CHECK-NEXT:  i32.const 1
 ;; CHECK-NEXT:  ref.i31
 ;; CHECK-NEXT:  ref.eq
 ;; CHECK-NEXT:  if
 ;; CHECK-NEXT:   i32.const 2
 ;; CHECK-NEXT:   i32.const 3
 ;; CHECK-NEXT:   ref.i31
 ;; CHECK-NEXT:   tuple.make 2
 ;; CHECK-NEXT:   local.set $temp
 ;; CHECK-NEXT:  else
 ;; CHECK-NEXT:   i32.const 4
 ;; CHECK-NEXT:   i32.const 5
 ;; CHECK-NEXT:   ref.i31
 ;; CHECK-NEXT:   tuple.make 2
 ;; CHECK-NEXT:   local.set $temp
 ;; CHECK-NEXT:  end
 ;; CHECK-NEXT:  local.get $temp
 ;; CHECK-NEXT:  tuple.extract 2 1
 ;; CHECK-NEXT: )
 (func $if-defaultable (param $param (ref null eq)) (result (ref null eq))
  (local $temp (tuple i32 (ref null eq)))
  ;; As the last testcase, but now $temp is a defaultable tuple. We still do not
  ;; optimize away the set here, as we ignore tuples in local2stack.
  (local.set $temp
   (tuple.make 2
    (i32.const 0)
    (local.get $param)
   )
  )
  (if
   (ref.eq
    (tuple.extract 2 1
     (local.get $temp)
    )
    (ref.i31
     (i32.const 1)
    )
   )
   (then
    (local.set $temp
     (tuple.make 2
      (i32.const 2)
      (ref.i31
       (i32.const 3)
      )
     )
    )
   )
   (else
    (local.set $temp
     (tuple.make 2
      (i32.const 4)
      (ref.i31
       (i32.const 5)
      )
     )
    )
   )
  )
  (tuple.extract 2 1
   (local.get $temp)
  )
 )

 ;; CHECK:      (func $if-non-ref (type $5) (param $param i32) (result i32)
 ;; CHECK-NEXT:  (local $temp i32)
 ;; CHECK-NEXT:  local.get $param
 ;; CHECK-NEXT:  i32.eqz
 ;; CHECK-NEXT:  if
 ;; CHECK-NEXT:   i32.const 1
 ;; CHECK-NEXT:   local.set $temp
 ;; CHECK-NEXT:  else
 ;; CHECK-NEXT:   i32.const 2
 ;; CHECK-NEXT:   local.set $temp
 ;; CHECK-NEXT:  end
 ;; CHECK-NEXT:  local.get $temp
 ;; CHECK-NEXT: )
 (func $if-non-ref (param $param i32) (result i32)
  (local $temp i32)
  ;; As the original testcase, but now $temp is not a ref. Validation is no
  ;; longer an issue, so we can optimize away the pair.
  (local.set $temp
   (local.get $param)
  )
  (if
   (i32.eqz
    (local.get $temp)
   )
   (then
    (local.set $temp
     (i32.const 1)
    )
   )
   (else
    (local.set $temp
     (i32.const 2)
    )
   )
  )
  (local.get $temp)
 )

 ;; CHECK:      (func $nesting (type $0) (param $param (ref eq))
 ;; CHECK-NEXT:  (local $temp (ref eq))
 ;; CHECK-NEXT:  local.get $param
 ;; CHECK-NEXT:  local.set $temp
 ;; CHECK-NEXT:  i32.const 0
 ;; CHECK-NEXT:  if
 ;; CHECK-NEXT:   local.get $param
 ;; CHECK-NEXT:   local.set $temp
 ;; CHECK-NEXT:   local.get $temp
 ;; CHECK-NEXT:   drop
 ;; CHECK-NEXT:   local.get $temp
 ;; CHECK-NEXT:   drop
 ;; CHECK-NEXT:  else
 ;; CHECK-NEXT:   local.get $param
 ;; CHECK-NEXT:   local.set $temp
 ;; CHECK-NEXT:   local.get $temp
 ;; CHECK-NEXT:   drop
 ;; CHECK-NEXT:   local.get $temp
 ;; CHECK-NEXT:   drop
 ;; CHECK-NEXT:  end
 ;; CHECK-NEXT:  local.get $temp
 ;; CHECK-NEXT:  drop
 ;; CHECK-NEXT: )
 (func $nesting (param $param (ref eq))
  (local $temp (ref eq))
  ;; The if arms contain optimization opportunities, even though there are 2
  ;; gets in each one, because this top set helps them all validate. Atm we do
  ;; not look backwards, however, so we fail to optimize here.
  (local.set $temp
   (local.get $param)
  )
  (if
   (i32.const 0)
   (then
    (block
     (local.set $temp
      (local.get $param)
     )
     (drop
      (local.get $temp)
     )
     (drop
      (local.get $temp)
     )
    )
   )
   (else
    (block
     (local.set $temp
      (local.get $param)
     )
     (drop
      (local.get $temp)
     )
     (drop
      (local.get $temp)
     )
    )
   )
  )
  (drop
   (local.get $temp)
  )
 )

 ;; CHECK:      (func $nesting-left (type $0) (param $param (ref eq))
 ;; CHECK-NEXT:  (local $temp (ref eq))
 ;; CHECK-NEXT:  local.get $param
 ;; CHECK-NEXT:  local.set $temp
 ;; CHECK-NEXT:  i32.const 0
 ;; CHECK-NEXT:  if
 ;; CHECK-NEXT:   local.get $param
 ;; CHECK-NEXT:   drop
 ;; CHECK-NEXT:  else
 ;; CHECK-NEXT:   local.get $param
 ;; CHECK-NEXT:   local.set $temp
 ;; CHECK-NEXT:   local.get $temp
 ;; CHECK-NEXT:   drop
 ;; CHECK-NEXT:   local.get $temp
 ;; CHECK-NEXT:   drop
 ;; CHECK-NEXT:  end
 ;; CHECK-NEXT: )
 (func $nesting-left (param $param (ref eq))
  (local $temp (ref eq))
  ;; As $nesting, but now the left arm has one get, and can be optimized.
  (local.set $temp
   (local.get $param)
  )
  (if
   (i32.const 0)
   (then
    (block
     (local.set $temp
      (local.get $param)
     )
     (drop
      (local.get $temp)
     )
     ;; A get was removed here.
    )
   )
   (else
    (block
     (local.set $temp
      (local.get $param)
     )
     (drop
      (local.get $temp)
     )
     (drop
      (local.get $temp)
     )
    )
   )
  )
 )

 ;; CHECK:      (func $nesting-right (type $0) (param $param (ref eq))
 ;; CHECK-NEXT:  (local $temp (ref eq))
 ;; CHECK-NEXT:  local.get $param
 ;; CHECK-NEXT:  local.set $temp
 ;; CHECK-NEXT:  i32.const 0
 ;; CHECK-NEXT:  if
 ;; CHECK-NEXT:   local.get $param
 ;; CHECK-NEXT:   local.set $temp
 ;; CHECK-NEXT:   local.get $temp
 ;; CHECK-NEXT:   drop
 ;; CHECK-NEXT:   local.get $temp
 ;; CHECK-NEXT:   drop
 ;; CHECK-NEXT:  else
 ;; CHECK-NEXT:   local.get $param
 ;; CHECK-NEXT:   drop
 ;; CHECK-NEXT:  end
 ;; CHECK-NEXT: )
 (func $nesting-right (param $param (ref eq))
  (local $temp (ref eq))
  ;; As above, but now we can optimize the right arm.
  (local.set $temp
   (local.get $param)
  )
  (if
   (i32.const 0)
   (then
    (block
     (local.set $temp
      (local.get $param)
     )
     (drop
      (local.get $temp)
     )
     (drop
      (local.get $temp)
     )
    )
   )
   (else
    (block
     (local.set $temp
      (local.get $param)
     )
     (drop
      (local.get $temp)
     )
     ;; A get was removed here.
    )
   )
  )
 )

 ;; CHECK:      (func $nesting-both (type $0) (param $param (ref eq))
 ;; CHECK-NEXT:  (local $temp (ref eq))
 ;; CHECK-NEXT:  local.get $param
 ;; CHECK-NEXT:  local.set $temp
 ;; CHECK-NEXT:  i32.const 0
 ;; CHECK-NEXT:  if
 ;; CHECK-NEXT:   local.get $param
 ;; CHECK-NEXT:   drop
 ;; CHECK-NEXT:  else
 ;; CHECK-NEXT:   local.get $param
 ;; CHECK-NEXT:   drop
 ;; CHECK-NEXT:  end
 ;; CHECK-NEXT: )
 (func $nesting-both (param $param (ref eq))
  (local $temp (ref eq))
  ;; As above, but now we can optimize both arms.
  (local.set $temp
   (local.get $param)
  )
  (if
   (i32.const 0)
   (then
    (block
     (local.set $temp
      (local.get $param)
     )
     (drop
      (local.get $temp)
     )
     ;; A get was removed here.
    )
   )
   (else
    (block
     (local.set $temp
      (local.get $param)
     )
     (drop
      (local.get $temp)
     )
     ;; A get was removed here.
    )
   )
  )
 )

 ;; CHECK:      (func $nesting-both-less (type $0) (param $param (ref eq))
 ;; CHECK-NEXT:  (local $temp (ref eq))
 ;; CHECK-NEXT:  i32.const 0
 ;; CHECK-NEXT:  if
 ;; CHECK-NEXT:   local.get $param
 ;; CHECK-NEXT:   drop
 ;; CHECK-NEXT:  else
 ;; CHECK-NEXT:   local.get $param
 ;; CHECK-NEXT:   drop
 ;; CHECK-NEXT:  end
 ;; CHECK-NEXT: )
 (func $nesting-both-less (param $param (ref eq))
  (local $temp (ref eq))
  ;; As above, but without the initial set-get at the top. We can still
  ;; optimize both arms.
  (if
   (i32.const 0)
   (then
    (block
     (local.set $temp
      (local.get $param)
     )
     (drop
      (local.get $temp)
     )
    )
   )
   (else
    (block
     (local.set $temp
      (local.get $param)
     )
     (drop
      (local.get $temp)
     )
    )
   )
  )
 )

 ;; CHECK:      (func $nesting-both-after (type $0) (param $param (ref eq))
 ;; CHECK-NEXT:  (local $temp (ref eq))
 ;; CHECK-NEXT:  i32.const 0
 ;; CHECK-NEXT:  if
 ;; CHECK-NEXT:   local.get $param
 ;; CHECK-NEXT:   drop
 ;; CHECK-NEXT:  else
 ;; CHECK-NEXT:   local.get $param
 ;; CHECK-NEXT:   drop
 ;; CHECK-NEXT:  end
 ;; CHECK-NEXT:  local.get $param
 ;; CHECK-NEXT:  drop
 ;; CHECK-NEXT: )
 (func $nesting-both-after (param $param (ref eq))
  (local $temp (ref eq))
  ;; As above, but now there is a set-get at the end of the function. The get
  ;; there should not confuse us; we can still optimize both arms, and after
  ;; them as well.
  (if
   (i32.const 0)
   (then
    (block
     (local.set $temp
      (local.get $param)
     )
     (drop
      (local.get $temp)
     )
    )
   )
   (else
    (block
     (local.set $temp
      (local.get $param)
     )
     (drop
      (local.get $temp)
     )
    )
   )
  )
  (local.set $temp
   (local.get $param)
  )
  (drop
   (local.get $temp)
  )
 )

 ;; CHECK:      (func $nesting-irrelevant (type $0) (param $param (ref eq))
 ;; CHECK-NEXT:  (local $temp (ref eq))
 ;; CHECK-NEXT:  local.get $param
 ;; CHECK-NEXT:  drop
 ;; CHECK-NEXT: )
 (func $nesting-irrelevant (param $param (ref eq))
  (local $temp (ref eq))
  ;; The block in the middle here adds a scope, but it does not prevent us from
  ;; optimizing.
  (local.set $temp
   (local.get $param)
  )
  (block $block
  )
  (drop
   (local.get $temp)
  )
 )

 ;; CHECK:      (func $nesting-relevant (type $0) (param $param (ref eq))
 ;; CHECK-NEXT:  (local $temp (ref eq))
 ;; CHECK-NEXT:  local.get $param
 ;; CHECK-NEXT:  local.set $temp
 ;; CHECK-NEXT:  local.get $temp
 ;; CHECK-NEXT:  drop
 ;; CHECK-NEXT:  local.get $temp
 ;; CHECK-NEXT:  drop
 ;; CHECK-NEXT: )
 (func $nesting-relevant (param $param (ref eq))
  (local $temp (ref eq))
  ;; As above, but now there is a get in that scope, which is a problem.
  (local.set $temp
   (local.get $param)
  )
  (block $block
   (drop
    (local.get $temp)
   )
  )
  (drop
   (local.get $temp)
  )
 )

 ;; CHECK:      (func $nesting-after (type $0) (param $param (ref eq))
 ;; CHECK-NEXT:  (local $temp (ref eq))
 ;; CHECK-NEXT:  local.get $param
 ;; CHECK-NEXT:  drop
 ;; CHECK-NEXT:  local.get $param
 ;; CHECK-NEXT:  drop
 ;; CHECK-NEXT: )
 (func $nesting-after (param $param (ref eq))
  (local $temp (ref eq))
  ;; A set-get pair with another after it in a block. We can optimize both.
  (local.set $temp
   (local.get $param)
  )
  (drop
   (local.get $temp)
  )
  (block $block
   (local.set $temp
    (local.get $param)
   )
   (drop
    (local.get $temp)
   )
  )
 )

 ;; CHECK:      (func $nesting-reverse (type $0) (param $param (ref eq))
 ;; CHECK-NEXT:  (local $temp (ref eq))
 ;; CHECK-NEXT:  local.get $param
 ;; CHECK-NEXT:  drop
 ;; CHECK-NEXT:  local.get $param
 ;; CHECK-NEXT:  drop
 ;; CHECK-NEXT: )
 (func $nesting-reverse (param $param (ref eq))
  (local $temp (ref eq))
  ;; The reverse of the last case, now the block is first. We can optimize
  ;; both pairs.
  (block $block
   (local.set $temp
    (local.get $param)
   )
   (drop
    (local.get $temp)
   )
  )
  (local.set $temp
   (local.get $param)
  )
  (drop
   (local.get $temp)
  )
 )

 ;; CHECK:      (func $nesting-covered-but-ended (type $0) (param $param (ref eq))
 ;; CHECK-NEXT:  (local $temp (ref eq))
 ;; CHECK-NEXT:  local.get $param
 ;; CHECK-NEXT:  local.set $temp
 ;; CHECK-NEXT:  local.get $temp
 ;; CHECK-NEXT:  drop
 ;; CHECK-NEXT:  local.get $param
 ;; CHECK-NEXT:  local.set $temp
 ;; CHECK-NEXT:  local.get $temp
 ;; CHECK-NEXT:  drop
 ;; CHECK-NEXT:  local.get $temp
 ;; CHECK-NEXT:  drop
 ;; CHECK-NEXT: )
 (func $nesting-covered-but-ended (param $param (ref eq))
  (local $temp (ref eq))
  ;; We cannot optimize this first pair, because while we see another set after
  ;; us, its scope ends, so our set must help the very final get validate.
  (local.set $temp
   (local.get $param)
  )
  (drop
   (local.get $temp)
  )
  (block $block
   ;; This pair we can almost optimize, but the get after the block reads from
   ;; it, so we don't.
   (local.set $temp
    (local.get $param)
   )
   (drop
    (local.get $temp)
   )
  )
  (drop
   (local.get $temp)
  )
 )

 ;; CHECK:      (func $two-covers (type $1) (param $param (ref eq)) (result (ref eq))
 ;; CHECK-NEXT:  (local $temp (ref eq))
 ;; CHECK-NEXT:  local.get $param
 ;; CHECK-NEXT:  local.set $temp
 ;; CHECK-NEXT:  local.get $temp
 ;; CHECK-NEXT:  i32.const 0
 ;; CHECK-NEXT:  ref.i31
 ;; CHECK-NEXT:  ref.eq
 ;; CHECK-NEXT:  if
 ;; CHECK-NEXT:   local.get $param
 ;; CHECK-NEXT:   local.tee $temp
 ;; CHECK-NEXT:   local.set $temp
 ;; CHECK-NEXT:  else
 ;; CHECK-NEXT:   local.get $param
 ;; CHECK-NEXT:   local.set $temp
 ;; CHECK-NEXT:  end
 ;; CHECK-NEXT:  local.get $temp
 ;; CHECK-NEXT: )
 (func $two-covers (param $param (ref eq)) (result (ref eq))
  (local $temp (ref eq))
  (local.set $temp
   (local.get $param)
  )
  (if
   (ref.eq
    (local.get $temp)
    (ref.i31
     (i32.const 0)
    )
   )
   ;; In this if arm we write to $temp twice. That shouldn't confuse us; there's
   ;; still a use after the if, and we should not remove the set-get pair before
   ;; the if.
   (then
    (local.set $temp
     (local.tee $temp
      (local.get $param)
     )
    )
   )
   (else
    (local.set $temp
     (local.get $param)
    )
   )
  )
  (local.get $temp)
 )
)