| ;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. |
| |
| ;; RUN: wasm-opt %s --optimize-instructions -all -S -o - | filecheck %s |
| |
| ;; Also verify that the "never-fold-or-reorder" flag is respected: when set, we |
| ;; do not fold code together (important, as we keep one of the branch hints, and |
| ;; it may be wrong, which can confuse the fuzzer), and we never reorder (which |
| ;; can move a branch hint to execute before a trap, which can also cause the |
| ;; fuzzer to alert). |
| |
| ;; RUN: wasm-opt %s --optimize-instructions -all --pass-arg=optimize-instructions-never-fold-or-reorder -S -o - \ |
| ;; RUN: | filecheck %s --check-prefix=NO_FO |
| |
| (module |
| ;; CHECK: (func $conditionals (type $1) (param $x i32) (result i32) |
| ;; CHECK-NEXT: (@metadata.code.branch_hint "\01") |
| ;; CHECK-NEXT: (if (result i32) |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (i32.const 1337) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NO_FO: (func $conditionals (type $1) (param $x i32) (result i32) |
| ;; NO_FO-NEXT: (@metadata.code.branch_hint "\01") |
| ;; NO_FO-NEXT: (if (result i32) |
| ;; NO_FO-NEXT: (local.get $x) |
| ;; NO_FO-NEXT: (then |
| ;; NO_FO-NEXT: (i32.const 1337) |
| ;; NO_FO-NEXT: ) |
| ;; NO_FO-NEXT: (else |
| ;; NO_FO-NEXT: (i32.const 42) |
| ;; NO_FO-NEXT: ) |
| ;; NO_FO-NEXT: ) |
| ;; NO_FO-NEXT: ) |
| (func $conditionals (param $x i32) (result i32) |
| ;; When we flip the if, the hint should flip too. |
| (@metadata.code.branch_hint "\00") |
| (if (result i32) |
| (i32.eqz |
| (local.get $x) |
| ) |
| (then |
| (i32.const 42) |
| ) |
| (else |
| (i32.const 1337) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $still-fold (type $0) (param $x i32) (param $y i32) |
| ;; CHECK-NEXT: (@metadata.code.branch_hint "\00") |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $y) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NO_FO: (func $still-fold (type $0) (param $x i32) (param $y i32) |
| ;; NO_FO-NEXT: (if |
| ;; NO_FO-NEXT: (local.get $x) |
| ;; NO_FO-NEXT: (then |
| ;; NO_FO-NEXT: (@metadata.code.branch_hint "\00") |
| ;; NO_FO-NEXT: (if |
| ;; NO_FO-NEXT: (local.get $y) |
| ;; NO_FO-NEXT: (then |
| ;; NO_FO-NEXT: (unreachable) |
| ;; NO_FO-NEXT: ) |
| ;; NO_FO-NEXT: ) |
| ;; NO_FO-NEXT: ) |
| ;; NO_FO-NEXT: (else |
| ;; NO_FO-NEXT: (@metadata.code.branch_hint "\01") |
| ;; NO_FO-NEXT: (if |
| ;; NO_FO-NEXT: (local.get $y) |
| ;; NO_FO-NEXT: (then |
| ;; NO_FO-NEXT: (unreachable) |
| ;; NO_FO-NEXT: ) |
| ;; NO_FO-NEXT: ) |
| ;; NO_FO-NEXT: ) |
| ;; NO_FO-NEXT: ) |
| ;; NO_FO-NEXT: ) |
| (func $still-fold (param $x i32) (param $y i32) |
| ;; We fold if arms even if metadata differs (like LLVM). We do not fold if the |
| ;; flag was passed, however. |
| (if |
| (local.get $x) |
| (then |
| (@metadata.code.branch_hint "\00") |
| (if |
| (local.get $y) |
| (then |
| (unreachable) |
| ) |
| ) |
| ) |
| (else |
| (@metadata.code.branch_hint "\01") |
| (if |
| (local.get $y) |
| (then |
| (unreachable) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $yes-fold (type $0) (param $x i32) (param $y i32) |
| ;; CHECK-NEXT: (@metadata.code.branch_hint "\01") |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $y) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NO_FO: (func $yes-fold (type $0) (param $x i32) (param $y i32) |
| ;; NO_FO-NEXT: (if |
| ;; NO_FO-NEXT: (local.get $x) |
| ;; NO_FO-NEXT: (then |
| ;; NO_FO-NEXT: (@metadata.code.branch_hint "\01") |
| ;; NO_FO-NEXT: (if |
| ;; NO_FO-NEXT: (local.get $y) |
| ;; NO_FO-NEXT: (then |
| ;; NO_FO-NEXT: (unreachable) |
| ;; NO_FO-NEXT: ) |
| ;; NO_FO-NEXT: ) |
| ;; NO_FO-NEXT: ) |
| ;; NO_FO-NEXT: (else |
| ;; NO_FO-NEXT: (@metadata.code.branch_hint "\01") |
| ;; NO_FO-NEXT: (if |
| ;; NO_FO-NEXT: (local.get $y) |
| ;; NO_FO-NEXT: (then |
| ;; NO_FO-NEXT: (unreachable) |
| ;; NO_FO-NEXT: ) |
| ;; NO_FO-NEXT: ) |
| ;; NO_FO-NEXT: ) |
| ;; NO_FO-NEXT: ) |
| ;; NO_FO-NEXT: ) |
| (func $yes-fold (param $x i32) (param $y i32) |
| ;; Now the hints match, so we definitely fold (without the flag). |
| (if |
| (local.get $x) |
| (then |
| (@metadata.code.branch_hint "\01") |
| (if |
| (local.get $y) |
| (then |
| (unreachable) |
| ) |
| ) |
| ) |
| (else |
| (@metadata.code.branch_hint "\01") |
| (if |
| (local.get $y) |
| (then |
| (unreachable) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $always-fold-select (type $2) (param $x i32) (param $y i32) (result i32) |
| ;; CHECK-NEXT: (@metadata.code.branch_hint "\00") |
| ;; CHECK-NEXT: (if (result i32) |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (i32.const 20) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NO_FO: (func $always-fold-select (type $2) (param $x i32) (param $y i32) (result i32) |
| ;; NO_FO-NEXT: (@metadata.code.branch_hint "\00") |
| ;; NO_FO-NEXT: (if (result i32) |
| ;; NO_FO-NEXT: (local.get $x) |
| ;; NO_FO-NEXT: (then |
| ;; NO_FO-NEXT: (i32.const 10) |
| ;; NO_FO-NEXT: ) |
| ;; NO_FO-NEXT: (else |
| ;; NO_FO-NEXT: (i32.const 20) |
| ;; NO_FO-NEXT: ) |
| ;; NO_FO-NEXT: ) |
| ;; NO_FO-NEXT: ) |
| (func $always-fold-select (param $x i32) (param $y i32) (result i32) |
| ;; A select with different metadata is still foldable: the code was executed |
| ;; anyhow, so it's fine if we execute just one of the two (we pick the first, |
| ;; arbitrarily). We do so even with the flag. |
| (select |
| (@metadata.code.branch_hint "\00") |
| (if (result i32) |
| (local.get $x) |
| (then |
| (i32.const 10) |
| ) |
| (else |
| (i32.const 20) |
| ) |
| ) |
| (@metadata.code.branch_hint "\01") |
| (if (result i32) |
| (local.get $x) |
| (then |
| (i32.const 10) |
| ) |
| (else |
| (i32.const 20) |
| ) |
| ) |
| (local.get $y) |
| ) |
| ) |
| |
| ;; CHECK: (func $ordering (type $3) (param $x i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.add |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.add |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (i32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; NO_FO: (func $ordering (type $3) (param $x i32) |
| ;; NO_FO-NEXT: (drop |
| ;; NO_FO-NEXT: (i32.add |
| ;; NO_FO-NEXT: (local.get $x) |
| ;; NO_FO-NEXT: (i32.const 42) |
| ;; NO_FO-NEXT: ) |
| ;; NO_FO-NEXT: ) |
| ;; NO_FO-NEXT: (drop |
| ;; NO_FO-NEXT: (i32.add |
| ;; NO_FO-NEXT: (i32.const 42) |
| ;; NO_FO-NEXT: (local.get $x) |
| ;; NO_FO-NEXT: ) |
| ;; NO_FO-NEXT: ) |
| ;; NO_FO-NEXT: ) |
| (func $ordering (param $x i32) |
| ;; Normally we canonicalize the sides of a binary like this (so after the |
| ;; pass, both the below expressions would be identical), but we refrain from |
| ;; doing so with the flag. |
| (drop |
| (i32.add |
| (local.get $x) |
| (i32.const 42) |
| ) |
| ) |
| (drop |
| (i32.add |
| (i32.const 42) |
| (local.get $x) |
| ) |
| ) |
| ) |
| ) |