diff options
author | Alon Zakai <azakai@google.com> | 2022-03-28 09:44:00 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-03-28 16:44:00 +0000 |
commit | 4643f26e113476ebb273ee4f69e89c61801c7c2f (patch) | |
tree | 5de7dc236aa367d11b88f1c639aba44c7efb9b2d /test | |
parent | 11932cc31e88d3d368714fcca43df979f7694bd1 (diff) | |
download | binaryen-4643f26e113476ebb273ee4f69e89c61801c7c2f.tar.gz binaryen-4643f26e113476ebb273ee4f69e89c61801c7c2f.tar.bz2 binaryen-4643f26e113476ebb273ee4f69e89c61801c7c2f.zip |
Generalize PossibleConstantValues for immutable globals (#4549)
This moves more logic from ConstantFieldPropagation into PossibleConstantValues,
that is, instead of handling the two cases of a Literal or a Name before calling
PossibleConstantValues, move that code into the helper class. That way all users of
PossibleConstantValues can benefit from it. In particular, this makes
DeadArgumentElimination now support optimizing immutable globals, as well as
ref.func and ref.null.
(Changes to test/lit/passes/dae-gc-refine-params.wast are to avoid the new
optimizations from kicking in, so that it still tests what it tested before.)
Diffstat (limited to 'test')
-rw-r--r-- | test/lit/passes/dae-gc-refine-params.wast | 56 | ||||
-rw-r--r-- | test/lit/passes/dae-gc.wast | 161 | ||||
-rw-r--r-- | test/lit/passes/dae_all-features.wast | 112 |
3 files changed, 314 insertions, 15 deletions
diff --git a/test/lit/passes/dae-gc-refine-params.wast b/test/lit/passes/dae-gc-refine-params.wast index e7059b0e0..767c2d716 100644 --- a/test/lit/passes/dae-gc-refine-params.wast +++ b/test/lit/passes/dae-gc-refine-params.wast @@ -27,34 +27,36 @@ ;; CHECK: (func $call-various-params-no ;; CHECK-NEXT: (call $various-params-no - ;; CHECK-NEXT: (ref.null ${}) - ;; CHECK-NEXT: (ref.null ${i32}) + ;; CHECK-NEXT: (call $get_{}) + ;; CHECK-NEXT: (call $get_{i32}) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (call $various-params-no - ;; CHECK-NEXT: (ref.null ${i32}) - ;; CHECK-NEXT: (ref.null ${f64}) + ;; CHECK-NEXT: (call $get_{i32}) + ;; CHECK-NEXT: (call $get_{f64}) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; NOMNL: (func $call-various-params-no (type $none_=>_none) ;; NOMNL-NEXT: (call $various-params-no - ;; NOMNL-NEXT: (ref.null ${}) - ;; NOMNL-NEXT: (ref.null ${i32}) + ;; NOMNL-NEXT: (call $get_{}) + ;; NOMNL-NEXT: (call $get_{i32}) ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: (call $various-params-no - ;; NOMNL-NEXT: (ref.null ${i32}) - ;; NOMNL-NEXT: (ref.null ${f64}) + ;; NOMNL-NEXT: (call $get_{i32}) + ;; NOMNL-NEXT: (call $get_{f64}) ;; NOMNL-NEXT: ) ;; NOMNL-NEXT: ) (func $call-various-params-no ;; The first argument gets {} and {i32}; the second {i32} and {f64}; none of - ;; those pairs can be optimized. + ;; those pairs can be optimized. Note that we do not pass in all nulls, as + ;; all nulls are identical and we could do other optimization work due to + ;; that. (call $various-params-no - (ref.null ${}) - (ref.null ${i32}) + (call $get_{}) + (call $get_{i32}) ) (call $various-params-no - (ref.null ${i32}) - (ref.null ${f64}) + (call $get_{i32}) + (call $get_{f64}) ) ) ;; This function is called in ways that do not allow us to alter the types of @@ -81,6 +83,34 @@ (drop (local.get $y)) ) + ;; CHECK: (func $get_{} (result (ref null ${})) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; NOMNL: (func $get_{} (type $none_=>_ref?|${}|) (result (ref null ${})) + ;; NOMNL-NEXT: (unreachable) + ;; NOMNL-NEXT: ) + (func $get_{} (result (ref null ${})) + (unreachable) + ) + ;; CHECK: (func $get_{i32} (result (ref null ${i32})) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; NOMNL: (func $get_{i32} (type $none_=>_ref?|${i32}|) (result (ref null ${i32})) + ;; NOMNL-NEXT: (unreachable) + ;; NOMNL-NEXT: ) + (func $get_{i32} (result (ref null ${i32})) + (unreachable) + ) + ;; CHECK: (func $get_{f64} (result (ref null ${f64})) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; NOMNL: (func $get_{f64} (type $none_=>_ref?|${f64}|) (result (ref null ${f64})) + ;; NOMNL-NEXT: (unreachable) + ;; NOMNL-NEXT: ) + (func $get_{f64} (result (ref null ${f64})) + (unreachable) + ) + ;; CHECK: (func $call-various-params-yes ;; CHECK-NEXT: (call $various-params-yes ;; CHECK-NEXT: (call $get_null_{i32}) diff --git a/test/lit/passes/dae-gc.wast b/test/lit/passes/dae-gc.wast index bcf177f74..1d12150eb 100644 --- a/test/lit/passes/dae-gc.wast +++ b/test/lit/passes/dae-gc.wast @@ -1,6 +1,6 @@ ;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. -;; RUN: wasm-opt %s -all --dae -S -o - | filecheck %s -;; RUN: wasm-opt %s -all --dae --nominal -S -o - | filecheck %s --check-prefix=NOMNL +;; RUN: foreach %s %t wasm-opt -all --dae -S -o - | filecheck %s +;; RUN: foreach %s %t wasm-opt -all --dae --nominal -S -o - | filecheck %s --check-prefix=NOMNL (module ;; CHECK: (type ${} (struct )) @@ -96,3 +96,160 @@ ) ) ) + +;; Test ref.func and ref.null optimization of constant parameter values. +(module + ;; CHECK: (func $foo (param $0 (ref $none_=>_none)) + ;; CHECK-NEXT: (local $1 (ref null $none_=>_none)) + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (ref.func $a) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; NOMNL: (func $foo (type $ref|none_->_none|_=>_none) (param $0 (ref $none_=>_none)) + ;; NOMNL-NEXT: (local $1 (ref null $none_=>_none)) + ;; NOMNL-NEXT: (local.set $1 + ;; NOMNL-NEXT: (ref.func $a) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (block + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (ref.as_non_null + ;; NOMNL-NEXT: (local.get $1) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (local.get $0) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + (func $foo (param $x (ref func)) (param $y (ref func)) + ;; "Use" the params to avoid other optimizations kicking in. + (drop (local.get $x)) + (drop (local.get $y)) + ) + + ;; CHECK: (func $call-foo + ;; CHECK-NEXT: (call $foo + ;; CHECK-NEXT: (ref.func $b) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $foo + ;; CHECK-NEXT: (ref.func $c) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; NOMNL: (func $call-foo (type $none_=>_none) + ;; NOMNL-NEXT: (call $foo + ;; NOMNL-NEXT: (ref.func $b) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (call $foo + ;; NOMNL-NEXT: (ref.func $c) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + (func $call-foo + ;; Call $foo with a constant function in the first param, which we + ;; can optimize, but different ones in the second. + (call $foo + (ref.func $a) + (ref.func $b) + ) + (call $foo + (ref.func $a) + (ref.func $c) + ) + ) + + ;; CHECK: (func $bar (param $0 (ref null $none_=>_none)) + ;; CHECK-NEXT: (local $1 anyref) + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (ref.null func) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; NOMNL: (func $bar (type $ref?|none_->_none|_=>_none) (param $0 (ref null $none_=>_none)) + ;; NOMNL-NEXT: (local $1 anyref) + ;; NOMNL-NEXT: (local.set $1 + ;; NOMNL-NEXT: (ref.null func) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (block + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (local.get $1) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (drop + ;; NOMNL-NEXT: (local.get $0) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + (func $bar (param $x (ref null any)) (param $y (ref null any)) + ;; "Use" the params to avoid other optimizations kicking in. + (drop (local.get $x)) + (drop (local.get $y)) + ) + + ;; CHECK: (func $call-bar + ;; CHECK-NEXT: (call $bar + ;; CHECK-NEXT: (ref.null $none_=>_none) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $bar + ;; CHECK-NEXT: (ref.func $a) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; NOMNL: (func $call-bar (type $none_=>_none) + ;; NOMNL-NEXT: (call $bar + ;; NOMNL-NEXT: (ref.null $none_=>_none) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: (call $bar + ;; NOMNL-NEXT: (ref.func $a) + ;; NOMNL-NEXT: ) + ;; NOMNL-NEXT: ) + (func $call-bar + ;; Call with nulls. Mixing nulls is fine as they all have the same value, and + ;; we can optimize. However, mixing a null with a reference stops us in the + ;; second param. + (call $bar + (ref.null func) + (ref.null func) + ) + (call $bar + (ref.null any) + (ref.func $a) + ) + ) + + ;; Helper functions so we have something to take the reference of. + ;; CHECK: (func $a + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + ;; NOMNL: (func $a (type $none_=>_none) + ;; NOMNL-NEXT: (nop) + ;; NOMNL-NEXT: ) + (func $a) + ;; CHECK: (func $b + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + ;; NOMNL: (func $b (type $none_=>_none) + ;; NOMNL-NEXT: (nop) + ;; NOMNL-NEXT: ) + (func $b) + ;; CHECK: (func $c + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + ;; NOMNL: (func $c (type $none_=>_none) + ;; NOMNL-NEXT: (nop) + ;; NOMNL-NEXT: ) + (func $c) +) diff --git a/test/lit/passes/dae_all-features.wast b/test/lit/passes/dae_all-features.wast index 42a47eb68..41655d849 100644 --- a/test/lit/passes/dae_all-features.wast +++ b/test/lit/passes/dae_all-features.wast @@ -538,3 +538,115 @@ ) ) ) + +;; Arguments that read an immutable global can be optimized, as that is a +;; constant value. +(module + ;; CHECK: (type $none_=>_none (func)) + + ;; CHECK: (type $i32_=>_none (func (param i32))) + + ;; CHECK: (type $i32_i32_=>_none (func (param i32 i32))) + + ;; CHECK: (global $immut i32 (i32.const 42)) + (global $immut i32 (i32.const 42)) + + ;; CHECK: (global $immut2 i32 (i32.const 43)) + (global $immut2 i32 (i32.const 43)) + + ;; CHECK: (global $mut (mut i32) (i32.const 1337)) + (global $mut (mut i32) (i32.const 1337)) + + ;; CHECK: (func $foo (param $0 i32) + ;; CHECK-NEXT: (local $1 i32) + ;; CHECK-NEXT: (local.set $1 + ;; CHECK-NEXT: (global.get $immut) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (block + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $foo (param $x i32) (param $y i32) + ;; "Use" the params to avoid other optimizations kicking in. + (drop (local.get $x)) + (drop (local.get $y)) + ) + + ;; CHECK: (func $foo-caller + ;; CHECK-NEXT: (global.set $mut + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $foo + ;; CHECK-NEXT: (global.get $mut) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (global.set $mut + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $foo + ;; CHECK-NEXT: (global.get $mut) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $foo-caller + ;; Note how the mutable param has a different value in each call, which shows + ;; the reason that we cannot optimize in this case. But we can optimize the + ;; immutable param. + (global.set $mut (i32.const 1)) + (call $foo + (global.get $immut) + (global.get $mut) + ) + (global.set $mut (i32.const 2)) + (call $foo + (global.get $immut) + (global.get $mut) + ) + ) + + ;; CHECK: (func $bar (param $x i32) (param $y i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (local.get $y) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $bar (param $x i32) (param $y i32) + (drop (local.get $x)) + (drop (local.get $y)) + ) + + ;; CHECK: (func $bar-caller + ;; CHECK-NEXT: (global.set $mut + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $bar + ;; CHECK-NEXT: (global.get $immut) + ;; CHECK-NEXT: (global.get $immut) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (global.set $mut + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call $bar + ;; CHECK-NEXT: (global.get $mut) + ;; CHECK-NEXT: (global.get $immut2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $bar-caller + ;; Corner cases of mixing mutable with immutable and mixing two immutables. + (global.set $mut (i32.const 1)) + (call $bar + (global.get $immut) + (global.get $immut) + ) + (global.set $mut (i32.const 2)) + (call $bar + (global.get $mut) + (global.get $immut2) + ) + ) +) |