diff options
author | Alon Zakai <azakai@google.com> | 2022-06-08 15:54:09 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-06-08 15:54:09 -0700 |
commit | 0b66981d0e3aa958877b5c04769e75836497b4c0 (patch) | |
tree | 6ae22c373eacba3bd31c283170de07ca7761ec21 /test | |
parent | b76d2fb1e5bb839249b25b7094db94219695f515 (diff) | |
download | binaryen-0b66981d0e3aa958877b5c04769e75836497b4c0.tar.gz binaryen-0b66981d0e3aa958877b5c04769e75836497b4c0.tar.bz2 binaryen-0b66981d0e3aa958877b5c04769e75836497b4c0.zip |
GlobalStructInference: Handle >2 globals if values coincide (#4714)
In GSI we look for a read of a global in a situation like this:
$global1: value1
$global2: value2
(struct.get $Type (ref))
If global inference shows this get must be of either $global1 or $global2, then we
can optimize to this:
(ref) == $global1 ? value1 : value2
We focus on the case of two values because 1 is handled by other passes, and >2
makes the tradeoffs less clear.
However, a simple extension is the case where there are more than 2 globals, but
there are only two values, and one value is unique to one global:
$global1: valueA
$global2: valueB
$global3: valueA
=>
(ref) == $global2 ? valueB : valueA
We can still use a single comparison here, on the global that has the
unique value. Then the else will handle all the other globals.
This increases the cases that GSI can optimize J2Wasm output by over 50%.
Diffstat (limited to 'test')
-rw-r--r-- | test/lit/passes/gsi.wast | 264 |
1 files changed, 264 insertions, 0 deletions
diff --git a/test/lit/passes/gsi.wast b/test/lit/passes/gsi.wast index 9b54a98ca..e378fa262 100644 --- a/test/lit/passes/gsi.wast +++ b/test/lit/passes/gsi.wast @@ -162,6 +162,270 @@ ) ) +;; Three globals, as above, but now two agree on their values. We can optimize +;; by comparing to the one that has a single value. +(module + ;; CHECK: (type $struct (struct_subtype (field i32) data)) + (type $struct (struct i32)) + + ;; CHECK: (type $none_=>_none (func_subtype func)) + + ;; CHECK: (global $global1 (ref $struct) (struct.new $struct + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: )) + (global $global1 (ref $struct) (struct.new $struct + (i32.const 42) + )) + + ;; CHECK: (global $global2 (ref $struct) (struct.new $struct + ;; CHECK-NEXT: (i32.const 1337) + ;; CHECK-NEXT: )) + (global $global2 (ref $struct) (struct.new $struct + (i32.const 1337) + )) + + ;; CHECK: (global $global3 (ref $struct) (struct.new $struct + ;; CHECK-NEXT: (i32.const 1337) + ;; CHECK-NEXT: )) + (global $global3 (ref $struct) (struct.new $struct + (i32.const 1337) + )) + + ;; CHECK: (func $test (type $none_=>_none) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: (i32.const 1337) + ;; CHECK-NEXT: (ref.eq + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (ref.null $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (global.get $global1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test + (drop + (struct.get $struct 0 + (ref.null $struct) + ) + ) + ) +) + +;; As above, but move the different value of the three to the middle. +(module + ;; CHECK: (type $struct (struct_subtype (field i32) data)) + (type $struct (struct i32)) + + ;; CHECK: (type $none_=>_none (func_subtype func)) + + ;; CHECK: (global $global1 (ref $struct) (struct.new $struct + ;; CHECK-NEXT: (i32.const 1337) + ;; CHECK-NEXT: )) + (global $global1 (ref $struct) (struct.new $struct + (i32.const 1337) + )) + + ;; CHECK: (global $global2 (ref $struct) (struct.new $struct + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: )) + (global $global2 (ref $struct) (struct.new $struct + (i32.const 42) + )) + + ;; CHECK: (global $global3 (ref $struct) (struct.new $struct + ;; CHECK-NEXT: (i32.const 1337) + ;; CHECK-NEXT: )) + (global $global3 (ref $struct) (struct.new $struct + (i32.const 1337) + )) + + ;; CHECK: (func $test (type $none_=>_none) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: (i32.const 1337) + ;; CHECK-NEXT: (ref.eq + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (ref.null $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (global.get $global2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test + (drop + (struct.get $struct 0 + (ref.null $struct) + ) + ) + ) +) + +;; As above, but move the different value of the three to the end. +(module + ;; CHECK: (type $struct (struct_subtype (field i32) data)) + (type $struct (struct i32)) + + ;; CHECK: (type $none_=>_none (func_subtype func)) + + ;; CHECK: (global $global1 (ref $struct) (struct.new $struct + ;; CHECK-NEXT: (i32.const 1337) + ;; CHECK-NEXT: )) + (global $global1 (ref $struct) (struct.new $struct + (i32.const 1337) + )) + + ;; CHECK: (global $global2 (ref $struct) (struct.new $struct + ;; CHECK-NEXT: (i32.const 1337) + ;; CHECK-NEXT: )) + (global $global2 (ref $struct) (struct.new $struct + (i32.const 1337) + )) + + ;; CHECK: (global $global3 (ref $struct) (struct.new $struct + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: )) + (global $global3 (ref $struct) (struct.new $struct + (i32.const 42) + )) + + ;; CHECK: (func $test (type $none_=>_none) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: (i32.const 1337) + ;; CHECK-NEXT: (ref.eq + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (ref.null $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (global.get $global3) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test + (drop + (struct.get $struct 0 + (ref.null $struct) + ) + ) + ) +) + +;; Four values, two pairs of equal ones. We do not optimize this. +(module + ;; CHECK: (type $struct (struct_subtype (field i32) data)) + (type $struct (struct i32)) + + ;; CHECK: (type $none_=>_none (func_subtype func)) + + ;; CHECK: (global $global1 (ref $struct) (struct.new $struct + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: )) + (global $global1 (ref $struct) (struct.new $struct + (i32.const 42) + )) + + ;; CHECK: (global $global2 (ref $struct) (struct.new $struct + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: )) + (global $global2 (ref $struct) (struct.new $struct + (i32.const 42) + )) + + ;; CHECK: (global $global3 (ref $struct) (struct.new $struct + ;; CHECK-NEXT: (i32.const 1337) + ;; CHECK-NEXT: )) + (global $global3 (ref $struct) (struct.new $struct + (i32.const 1337) + )) + + ;; CHECK: (global $global4 (ref $struct) (struct.new $struct + ;; CHECK-NEXT: (i32.const 1337) + ;; CHECK-NEXT: )) + (global $global4 (ref $struct) (struct.new $struct + (i32.const 1337) + )) + + ;; CHECK: (func $test (type $none_=>_none) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $struct 0 + ;; CHECK-NEXT: (ref.null $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test + (drop + (struct.get $struct 0 + (ref.null $struct) + ) + ) + ) +) + +;; Four values, three equal and one unique. We can optimize this with a single +;; comparison on the unique one. +(module + ;; CHECK: (type $struct (struct_subtype (field i32) data)) + (type $struct (struct i32)) + + ;; CHECK: (type $none_=>_none (func_subtype func)) + + ;; CHECK: (global $global1 (ref $struct) (struct.new $struct + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: )) + (global $global1 (ref $struct) (struct.new $struct + (i32.const 42) + )) + + ;; CHECK: (global $global2 (ref $struct) (struct.new $struct + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: )) + (global $global2 (ref $struct) (struct.new $struct + (i32.const 42) + )) + + ;; CHECK: (global $global3 (ref $struct) (struct.new $struct + ;; CHECK-NEXT: (i32.const 1337) + ;; CHECK-NEXT: )) + (global $global3 (ref $struct) (struct.new $struct + (i32.const 1337) + )) + + ;; CHECK: (global $global4 (ref $struct) (struct.new $struct + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: )) + (global $global4 (ref $struct) (struct.new $struct + (i32.const 42) + )) + + ;; CHECK: (func $test (type $none_=>_none) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: (i32.const 1337) + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: (ref.eq + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (ref.null $struct) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (global.get $global3) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test + (drop + (struct.get $struct 0 + (ref.null $struct) + ) + ) + ) +) + ;; A struct.new inside a function stops us from optimizing. (module ;; CHECK: (type $struct (struct_subtype (field i32) data)) |