| ;; 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) |
| ) |
| ) |