summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorAlon Zakai <azakai@google.com>2022-06-01 07:17:21 -0700
committerGitHub <noreply@github.com>2022-06-01 14:17:21 +0000
commit623e08e88db3ebc913fe76e7f60e89fa030f884d (patch)
tree3e3ccd5c1b45ade4d7f65066dced5991cd294a71 /test
parent49763aa9a7fb0f07588a9d19db6896356e52c5f8 (diff)
downloadbinaryen-623e08e88db3ebc913fe76e7f60e89fa030f884d.tar.gz
binaryen-623e08e88db3ebc913fe76e7f60e89fa030f884d.tar.bz2
binaryen-623e08e88db3ebc913fe76e7f60e89fa030f884d.zip
Global Struct Inference pass: Infer two constants in struct.get (#4659)
This optimizes constants in the megamorphic case of two: when we know two function references are possible, we could in theory emit this: (select (ref.func A) (ref.func B) (ref.eq (..ref value..) ;; globally, only 2 things are possible here, and one has ;; ref.func A as its value, and the other ref.func B (ref.func A)) That is, compare to one of the values, and emit the two possible values there. Other optimizations can then turn a call_ref on this select into an if over two direct calls, leading to devirtualization. We cannot compare a ref.func directly (since function references are not comparable), and so instead we look at immutable global structs. If we find a struct type that has only two possible values in some field, and the structs are in immutable globals (which happens in the vtable case in j2wasm for example), then we can compare the references of the struct to decide between the two values in the field.
Diffstat (limited to 'test')
-rw-r--r--test/lit/help/wasm-opt.test2
-rw-r--r--test/lit/help/wasm2js.test2
-rw-r--r--test/lit/passes/gsi.wast806
3 files changed, 810 insertions, 0 deletions
diff --git a/test/lit/help/wasm-opt.test b/test/lit/help/wasm-opt.test
index d786bfdfc..1985d70ec 100644
--- a/test/lit/help/wasm-opt.test
+++ b/test/lit/help/wasm-opt.test
@@ -166,6 +166,8 @@
;; CHECK-NEXT:
;; CHECK-NEXT: --global-refining refine the types of globals
;; CHECK-NEXT:
+;; CHECK-NEXT: --gsi globally optimize struct values
+;; CHECK-NEXT:
;; CHECK-NEXT: --gto globally optimize GC types
;; CHECK-NEXT:
;; CHECK-NEXT: --heap2local replace GC allocations with
diff --git a/test/lit/help/wasm2js.test b/test/lit/help/wasm2js.test
index 504e08726..5c0f249e3 100644
--- a/test/lit/help/wasm2js.test
+++ b/test/lit/help/wasm2js.test
@@ -128,6 +128,8 @@
;; CHECK-NEXT:
;; CHECK-NEXT: --global-refining refine the types of globals
;; CHECK-NEXT:
+;; CHECK-NEXT: --gsi globally optimize struct values
+;; CHECK-NEXT:
;; CHECK-NEXT: --gto globally optimize GC types
;; CHECK-NEXT:
;; CHECK-NEXT: --heap2local replace GC allocations with
diff --git a/test/lit/passes/gsi.wast b/test/lit/passes/gsi.wast
new file mode 100644
index 000000000..9b54a98ca
--- /dev/null
+++ b/test/lit/passes/gsi.wast
@@ -0,0 +1,806 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
+;; RUN: foreach %s %t wasm-opt --nominal --gsi -all -S -o - | filecheck %s
+
+(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)
+ ))
+
+ ;; A non-reference global does not confuse us.
+ ;; CHECK: (global $global-other i32 (i32.const 123456))
+ (global $global-other i32 (i32.const 123456))
+
+ ;; 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
+ ;; We can infer that this get can reference either $global1 or $global2,
+ ;; and nothing else (aside from a null), and can emit a select between
+ ;; those values.
+ (drop
+ (struct.get $struct 0
+ (ref.null $struct)
+ )
+ )
+ )
+)
+
+;; As above, but now the field is mutable, so we cannot optimize.
+(module
+ ;; CHECK: (type $struct (struct_subtype (field (mut i32)) data))
+ (type $struct (struct (mut 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: (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)
+ )
+ )
+ )
+)
+
+;; Just one global. We do not optimize here - we let other passes do that.
+(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: (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)
+ )
+ )
+ )
+)
+
+;; Three globals. For now, we do not optimize here.
+(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 99999)
+ ;; CHECK-NEXT: ))
+ (global $global3 (ref $struct) (struct.new $struct
+ (i32.const 99999)
+ ))
+
+ ;; 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)
+ )
+ )
+ )
+)
+
+;; A struct.new inside a function stops us from optimizing.
+(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: (func $test (type $none_=>_none)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $struct
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.get $struct 0
+ ;; CHECK-NEXT: (ref.null $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $test
+ (drop
+ (struct.new $struct
+ (i32.const 1)
+ )
+ )
+ (drop
+ (struct.get $struct 0
+ (ref.null $struct)
+ )
+ )
+ )
+)
+
+;; We ignore imports, as we assume a closed world, but that might change in the
+;; future. For now, we will optimize here.
+(module
+ ;; CHECK: (type $struct (struct_subtype (field i32) data))
+ (type $struct (struct i32))
+
+ ;; CHECK: (type $none_=>_none (func_subtype func))
+
+ ;; CHECK: (import "a" "b" (global $global-import (ref $struct)))
+ (import "a" "b" (global $global-import (ref $struct)))
+
+ ;; 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: (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)
+ )
+ )
+ )
+)
+
+;; A struct.new in a non-toplevel position in a global stops us from
+;; optimizing.
+(module
+ ;; CHECK: (type $struct (struct_subtype (field i32) data))
+ (type $struct (struct i32))
+
+ ;; CHECK: (type $tuple (struct_subtype (field anyref) (field anyref) data))
+ (type $tuple (struct anyref anyref))
+
+ ;; 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 $global-tuple (ref $tuple) (struct.new $tuple
+ ;; CHECK-NEXT: (struct.new $struct
+ ;; CHECK-NEXT: (i32.const 999999)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null any)
+ ;; CHECK-NEXT: ))
+ (global $global-tuple (ref $tuple) (struct.new $tuple
+ (struct.new $struct
+ (i32.const 999999)
+ )
+ (ref.null any)
+ ))
+
+ ;; 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)
+ )
+ )
+ )
+)
+
+;; As above, but remove the struct.new in a nested position, while keeping all
+;; the other stuff in the above test. Now we should optimize.
+(module
+ ;; CHECK: (type $struct (struct_subtype (field i32) data))
+ (type $struct (struct i32))
+
+ ;; CHECK: (type $tuple (struct_subtype (field anyref) (field anyref) data))
+ (type $tuple (struct anyref anyref))
+
+ ;; 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 $global-tuple (ref $tuple) (struct.new $tuple
+ ;; CHECK-NEXT: (ref.null any)
+ ;; CHECK-NEXT: (ref.null any)
+ ;; CHECK-NEXT: ))
+ (global $global-tuple (ref $tuple) (struct.new $tuple
+ (ref.null any)
+ (ref.null any)
+ ))
+
+ ;; 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)
+ )
+ )
+ )
+)
+
+;; When one of the globals is mutable, we cannot optimize.
+(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 (mut (ref $struct)) (struct.new $struct
+ ;; CHECK-NEXT: (i32.const 1337)
+ ;; CHECK-NEXT: ))
+ (global $global2 (mut (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)
+ )
+ )
+ )
+)
+
+;; A subtype is not optimizable, which prevents $struct from being optimized.
+(module
+ ;; CHECK: (type $struct (struct_subtype (field i32) data))
+ (type $struct (struct_subtype i32 data))
+
+ ;; CHECK: (type $none_=>_none (func_subtype func))
+
+ ;; CHECK: (type $sub-struct (struct_subtype (field i32) $struct))
+ (type $sub-struct (struct_subtype i32 $struct))
+
+ ;; 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: (func $test (type $none_=>_none)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $sub-struct
+ ;; CHECK-NEXT: (i32.const 999999)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.get $struct 0
+ ;; CHECK-NEXT: (ref.null $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $test
+ (drop
+ (struct.new $sub-struct
+ (i32.const 999999)
+ )
+ )
+ (drop
+ (struct.get $struct 0
+ (ref.null $struct)
+ )
+ )
+ )
+)
+
+;; A *super*-type is not optimizable, but that does not block us, and we can
+;; optimize.
+(module
+ ;; CHECK: (type $super-struct (struct_subtype (field i32) data))
+ (type $super-struct (struct_subtype i32 data))
+
+ ;; CHECK: (type $struct (struct_subtype (field i32) $super-struct))
+ (type $struct (struct_subtype i32 $super-struct))
+
+ ;; 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: (func $test (type $none_=>_none)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $super-struct
+ ;; CHECK-NEXT: (i32.const 999999)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; 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.new $super-struct
+ (i32.const 999999)
+ )
+ )
+ (drop
+ (struct.get $struct 0
+ (ref.null $struct)
+ )
+ )
+ )
+)
+
+;; One global for each of the type and the subtype. The optimization will pick
+;; between their 2 values.
+(module
+ ;; CHECK: (type $super-struct (struct_subtype (field i32) data))
+ (type $super-struct (struct_subtype i32 data))
+
+ ;; CHECK: (type $struct (struct_subtype (field i32) $super-struct))
+ (type $struct (struct_subtype i32 $super-struct))
+
+ ;; CHECK: (type $none_=>_none (func_subtype func))
+
+ ;; CHECK: (global $global1 (ref $super-struct) (struct.new $super-struct
+ ;; CHECK-NEXT: (i32.const 42)
+ ;; CHECK-NEXT: ))
+ (global $global1 (ref $super-struct) (struct.new $super-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: (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: (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 $super-struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (global.get $global1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $test
+ ;; We cannot optimize the first - it has just one global - but the second
+ ;; will consider the struct and sub-struct, find 2 possible values, and
+ ;; optimize.
+ (drop
+ (struct.get $struct 0
+ (ref.null $struct)
+ )
+ )
+ (drop
+ (struct.get $super-struct 0
+ (ref.null $super-struct)
+ )
+ )
+ )
+)
+
+;; One global has a non-constant field, so we cannot optimize.
+(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.add
+ ;; CHECK-NEXT: (i32.const 41)
+ ;; CHECK-NEXT: (i32.const 1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: ))
+ (global $global1 (ref $struct) (struct.new $struct
+ (i32.add
+ (i32.const 41)
+ (i32.const 1)
+ )
+ ))
+
+ ;; 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: (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)
+ )
+ )
+ )
+)
+
+;; One global each for two subtypes of a common supertype, and one for the
+;; supertype.
+(module
+ ;; CHECK: (type $super-struct (struct_subtype (field i32) data))
+ (type $super-struct (struct_subtype i32 data))
+
+ ;; CHECK: (type $struct1 (struct_subtype (field i32) (field f32) $super-struct))
+ (type $struct1 (struct_subtype i32 f32 $super-struct))
+
+ ;; CHECK: (type $struct2 (struct_subtype (field i32) (field f64) $super-struct))
+ (type $struct2 (struct_subtype i32 f64 $super-struct))
+
+
+ ;; CHECK: (type $none_=>_none (func_subtype func))
+
+ ;; CHECK: (global $global0 (ref $super-struct) (struct.new $super-struct
+ ;; CHECK-NEXT: (i32.const 42)
+ ;; CHECK-NEXT: ))
+ (global $global0 (ref $super-struct) (struct.new $super-struct
+ (i32.const 42)
+ ))
+
+ ;; CHECK: (global $global1 (ref $struct1) (struct.new $struct1
+ ;; CHECK-NEXT: (i32.const 1337)
+ ;; CHECK-NEXT: (f32.const 3.141590118408203)
+ ;; CHECK-NEXT: ))
+ (global $global1 (ref $struct1) (struct.new $struct1
+ (i32.const 1337)
+ (f32.const 3.14159)
+ ))
+
+ ;; CHECK: (global $global2 (ref $struct2) (struct.new $struct2
+ ;; CHECK-NEXT: (i32.const 99999)
+ ;; CHECK-NEXT: (f64.const 2.71828)
+ ;; CHECK-NEXT: ))
+ (global $global2 (ref $struct2) (struct.new $struct2
+ (i32.const 99999)
+ (f64.const 2.71828)
+ ))
+
+ ;; CHECK: (func $test (type $none_=>_none)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.get $super-struct 0
+ ;; CHECK-NEXT: (ref.null $super-struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.get $struct1 0
+ ;; CHECK-NEXT: (ref.null $struct1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.get $struct2 0
+ ;; CHECK-NEXT: (ref.null $struct2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $test
+ ;; This has three possible values due to the two children, so we do not
+ ;; optimize.
+ (drop
+ (struct.get $super-struct 0
+ (ref.null $super-struct)
+ )
+ )
+ ;; These each have one possible value, so we also do not optimize.
+ (drop
+ (struct.get $struct1 0
+ (ref.null $struct1)
+ )
+ )
+ (drop
+ (struct.get $struct2 0
+ (ref.null $struct2)
+ )
+ )
+ )
+)
+
+;; As above, but now the subtypes each have 2 values, and we can optimize.
+(module
+ ;; CHECK: (type $super-struct (struct_subtype (field i32) data))
+ (type $super-struct (struct_subtype i32 data))
+
+ ;; CHECK: (type $struct1 (struct_subtype (field i32) (field f32) $super-struct))
+ (type $struct1 (struct_subtype i32 f32 $super-struct))
+
+ ;; CHECK: (type $struct2 (struct_subtype (field i32) (field f64) $super-struct))
+ (type $struct2 (struct_subtype i32 f64 $super-struct))
+
+
+ ;; CHECK: (type $none_=>_none (func_subtype func))
+
+ ;; CHECK: (global $global0 (ref $super-struct) (struct.new $super-struct
+ ;; CHECK-NEXT: (i32.const 42)
+ ;; CHECK-NEXT: ))
+ (global $global0 (ref $super-struct) (struct.new $super-struct
+ (i32.const 42)
+ ))
+
+ ;; CHECK: (global $global1 (ref $struct1) (struct.new $struct1
+ ;; CHECK-NEXT: (i32.const 1337)
+ ;; CHECK-NEXT: (f32.const 3.141590118408203)
+ ;; CHECK-NEXT: ))
+ (global $global1 (ref $struct1) (struct.new $struct1
+ (i32.const 1337)
+ (f32.const 3.14159)
+ ))
+
+ ;; CHECK: (global $global1b (ref $struct1) (struct.new $struct1
+ ;; CHECK-NEXT: (i32.const 1338)
+ ;; CHECK-NEXT: (f32.const 3.141590118408203)
+ ;; CHECK-NEXT: ))
+ (global $global1b (ref $struct1) (struct.new $struct1
+ (i32.const 1338)
+ (f32.const 3.14159)
+ ))
+
+ ;; CHECK: (global $global2 (ref $struct2) (struct.new $struct2
+ ;; CHECK-NEXT: (i32.const 99999)
+ ;; CHECK-NEXT: (f64.const 2.71828)
+ ;; CHECK-NEXT: ))
+ (global $global2 (ref $struct2) (struct.new $struct2
+ (i32.const 99999)
+ (f64.const 2.71828)
+ ))
+
+ ;; CHECK: (global $global2b (ref $struct2) (struct.new $struct2
+ ;; CHECK-NEXT: (i32.const 99998)
+ ;; CHECK-NEXT: (f64.const 2.71828)
+ ;; CHECK-NEXT: ))
+ (global $global2b (ref $struct2) (struct.new $struct2
+ (i32.const 99998)
+ (f64.const 2.71828)
+ ))
+
+ ;; CHECK: (func $test (type $none_=>_none)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.get $super-struct 0
+ ;; CHECK-NEXT: (ref.null $super-struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (select
+ ;; CHECK-NEXT: (i32.const 1337)
+ ;; CHECK-NEXT: (i32.const 1338)
+ ;; CHECK-NEXT: (ref.eq
+ ;; CHECK-NEXT: (ref.as_non_null
+ ;; CHECK-NEXT: (ref.null $struct1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (global.get $global1)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (select
+ ;; CHECK-NEXT: (i32.const 99999)
+ ;; CHECK-NEXT: (i32.const 99998)
+ ;; CHECK-NEXT: (ref.eq
+ ;; CHECK-NEXT: (ref.as_non_null
+ ;; CHECK-NEXT: (ref.null $struct2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (global.get $global2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $test
+ ;; This still cannot be optimized.
+ (drop
+ (struct.get $super-struct 0
+ (ref.null $super-struct)
+ )
+ )
+ ;; These can be optimized, and will be different from one another.
+ (drop
+ (struct.get $struct1 0
+ (ref.null $struct1)
+ )
+ )
+ (drop
+ (struct.get $struct2 0
+ (ref.null $struct2)
+ )
+ )
+ )
+)