| ;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. |
| ;; RUN: wasm-opt %s --remove-unused-brs -all -S -o - \ |
| ;; RUN: | filecheck %s |
| |
| |
| (module |
| ;; Regression test in which we need to calculate a proper LUB. |
| ;; CHECK: (func $selectify-fresh-lub (type $3) (param $x i32) (result anyref) |
| ;; CHECK-NEXT: (select (result i31ref) |
| ;; CHECK-NEXT: (ref.null none) |
| ;; CHECK-NEXT: (ref.i31 |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $selectify-fresh-lub (param $x i32) (result anyref) |
| (if |
| (local.get $x) |
| (then |
| (return |
| (ref.null none) |
| ) |
| ) |
| (else |
| (return |
| (ref.i31 (i32.const 0)) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $selectify-simple (type $0) (param $0 i32) (result i32) |
| ;; CHECK-NEXT: (select |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: (i32.lt_u |
| ;; CHECK-NEXT: (i32.sub |
| ;; CHECK-NEXT: (local.get $0) |
| ;; CHECK-NEXT: (i32.const 97) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 6) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.lt_u |
| ;; CHECK-NEXT: (i32.sub |
| ;; CHECK-NEXT: (local.get $0) |
| ;; CHECK-NEXT: (i32.const 48) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $selectify-simple (param $0 i32) (result i32) |
| (if (result i32) |
| (i32.lt_u |
| (i32.sub |
| (local.get $0) |
| (i32.const 48) |
| ) |
| (i32.const 10) |
| ) |
| (then |
| (i32.const 1) |
| ) |
| (else |
| (i32.lt_u |
| (i32.sub |
| (local.get $0) |
| (i32.const 97) |
| ) |
| (i32.const 6) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $restructure-br_if (type $0) (param $x i32) (result i32) |
| ;; CHECK-NEXT: (if (result i32) |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (i32.const 100) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (block $x (result i32) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 200) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 300) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $restructure-br_if (param $x i32) (result i32) |
| ;; this block+br_if can be turned into an if. |
| (block $x (result i32) |
| (drop |
| (br_if $x |
| (i32.const 100) |
| (local.get $x) |
| ) |
| ) |
| (drop (i32.const 200)) |
| (i32.const 300) |
| ) |
| ) |
| |
| ;; CHECK: (func $nothing (type $1) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: ) |
| (func $nothing) |
| |
| |
| ;; CHECK: (func $restructure-br_if-condition-reorderable (type $0) (param $x i32) (result i32) |
| ;; CHECK-NEXT: (if (result i32) |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (call $nothing) |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (i32.const 100) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (block $x (result i32) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 200) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 300) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $restructure-br_if-condition-reorderable (param $x i32) (result i32) |
| (block $x (result i32) |
| (drop |
| (br_if $x |
| (i32.const 100) |
| ;; the condition has side effects, but can be reordered with the value |
| (block (result i32) |
| (call $nothing) |
| (local.get $x) |
| ) |
| ) |
| ) |
| (drop (i32.const 200)) |
| (i32.const 300) |
| ) |
| ) |
| |
| ;; CHECK: (func $restructure-br_if-value-effectful (type $0) (param $x i32) (result i32) |
| ;; CHECK-NEXT: (select |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (call $nothing) |
| ;; CHECK-NEXT: (i32.const 100) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (block $x (result i32) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 200) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 300) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (call $nothing) |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $restructure-br_if-value-effectful (param $x i32) (result i32) |
| (block $x (result i32) |
| (drop |
| (br_if $x |
| ;; the value has side effects, but we can use a select instead |
| ;; of an if, which keeps the value first |
| (block (result i32) |
| (call $nothing) |
| (i32.const 100) |
| ) |
| ;; the condition has side effects too, but can be be reordered |
| ;; to the end of the block |
| (block (result i32) |
| (call $nothing) |
| (local.get $x) |
| ) |
| ) |
| ) |
| (drop (i32.const 200)) |
| (i32.const 300) |
| ) |
| ) |
| |
| ;; CHECK: (func $restructure-br_if-value-effectful-corner-case-1 (type $0) (param $x i32) (result i32) |
| ;; CHECK-NEXT: (block $x (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (br_if $x |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (call $nothing) |
| ;; CHECK-NEXT: (i32.const 100) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (call $nothing) |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (call $nothing) |
| ;; CHECK-NEXT: (i32.const 300) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $restructure-br_if-value-effectful-corner-case-1 (param $x i32) (result i32) |
| (block $x (result i32) |
| (drop |
| (br_if $x |
| (block (result i32) |
| (call $nothing) |
| (i32.const 100) |
| ) |
| (block (result i32) |
| (call $nothing) |
| (local.get $x) |
| ) |
| ) |
| ) |
| ;; the condition cannot be reordered with this |
| (call $nothing) |
| (i32.const 300) |
| ) |
| ) |
| |
| ;; CHECK: (func $get-i32 (type $4) (result i32) |
| ;; CHECK-NEXT: (i32.const 400) |
| ;; CHECK-NEXT: ) |
| (func $get-i32 (result i32) |
| (i32.const 400) |
| ) |
| |
| ;; CHECK: (func $restructure-br_if-value-effectful-corner-case-2 (type $0) (param $x i32) (result i32) |
| ;; CHECK-NEXT: (block $x (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (br_if $x |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (call $nothing) |
| ;; CHECK-NEXT: (i32.const 100) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (call $nothing) |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 300) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (call $get-i32) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $restructure-br_if-value-effectful-corner-case-2 (param $x i32) (result i32) |
| (block $x (result i32) |
| (drop |
| (br_if $x |
| (block (result i32) |
| (call $nothing) |
| (i32.const 100) |
| ) |
| (block (result i32) |
| (call $nothing) |
| (local.get $x) |
| ) |
| ) |
| ) |
| (drop (i32.const 300)) |
| ;; the condition cannot be reordered with this |
| (call $get-i32) |
| ) |
| ) |
| ;; CHECK: (func $restructure-br_if-value-effectful-corner-case-3 (type $0) (param $x i32) (result i32) |
| ;; CHECK-NEXT: (block $x (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (br_if $x |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (call $nothing) |
| ;; CHECK-NEXT: (i32.const 100) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (call $nothing) |
| ;; CHECK-NEXT: (i32.const 100) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $restructure-br_if-value-effectful-corner-case-3 (param $x i32) (result i32) |
| (block $x (result i32) |
| (drop |
| (br_if $x |
| ;; we can't do an if because of effects here |
| (block (result i32) |
| (call $nothing) |
| (i32.const 100) |
| ) |
| (local.get $x) |
| ) |
| ) |
| ;; and we can't do a select because of effects here |
| (call $nothing) |
| (i32.const 100) |
| ) |
| ) |
| |
| ;; CHECK: (func $restructure-br_if-value-effectful-corner-case-4 (type $0) (param $x i32) (result i32) |
| ;; CHECK-NEXT: (block $x (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (br_if $x |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (call $nothing) |
| ;; CHECK-NEXT: (i32.const 100) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 300) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (call $get-i32) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $restructure-br_if-value-effectful-corner-case-4 (param $x i32) (result i32) |
| (block $x (result i32) |
| (drop |
| (br_if $x |
| ;; we can't do an if because of effects here |
| (block (result i32) |
| (call $nothing) |
| (i32.const 100) |
| ) |
| (local.get $x) |
| ) |
| ) |
| (drop (i32.const 300)) |
| ;; and we can't do a select because of effects here |
| (call $get-i32) |
| ) |
| ) |
| |
| ;; CHECK: (func $restructure-select-no-multivalue (type $1) |
| ;; CHECK-NEXT: (tuple.drop 2 |
| ;; CHECK-NEXT: (block $block (type $2) (result i32 i32) |
| ;; CHECK-NEXT: (tuple.drop 2 |
| ;; CHECK-NEXT: (br_if $block |
| ;; CHECK-NEXT: (tuple.make 2 |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: (call $restructure-br_if |
| ;; CHECK-NEXT: (i32.const 2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 3) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (tuple.make 2 |
| ;; CHECK-NEXT: (i32.const 4) |
| ;; CHECK-NEXT: (i32.const 5) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $restructure-select-no-multivalue |
| (tuple.drop 2 |
| (block $block (result i32 i32) |
| (tuple.drop 2 |
| (br_if $block |
| (tuple.make 2 |
| (i32.const 1) |
| ;; Add a side effect to prevent us turning $block into a |
| ;; restructured if - instead, we will try a restructured select. |
| ;; But, selects cannot return multiple values in the spec, so we |
| ;; can do nothing here. |
| (call $restructure-br_if |
| (i32.const 2) |
| ) |
| ) |
| (i32.const 3) |
| ) |
| ) |
| (tuple.make 2 |
| (i32.const 4) |
| (i32.const 5) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $if-of-if (type $1) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (select |
| ;; CHECK-NEXT: (local.tee $x |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (call $if-of-if) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $if-of-if |
| (local $x i32) |
| ;; The outer if has side effects in the condition while the inner one does |
| ;; not, which means we can fold them. |
| (if |
| (local.tee $x |
| (i32.const 1) |
| ) |
| (then |
| (if |
| (local.get $x) |
| (then |
| (call $if-of-if) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $if-of-if-but-side-effects (type $1) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.tee $x |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.tee $x |
| ;; CHECK-NEXT: (i32.const 2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (call $if-of-if-but-side-effects) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $if-of-if-but-side-effects |
| (local $x i32) |
| ;; The inner if has side effects in the condition, which prevents this |
| ;; optimization. |
| (if |
| (local.tee $x |
| (i32.const 1) |
| ) |
| (then |
| (if |
| (local.tee $x |
| (i32.const 2) |
| ) |
| (then |
| (call $if-of-if-but-side-effects) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $if-of-if-but-too-costly (type $1) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.tee $x |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (i32.eqz |
| ;; CHECK-NEXT: (i32.eqz |
| ;; CHECK-NEXT: (i32.eqz |
| ;; CHECK-NEXT: (i32.eqz |
| ;; CHECK-NEXT: (i32.eqz |
| ;; CHECK-NEXT: (i32.eqz |
| ;; CHECK-NEXT: (i32.eqz |
| ;; CHECK-NEXT: (i32.eqz |
| ;; CHECK-NEXT: (i32.eqz |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (call $if-of-if-but-too-costly) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $if-of-if-but-too-costly |
| (local $x i32) |
| ;; The inner if's condition has no effects, but it is very costly, so do not |
| ;; run it unconditionally - leave this unoptimized. |
| (if |
| (local.tee $x |
| (i32.const 1) |
| ) |
| (then |
| (if |
| (i32.eqz (i32.eqz (i32.eqz (i32.eqz (i32.eqz (i32.eqz (i32.eqz (i32.eqz (i32.eqz |
| (local.get $x) |
| ))))))))) |
| (then |
| (call $if-of-if-but-too-costly) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $if-of-if-but-inner-else (type $1) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.tee $x |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (call $if-of-if-but-inner-else) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (call $if-of-if) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $if-of-if-but-inner-else |
| (local $x i32) |
| ;; The inner if has an else. For now, leave this unoptimized. |
| (if |
| (local.tee $x |
| (i32.const 1) |
| ) |
| (then |
| (if |
| (local.get $x) |
| (then |
| (call $if-of-if-but-inner-else) |
| ) |
| (else |
| (call $if-of-if) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $if-of-if-but-outer-else (type $1) |
| ;; CHECK-NEXT: (local $x i32) |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.tee $x |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (if |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (call $if-of-if-but-outer-else) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (call $if-of-if) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $if-of-if-but-outer-else |
| (local $x i32) |
| ;; The outer if has an else. For now, leave this unoptimized. |
| (if |
| (local.tee $x |
| (i32.const 1) |
| ) |
| (then |
| (if |
| (local.get $x) |
| (then |
| (call $if-of-if-but-outer-else) |
| ) |
| ) |
| ) |
| (else |
| (call $if-of-if) |
| ) |
| ) |
| ) |
| ) |