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