| ;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. |
| ;; RUN: wasm-opt %s --code-pushing -all -S -o - | filecheck %s |
| |
| (module |
| ;; CHECK: (import "binaryen-intrinsics" "call.without.effects" (func $call.without.effects (type $2) (param i32 funcref) (result i32))) |
| (import "binaryen-intrinsics" "call.without.effects" (func $call.without.effects (param i32 funcref) (result i32))) |
| |
| ;; CHECK: (func $if-nop (type $0) (param $p i32) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $p) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $if-nop (param $p i32) |
| (local $x i32) |
| ;; The set local is not used in any if arm; do nothing. |
| (local.set $x (i32.const 1)) |
| (if |
| (local.get $p) |
| (then |
| (nop) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $if-nop-nop (type $0) (param $p i32) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $p) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $if-nop-nop (param $p i32) |
| (local $x i32) |
| (local.set $x (i32.const 1)) |
| (if |
| (local.get $p) |
| (then |
| (nop) |
| ) |
| (else |
| (nop) ;; add a nop here compared to the last testcase (no output change) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $if-use (type $0) (param $p i32) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $p) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $if-use (param $p i32) |
| (local $x i32) |
| ;; The set local is used in one arm and nowhere else; push it there. |
| (local.set $x (i32.const 1)) |
| (if |
| (local.get $p) |
| (then |
| (drop (local.get $x)) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $if-use-nop (type $0) (param $p i32) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $p) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $if-use-nop (param $p i32) |
| (local $x i32) |
| (local.set $x (i32.const 1)) |
| (if |
| (local.get $p) |
| (then |
| (drop (local.get $x)) |
| ) |
| (else |
| (nop) ;; add a nop here compared to the last testcase (no output change) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $if-else-use (type $0) (param $p i32) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $p) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $if-else-use (param $p i32) |
| (local $x i32) |
| ;; The set local is used in one arm and nowhere else; push it there. |
| (local.set $x (i32.const 1)) |
| (if |
| (local.get $p) |
| (then |
| (nop) |
| ) |
| (else |
| (drop (local.get $x)) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $unpushed-interference (type $0) (param $p i32) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local $y i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $y |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $p) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $unpushed-interference (param $p i32) |
| (local $x i32) |
| (local $y i32) |
| (local.set $x (i32.const 1)) |
| ;; This set is not pushed (as it is not used in the if) and it will then |
| ;; prevent the previous set of $x from being pushed, since we can't push a |
| ;; set of $x past a get of it. |
| (local.set $y (local.get $x)) |
| (if |
| (local.get $p) |
| (then |
| (drop (local.get $x)) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $if-use-use (type $0) (param $p i32) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $p) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $if-use-use (param $p i32) |
| (local $x i32) |
| ;; The set local is used in both arms, so we can't do anything. |
| (local.set $x (i32.const 1)) |
| (if |
| (local.get $p) |
| (then |
| (drop (local.get $x)) |
| ) |
| (else |
| (drop (local.get $x)) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $if-use-after (type $0) (param $p i32) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $p) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $if-use-after (param $p i32) |
| (local $x i32) |
| ;; The use after the if prevents optimization. |
| (local.set $x (i32.const 1)) |
| (if |
| (local.get $p) |
| (then |
| (drop (local.get $x)) |
| ) |
| ) |
| (drop (local.get $x)) |
| ) |
| |
| ;; CHECK: (func $if-use-after-nop (type $0) (param $p i32) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $p) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $if-use-after-nop (param $p i32) |
| (local $x i32) |
| (local.set $x (i32.const 1)) |
| (if |
| (local.get $p) |
| (then |
| (drop (local.get $x)) |
| ) |
| (else |
| (nop) ;; add a nop here compared to the last testcase (no output change) |
| ) |
| ) |
| (drop (local.get $x)) |
| ) |
| |
| ;; CHECK: (func $if-else-use-after (type $0) (param $p i32) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $p) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $if-else-use-after (param $p i32) |
| (local $x i32) |
| (local.set $x (i32.const 1)) |
| (if |
| (local.get $p) |
| (then |
| (nop) |
| ) |
| (else |
| (drop (local.get $x)) ;; now the use in the if is in the else arm |
| ) |
| ) |
| (drop (local.get $x)) |
| ) |
| |
| ;; CHECK: (func $if-use-after-unreachable (type $0) (param $p i32) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $p) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (return) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $if-use-after-unreachable (param $p i32) |
| (local $x i32) |
| ;; A use after the if is ok as the other arm is unreachable. |
| (local.set $x (i32.const 1)) |
| (if |
| (local.get $p) |
| (then |
| (drop (local.get $x)) |
| ) |
| (else |
| (return) |
| ) |
| ) |
| (drop (local.get $x)) |
| ) |
| |
| ;; CHECK: (func $if-use-after-unreachable-else (type $0) (param $p i32) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $p) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (return) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $if-use-after-unreachable-else (param $p i32) |
| (local $x i32) |
| (local.set $x (i32.const 1)) |
| (if |
| (local.get $p) |
| (then |
| (return) ;; as above, but with arms flipped |
| ) |
| (else |
| (drop (local.get $x)) |
| ) |
| ) |
| (drop (local.get $x)) |
| ) |
| |
| ;; CHECK: (func $optimize-many (type $0) (param $p i32) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local $y i32) |
| ;; CHECK-NEXT: (local $z i32) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $p) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $z |
| ;; CHECK-NEXT: (i32.const 3) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $z) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (local.set $y |
| ;; CHECK-NEXT: (i32.const 2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $y) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $optimize-many (param $p i32) |
| (local $x i32) |
| (local $y i32) |
| (local $z i32) |
| ;; Multiple things we can push, to various arms. |
| (local.set $x (i32.const 1)) |
| (local.set $y (i32.const 2)) |
| (local.set $z (i32.const 3)) |
| (if |
| (local.get $p) |
| (then |
| (block |
| (drop (local.get $x)) |
| (drop (local.get $z)) |
| ) |
| ) |
| (else |
| (drop (local.get $y)) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $past-other (type $0) (param $p i32) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local $t i32) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $p) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (local.get $t) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $past-other (param $p i32) |
| (local $x i32) |
| (local $t i32) |
| ;; We can push this past the drop after it. |
| (local.set $x (local.get $t)) |
| (drop (i32.const 2)) |
| (if |
| (local.get $p) |
| (then |
| (drop (local.get $x)) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $past-other-no (type $0) (param $p i32) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local $t i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (local.get $t) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.tee $t |
| ;; CHECK-NEXT: (i32.const 2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $p) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $past-other-no (param $p i32) |
| (local $x i32) |
| (local $t i32) |
| ;; We cannot push this due to the tee, which interferes with us. |
| (local.set $x (local.get $t)) |
| (drop (local.tee $t (i32.const 2))) |
| (if |
| (local.get $p) |
| (then |
| (drop (local.get $x)) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $past-condition-no (type $0) (param $p i32) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local $t i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (local.get $t) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.tee $t |
| ;; CHECK-NEXT: (local.get $p) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $past-condition-no (param $p i32) |
| (local $x i32) |
| (local $t i32) |
| ;; We cannot push this due to the tee in the if condition. |
| (local.set $x (local.get $t)) |
| (if |
| (local.tee $t (local.get $p)) |
| (then |
| (drop (local.get $x)) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $past-condition-no-2 (type $3) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local $t i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (local.get $t) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $past-condition-no-2 |
| (local $x i32) |
| (local $t i32) |
| ;; We cannot push this due to the read of $x in the if condition. |
| (local.set $x (local.get $t)) |
| (if |
| (local.get $x) |
| (then |
| (drop (local.get $x)) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $past-condition-no-3 (type $0) (param $p i32) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local $t i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (local.get $t) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.tee $x |
| ;; CHECK-NEXT: (local.get $p) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $past-condition-no-3 (param $p i32) |
| (local $x i32) |
| (local $t i32) |
| ;; We cannot push this due to the write of $x in the if condition. |
| (local.set $x (local.get $t)) |
| (if |
| (local.tee $x (local.get $p)) |
| (then |
| (drop (local.get $x)) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $if-condition-return (type $0) (param $p i32) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (return) |
| ;; CHECK-NEXT: (local.get $p) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $if-condition-return (param $p i32) |
| (local $x i32) |
| (local.set $x (i32.const 1)) |
| (if |
| (block (result i32) |
| (return) ;; This return does not prevent us from optimizing; if it |
| ;; happens then we don't need the local.set to execute |
| ;; anyhow. |
| (local.get $p) |
| ) |
| (then |
| (drop (local.get $x)) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $if-condition-break-used (type $0) (param $p i32) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (block $out |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (br $out) |
| ;; CHECK-NEXT: (local.get $p) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (return) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $if-condition-break-used (param $p i32) |
| (local $x i32) |
| (local.set $x (i32.const 1)) |
| ;; As above, but the return is replaced with a break. The break goes to a |
| ;; location with a use of the local, which prevents optimization. |
| (block $out |
| (if |
| (block (result i32) |
| (br $out) |
| (local.get $p) |
| ) |
| (then |
| (drop (local.get $x)) |
| ) |
| ) |
| (return) |
| ) |
| (drop (local.get $x)) |
| ) |
| |
| ;; CHECK: (func $one-push-prevents-another (type $0) (param $p i32) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local $y i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $p) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (local.set $y |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $y) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $one-push-prevents-another (param $p i32) |
| (local $x i32) |
| (local $y i32) |
| ;; We will push $y into one arm, and as a result both arms will have a get |
| ;; of $x, which prevents pushing $x. |
| (local.set $x (i32.const 1)) |
| (local.set $y (local.get $x)) |
| (if |
| (local.get $p) |
| (then |
| (drop (local.get $x)) |
| ) |
| (else |
| (drop (local.get $y)) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $one-push-prevents-another-flipped (type $0) (param $p i32) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local $y i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $p) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (local.set $y |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $y) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $one-push-prevents-another-flipped (param $p i32) |
| (local $x i32) |
| (local $y i32) |
| ;; As above but with if arms flipped. The result should be similar, with |
| ;; only $y pushed. |
| (local.set $x (i32.const 1)) |
| (local.set $y (local.get $x)) |
| (if |
| (local.get $p) |
| (then |
| (drop (local.get $y)) |
| ) |
| (else |
| (drop (local.get $x)) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $sink-call (type $1) (param $p i32) (result i32) |
| ;; CHECK-NEXT: (local $temp i32) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $p) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (local.set $temp |
| ;; CHECK-NEXT: (call $call.without.effects |
| ;; CHECK-NEXT: (i32.const 1234) |
| ;; CHECK-NEXT: (ref.func $sink-call) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (return |
| ;; CHECK-NEXT: (local.get $temp) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| (func $sink-call (param $p i32) (result i32) |
| (local $temp i32) |
| |
| ;; This local has a call, but the call is an intrinsic indicating no |
| ;; effects, so it is safe to sink into the if. |
| (local.set $temp |
| (call $call.without.effects |
| (i32.const 1234) |
| (ref.func $sink-call) |
| ) |
| ) |
| (if |
| (local.get $p) |
| (then |
| (return |
| (local.get $temp) |
| ) |
| ) |
| ) |
| (i32.const 0) |
| ) |
| |
| ;; CHECK: (func $no-sink-call (type $1) (param $p i32) (result i32) |
| ;; CHECK-NEXT: (local $temp i32) |
| ;; CHECK-NEXT: (local.set $temp |
| ;; CHECK-NEXT: (call $call.without.effects |
| ;; CHECK-NEXT: (i32.const 1234) |
| ;; CHECK-NEXT: (ref.func $no-sink-call) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $p) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (return |
| ;; CHECK-NEXT: (local.get $temp) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $temp) |
| ;; CHECK-NEXT: ) |
| (func $no-sink-call (param $p i32) (result i32) |
| (local $temp i32) |
| |
| ;; As above, but now after the if we have a get of the temp local, so we |
| ;; cannot sink. This + the previous testcase show we scan for such local |
| ;; uses in exactly the right places. |
| (local.set $temp |
| (call $call.without.effects |
| (i32.const 1234) |
| (ref.func $no-sink-call) |
| ) |
| ) |
| (if |
| (local.get $p) |
| (then |
| (return |
| (local.get $temp) |
| ) |
| ) |
| ) |
| (local.get $temp) ;; this line changed. |
| ) |
| |
| ;; CHECK: (func $no-sink-call-2 (type $1) (param $p i32) (result i32) |
| ;; CHECK-NEXT: (local $temp i32) |
| ;; CHECK-NEXT: (local.set $temp |
| ;; CHECK-NEXT: (call $call.without.effects |
| ;; CHECK-NEXT: (i32.const 1234) |
| ;; CHECK-NEXT: (ref.func $no-sink-call-2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $p) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (return |
| ;; CHECK-NEXT: (local.get $temp) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: (local.get $temp) |
| ;; CHECK-NEXT: ) |
| (func $no-sink-call-2 (param $p i32) (result i32) |
| (local $temp i32) |
| |
| ;; As above, but add a nop before the final value. We still should not |
| ;; optimize. |
| (local.set $temp |
| (call $call.without.effects |
| (i32.const 1234) |
| (ref.func $no-sink-call-2) |
| ) |
| ) |
| (if |
| (local.get $p) |
| (then |
| (return |
| (local.get $temp) |
| ) |
| ) |
| ) |
| (nop) ;; this line was added. |
| (local.get $temp) |
| ) |
| |
| ;; CHECK: (func $no-sink-call-3 (type $1) (param $p i32) (result i32) |
| ;; CHECK-NEXT: (local $temp i32) |
| ;; CHECK-NEXT: (local.set $temp |
| ;; CHECK-NEXT: (call $call.without.effects |
| ;; CHECK-NEXT: (i32.const 1234) |
| ;; CHECK-NEXT: (ref.func $no-sink-call-3) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $p) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (return |
| ;; CHECK-NEXT: (local.get $temp) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $temp) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| (func $no-sink-call-3 (param $p i32) (result i32) |
| (local $temp i32) |
| |
| ;; As above, but add a nop after the get of the local after the if. We still |
| ;; should not optimize. |
| (local.set $temp |
| (call $call.without.effects |
| (i32.const 1234) |
| (ref.func $no-sink-call-3) |
| ) |
| ) |
| (if |
| (local.get $p) |
| (then |
| (return |
| (local.get $temp) |
| ) |
| ) |
| ) |
| (nop) |
| (drop |
| (local.get $temp) ;; this get is now dropped. |
| ) |
| (nop) ;; this line was added; |
| (i32.const 0) ;; this line was added. |
| ) |
| |
| ;; CHECK: (func $sink-call-3 (type $1) (param $p i32) (result i32) |
| ;; CHECK-NEXT: (local $temp i32) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $p) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (local.set $temp |
| ;; CHECK-NEXT: (call $call.without.effects |
| ;; CHECK-NEXT: (i32.const 1234) |
| ;; CHECK-NEXT: (ref.func $no-sink-call-3) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (return |
| ;; CHECK-NEXT: (local.get $temp) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $p) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| (func $sink-call-3 (param $p i32) (result i32) |
| (local $temp i32) |
| |
| ;; As above, but stop reading the relevant local after the if, keeping the |
| ;; number of other items unchanged. This verifies the presence of multiple |
| ;; items is not a problem and we can optimize. |
| (local.set $temp |
| (call $call.without.effects |
| (i32.const 1234) |
| (ref.func $no-sink-call-3) |
| ) |
| ) |
| (if |
| (local.get $p) |
| (then |
| (return |
| (local.get $temp) |
| ) |
| ) |
| ) |
| (nop) |
| (drop |
| (local.get $p) ;; this get now reads $p |
| ) |
| (nop) |
| (i32.const 0) |
| ) |
| |
| ;; CHECK: (func $no-sink-call-sub (type $1) (param $p i32) (result i32) |
| ;; CHECK-NEXT: (local $temp i32) |
| ;; CHECK-NEXT: (local $other i32) |
| ;; CHECK-NEXT: (local.set $temp |
| ;; CHECK-NEXT: (call $call.without.effects |
| ;; CHECK-NEXT: (local.tee $other |
| ;; CHECK-NEXT: (i32.const 1234) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.func $no-sink-call) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $p) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (return |
| ;; CHECK-NEXT: (local.get $temp) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| (func $no-sink-call-sub (param $p i32) (result i32) |
| (local $temp i32) |
| (local $other i32) |
| |
| ;; The call has no effects, but one of the arguments to it does, so we |
| ;; cannot optimize. |
| (local.set $temp |
| (call $call.without.effects |
| (local.tee $other ;; an effect |
| (i32.const 1234) |
| ) |
| (ref.func $no-sink-call) |
| ) |
| ) |
| (if |
| (local.get $p) |
| (then |
| (return |
| (local.get $temp) |
| ) |
| ) |
| ) |
| (i32.const 0) |
| ) |
| |
| ;; CHECK: (func $ref-into-if (type $4) (param $0 (ref any)) |
| ;; CHECK-NEXT: (local $1 anyref) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (block $label$3 |
| ;; CHECK-NEXT: (local.set $1 |
| ;; CHECK-NEXT: (local.get $0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $ref-into-if (param $0 (ref any)) |
| (local $1 (ref any)) |
| ;; This can be pushed into the reachable if arm. After doing so, however, |
| ;; the local $1 no longer has a single set that dominates it in the sense of |
| ;; the wasm validation rules for non-nullable locals, so the local must be |
| ;; turned into a nullable one. |
| (local.set $1 |
| (local.get $0) |
| ) |
| (if |
| (i32.const 1) |
| (then |
| (unreachable) |
| ) |
| (else |
| (block $label$3 |
| (drop |
| (local.get $1) |
| ) |
| ) |
| ) |
| ) |
| (drop |
| (local.get $1) |
| ) |
| ) |
| |
| ) |