blob: 508fe574f6ca3119824be84cfef563251d6d5675 [file] [log] [blame] [edit]
;; 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)
)
)