| ;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. |
| |
| ;; RUN: wasm-opt %s --constraint-analysis -all -S -o - | filecheck %s |
| |
| ;; Also run after optimize-instructions, which canonicalizes the order of |
| ;; things like binary children. We want to see that this pass optimizes the |
| ;; IR that optimize-instructions emits. |
| |
| ;; RUN: wasm-opt %s --optimize-instructions --constraint-analysis -all -S -o - | filecheck %s --check-prefix=OPTIN |
| |
| (module |
| ;; CHECK: (type $array (array (mut i32))) |
| ;; OPTIN: (type $array (array (mut i32))) |
| (type $array (array (mut i32))) |
| |
| ;; CHECK: (import "a" "b" (func $import (type $7) (result i32))) |
| ;; OPTIN: (import "a" "b" (func $import (type $7) (result i32))) |
| (import "a" "b" (func $import (result i32))) |
| |
| ;; CHECK: (func $simple (type $1) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $simple (type $1) |
| ;; OPTIN-NEXT: (local $x i32) |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $simple |
| (local $x i32) |
| ;; Set x to 10, and then compare it to 10 and 20 using == and !=, all in a |
| ;; single basic block. |
| (local.set $x |
| (i32.const 10) |
| ) |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 20) |
| ) |
| ) |
| (drop |
| (i32.ne |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.ne |
| (local.get $x) |
| (i32.const 20) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $unknown (type $1) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.add |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: (i32.const 20) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (i32.const 30) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.div_s |
| ;; CHECK-NEXT: (i32.const 1337) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (i32.const 31) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $unknown (type $1) |
| ;; OPTIN-NEXT: (local $x i32) |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (i32.const 30) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (i32.div_u |
| ;; OPTIN-NEXT: (i32.const 1337) |
| ;; OPTIN-NEXT: (i32.const 42) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 31) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $unknown |
| (local $x i32) |
| ;; Set x to an add. We can only optimize this if optimize-instructions first |
| ;; simplifies it to a constant. |
| (local.set $x |
| (i32.add |
| (i32.const 10) |
| (i32.const 20) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 30) |
| ) |
| ) |
| ;; When optimize-instructions does not help, we infer nothing. |
| (local.set $x |
| (i32.div_s |
| (i32.const 1337) |
| (i32.const 42) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 31) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $multi-local (type $1) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local $y i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $y |
| ;; CHECK-NEXT: (i32.const 20) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 15) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $multi-local (type $1) |
| ;; OPTIN-NEXT: (local $x i32) |
| ;; OPTIN-NEXT: (local $y i32) |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (local.set $y |
| ;; OPTIN-NEXT: (i32.const 20) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (i32.const 15) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $multi-local |
| (local $x i32) |
| (local $y i32) |
| ;; x is 10, y is 20 |
| (local.set $x |
| (i32.const 10) |
| ) |
| (local.set $y |
| (i32.const 20) |
| ) |
| ;; Verify those values. |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $y) |
| (i32.const 20) |
| ) |
| ) |
| ;; Overwrite x to 15. |
| (local.set $x |
| (i32.const 15) |
| ) |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 15) |
| ) |
| ) |
| ;; y is unchanged. |
| (drop |
| (i32.eq |
| (local.get $y) |
| (i32.const 20) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $multi-block (type $0) (param $param i32) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $param) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $multi-block (type $0) (param $param i32) |
| ;; OPTIN-NEXT: (local $x i32) |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (if (result i32) |
| ;; OPTIN-NEXT: (local.get $param) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (else |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $multi-block (param $param i32) |
| (local $x i32) |
| (local.set $x |
| (i32.const 10) |
| ) |
| ;; We can infer into these basic blocks. |
| (if |
| (local.get $param) |
| (then |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| ) |
| (else |
| (drop |
| (i32.ne ;; also test a not-equals here; result is 0 |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| ) |
| ) |
| ;; We can infer after the merge. |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $multi-block-split (type $0) (param $param i32) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $param) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 20) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (i32.const 20) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $multi-block-split (type $0) (param $param i32) |
| ;; OPTIN-NEXT: (local $x i32) |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (if (result i32) |
| ;; OPTIN-NEXT: (local.get $param) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (else |
| ;; OPTIN-NEXT: (i32.const 20) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 20) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $multi-block-split (param $param i32) |
| (local $x i32) |
| (if |
| (local.get $param) |
| (then |
| (local.set $x |
| (i32.const 10) |
| ) |
| ) |
| (else |
| (local.set $x |
| (i32.const 20) |
| ) |
| ) |
| ) |
| ;; x is either 10 or 20, but not known to be one or the other. |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 20) |
| ) |
| ) |
| ;; TODO: test we can infer x >= 10 etc. |
| ) |
| |
| ;; CHECK: (func $multi-block-split-2 (type $0) (param $param i32) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $param) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 20) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (i32.const 20) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $multi-block-split-2 (type $0) (param $param i32) |
| ;; OPTIN-NEXT: (local $x i32) |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (local.get $param) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (i32.const 20) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 20) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $multi-block-split-2 (param $param i32) |
| (local $x i32) |
| ;; As above, but one if arm, and a set before the if. |
| (local.set $x |
| (i32.const 10) |
| ) |
| (if |
| (local.get $param) |
| (then |
| (local.set $x |
| (i32.const 20) |
| ) |
| ) |
| ) |
| ;; Again, we cannot infer. |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 20) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $multi-block-split-yes (type $0) (param $param i32) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $param) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $multi-block-split-yes (type $0) (param $param i32) |
| ;; OPTIN-NEXT: (local $x i32) |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (local.get $param) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $multi-block-split-yes (param $param i32) |
| (local $x i32) |
| ;; As above, but now we set 10 again in the if arm. |
| (local.set $x |
| (i32.const 10) |
| ) |
| (if |
| (local.get $param) |
| (then |
| (local.set $x |
| (i32.const 10) |
| ) |
| ) |
| ) |
| ;; Now we can infer 1 and 0 here. |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 20) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $loop (type $0) (param $param i32) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (loop $loop |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (br_if $loop |
| ;; CHECK-NEXT: (local.get $param) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $loop (type $0) (param $param i32) |
| ;; OPTIN-NEXT: (local $x i32) |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (loop $loop |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (br_if $loop |
| ;; OPTIN-NEXT: (local.get $param) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $loop (param $param i32) |
| (local $x i32) |
| ;; Set $x to 10 before the loop. |
| (local.set $x |
| (i32.const 10) |
| ) |
| (loop $loop |
| ;; Despite the backedges, we can infer x is 10 and not 20. |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.ne |
| (local.get $x) |
| (i32.const 20) |
| ) |
| ) |
| (br_if $loop |
| (local.get $param) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $loop-no (type $0) (param $param i32) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (loop $loop |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.ne |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (i32.const 20) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 20) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (br_if $loop |
| ;; CHECK-NEXT: (local.get $param) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $loop-no (type $0) (param $param i32) |
| ;; OPTIN-NEXT: (local $x i32) |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (loop $loop |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.ne |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 20) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (i32.const 20) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (br_if $loop |
| ;; OPTIN-NEXT: (local.get $param) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $loop-no (param $param i32) |
| (local $x i32) |
| ;; As above, but now with another value set in the loop. We cannot infer. |
| (local.set $x |
| (i32.const 10) |
| ) |
| (loop $loop |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.ne |
| (local.get $x) |
| (i32.const 20) |
| ) |
| ) |
| (local.set $x |
| (i32.const 20) |
| ) |
| (br_if $loop |
| (local.get $param) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $default-var (type $0) (param $param i32) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local $eq eqref) |
| ;; CHECK-NEXT: (local $nn-eq (ref eq)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $param) |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $default-var (type $0) (param $param i32) |
| ;; OPTIN-NEXT: (local $x i32) |
| ;; OPTIN-NEXT: (local $eq eqref) |
| ;; OPTIN-NEXT: (local $nn-eq (ref eq)) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eqz |
| ;; OPTIN-NEXT: (local.get $param) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $default-var (param $param i32) |
| (local $x i32) |
| (local $eq eqref) |
| ;; A non-nullable local. We cannot add a get for it (it would not validate), |
| ;; but check we do not error. |
| (local $nn-eq (ref eq)) |
| ;; locals begin with default values, so we can infer here. |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 0) |
| ) |
| ) |
| (drop |
| (i32.eqz |
| (local.get $x) |
| ) |
| ) |
| ;; We can infer nothing for a param. |
| (drop |
| (i32.eq |
| (local.get $param) |
| (i32.const 0) |
| ) |
| ) |
| ;; We can infer a null reference. |
| (drop |
| (ref.eq |
| (local.get $eq) |
| (ref.null eq) |
| ) |
| ) |
| (drop |
| (ref.is_null |
| (local.get $eq) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $conditional (type $0) (param $param i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $param) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eqz |
| ;; CHECK-NEXT: (local.get $param) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $param) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $conditional (type $0) (param $param i32) |
| ;; OPTIN-NEXT: (block |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eqz |
| ;; OPTIN-NEXT: (local.get $param) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.ne |
| ;; OPTIN-NEXT: (local.get $param) |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eqz |
| ;; OPTIN-NEXT: (local.get $param) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (local.get $param) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $conditional (param $param i32) |
| ;; We can infer $param is non-zero in the if's first arm, but not the second |
| ;; nor after the if. |
| ;; (Note: we fail to optimize this in OPTIN, because it folds the if arms |
| ;; first.) |
| (if |
| (local.get $param) |
| (then |
| ;; The first is false, the second true. |
| (drop |
| (i32.eqz |
| (local.get $param) |
| ) |
| ) |
| (drop |
| (i32.ne |
| (local.get $param) |
| (i32.const 0) |
| ) |
| ) |
| ) |
| (else |
| ;; Flipped. |
| (drop |
| (i32.eqz |
| (local.get $param) |
| ) |
| ) |
| (drop |
| (i32.ne |
| (local.get $param) |
| (i32.const 0) |
| ) |
| ) |
| ) |
| ) |
| (drop |
| (i32.eqz |
| (local.get $param) |
| ) |
| ) |
| (drop |
| (local.get $param) |
| ) |
| ) |
| |
| ;; CHECK: (func $conditional-binary (type $0) (param $param i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $param) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $param) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.ne |
| ;; CHECK-NEXT: (local.get $param) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $conditional-binary (type $0) (param $param i32) |
| ;; OPTIN-NEXT: (block |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $param) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.ne |
| ;; OPTIN-NEXT: (local.get $param) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $param) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.ne |
| ;; OPTIN-NEXT: (local.get $param) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $conditional-binary (param $param i32) |
| ;; As above, but comparing param to 10. |
| (if |
| (i32.eq |
| (local.get $param) |
| (i32.const 10) |
| ) |
| (then |
| (drop |
| (i32.eq |
| (local.get $param) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.ne |
| (local.get $param) |
| (i32.const 10) |
| ) |
| ) |
| ) |
| (else |
| (drop |
| (i32.eq |
| (local.get $param) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.ne |
| (local.get $param) |
| (i32.const 10) |
| ) |
| ) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $param) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.ne |
| (local.get $param) |
| (i32.const 10) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $conditional-binary-nesting (type $2) (param $x i32) (param $y i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $y) |
| ;; CHECK-NEXT: (i32.const 20) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $conditional-binary-nesting (type $2) (param $x i32) (param $y i32) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $y) |
| ;; OPTIN-NEXT: (i32.const 20) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $conditional-binary-nesting (param $x i32) (param $y i32) |
| (if |
| (i32.eq |
| (local.get $x) |
| (i32.const 10) |
| ) |
| (then |
| (if |
| (i32.eq |
| (local.get $y) |
| (i32.const 20) |
| ) |
| (then |
| ;; x is 10 here, and y is 20. |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 20) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $y) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $y) |
| (i32.const 20) |
| ) |
| ) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $conditional-binary-contradiction (type $0) (param $x i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $conditional-binary-contradiction (type $0) (param $x i32) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (block |
| ;; OPTIN-NEXT: (unreachable) |
| ;; OPTIN-NEXT: (unreachable) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (block |
| ;; OPTIN-NEXT: (unreachable) |
| ;; OPTIN-NEXT: (unreachable) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $conditional-binary-contradiction (param $x i32) |
| ;; As above, but with only $x. |
| (if |
| (i32.eq |
| (local.get $x) |
| (i32.const 10) |
| ) |
| (then |
| (if |
| (i32.eq |
| (local.get $x) |
| (i32.const 20) |
| ) |
| (then |
| ;; This is only reached if x is both 10 and 20, which is a |
| ;; contradiction, so it is unreachable. We optimize to unreachable |
| ;; here. |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 20) |
| ) |
| ) |
| ) |
| ) |
| ;; After that contradiction, control flow merges, and we can infer 1 and |
| ;; 0 here (since we are still inside the x == 10 If's body). |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 20) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $conditional-binary-contradiction-2 (type $0) (param $x i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $conditional-binary-contradiction-2 (type $0) (param $x i32) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $conditional-binary-contradiction-2 (param $x i32) |
| ;; As above, but now the contradiction is in the inner if-else. |
| (if |
| (i32.eq |
| (local.get $x) |
| (i32.const 10) |
| ) |
| (then |
| (if |
| (i32.eq |
| (local.get $x) |
| (i32.const 10) |
| ) |
| (then |
| ;; x is 10 here. |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| ) |
| (else |
| ;; We cannot get here: it would require x == 10 and x != 10. |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| ) |
| ) |
| ;; After that contradiction, control flow merges, and we can infer 1 and |
| ;; 0 here (since we are still inside the x == 10 If's body). |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 20) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $conditional-binary-contradiction-other (type $0) (param $x i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $conditional-binary-contradiction-other (type $0) (param $x i32) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 30) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $conditional-binary-contradiction-other (param $x i32) |
| (if |
| (i32.eq |
| (local.get $x) |
| (i32.const 10) |
| ) |
| (then |
| (if |
| (i32.eq |
| (local.get $x) |
| (i32.const 20) |
| ) |
| (then |
| ;; This is only reached if x is both 10 and 20, which is a |
| ;; contradiction, so it is unreachable. We optimize to unreachable |
| ;; here, even though this is a Binary that we do not have anything |
| ;; to do with otherwise (no constraint on a local is implied by this |
| ;; expression). This checks that we optimize unreachability even on |
| ;; expressions without relevant locals. |
| (drop |
| (i32.add |
| (i32.const 10) |
| (i32.const 20) |
| ) |
| ) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $conditional-binary-contradiction-other-default (type $1) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $conditional-binary-contradiction-other-default (type $1) |
| ;; OPTIN-NEXT: (local $x i32) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 30) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $conditional-binary-contradiction-other-default |
| (local $x i32) |
| ;; As above, but now with a single if. The contradiction tested is |
| ;; between the default value and the if condition. |
| (if |
| (local.get $x) ;; this is 0, hence the if is not taken |
| (then |
| (drop |
| ;; This is unreachable. |
| (i32.add |
| (i32.const 10) |
| (i32.const 20) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $conditional-binary-contradiction-other-set (type $1) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $conditional-binary-contradiction-other-set (type $1) |
| ;; OPTIN-NEXT: (local $x i32) |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 30) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $conditional-binary-contradiction-other-set |
| (local $x i32) |
| ;; As above, but now the contradiction tested is between a local.set and |
| ;; the if condition. |
| (local.set $x |
| (i32.const 10) |
| ) |
| (if |
| (i32.eq |
| (local.get $x) |
| (i32.const 20) |
| ) |
| (then |
| (drop |
| ;; This is unreachable. |
| (i32.add |
| (i32.const 10) |
| (i32.const 20) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $contadiction-during-flipping (type $1) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local $y i32) |
| ;; CHECK-NEXT: (local.set $y |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $contadiction-during-flipping (type $1) |
| ;; OPTIN-NEXT: (local $x i32) |
| ;; OPTIN-NEXT: (local $y i32) |
| ;; OPTIN-NEXT: (local.set $y |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (block |
| ;; OPTIN-NEXT: (unreachable) |
| ;; OPTIN-NEXT: (unreachable) |
| ;; OPTIN-NEXT: (unreachable) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $contadiction-during-flipping |
| (local $x i32) |
| (local $y i32) |
| (local.set $y |
| (i32.const 1) |
| ) |
| ;; $x == 0, $y == 1, so they are never equal, and the if body is |
| ;; unreachable. We find this out while applying the secondary facts of a |
| ;; constraint: we add $y == $x, and then apply $x's constraints to $y, |
| ;; ending up in $y with $y == 1 && $y == 0. We can also infer 0 for the if |
| ;; condition. |
| (if |
| (i32.eq |
| (local.get $y) |
| (local.get $x) |
| ) |
| (then |
| ;; This becomes unreachable. |
| (drop |
| (i32.eq |
| (local.get $y) |
| (local.get $x) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $conditional-br_if (type $0) (param $param i32) |
| ;; CHECK-NEXT: (block $block |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $param) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (br_if $block |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $param) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $param) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $conditional-br_if (type $0) (param $param i32) |
| ;; OPTIN-NEXT: (block $block |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $param) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (br_if $block |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $param) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $param) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $conditional-br_if (param $param i32) |
| (block $block |
| ;; We can infer nothing yet. |
| (drop |
| (i32.eq |
| (local.get $param) |
| (i32.const 10) |
| ) |
| ) |
| (br_if $block |
| (i32.eq |
| (local.get $param) |
| (i32.const 10) |
| ) |
| ) |
| ;; Now we can, this is false. |
| (drop |
| (i32.eq |
| (local.get $param) |
| (i32.const 10) |
| ) |
| ) |
| ) |
| ;; Merging after the block, we can't. |
| (drop |
| (i32.eq |
| (local.get $param) |
| (i32.const 10) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $conditional-br_if_2 (type $0) (param $param i32) |
| ;; CHECK-NEXT: (block $block |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $param) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (br_if $block |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $param) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (return) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $conditional-br_if_2 (type $0) (param $param i32) |
| ;; OPTIN-NEXT: (block $block |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $param) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (br_if $block |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $param) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (return) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $conditional-br_if_2 (param $param i32) |
| ;; As above, but with a return to avoid a final merge. |
| (block $block |
| ;; As before, we cannot infer here. |
| (drop |
| (i32.eq |
| (local.get $param) |
| (i32.const 10) |
| ) |
| ) |
| (br_if $block |
| (i32.eq |
| (local.get $param) |
| (i32.const 10) |
| ) |
| ) |
| ;; As before, we can infer 0 here. |
| (drop |
| (i32.eq |
| (local.get $param) |
| (i32.const 10) |
| ) |
| ) |
| (return) ;; this was added |
| ) |
| ;; Because of the return, we can infer 1 here. |
| (drop |
| (i32.eq |
| (local.get $param) |
| (i32.const 10) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $unreachable-br_if (type $0) (param $param i32) |
| ;; CHECK-NEXT: (block $block |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: (br_if $block |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $param) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $unreachable-br_if (type $0) (param $param i32) |
| ;; OPTIN-NEXT: (block $block |
| ;; OPTIN-NEXT: (unreachable) |
| ;; OPTIN-NEXT: (br_if $block |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $param) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $unreachable-br_if (param $param i32) |
| (block $block |
| ;; We should not error on br_if in unreachable code. |
| (unreachable) |
| (br_if $block |
| (i32.eq |
| (local.get $param) |
| (i32.const 10) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $br_on_null (type $4) (param $param anyref) |
| ;; CHECK-NEXT: (block $block |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.is_null |
| ;; CHECK-NEXT: (local.get $param) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (br_on_null $block |
| ;; CHECK-NEXT: (local.get $param) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (return) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $br_on_null (type $4) (param $param anyref) |
| ;; OPTIN-NEXT: (block $block |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (ref.is_null |
| ;; OPTIN-NEXT: (local.get $param) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (br_on_null $block |
| ;; OPTIN-NEXT: (local.get $param) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (return) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $br_on_null (param $param anyref) |
| (block $block |
| ;; We cannot infer here. |
| (drop |
| (ref.is_null |
| (local.get $param) |
| ) |
| ) |
| (drop |
| (br_on_null $block |
| (local.get $param) |
| ) |
| ) |
| ;; We can infer 0 here. |
| (drop |
| (ref.is_null |
| (local.get $param) |
| ) |
| ) |
| (return) |
| ) |
| ;; Because of the return, we can infer 1 here. |
| (drop |
| (ref.is_null |
| (local.get $param) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $br_on_non_null (type $4) (param $param anyref) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block $block (result (ref any)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.is_null |
| ;; CHECK-NEXT: (local.get $param) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (br_on_non_null $block |
| ;; CHECK-NEXT: (local.get $param) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (return) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $br_on_non_null (type $4) (param $param anyref) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (block $block (result (ref any)) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (ref.is_null |
| ;; OPTIN-NEXT: (local.get $param) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (br_on_non_null $block |
| ;; OPTIN-NEXT: (local.get $param) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (return) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $br_on_non_null (param $param anyref) |
| (drop |
| (block $block (result (ref any)) |
| ;; We cannot infer here. |
| (drop |
| (ref.is_null |
| (local.get $param) |
| ) |
| ) |
| (br_on_non_null $block |
| (local.get $param) |
| ) |
| ;; We can infer 1 here. |
| (drop |
| (ref.is_null |
| (local.get $param) |
| ) |
| ) |
| (return) |
| ) |
| ) |
| ;; Because of the return, we can infer 0 here. |
| (drop |
| (ref.is_null |
| (local.get $param) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $inequalities-lt_u (type $0) (param $x i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.lt_u |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $inequalities-lt_u (type $0) (param $x i32) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.lt_u |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.ge_u |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $inequalities-lt_u (param $x i32) |
| ;; The simple case where an inequality is seen again, in exact form. |
| (if |
| (i32.lt_u |
| (local.get $x) |
| (i32.const 10) |
| ) |
| (then |
| ;; We can infer 1, 0 here. |
| (drop |
| (i32.lt_u |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.ge_u |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| ) |
| (else |
| ;; We can infer 0, 1 here. |
| (drop |
| (i32.lt_u |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.ge_u |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $inequalities-lt_s (type $0) (param $x i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.lt_s |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $inequalities-lt_s (type $0) (param $x i32) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.lt_s |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.ge_s |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $inequalities-lt_s (param $x i32) |
| (if |
| (i32.lt_s |
| (local.get $x) |
| (i32.const 10) |
| ) |
| (then |
| (drop |
| (i32.lt_s |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.ge_s |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| ) |
| (else |
| (drop |
| (i32.lt_s |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.ge_s |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $inequalities-le_u (type $0) (param $x i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.le_u |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $inequalities-le_u (type $0) (param $x i32) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.le_u |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.gt_u |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $inequalities-le_u (param $x i32) |
| (if |
| (i32.le_u |
| (local.get $x) |
| (i32.const 10) |
| ) |
| (then |
| (drop |
| (i32.le_u |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.gt_u |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| ) |
| (else |
| (drop |
| (i32.le_u |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.gt_u |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $inequalities-le_s (type $0) (param $x i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.le_s |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $inequalities-le_s (type $0) (param $x i32) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.le_s |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.gt_s |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $inequalities-le_s (param $x i32) |
| (if |
| (i32.le_s |
| (local.get $x) |
| (i32.const 10) |
| ) |
| (then |
| (drop |
| (i32.le_s |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.gt_s |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| ) |
| (else |
| (drop |
| (i32.le_s |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.gt_s |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $inequalities-gt_u (type $0) (param $x i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.gt_u |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $inequalities-gt_u (type $0) (param $x i32) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.gt_u |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.le_u |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $inequalities-gt_u (param $x i32) |
| (if |
| (i32.gt_u |
| (local.get $x) |
| (i32.const 10) |
| ) |
| (then |
| (drop |
| (i32.gt_u |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.le_u |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| ) |
| (else |
| (drop |
| (i32.gt_u |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.le_u |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $inequalities-gt_s (type $0) (param $x i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.gt_s |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $inequalities-gt_s (type $0) (param $x i32) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.gt_s |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.le_s |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $inequalities-gt_s (param $x i32) |
| (if |
| (i32.gt_s |
| (local.get $x) |
| (i32.const 10) |
| ) |
| (then |
| (drop |
| (i32.gt_s |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.le_s |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| ) |
| (else |
| (drop |
| (i32.gt_s |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.le_s |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $inequalities-ge_u (type $0) (param $x i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.ge_u |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $inequalities-ge_u (type $0) (param $x i32) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.ge_u |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.lt_u |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $inequalities-ge_u (param $x i32) |
| (if |
| (i32.ge_u |
| (local.get $x) |
| (i32.const 10) |
| ) |
| (then |
| (drop |
| (i32.ge_u |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.lt_u |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| ) |
| (else |
| (drop |
| (i32.ge_u |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.lt_u |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $inequalities-ge_s (type $0) (param $x i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.ge_s |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $inequalities-ge_s (type $0) (param $x i32) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.ge_s |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.lt_s |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $inequalities-ge_s (param $x i32) |
| (if |
| (i32.ge_s |
| (local.get $x) |
| (i32.const 10) |
| ) |
| (then |
| (drop |
| (i32.ge_s |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.lt_s |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| ) |
| (else |
| (drop |
| (i32.ge_s |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.lt_s |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $local-changes (type $3) (param $x i32) (param $y i32) (param $z i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (local.get $y) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (local.get $z) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $z) |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (local.get $z) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (local.get $y) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $y) |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $y) |
| ;; CHECK-NEXT: (local.get $z) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $z) |
| ;; CHECK-NEXT: (local.get $y) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $local-changes (type $3) (param $x i32) (param $y i32) (param $z i32) |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (local.get $y) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (local.get $z) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (local.get $z) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (local.get $z) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (local.get $y) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (local.get $y) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $y) |
| ;; OPTIN-NEXT: (local.get $z) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $y) |
| ;; OPTIN-NEXT: (local.get $z) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $local-changes (param $x i32) (param $y i32) (param $z i32) |
| (local.set $x |
| (local.get $y) |
| ) |
| ;; x == y but not z. |
| (drop |
| (i32.eq |
| (local.get $x) |
| (local.get $y) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $y) |
| (local.get $x) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $x) |
| (local.get $z) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $z) |
| (local.get $x) |
| ) |
| ) |
| |
| ;; Set x to z. Now x == z but we can prove nothing about x and y. |
| (local.set $x |
| (local.get $z) |
| ) |
| (drop |
| (i32.eq |
| (local.get $x) |
| (local.get $y) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $y) |
| (local.get $x) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $x) |
| (local.get $z) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $z) |
| (local.get $x) |
| ) |
| ) |
| |
| ;; No idea about y vs z. |
| (drop |
| (i32.eq |
| (local.get $y) |
| (local.get $z) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $z) |
| (local.get $y) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $local-changes-2 (type $3) (param $x i32) (param $y i32) (param $z i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (local.get $y) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (local.get $z) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $z) |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $y |
| ;; CHECK-NEXT: (local.get $z) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (local.get $y) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $y) |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (local.get $z) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $z) |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $local-changes-2 (type $3) (param $x i32) (param $y i32) (param $z i32) |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (local.get $y) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (local.get $z) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (local.get $z) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (local.set $y |
| ;; OPTIN-NEXT: (local.get $z) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (local.get $y) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (local.get $y) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (local.get $z) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (local.get $z) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $local-changes-2 (param $x i32) (param $y i32) (param $z i32) |
| ;; As above, but the set in the middle is on y, not x. |
| (local.set $x |
| (local.get $y) |
| ) |
| ;; x == y but not z, as above. |
| (drop |
| (i32.eq |
| (local.get $x) |
| (local.get $y) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $y) |
| (local.get $x) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $x) |
| (local.get $z) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $z) |
| (local.get $x) |
| ) |
| ) |
| |
| (local.set $y ;; this changed |
| (local.get $z) |
| ) |
| ;; We can prove nothing here. |
| (drop |
| (i32.eq |
| (local.get $x) |
| (local.get $y) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $y) |
| (local.get $x) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $x) |
| (local.get $z) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $z) |
| (local.get $x) |
| ) |
| ) |
| |
| ;; But y == z. |
| (drop |
| (i32.eq |
| (local.get $y) |
| (local.get $z) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $z) |
| (local.get $y) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $local-changes-if (type $8) (param $x i32) (param $y i32) (param $z i32) (param $w i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (local.get $y) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (local.get $z) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $y |
| ;; CHECK-NEXT: (local.get $w) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (local.get $y) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $y) |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $local-changes-if (type $8) (param $x i32) (param $y i32) (param $z i32) (param $w i32) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (local.get $y) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (local.get $z) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (local.set $y |
| ;; OPTIN-NEXT: (local.get $w) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (local.get $y) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (local.get $y) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $local-changes-if (param $x i32) (param $y i32) (param $z i32) (param $w i32) |
| ;; Local changes after if conditions set up constraints. |
| (if |
| (i32.eq |
| (local.get $x) |
| (local.get $y) |
| ) |
| (then |
| (if |
| (i32.eq |
| (local.get $x) |
| (local.get $z) |
| ) |
| (then |
| ;; x == y and x == z here. |
| (drop |
| (i32.eq |
| (local.get $x) |
| (local.get $y) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $y) |
| (local.get $x) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $x) |
| (local.get $z) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $z) |
| (local.get $x) |
| ) |
| ) |
| ;; TODO: y == z |
| |
| ;; Modify y to the value of w. x is still equal to z, but no longer |
| ;; to y. |
| (local.set $y |
| (local.get $w) |
| ) |
| (drop |
| (i32.eq |
| (local.get $x) |
| (local.get $y) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $y) |
| (local.get $x) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $x) |
| (local.get $z) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $z) |
| (local.get $x) |
| ) |
| ) |
| ;; But y == w. |
| (drop |
| (i32.eq |
| (local.get $y) |
| (local.get $w) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $w) |
| (local.get $y) |
| ) |
| ) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $local-changes-ne (type $3) (param $x i32) (param $y i32) (param $z i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.ne |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (local.get $y) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.ne |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (local.get $z) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (local.get $z) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (local.get $y) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.ne |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (local.get $y) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $y) |
| ;; CHECK-NEXT: (local.get $z) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.ne |
| ;; CHECK-NEXT: (local.get $y) |
| ;; CHECK-NEXT: (local.get $z) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $local-changes-ne (type $3) (param $x i32) (param $y i32) (param $z i32) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (i32.ne |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (local.get $y) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.ne |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (local.get $z) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (local.get $z) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (local.get $y) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.ne |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (local.get $y) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $y) |
| ;; OPTIN-NEXT: (local.get $z) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.ne |
| ;; OPTIN-NEXT: (local.get $y) |
| ;; OPTIN-NEXT: (local.get $z) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $local-changes-ne (param $x i32) (param $y i32) (param $z i32) |
| ;; Similar to above, but testing != rather than == |
| (if |
| (i32.ne |
| (local.get $x) |
| (local.get $y) |
| ) |
| (then |
| ;; x == y is false, and we know nothing about z. |
| (drop |
| (i32.eq |
| (local.get $x) |
| (local.get $y) |
| ) |
| ) |
| (drop |
| (i32.ne |
| (local.get $x) |
| (local.get $y) |
| ) |
| ) |
| (drop |
| (i32.ne |
| (local.get $x) |
| (local.get $z) |
| ) |
| ) |
| |
| ;; Set x to z. Now x != z is false, but we can prove nothing about |
| ;; x and y. |
| (local.set $x |
| (local.get $z) |
| ) |
| (drop |
| (i32.eq |
| (local.get $x) |
| (local.get $y) |
| ) |
| ) |
| (drop |
| (i32.ne |
| (local.get $x) |
| (local.get $y) |
| ) |
| ) |
| (drop |
| (i32.ne |
| (local.get $x) |
| (local.get $z) |
| ) |
| ) |
| |
| ;; No idea about y vs z. |
| (drop |
| (i32.eq |
| (local.get $y) |
| (local.get $z) |
| ) |
| ) |
| (drop |
| (i32.ne |
| (local.get $y) |
| (local.get $z) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $multi-local-copy (type $1) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local $y i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $y |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $multi-local-copy (type $1) |
| ;; OPTIN-NEXT: (local $x i32) |
| ;; OPTIN-NEXT: (local $y i32) |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (local.set $y |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $multi-local-copy |
| (local $x i32) |
| (local $y i32) |
| ;; x is 10, y is copied. |
| (local.set $x |
| (i32.const 10) |
| ) |
| (local.set $y |
| (local.get $x) |
| ) |
| ;; Verify those values: both x and y are equal to 10. |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $y) |
| (i32.const 10) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $multi-local-copy-if (type $2) (param $x i32) (param $y i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $y) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $y |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $y) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $y |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $multi-local-copy-if (type $2) (param $x i32) (param $y i32) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $y) |
| ;; OPTIN-NEXT: (i32.const 42) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (local.set $y |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $y) |
| ;; OPTIN-NEXT: (i32.const 42) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 42) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $multi-local-copy-if (param $x i32) (param $y i32) |
| (if |
| (i32.eq |
| (local.get $x) |
| (i32.const 42) |
| ) |
| (then |
| ;; We don't know anything yet. |
| (drop |
| (i32.eq |
| (local.get $y) |
| (i32.const 42) |
| ) |
| ) |
| ;; Copy x (which is 42) into y, and see that it is now equal to 42. |
| (local.set $y |
| (local.get $x) |
| ) |
| (drop |
| (i32.eq |
| (local.get $y) |
| (i32.const 42) |
| ) |
| ) |
| ;; x was not changed (equal to 42) |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 42) |
| ) |
| ) |
| ) |
| (else |
| ;; We don't know anything yet. |
| (drop |
| (i32.eq |
| (local.get $y) |
| (i32.const 42) |
| ) |
| ) |
| ;; Copy x into y, and see that it is now *not* equal to 42. |
| (local.set $y |
| (local.get $x) |
| ) |
| (drop |
| (i32.eq |
| (local.get $y) |
| (i32.const 42) |
| ) |
| ) |
| ;; x was not changed (not equal to 42) |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 42) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $multi-local-copy-if-2 (type $2) (param $x i32) (param $y i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: (local.get $y) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $multi-local-copy-if-2 (type $2) (param $x i32) (param $y i32) |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (i32.const 42) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (i32.const 42) |
| ;; OPTIN-NEXT: (local.get $y) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $multi-local-copy-if-2 (param $x i32) (param $y i32) |
| ;; As above, but the if's condition is on x == y, so that is where we |
| ;; apply equality between them. |
| (local.set $x |
| (i32.const 42) |
| ) |
| (if |
| (i32.eq |
| (local.get $x) ;; this is 42 |
| (local.get $y) |
| ) |
| (then |
| ;; This is true. |
| (drop |
| (i32.eq |
| (local.get $y) |
| (i32.const 42) |
| ) |
| ) |
| ;; x was not changed, so this is still true. |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 42) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $multi-local-copy-if-3 (type $2) (param $x i32) (param $y i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $y) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $multi-local-copy-if-3 (type $2) (param $x i32) (param $y i32) |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (i32.const 42) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (i32.const 42) |
| ;; OPTIN-NEXT: (local.get $y) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $multi-local-copy-if-3 (param $x i32) (param $y i32) |
| ;; As above, but the if's condition is flipped. |
| (local.set $x |
| (i32.const 42) |
| ) |
| (if |
| (i32.eq |
| (local.get $y) |
| (local.get $x) |
| ) |
| (then |
| ;; These are true. |
| (drop |
| (i32.eq |
| (local.get $y) |
| (i32.const 42) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 42) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $simple-array-sum (type $9) (param $param (ref $array)) (result i32) |
| ;; CHECK-NEXT: (local $index i32) |
| ;; CHECK-NEXT: (local $sum i32) |
| ;; CHECK-NEXT: (local $len i32) |
| ;; CHECK-NEXT: (local.set $len |
| ;; CHECK-NEXT: (array.len |
| ;; CHECK-NEXT: (local.get $param) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (loop $loop |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.ge_u |
| ;; CHECK-NEXT: (local.get $index) |
| ;; CHECK-NEXT: (local.get $len) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (return |
| ;; CHECK-NEXT: (local.get $sum) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $sum |
| ;; CHECK-NEXT: (i32.add |
| ;; CHECK-NEXT: (local.get $sum) |
| ;; CHECK-NEXT: (array.get $array |
| ;; CHECK-NEXT: (local.get $param) |
| ;; CHECK-NEXT: (local.get $index) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $index |
| ;; CHECK-NEXT: (i32.add |
| ;; CHECK-NEXT: (local.get $index) |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (br $loop) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $simple-array-sum (type $9) (param $param (ref $array)) (result i32) |
| ;; OPTIN-NEXT: (local $index i32) |
| ;; OPTIN-NEXT: (local $sum i32) |
| ;; OPTIN-NEXT: (local $len i32) |
| ;; OPTIN-NEXT: (local.set $len |
| ;; OPTIN-NEXT: (array.len |
| ;; OPTIN-NEXT: (local.get $param) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (loop $loop |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (i32.ge_u |
| ;; OPTIN-NEXT: (local.get $index) |
| ;; OPTIN-NEXT: (local.get $len) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (return |
| ;; OPTIN-NEXT: (local.get $sum) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (unreachable) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (local.set $sum |
| ;; OPTIN-NEXT: (i32.add |
| ;; OPTIN-NEXT: (array.get $array |
| ;; OPTIN-NEXT: (local.get $param) |
| ;; OPTIN-NEXT: (local.get $index) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (local.get $sum) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (local.set $index |
| ;; OPTIN-NEXT: (i32.add |
| ;; OPTIN-NEXT: (local.get $index) |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (br $loop) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $simple-array-sum (param $param (ref $array)) (result i32) |
| ;; A simple but realistic software bounds check that we can remove. |
| (local $index i32) |
| (local $sum i32) |
| (local $len i32) |
| ;; Stash the length in a local. |
| (local.set $len |
| (array.len |
| (local.get $param) |
| ) |
| ) |
| ;; Loop over the array. |
| (loop $loop |
| ;; If we reached the end of the array, return the sum. |
| (if |
| (i32.ge_u |
| (local.get $index) |
| (local.get $len) |
| ) |
| (then |
| (return |
| (local.get $sum) |
| ) |
| ) |
| ) |
| ;; Software bounds check, right before the actual access. After we |
| ;; optimize, the condition here becomes zero, so it will be removed later. |
| (if |
| (i32.ge_u |
| (local.get $index) |
| (local.get $len) |
| ) |
| (then |
| (unreachable) |
| ) |
| ) |
| ;; Read the array and add to the sum. |
| (local.set $sum |
| (i32.add |
| (local.get $sum) |
| (array.get $array |
| (local.get $param) |
| (local.get $index) |
| ) |
| ) |
| ) |
| ;; Increment. |
| (local.set $index |
| (i32.add |
| (local.get $index) |
| (i32.const 1) |
| ) |
| ) |
| ;; Keep looping. |
| (br $loop) |
| ) |
| ) |
| |
| ;; CHECK: (func $unreachable-loop (type $1) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: (block |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (loop $loop (result i32) |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $unreachable-loop (type $1) |
| ;; OPTIN-NEXT: (local $x i32) |
| ;; OPTIN-NEXT: (unreachable) |
| ;; OPTIN-NEXT: (block |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (loop $loop (result i32) |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (unreachable) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $unreachable-loop |
| (local $x i32) |
| ;; The entire loop is unreachable. The control flow here should not cause any |
| ;; internal errors, and we can just optimize this to unreachable (though we |
| ;; keep the local.set, because of non-nullable local validation). |
| (unreachable) |
| (local.set $x |
| (loop $loop (result i32) |
| (i32.const 0) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $set-silly (type $0) (param $x i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $set-silly (type $0) (param $x i32) |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $set-silly (param $x i32) |
| ;; Setting x to x adds no information, and we do not store it in the IR. This |
| ;; must not lead to internal errors (we have asserts on not storing |
| ;; "proves-nothing" in the internal map, and if we wrote this, it would be |
| ;; that). |
| (local.set $x |
| (local.get $x) |
| ) |
| ;; Add some control flow to exercise the assert. |
| (if |
| (i32.const 0) |
| (then) |
| ) |
| ) |
| |
| ;; CHECK: (func $iloop (type $10) (param $0 f32) |
| ;; CHECK-NEXT: (local $1 f32) |
| ;; CHECK-NEXT: (local.set $0 |
| ;; CHECK-NEXT: (local.get $1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (loop |
| ;; CHECK-NEXT: (local.set $1 |
| ;; CHECK-NEXT: (local.get $0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (loop $label |
| ;; CHECK-NEXT: (loop |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (br_if $label |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $iloop (type $10) (param $0 f32) |
| ;; OPTIN-NEXT: (local $1 f32) |
| ;; OPTIN-NEXT: (local.set $0 |
| ;; OPTIN-NEXT: (local.get $1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (loop |
| ;; OPTIN-NEXT: (local.set $1 |
| ;; OPTIN-NEXT: (local.get $0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (else |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (loop $label |
| ;; OPTIN-NEXT: (loop |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (br_if $label |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $iloop (param $0 f32) |
| ;; Regression test for an infinite loop: the specific cfg here + the |
| ;; constraints lead to a situation where, if we were not careful, we would |
| ;; think we have an infinite stream of updates in flow(). Specifically, we |
| ;; end up updating a location to a combination of two constraints {A, B} and |
| ;; then end up finding {B, A} in the next cycle, and then alternate those |
| ;; two forever. This is fixed by sorting the constraints. |
| ;; |
| ;; (There is nothing to optimize here, we just should not hang or error.) |
| (local $1 f32) |
| (local.set $0 |
| (local.get $1) |
| ) |
| (if |
| (i32.const 0) |
| (then |
| (loop |
| (local.set $1 |
| (local.get $0) |
| ) |
| ) |
| ) |
| (else |
| ) |
| ) |
| (loop $label |
| (loop |
| ) |
| (br_if $label |
| (i32.const 0) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $nested-binaries (type $1) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local $e eqref) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $nested-binaries (type $1) |
| ;; OPTIN-NEXT: (local $x i32) |
| ;; OPTIN-NEXT: (local $e eqref) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.gt_u |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $nested-binaries |
| (local $x i32) |
| (local $e eqref) |
| ;; Nested binaries of different types. We can apply the local.gets and then |
| ;; optimize the ref.eq to 1 and the lt_u to 1 as well. (This does not fully |
| ;; work out in OPTIN due to reordering, but Precompute would handle it.) |
| (drop |
| (i32.lt_u |
| (local.get $x) |
| (ref.eq |
| (local.get $e) |
| (ref.null none) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $relevant-copy (type $1) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local $y i32) |
| ;; CHECK-NEXT: (local.set $y |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $relevant-copy (type $1) |
| ;; OPTIN-NEXT: (local $x i32) |
| ;; OPTIN-NEXT: (local $y i32) |
| ;; OPTIN-NEXT: (local.set $y |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $relevant-copy |
| (local $x i32) |
| (local $y i32) |
| ;; x has no sets or uses but for a copy to $y, but we still optimize here. |
| (local.set $y |
| (local.get $x) |
| ) |
| (drop |
| (i32.eq |
| (local.get $y) |
| (i32.const 0) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $constant-inequalities-lt_s (type $1) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $constant-inequalities-lt_s (type $1) |
| ;; OPTIN-NEXT: (local $x i32) |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (i32.const 42) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $constant-inequalities-lt_s |
| ;; A constant in $x is checked against inequalities using constants. |
| (local $x i32) |
| (local.set $x |
| (i32.const 42) |
| ) |
| (drop |
| (i32.lt_s |
| (local.get $x) |
| (i32.const 41) |
| ) |
| ) |
| (drop |
| (i32.lt_s |
| (local.get $x) |
| (i32.const 42) |
| ) |
| ) |
| (drop |
| (i32.lt_s |
| (local.get $x) |
| (i32.const 43) |
| ) |
| ) |
| (drop |
| (i32.lt_s |
| (local.get $x) |
| (i32.const -1) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $constant-inequalities-le_s (type $1) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $constant-inequalities-le_s (type $1) |
| ;; OPTIN-NEXT: (local $x i32) |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (i32.const 42) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $constant-inequalities-le_s |
| (local $x i32) |
| (local.set $x |
| (i32.const 42) |
| ) |
| (drop |
| (i32.le_s |
| (local.get $x) |
| (i32.const 41) |
| ) |
| ) |
| (drop |
| (i32.le_s |
| (local.get $x) |
| (i32.const 42) |
| ) |
| ) |
| (drop |
| (i32.le_s |
| (local.get $x) |
| (i32.const 43) |
| ) |
| ) |
| (drop |
| (i32.le_s |
| (local.get $x) |
| (i32.const -1) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $constant-inequalities-gt_s (type $1) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $constant-inequalities-gt_s (type $1) |
| ;; OPTIN-NEXT: (local $x i32) |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (i32.const 42) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $constant-inequalities-gt_s |
| (local $x i32) |
| (local.set $x |
| (i32.const 42) |
| ) |
| (drop |
| (i32.gt_s |
| (local.get $x) |
| (i32.const 41) |
| ) |
| ) |
| (drop |
| (i32.gt_s |
| (local.get $x) |
| (i32.const 42) |
| ) |
| ) |
| (drop |
| (i32.gt_s |
| (local.get $x) |
| (i32.const 43) |
| ) |
| ) |
| (drop |
| (i32.gt_s |
| (local.get $x) |
| (i32.const -1) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $constant-inequalities-ge_s (type $1) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $constant-inequalities-ge_s (type $1) |
| ;; OPTIN-NEXT: (local $x i32) |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (i32.const 42) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $constant-inequalities-ge_s |
| (local $x i32) |
| (local.set $x |
| (i32.const 42) |
| ) |
| (drop |
| (i32.ge_s |
| (local.get $x) |
| (i32.const 41) |
| ) |
| ) |
| (drop |
| (i32.ge_s |
| (local.get $x) |
| (i32.const 42) |
| ) |
| ) |
| (drop |
| (i32.ge_s |
| (local.get $x) |
| (i32.const 43) |
| ) |
| ) |
| (drop |
| (i32.ge_s |
| (local.get $x) |
| (i32.const -1) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $constant-inequalities-lt_u (type $1) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $constant-inequalities-lt_u (type $1) |
| ;; OPTIN-NEXT: (local $x i32) |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (i32.const 42) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $constant-inequalities-lt_u |
| (local $x i32) |
| (local.set $x |
| (i32.const 42) |
| ) |
| (drop |
| (i32.lt_u |
| (local.get $x) |
| (i32.const 41) |
| ) |
| ) |
| (drop |
| (i32.lt_u |
| (local.get $x) |
| (i32.const 42) |
| ) |
| ) |
| (drop |
| (i32.lt_u |
| (local.get $x) |
| (i32.const 43) |
| ) |
| ) |
| (drop |
| (i32.lt_u |
| (local.get $x) |
| (i32.const -1) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $constant-inequalities-le_u (type $1) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $constant-inequalities-le_u (type $1) |
| ;; OPTIN-NEXT: (local $x i32) |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (i32.const 42) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $constant-inequalities-le_u |
| (local $x i32) |
| (local.set $x |
| (i32.const 42) |
| ) |
| (drop |
| (i32.le_u |
| (local.get $x) |
| (i32.const 41) |
| ) |
| ) |
| (drop |
| (i32.le_u |
| (local.get $x) |
| (i32.const 42) |
| ) |
| ) |
| (drop |
| (i32.le_u |
| (local.get $x) |
| (i32.const 43) |
| ) |
| ) |
| (drop |
| (i32.le_u |
| (local.get $x) |
| (i32.const -1) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $constant-inequalities-gt_u (type $1) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $constant-inequalities-gt_u (type $1) |
| ;; OPTIN-NEXT: (local $x i32) |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (i32.const 42) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $constant-inequalities-gt_u |
| (local $x i32) |
| (local.set $x |
| (i32.const 42) |
| ) |
| (drop |
| (i32.gt_u |
| (local.get $x) |
| (i32.const 41) |
| ) |
| ) |
| (drop |
| (i32.gt_u |
| (local.get $x) |
| (i32.const 42) |
| ) |
| ) |
| (drop |
| (i32.gt_u |
| (local.get $x) |
| (i32.const 43) |
| ) |
| ) |
| (drop |
| (i32.gt_u |
| (local.get $x) |
| (i32.const -1) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $constant-inequalities-ge_u (type $1) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $constant-inequalities-ge_u (type $1) |
| ;; OPTIN-NEXT: (local $x i32) |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (i32.const 42) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $constant-inequalities-ge_u |
| (local $x i32) |
| (local.set $x |
| (i32.const 42) |
| ) |
| (drop |
| (i32.ge_u |
| (local.get $x) |
| (i32.const 41) |
| ) |
| ) |
| (drop |
| (i32.ge_u |
| (local.get $x) |
| (i32.const 42) |
| ) |
| ) |
| (drop |
| (i32.ge_u |
| (local.get $x) |
| (i32.const 43) |
| ) |
| ) |
| (drop |
| (i32.ge_u |
| (local.get $x) |
| (i32.const -1) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $flipped-contradiction (type $6) (param $x i32) (result i32) |
| ;; CHECK-NEXT: (loop $loop |
| ;; CHECK-NEXT: (br_if $loop |
| ;; CHECK-NEXT: (i32.lt_u |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (br_if $loop |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $flipped-contradiction (type $6) (param $x i32) (result i32) |
| ;; OPTIN-NEXT: (loop $loop |
| ;; OPTIN-NEXT: (br_if $loop |
| ;; OPTIN-NEXT: (i32.eqz |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (br_if $loop |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (unreachable) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $flipped-contradiction (param $x i32) (result i32) |
| (loop $loop (result i32) |
| ;; If we do not branch, we add the constraint x >= 1. |
| (br_if $loop |
| (i32.lt_u |
| (local.get $x) |
| (i32.const 1) |
| ) |
| ) |
| ;; If we do not branch, we add the constraint x == 0. This contradicts the |
| ;; one before, making the code after us unreachable. |
| (br_if $loop |
| (local.get $x) |
| ) |
| ;; An eqz that will become unreachable. |
| (i32.eqz |
| (i32.const 0) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $flipped-contradiction-no (type $6) (param $x i32) (result i32) |
| ;; CHECK-NEXT: (loop $loop (result i32) |
| ;; CHECK-NEXT: (br_if $loop |
| ;; CHECK-NEXT: (i32.gt_u |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (br_if $loop |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.eqz |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $flipped-contradiction-no (type $6) (param $x i32) (result i32) |
| ;; OPTIN-NEXT: (loop $loop (result i32) |
| ;; OPTIN-NEXT: (br_if $loop |
| ;; OPTIN-NEXT: (i32.gt_u |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (br_if $loop |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (i32.eqz |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $flipped-contradiction-no (param $x i32) (result i32) |
| ;; As above, but with lt replaced by gt. Now the constraints are x <= 1 and |
| ;; x == 0, which do not contradict, and nothing becomes unreachable. |
| (loop $loop (result i32) |
| (br_if $loop |
| (i32.gt_u |
| (local.get $x) |
| (i32.const 1) |
| ) |
| ) |
| (br_if $loop |
| (local.get $x) |
| ) |
| (i32.eqz |
| (i32.const 0) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $tee (type $1) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local $y i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (local.tee $y |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $tee (type $1) |
| ;; OPTIN-NEXT: (local $x i32) |
| ;; OPTIN-NEXT: (local $y i32) |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (local.tee $y |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $tee |
| ;; We can read values through tees. |
| (local $x i32) |
| (local $y i32) |
| (local.set $x |
| (local.tee $y |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $y) |
| (i32.const 10) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $fallthrough (type $1) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (call $fallthrough) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $fallthrough (type $1) |
| ;; OPTIN-NEXT: (local $x i32) |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (block (result i32) |
| ;; OPTIN-NEXT: (call $fallthrough) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $fallthrough |
| ;; We can read values through a fallthrough. |
| (local $x i32) |
| (local.set $x |
| (block (result i32) |
| (call $fallthrough) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $fallthrough-get (type $1) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local $z i32) |
| ;; CHECK-NEXT: (local $w i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $z |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $w |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $fallthrough-get (type $1) |
| ;; OPTIN-NEXT: (local $x i32) |
| ;; OPTIN-NEXT: (local $z i32) |
| ;; OPTIN-NEXT: (local $w i32) |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (block (result i32) |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (local.set $z |
| ;; OPTIN-NEXT: (i32.const 42) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (local.set $w |
| ;; OPTIN-NEXT: (block (result i32) |
| ;; OPTIN-NEXT: (i32.const 42) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $fallthrough-get |
| (local $x i32) |
| (local $z i32) |
| (local $w i32) |
| ;; $x gets the initial value of $z (0) via block fallthrough. We should |
| ;; notice that $z is relevant, i.e., we need to track its values, even |
| ;; though we get it through a fallthrough and not directly. |
| (local.set $x |
| (block (result i32) |
| (local.get $z) |
| ) |
| ) |
| |
| ;; $z is modified to 42. |
| (local.set $z (i32.const 42)) |
| |
| ;; $w gets the updated value of $z (42), also via block fallthrough. |
| (local.set $w |
| (block (result i32) |
| (local.get $z) |
| ) |
| ) |
| |
| ;; 0 == 42 is 0 at runtime. If we did not mark $z as relevant, we would see |
| ;; $x and $w as both equal to $z, i.e., that they are themselves equal, and |
| ;; misoptimize this to 1. We optimize to 0 here. |
| (drop |
| (i32.eq |
| (local.get $x) |
| (local.get $w) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $fallthrough-tee (type $0) (param $param i32) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (local $z i32) |
| ;; CHECK-NEXT: (local $w i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (local.tee $param |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $z |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $w |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $fallthrough-tee (type $0) (param $param i32) |
| ;; OPTIN-NEXT: (local $x i32) |
| ;; OPTIN-NEXT: (local $z i32) |
| ;; OPTIN-NEXT: (local $w i32) |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (local.tee $param |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (local.set $z |
| ;; OPTIN-NEXT: (i32.const 42) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (local.set $w |
| ;; OPTIN-NEXT: (block (result i32) |
| ;; OPTIN-NEXT: (i32.const 42) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $fallthrough-tee (param $param i32) |
| (local $x i32) |
| (local $z i32) |
| (local $w i32) |
| ;; Similar to above, but now a tee is used for the fallthrough. |
| (local.set $x |
| (local.tee $param ;; this changed |
| (local.get $z) |
| ) |
| ) |
| |
| (local.set $z (i32.const 42)) |
| |
| (local.set $w |
| (block (result i32) |
| (local.get $z) |
| ) |
| ) |
| |
| ;; As before, this is optimized to 0. |
| (drop |
| (i32.eq |
| (local.get $x) |
| (local.get $w) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $eqz-condition (type $0) (param $x i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.eqz |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $eqz-condition (type $0) (param $x i32) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (i32.eqz |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $eqz-condition (param $x i32) |
| ;; Verify we parse an eqz condition and can optimize after it. |
| (if |
| (i32.eqz |
| (local.get $x) |
| ) |
| (then |
| ;; Also test we parse this binary properly, and see it is equivalent to |
| ;; the eqz. |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 0) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $eqz-condition-64 (type $11) (param $x i64) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i64.eqz |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $eqz-condition-64 (type $11) (param $x i64) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (i64.eqz |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $eqz-condition-64 (param $x i64) |
| ;; As above, but i64. |
| (if |
| (i64.eqz |
| (local.get $x) |
| ) |
| (then |
| (drop |
| (i64.eq |
| (local.get $x) |
| (i64.const 0) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $sign-unsigned-less-more-mix (type $0) (param $0 i32) |
| ;; CHECK-NEXT: (block $block |
| ;; CHECK-NEXT: (br_if $block |
| ;; CHECK-NEXT: (i32.ge_s |
| ;; CHECK-NEXT: (local.get $0) |
| ;; CHECK-NEXT: (i32.const 1024) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.gt_u |
| ;; CHECK-NEXT: (local.get $0) |
| ;; CHECK-NEXT: (i32.const -1992) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $sign-unsigned-less-more-mix (type $0) (param $0 i32) |
| ;; OPTIN-NEXT: (block $block |
| ;; OPTIN-NEXT: (br_if $block |
| ;; OPTIN-NEXT: (i32.ge_s |
| ;; OPTIN-NEXT: (local.get $0) |
| ;; OPTIN-NEXT: (i32.const 1024) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.gt_u |
| ;; OPTIN-NEXT: (local.get $0) |
| ;; OPTIN-NEXT: (i32.const -1992) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $sign-unsigned-less-more-mix (param $0 i32) |
| (block $block |
| (br_if $block |
| (i32.ge_s |
| (local.get $0) |
| (i32.const 1024) |
| ) |
| ) |
| ;; Here we know $0 <_s 1024. That includes non-negative numbers like 0 as |
| ;; well as negative numbers. The following *unsigned* inequality might or |
| ;; might not be true, so we optimize nothing. |
| (drop |
| (i32.gt_u |
| (local.get $0) |
| (i32.const -1992) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $sign-unsigned-less-more-mix-2 (type $0) (param $0 i32) |
| ;; CHECK-NEXT: (block $block |
| ;; CHECK-NEXT: (br_if $block |
| ;; CHECK-NEXT: (i32.gt_s |
| ;; CHECK-NEXT: (local.get $0) |
| ;; CHECK-NEXT: (i32.const -1023) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.gt_u |
| ;; CHECK-NEXT: (local.get $0) |
| ;; CHECK-NEXT: (i32.const -1992) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $sign-unsigned-less-more-mix-2 (type $0) (param $0 i32) |
| ;; OPTIN-NEXT: (block $block |
| ;; OPTIN-NEXT: (br_if $block |
| ;; OPTIN-NEXT: (i32.gt_s |
| ;; OPTIN-NEXT: (local.get $0) |
| ;; OPTIN-NEXT: (i32.const -1023) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.gt_u |
| ;; OPTIN-NEXT: (local.get $0) |
| ;; OPTIN-NEXT: (i32.const -1992) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $sign-unsigned-less-more-mix-2 (param $0 i32) |
| ;; Similar to the above, but the 1024 was replaced by -1023. |
| (block $block |
| (br_if $block |
| (i32.gt_s |
| (local.get $0) |
| (i32.const -1023) ;; this changed |
| ) |
| ) |
| ;; Here we know $0 <=_s -1023. This includes most numbers with the sign |
| ;; bit set, except for the lowest in absolute value. That implies the |
| ;; following *unsigned* inequality might or might not be true, so we |
| ;; optimize nothing. |
| (drop |
| (i32.gt_u |
| (local.get $0) |
| (i32.const -1992) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $sign-unsigned-less-more-mix-3-yes (type $0) (param $0 i32) |
| ;; CHECK-NEXT: (block $block |
| ;; CHECK-NEXT: (br_if $block |
| ;; CHECK-NEXT: (i32.gt_s |
| ;; CHECK-NEXT: (local.get $0) |
| ;; CHECK-NEXT: (i32.const -4096) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $sign-unsigned-less-more-mix-3-yes (type $0) (param $0 i32) |
| ;; OPTIN-NEXT: (block $block |
| ;; OPTIN-NEXT: (br_if $block |
| ;; OPTIN-NEXT: (i32.gt_s |
| ;; OPTIN-NEXT: (local.get $0) |
| ;; OPTIN-NEXT: (i32.const -4096) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $sign-unsigned-less-more-mix-3-yes (param $0 i32) |
| ;; Similar to the above, but the 1024 was replaced by -4096. |
| (block $block |
| (br_if $block |
| (i32.gt_s |
| (local.get $0) |
| (i32.const -4096) ;; this changed |
| ) |
| ) |
| ;; Now we can optimize: there is no overlap possible, so this is 0. |
| (drop |
| (i32.gt_u |
| (local.get $0) |
| (i32.const -1992) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $eqz_eqz (type $0) (param $a i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.eqz |
| ;; CHECK-NEXT: (i32.eqz |
| ;; CHECK-NEXT: (local.get $a) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $eqz_eqz (type $0) (param $a i32) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (local.get $a) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $eqz_eqz (param $a i32) |
| (if |
| ;; !!a => a is not 0. |
| (i32.eqz |
| (i32.eqz |
| (local.get $a) |
| ) |
| ) |
| (then |
| (drop |
| ;; This is true. |
| (i32.ne |
| (local.get $a) |
| (i32.const 0) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $pair (type $2) (param $a i32) (param $b i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.and |
| ;; CHECK-NEXT: (i32.eqz |
| ;; CHECK-NEXT: (local.get $a) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $b) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $a) |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $b) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $a) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $pair (type $2) (param $a i32) (param $b i32) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (i32.and |
| ;; OPTIN-NEXT: (i32.eqz |
| ;; OPTIN-NEXT: (local.get $a) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $b) |
| ;; OPTIN-NEXT: (i32.const 42) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (else |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eqz |
| ;; OPTIN-NEXT: (local.get $a) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $b) |
| ;; OPTIN-NEXT: (i32.const 42) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $a) |
| ;; OPTIN-NEXT: (i32.const 42) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 42) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $pair (param $a i32) (param $b i32) |
| ;; Two constraints in one if condition. |
| (if |
| (i32.and |
| ;; a == 0 |
| (i32.eqz |
| (local.get $a) |
| ) |
| ;; b == 42 |
| (i32.eq |
| (local.get $b) |
| (i32.const 42) |
| ) |
| ) |
| (then |
| ;; These are all true. |
| (drop |
| (i32.eq |
| (local.get $a) |
| (i32.const 0) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $b) |
| (i32.const 42) |
| ) |
| ) |
| ;; This is not. |
| (drop |
| (i32.eq |
| (local.get $a) |
| (i32.const 42) |
| ) |
| ) |
| ) |
| (else |
| ;; The same expressions as in the (then ..). Here, at least one must be |
| ;; false, not not necessarily all of them, so we infer nothing. |
| (drop |
| (i32.eq |
| (local.get $a) |
| (i32.const 0) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $b) |
| (i32.const 42) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $a) |
| (i32.const 42) |
| ) |
| ) |
| ;; A silly extra instruction to stop optimize-instructions from |
| ;; folding the if-else arms. |
| (drop |
| (i32.const 42) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $pair-same-local (type $0) (param $a i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.and |
| ;; CHECK-NEXT: (i32.gt_u |
| ;; CHECK-NEXT: (local.get $a) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.gt_u |
| ;; CHECK-NEXT: (local.get $a) |
| ;; CHECK-NEXT: (i32.const 1337) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $pair-same-local (type $0) (param $a i32) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (if (result i32) |
| ;; OPTIN-NEXT: (i32.and |
| ;; OPTIN-NEXT: (i32.gt_u |
| ;; OPTIN-NEXT: (local.get $a) |
| ;; OPTIN-NEXT: (i32.const 42) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (i32.gt_u |
| ;; OPTIN-NEXT: (local.get $a) |
| ;; OPTIN-NEXT: (i32.const 1337) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (else |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $pair-same-local (param $a i32) |
| ;; Similar to above, but now the two ANDed conditions are about the same |
| ;; local |
| (if |
| (i32.and |
| ;; a > 42 |
| (i32.gt_u |
| (local.get $a) |
| (i32.const 42) |
| ) |
| ;; a > 1337 |
| (i32.gt_u |
| (local.get $a) |
| (i32.const 1337) |
| ) |
| ) |
| (then |
| ;; This is true. |
| (drop |
| (i32.gt_u |
| (local.get $a) |
| (i32.const 1337) |
| ) |
| ) |
| ) |
| (else |
| ;; The negation of a > 42 && a > 1337 is a <= 42 || a <= 1337, so we |
| ;; can infer a <= 1337. |
| (drop |
| (i32.le_u |
| (local.get $a) |
| (i32.const 1337) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $pair-same-local-bad-or (type $0) (param $a i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.and |
| ;; CHECK-NEXT: (i32.ne |
| ;; CHECK-NEXT: (local.get $a) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.ne |
| ;; CHECK-NEXT: (local.get $a) |
| ;; CHECK-NEXT: (i32.const 1337) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.ne |
| ;; CHECK-NEXT: (local.get $a) |
| ;; CHECK-NEXT: (i32.const 500) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $a) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $a) |
| ;; CHECK-NEXT: (i32.const 1337) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $a) |
| ;; CHECK-NEXT: (i32.const 500) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $pair-same-local-bad-or (type $0) (param $a i32) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (i32.and |
| ;; OPTIN-NEXT: (i32.ne |
| ;; OPTIN-NEXT: (local.get $a) |
| ;; OPTIN-NEXT: (i32.const 42) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (i32.ne |
| ;; OPTIN-NEXT: (local.get $a) |
| ;; OPTIN-NEXT: (i32.const 1337) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.ne |
| ;; OPTIN-NEXT: (local.get $a) |
| ;; OPTIN-NEXT: (i32.const 500) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (else |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $a) |
| ;; OPTIN-NEXT: (i32.const 42) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $a) |
| ;; OPTIN-NEXT: (i32.const 1337) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $a) |
| ;; OPTIN-NEXT: (i32.const 500) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $pair-same-local-bad-or (param $a i32) |
| ;; As above, but the OR we do for the else fails. |
| (if |
| (i32.and |
| ;; a != 42 && a != 1337 |
| (i32.ne |
| (local.get $a) |
| (i32.const 42) |
| ) |
| (i32.ne |
| (local.get $a) |
| (i32.const 1337) |
| ) |
| ) |
| (then |
| ;; These are true. |
| (drop |
| (i32.ne |
| (local.get $a) |
| (i32.const 42) |
| ) |
| ) |
| (drop |
| (i32.ne |
| (local.get $a) |
| (i32.const 1337) |
| ) |
| ) |
| ;; This is unknown. |
| (drop |
| (i32.ne |
| (local.get $a) |
| (i32.const 500) |
| ) |
| ) |
| ) |
| (else |
| ;; ORing a == 42 || a == 1337, we get nothing useful, and can prove |
| ;; nothing. TODO: we could infer a span here |
| (drop |
| (i32.eq |
| (local.get $a) |
| (i32.const 42) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $a) |
| (i32.const 1337) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $a) |
| (i32.const 500) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $pair-partial (type $0) (param $a i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.and |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $a) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (call $import) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $a) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $pair-partial (type $0) (param $a i32) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (i32.and |
| ;; OPTIN-NEXT: (call $import) |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $a) |
| ;; OPTIN-NEXT: (i32.const 42) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (else |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $a) |
| ;; OPTIN-NEXT: (i32.const 42) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 42) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $pair-partial (param $a i32) |
| (if |
| (i32.and |
| ;; a == 42 |
| (i32.eq |
| (local.get $a) |
| (i32.const 42) |
| ) |
| ;; ANDed with something we know nothing about |
| (call $import) |
| ) |
| (then |
| ;; a must be 42, to get here. |
| (drop |
| (i32.eq |
| (local.get $a) |
| (i32.const 42) |
| ) |
| ) |
| ) |
| (else |
| ;; But we don't know anything about a here: we get to this place when |
| ;; a != 42 OR the unknown was 0, so a could be anything. |
| (drop |
| (i32.eq |
| (local.get $a) |
| (i32.const 42) |
| ) |
| ) |
| ;; A silly extra instruction to stop optimize-instructions from |
| ;; folding the if-else arms. |
| (drop |
| (i32.const 42) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $several (type $12) (param $a i32) (param $b i32) (param $c i32) (param $d i32) (param $e i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.and |
| ;; CHECK-NEXT: (i32.and |
| ;; CHECK-NEXT: (i32.eqz |
| ;; CHECK-NEXT: (i32.eqz |
| ;; CHECK-NEXT: (local.get $a) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $b) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.and |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $d) |
| ;; CHECK-NEXT: (i32.const 20) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $e) |
| ;; CHECK-NEXT: (i32.const 30) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $c) |
| ;; CHECK-NEXT: (i32.const 40) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $several (type $12) (param $a i32) (param $b i32) (param $c i32) (param $d i32) (param $e i32) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (i32.and |
| ;; OPTIN-NEXT: (i32.and |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $b) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (i32.ne |
| ;; OPTIN-NEXT: (local.get $a) |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (i32.and |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $d) |
| ;; OPTIN-NEXT: (i32.const 20) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $e) |
| ;; OPTIN-NEXT: (i32.const 30) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $c) |
| ;; OPTIN-NEXT: (i32.const 40) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $several (param $a i32) (param $b i32) (param $c i32) (param $d i32) (param $e i32) |
| ;; Four constraints in one if condition. |
| (if |
| (i32.and |
| (i32.and |
| ;; !!a => a is not 0. |
| (i32.eqz |
| (i32.eqz |
| (local.get $a) |
| ) |
| ) |
| ;; b == 10 |
| (i32.eq |
| (local.get $b) |
| (i32.const 10) |
| ) |
| ) |
| (i32.and |
| ;; d == 20 |
| (i32.eq |
| (local.get $d) |
| (i32.const 20) |
| ) |
| ;; e == 30 |
| (i32.eq |
| (local.get $e) |
| (i32.const 30) |
| ) |
| ) |
| ) |
| (then |
| ;; These are all true. |
| (drop |
| (i32.ne |
| (local.get $a) |
| (i32.const 0) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $b) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $d) |
| (i32.const 20) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $e) |
| (i32.const 30) |
| ) |
| ) |
| ;; This is false. |
| (drop |
| (i32.eq |
| (local.get $a) |
| (i32.const 0) |
| ) |
| ) |
| ;; This local is unknown. |
| (drop |
| (i32.eq |
| (local.get $c) |
| (i32.const 40) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $three.contradiction.middle (type $0) (param $x i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.and |
| ;; CHECK-NEXT: (i32.eqz |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.and |
| ;; CHECK-NEXT: (i32.lt_s |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.ge_s |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $three.contradiction.middle (type $0) (param $x i32) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (i32.and |
| ;; OPTIN-NEXT: (i32.eqz |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (i32.and |
| ;; OPTIN-NEXT: (i32.lt_s |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (i32.ge_s |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (block |
| ;; OPTIN-NEXT: (unreachable) |
| ;; OPTIN-NEXT: (unreachable) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $three.contradiction.middle (param $x i32) |
| ;; First we set x == 0, then x < 0, then x >= 0. The first two are a |
| ;; contradiction. We should not error (after a contradiction, we should not |
| ;; apply further constraints), and can optimize away the if body. |
| (if |
| (i32.and |
| (i32.eqz |
| (local.get $x) |
| ) |
| (i32.and |
| (i32.lt_s |
| (local.get $x) |
| (i32.const 0) |
| ) |
| (i32.ge_s |
| (local.get $x) |
| (i32.const 0) |
| ) |
| ) |
| ) |
| (then |
| (drop |
| (i32.eqz |
| (local.get $x) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $br_if_and (type $0) (param $param i32) |
| ;; CHECK-NEXT: (block $block |
| ;; CHECK-NEXT: (br_if $block |
| ;; CHECK-NEXT: (i32.and |
| ;; CHECK-NEXT: (i32.ne |
| ;; CHECK-NEXT: (local.get $param) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.ne |
| ;; CHECK-NEXT: (local.get $param) |
| ;; CHECK-NEXT: (i32.const 20) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (return) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.and |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.ne |
| ;; CHECK-NEXT: (local.get $param) |
| ;; CHECK-NEXT: (i32.const 30) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $br_if_and (type $0) (param $param i32) |
| ;; OPTIN-NEXT: (block $block |
| ;; OPTIN-NEXT: (br_if $block |
| ;; OPTIN-NEXT: (i32.and |
| ;; OPTIN-NEXT: (i32.ne |
| ;; OPTIN-NEXT: (local.get $param) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (i32.ne |
| ;; OPTIN-NEXT: (local.get $param) |
| ;; OPTIN-NEXT: (i32.const 20) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (return) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.and |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.ne |
| ;; OPTIN-NEXT: (local.get $param) |
| ;; OPTIN-NEXT: (i32.const 30) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $br_if_and (param $param i32) |
| ;; An AND in a br_if condition. |
| (block $block |
| (br_if $block |
| (i32.and |
| (i32.ne |
| (local.get $param) |
| (i32.const 10) |
| ) |
| (i32.ne |
| (local.get $param) |
| (i32.const 20) |
| ) |
| ) |
| ) |
| (return) |
| ) |
| ;; If we get here, param != 10 && param != 20. We optimize each arm of the |
| ;; AND here. |
| (drop |
| (i32.and |
| (i32.ne |
| (local.get $param) |
| (i32.const 10) |
| ) |
| (i32.ne |
| (local.get $param) |
| (i32.const 20) |
| ) |
| ) |
| ) |
| ;; This one we don't know. |
| (drop |
| (i32.ne |
| (local.get $param) |
| (i32.const 30) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $tee-condition (type $0) (param $x i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.tee $x |
| ;; CHECK-NEXT: (call $import) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $tee-condition (type $0) (param $x i32) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.tee $x |
| ;; OPTIN-NEXT: (call $import) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (i32.const 42) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $tee-condition (param $x i32) |
| ;; We can parse the tee in the condition below. The constraint is saying |
| ;; $x == 42. |
| (if |
| (i32.eq |
| (local.tee $x |
| (call $import) |
| ) |
| (i32.const 42) |
| ) |
| (then |
| ;; $x == 42, so this is false. |
| (drop |
| (i32.eqz |
| (local.get $x) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $tee-condition-later-get (type $0) (param $x i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.and |
| ;; CHECK-NEXT: (i32.ne |
| ;; CHECK-NEXT: (local.tee $x |
| ;; CHECK-NEXT: (call $import) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.ne |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (i32.const 1337) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $tee-condition-later-get (type $0) (param $x i32) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (i32.and |
| ;; OPTIN-NEXT: (i32.ne |
| ;; OPTIN-NEXT: (local.tee $x |
| ;; OPTIN-NEXT: (call $import) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (i32.const 42) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (i32.ne |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 1337) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $tee-condition-later-get (param $x i32) |
| ;; The nested ANDs here include a tee and a later get, which is fine. |
| (if |
| (i32.and |
| (i32.ne |
| (local.tee $x |
| (call $import) |
| ) |
| (i32.const 42) |
| ) |
| (i32.ne |
| (local.get $x) |
| (i32.const 1337) |
| ) |
| ) |
| (then |
| ;; $x != 42 && $x != 1337, so these are true. |
| (drop |
| (i32.ne |
| (local.get $x) |
| (i32.const 42) |
| ) |
| ) |
| (drop |
| (i32.ne |
| (local.get $x) |
| (i32.const 1337) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $tee-condition-later-get-2 (type $0) (param $x i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.tee $x |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $tee-condition-later-get-2 (type $0) (param $x i32) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (block (result i32) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (local.tee $x |
| ;; OPTIN-NEXT: (i32.const 42) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $tee-condition-later-get-2 (param $x i32) |
| ;; A later get without two ANDs - in the same comparison. We can infer the |
| ;; comparison is 1. |
| (if |
| (i32.eq |
| (local.tee $x |
| (i32.const 42) |
| ) |
| (local.get $x) |
| ) |
| (then |
| ;; $x == 42, so this is true. |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 42) |
| ) |
| ) |
| ) |
| ) |
| ) |
| ;; CHECK: (func $tee-condition-later-tee (type $0) (param $x i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.and |
| ;; CHECK-NEXT: (i32.ne |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.ne |
| ;; CHECK-NEXT: (local.tee $x |
| ;; CHECK-NEXT: (call $import) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 1337) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.ne |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.ne |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (i32.const 1337) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $tee-condition-later-tee (type $0) (param $x i32) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (i32.and |
| ;; OPTIN-NEXT: (i32.ne |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 42) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (i32.ne |
| ;; OPTIN-NEXT: (local.tee $x |
| ;; OPTIN-NEXT: (call $import) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (i32.const 1337) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.ne |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 42) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.ne |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 1337) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $tee-condition-later-tee (param $x i32) |
| ;; Reverse the tee and get order. $x appears twice, with two different |
| ;; values, so we give up here. |
| ;; TODO: use SSA |
| (if |
| (i32.and |
| (i32.ne |
| (local.get $x) |
| (i32.const 42) |
| ) |
| (i32.ne |
| (local.tee $x |
| (call $import) |
| ) |
| (i32.const 1337) |
| ) |
| ) |
| (then |
| ;; We infer nothing here TODO: we could infer the last |
| (drop |
| (i32.ne |
| (local.get $x) |
| (i32.const 42) |
| ) |
| ) |
| (drop |
| (i32.ne |
| (local.get $x) |
| (i32.const 1337) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $tee-condition-later-tee-different-local (type $2) (param $x i32) (param $y i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.and |
| ;; CHECK-NEXT: (i32.ne |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.ne |
| ;; CHECK-NEXT: (local.tee $y |
| ;; CHECK-NEXT: (call $import) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 1337) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $tee-condition-later-tee-different-local (type $2) (param $x i32) (param $y i32) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (i32.and |
| ;; OPTIN-NEXT: (i32.ne |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 42) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (i32.ne |
| ;; OPTIN-NEXT: (local.tee $y |
| ;; OPTIN-NEXT: (call $import) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (i32.const 1337) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $tee-condition-later-tee-different-local (param $x i32) (param $y i32) |
| ;; As above, a get and a tee that look like they interfere, but now the |
| ;; local indexes are different. This is fine. |
| (if |
| (i32.and |
| (i32.ne |
| (local.get $x) |
| (i32.const 42) |
| ) |
| (i32.ne |
| (local.tee $y |
| (call $import) |
| ) |
| (i32.const 1337) |
| ) |
| ) |
| (then |
| ;; $x != 42 && $y != 1337, so these are true. |
| (drop |
| (i32.ne |
| (local.get $x) |
| (i32.const 42) |
| ) |
| ) |
| (drop |
| (i32.ne |
| (local.get $y) |
| (i32.const 1337) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $tee-condition-eqz (type $0) (param $x i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.eqz |
| ;; CHECK-NEXT: (local.tee $x |
| ;; CHECK-NEXT: (call $import) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $tee-condition-eqz (type $0) (param $x i32) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (i32.eqz |
| ;; OPTIN-NEXT: (local.tee $x |
| ;; OPTIN-NEXT: (call $import) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $tee-condition-eqz (param $x i32) |
| ;; Testing for parsing of tee in an eqz. |
| (if |
| (i32.eqz |
| (local.tee $x |
| (call $import) |
| ) |
| ) |
| (then |
| ;; This is true. |
| (drop |
| (i32.eqz |
| (local.get $x) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $tee-condition-eqz-eqz (type $0) (param $x i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.eqz |
| ;; CHECK-NEXT: (i32.eqz |
| ;; CHECK-NEXT: (local.tee $x |
| ;; CHECK-NEXT: (call $import) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $tee-condition-eqz-eqz (type $0) (param $x i32) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (local.tee $x |
| ;; OPTIN-NEXT: (call $import) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $tee-condition-eqz-eqz (param $x i32) |
| ;; Testing for parsing of tee in an eqz^2. In the OPTIN case, the double |
| ;; eqz is optimized out, and we test a bare local.tee in a condition. |
| (if |
| (i32.eqz |
| (i32.eqz |
| (local.tee $x |
| (call $import) |
| ) |
| ) |
| ) |
| (then |
| ;; This is false. |
| (drop |
| (i32.eqz |
| (local.get $x) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $tee-test (type $0) (param $x i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eqz |
| ;; CHECK-NEXT: (local.tee $x |
| ;; CHECK-NEXT: (call $import) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $tee-test (type $0) (param $x i32) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 42) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eqz |
| ;; OPTIN-NEXT: (local.tee $x |
| ;; OPTIN-NEXT: (call $import) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $tee-test (param $x i32) |
| ;; A tee not in the condition, but in the test that we want to optimize. |
| (if |
| ;; $x == 42. |
| (i32.eq |
| (local.get $x) |
| (i32.const 42) |
| ) |
| (then |
| ;; The $x being tested is not the same as the one the constraint was on, |
| ;; so we do not optimize. |
| (drop |
| (i32.eqz |
| (local.tee $x |
| (call $import) |
| ) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $tee-condition-another (type $0) (param $x i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.ne |
| ;; CHECK-NEXT: (local.tee $x |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.tee $x |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $tee-condition-another (type $0) (param $x i32) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (i32.ne |
| ;; OPTIN-NEXT: (local.tee $x |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (local.tee $x |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $tee-condition-another (param $x i32) |
| ;; Two tees of the same local. |
| (if |
| (i32.ne |
| (local.tee $x |
| (i32.const 0) |
| ) |
| (local.tee $x |
| (i32.const 1) |
| ) |
| ) |
| (then |
| ;; We know that $x == 1 here (the last tee; first should not confuse |
| ;; us). |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 1) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $tee-condition-nested (type $2) (param $x i32) (param $y i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.and |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.tee $y |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (i32.const 20) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (call $import) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 30) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $y) |
| ;; CHECK-NEXT: (i32.const 30) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $tee-condition-nested (type $2) (param $x i32) (param $y i32) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (i32.and |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.tee $y |
| ;; OPTIN-NEXT: (block (result i32) |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (i32.const 20) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (call $import) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (i32.const 30) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $y) |
| ;; OPTIN-NEXT: (i32.const 30) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $tee-condition-nested (param $x i32) (param $y i32) |
| ;; A second set is nested inside the tee. $x initially seems like it must be |
| ;; equal to 10 in the If body, but it is set to 20 after. |
| (if |
| (i32.and |
| (i32.eq |
| (local.get $x) |
| (i32.const 10) |
| ) |
| (i32.eq |
| (local.tee $y |
| (block (result i32) |
| (local.set $x |
| (i32.const 20) |
| ) |
| (call $import) |
| ) |
| ) |
| (i32.const 30) |
| ) |
| ) |
| (then |
| ;; This is false: $x was 10 (if the first ANDed expression was true), |
| ;; but $x was set to 20 after. |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| ;; This is true. |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 20) |
| ) |
| ) |
| ;; We don't infer this, even though we could: the get and later set of |
| ;; $x make us give up. TODO |
| (drop |
| (i32.eq |
| (local.get $y) |
| (i32.const 30) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $tee-condition-nested-2 (type $3) (param $x i32) (param $y i32) (param $z i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.and |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.tee $y |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (local.set $z |
| ;; CHECK-NEXT: (i32.const 20) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (call $import) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 30) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $tee-condition-nested-2 (type $3) (param $x i32) (param $y i32) (param $z i32) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (i32.and |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.tee $y |
| ;; OPTIN-NEXT: (block (result i32) |
| ;; OPTIN-NEXT: (local.set $z |
| ;; OPTIN-NEXT: (i32.const 20) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (call $import) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (i32.const 30) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $tee-condition-nested-2 (param $x i32) (param $y i32) (param $z i32) |
| ;; As above, but the second set is of another local. Without any conflict, |
| ;; we can infer more. |
| (if |
| (i32.and |
| (i32.eq |
| (local.get $x) |
| (i32.const 10) |
| ) |
| (i32.eq |
| (local.tee $y |
| (block (result i32) |
| (local.set $z ;; this changed |
| (i32.const 20) |
| ) |
| (call $import) |
| ) |
| ) |
| (i32.const 30) |
| ) |
| ) |
| (then |
| ;; These are all true. |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $y) |
| (i32.const 30) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $z) |
| (i32.const 20) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $tee-condition-nested-get (type $2) (param $x i32) (param $y i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.and |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.eq |
| ;; CHECK-NEXT: (local.tee $y |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (call $import) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 30) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $tee-condition-nested-get (type $2) (param $x i32) (param $y i32) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (i32.and |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: (i32.const 10) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (i32.eq |
| ;; OPTIN-NEXT: (local.tee $y |
| ;; OPTIN-NEXT: (block (result i32) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (call $import) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (i32.const 30) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.const 1) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $tee-condition-nested-get (param $x i32) (param $y i32) |
| ;; As $tee-condition-nested, but rather than a nested *set*, a get. A get |
| ;; is not a problem for us. |
| (if |
| (i32.and |
| (i32.eq |
| (local.get $x) |
| (i32.const 10) |
| ) |
| (i32.eq |
| (local.tee $y |
| (block (result i32) |
| (drop |
| (local.get $x) ;; this changed. |
| ) |
| (call $import) |
| ) |
| ) |
| (i32.const 30) |
| ) |
| ) |
| (then |
| ;; These are all true. |
| (drop |
| (i32.eq |
| (local.get $x) |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (i32.eq |
| (local.get $y) |
| (i32.const 30) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $tee-trample-simple (type $0) (param $x i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.and |
| ;; CHECK-NEXT: (i32.eqz |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.tee $x |
| ;; CHECK-NEXT: (call $import) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.eqz |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $tee-trample-simple (type $0) (param $x i32) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (i32.and |
| ;; OPTIN-NEXT: (i32.eqz |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (local.tee $x |
| ;; OPTIN-NEXT: (call $import) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (i32.eqz |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $tee-trample-simple (param $x i32) |
| (if |
| (i32.and |
| ;; $x == 0 |
| (i32.eqz |
| (local.get $x) |
| ) |
| ;; But then trample $x with an unknown value. |
| (local.tee $x |
| (call $import) |
| ) |
| ) |
| (then |
| ;; $x may not be 0 here, as it was trampled, so we infer nothing. |
| (drop |
| (i32.eqz |
| (local.get $x) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $eqz-ref-is-null (type $4) (param $x anyref) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.eqz |
| ;; CHECK-NEXT: (ref.is_null |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $eqz-ref-is-null (type $4) (param $x anyref) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (ref.is_null |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $eqz-ref-is-null (param $x anyref) |
| (if |
| (i32.eqz |
| (ref.is_null |
| (local.get $x) |
| ) |
| ) |
| (then |
| ;; $x is not null here, so ref.is_null($x) is false (0). |
| (drop |
| (ref.is_null |
| (local.get $x) |
| ) |
| ) |
| ) |
| (else |
| ;; $x is null here, so ref.is_null($x) is true (1). |
| (drop |
| (ref.is_null |
| (local.get $x) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $local.get (type $0) (param $x i32) |
| ;; 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: (else |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $local.get (type $0) (param $x i32) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $local.get (param $x i32) |
| (if |
| (local.get $x) |
| (then |
| (drop |
| ;; This is non-zero, but we can't optimze. |
| (local.get $x) |
| ) |
| ) |
| (else |
| (drop |
| ;; This is zero. |
| (local.get $x) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $local.get.refinalize (type $1) |
| ;; CHECK-NEXT: (local $x anyref) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result nullref) |
| ;; CHECK-NEXT: (ref.null none) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $local.get.refinalize (type $1) |
| ;; OPTIN-NEXT: (local $x anyref) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (block (result nullref) |
| ;; OPTIN-NEXT: (ref.null none) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $local.get.refinalize |
| (local $x anyref) |
| ;; The local.get below is null. After we apply a null there, we refinalize |
| ;; the block's type to nullref. |
| (drop |
| (block (result anyref) |
| (local.get $x) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $local.get.impossible.cast (type $13) (result (ref func)) |
| ;; CHECK-NEXT: (local $x (ref func)) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (ref.cast (ref nofunc) |
| ;; CHECK-NEXT: (ref.null nofunc) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $local.get.impossible.cast (type $13) (result (ref func)) |
| ;; OPTIN-NEXT: (local $x (ref func)) |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (unreachable) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: ) |
| (func $local.get.impossible.cast (result (ref func)) |
| (local $x (ref func)) |
| ;; The cast here traps at runtime. We do not have a valid value to put in |
| ;; place of the local.get (it is not refined enough), but we know it is |
| ;; unreachable. (In OPTIN, we figure out the set's value is unreachable even |
| ;; earlier.) |
| (local.set $x |
| (ref.cast (ref func) |
| (ref.null func) |
| ) |
| ) |
| (local.get $x) |
| ) |
| |
| ;; CHECK: (func $local.get.no.v128 (type $1) |
| ;; CHECK-NEXT: (local $x v128) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $local.get.no.v128 (type $1) |
| ;; OPTIN-NEXT: (local $x v128) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $local.get.no.v128 |
| (local $x v128) |
| ;; We know the value here, but do not copy v128 constants, which are large. |
| ;; TODO: should we optimize this? |
| (drop |
| (local.get $x) |
| ) |
| ) |
| |
| ;; CHECK: (func $local.get.unreachable (type $1) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $local.get.unreachable (type $1) |
| ;; OPTIN-NEXT: (local $x i32) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (i32.const 0) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (unreachable) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $local.get.unreachable |
| (local $x i32) |
| (if |
| (local.get $x) |
| (then |
| ;; $x is 0, so we never get here, and this is unreachable. |
| (drop |
| (local.get $x) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $local.get.float (type $1) |
| ;; CHECK-NEXT: (local $x f64) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (f64.eq |
| ;; CHECK-NEXT: (local.tee $x |
| ;; CHECK-NEXT: (f64.const nan:0x8000000000000) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $local.get.float (type $1) |
| ;; OPTIN-NEXT: (local $x f64) |
| ;; OPTIN-NEXT: (if |
| ;; OPTIN-NEXT: (f64.eq |
| ;; OPTIN-NEXT: (local.tee $x |
| ;; OPTIN-NEXT: (f64.const nan:0x8000000000000) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (then |
| ;; OPTIN-NEXT: (nop) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $local.get.float |
| (local $x f64) |
| ;; The condition here ends up comparing $x to itself. That is normally 1, |
| ;; but not with a nan. We do not optimize floats for this reason (without |
| ;; --fast-math, see constraint-analysis-float.wast). |
| (if |
| (f64.eq |
| (local.tee $x |
| (f64.const nan) |
| ) |
| (local.get $x) |
| ) |
| (then |
| (nop) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $local.get.internalized-string (type $1) |
| ;; CHECK-NEXT: (local $x anyref) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.null none) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.set $x |
| ;; CHECK-NEXT: (any.convert_extern |
| ;; CHECK-NEXT: (string.const "foo") |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; OPTIN: (func $local.get.internalized-string (type $1) |
| ;; OPTIN-NEXT: (local $x anyref) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (ref.null none) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (local.set $x |
| ;; OPTIN-NEXT: (any.convert_extern |
| ;; OPTIN-NEXT: (string.const "foo") |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: (drop |
| ;; OPTIN-NEXT: (local.get $x) |
| ;; OPTIN-NEXT: ) |
| ;; OPTIN-NEXT: ) |
| (func $local.get.internalized-string |
| (local $x anyref) |
| ;; A null can be propagated. |
| (drop |
| (local.get $x) |
| ) |
| ;; A non-null value, like an internalized string, is not optimized (we could |
| ;; emit an any.convert_extern of a strong.const, but it increases size, so |
| ;; we leave this for passes like Precompute and GUFA). |
| ;; TODO: should we optimize this? |
| (local.set $x |
| (any.convert_extern |
| (string.const "foo") |
| ) |
| ) |
| (drop |
| (local.get $x) |
| ) |
| ) |
| ) |