summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/lit/help/wasm-opt.test3
-rw-r--r--test/lit/help/wasm2js.test3
-rw-r--r--test/lit/passes/cfp-reftest.wast1458
-rw-r--r--test/lit/passes/cfp.wast4
4 files changed, 1468 insertions, 0 deletions
diff --git a/test/lit/help/wasm-opt.test b/test/lit/help/wasm-opt.test
index 59409dcff..76566049e 100644
--- a/test/lit/help/wasm-opt.test
+++ b/test/lit/help/wasm-opt.test
@@ -103,6 +103,9 @@
;; CHECK-NEXT: --cfp propagate constant struct field
;; CHECK-NEXT: values
;; CHECK-NEXT:
+;; CHECK-NEXT: --cfp-reftest propagate constant struct field
+;; CHECK-NEXT: values, using ref.test
+;; CHECK-NEXT:
;; CHECK-NEXT: --coalesce-locals reduce # of locals by coalescing
;; CHECK-NEXT:
;; CHECK-NEXT: --coalesce-locals-learning reduce # of locals by coalescing
diff --git a/test/lit/help/wasm2js.test b/test/lit/help/wasm2js.test
index 3c1da7ff2..8e305cfb9 100644
--- a/test/lit/help/wasm2js.test
+++ b/test/lit/help/wasm2js.test
@@ -57,6 +57,9 @@
;; CHECK-NEXT: --cfp propagate constant struct field
;; CHECK-NEXT: values
;; CHECK-NEXT:
+;; CHECK-NEXT: --cfp-reftest propagate constant struct field
+;; CHECK-NEXT: values, using ref.test
+;; CHECK-NEXT:
;; CHECK-NEXT: --coalesce-locals reduce # of locals by coalescing
;; CHECK-NEXT:
;; CHECK-NEXT: --coalesce-locals-learning reduce # of locals by coalescing
diff --git a/test/lit/passes/cfp-reftest.wast b/test/lit/passes/cfp-reftest.wast
new file mode 100644
index 000000000..c9fbbdc61
--- /dev/null
+++ b/test/lit/passes/cfp-reftest.wast
@@ -0,0 +1,1458 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
+
+;; RUN: foreach %s %t wasm-opt --cfp-reftest -all -S -o - | filecheck %s
+
+;; When a struct.get can only read from two types, and those types have a
+;; constant field, we can select between those two values using a ref.test.
+(module
+ ;; CHECK: (type $struct (sub (struct (field i32))))
+ (type $struct (sub (struct i32)))
+ ;; CHECK: (type $substruct (sub $struct (struct (field i32) (field f64))))
+ (type $substruct (sub $struct (struct i32 f64)))
+
+ ;; CHECK: (type $2 (func))
+
+ ;; CHECK: (type $3 (func (param (ref null $struct)) (result i32)))
+
+ ;; CHECK: (func $create (type $2)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $struct
+ ;; CHECK-NEXT: (i32.const 10)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $substruct
+ ;; CHECK-NEXT: (i32.const 20)
+ ;; CHECK-NEXT: (f64.const 3.14159)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $create
+ ;; Used below.
+ (drop
+ (struct.new $struct
+ (i32.const 10)
+ )
+ )
+ (drop
+ (struct.new $substruct
+ (i32.const 20)
+ (f64.const 3.14159)
+ )
+ )
+ )
+ ;; CHECK: (func $get (type $3) (param $struct (ref null $struct)) (result i32)
+ ;; CHECK-NEXT: (select
+ ;; CHECK-NEXT: (i32.const 20)
+ ;; CHECK-NEXT: (i32.const 10)
+ ;; CHECK-NEXT: (ref.test (ref $substruct)
+ ;; CHECK-NEXT: (ref.as_non_null
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get (param $struct (ref null $struct)) (result i32)
+ ;; Rather than load from the struct, we can test between the two types
+ ;; possible here.
+ (struct.get $struct 0
+ (local.get $struct)
+ )
+ )
+)
+
+;; As above, but now the child is a final type. This does not matter as we
+;; optimize either way, if the child has no children (since without children it
+;; could be marked final later, which we assume).
+(module
+ ;; CHECK: (type $struct (sub (struct (field i32))))
+ (type $struct (sub (struct i32)))
+ ;; CHECK: (type $substruct (sub final $struct (struct (field i32) (field f64))))
+ (type $substruct (sub final $struct (struct i32 f64)))
+
+ ;; CHECK: (type $2 (func))
+
+ ;; CHECK: (type $3 (func (param (ref null $struct)) (result i32)))
+
+ ;; CHECK: (func $create (type $2)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $struct
+ ;; CHECK-NEXT: (i32.const 10)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $substruct
+ ;; CHECK-NEXT: (i32.const 20)
+ ;; CHECK-NEXT: (f64.const 3.14159)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $create
+ (drop
+ (struct.new $struct
+ (i32.const 10)
+ )
+ )
+ (drop
+ (struct.new $substruct
+ (i32.const 20)
+ (f64.const 3.14159)
+ )
+ )
+ )
+ ;; CHECK: (func $get (type $3) (param $struct (ref null $struct)) (result i32)
+ ;; CHECK-NEXT: (select
+ ;; CHECK-NEXT: (i32.const 20)
+ ;; CHECK-NEXT: (i32.const 10)
+ ;; CHECK-NEXT: (ref.test (ref $substruct)
+ ;; CHECK-NEXT: (ref.as_non_null
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get (param $struct (ref null $struct)) (result i32)
+ (struct.get $struct 0
+ (local.get $struct)
+ )
+ )
+)
+
+;; As above, but now the subtype has subtypes.
+(module
+ ;; CHECK: (type $struct (sub (struct (field i32))))
+ (type $struct (sub (struct i32)))
+ ;; CHECK: (type $1 (func))
+
+ ;; CHECK: (type $substruct (sub $struct (struct (field i32) (field f64))))
+ (type $substruct (sub $struct (struct i32 f64)))
+
+ ;; CHECK: (type $3 (func (param (ref null $struct)) (result i32)))
+
+ ;; CHECK: (type $subsubstruct (sub $substruct (struct (field i32) (field f64))))
+ (type $subsubstruct (sub $substruct (struct i32 f64)))
+
+ ;; CHECK: (func $create (type $1)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $struct
+ ;; CHECK-NEXT: (i32.const 10)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $substruct
+ ;; CHECK-NEXT: (i32.const 20)
+ ;; CHECK-NEXT: (f64.const 3.14159)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $create
+ ;; Used below.
+ (drop
+ (struct.new $struct
+ (i32.const 10)
+ )
+ )
+ (drop
+ (struct.new $substruct
+ (i32.const 20)
+ (f64.const 3.14159)
+ )
+ )
+ )
+ ;; CHECK: (func $get (type $3) (param $struct (ref null $struct)) (result i32)
+ ;; CHECK-NEXT: (local $x (ref $subsubstruct))
+ ;; CHECK-NEXT: (struct.get $struct 0
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get (param $struct (ref null $struct)) (result i32)
+ ;; Keep this type alive.
+ (local $x (ref $subsubstruct))
+
+ ;; We only test on final types for efficiency, so we do not optimize here.
+ ;; The type we'd like to test on here has subtypes so it cannot be marked
+ ;; final; otherwise if there are no subtypes we assume it will be marked
+ ;; final later and optimize.
+ (struct.get $struct 0
+ (local.get $struct)
+ )
+ )
+)
+
+;; As above, but now one value is not constant.
+(module
+ ;; CHECK: (type $struct (sub (struct (field i32))))
+ (type $struct (sub (struct i32)))
+ ;; CHECK: (type $1 (func (param i32)))
+
+ ;; CHECK: (type $substruct (sub $struct (struct (field i32) (field f64))))
+ (type $substruct (sub $struct (struct i32 f64)))
+
+ ;; CHECK: (type $3 (func (param (ref null $struct)) (result i32)))
+
+ ;; CHECK: (func $create (type $1) (param $x i32)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $struct
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $substruct
+ ;; CHECK-NEXT: (i32.const 20)
+ ;; CHECK-NEXT: (f64.const 3.14159)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $create (param $x i32)
+ (drop
+ (struct.new $struct
+ (local.get $x) ;; this changed
+ )
+ )
+ (drop
+ (struct.new $substruct
+ (i32.const 20)
+ (f64.const 3.14159)
+ )
+ )
+ )
+ ;; CHECK: (func $get (type $3) (param $struct (ref null $struct)) (result i32)
+ ;; CHECK-NEXT: (struct.get $struct 0
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get (param $struct (ref null $struct)) (result i32)
+ ;; We cannot optimize here.
+ (struct.get $struct 0
+ (local.get $struct)
+ )
+ )
+)
+
+;; As above, but now the other value is not constant.
+(module
+ ;; CHECK: (type $struct (sub (struct (field i32))))
+ (type $struct (sub (struct i32)))
+ ;; CHECK: (type $1 (func (param i32)))
+
+ ;; CHECK: (type $substruct (sub $struct (struct (field i32) (field f64))))
+ (type $substruct (sub $struct (struct i32 f64)))
+
+ ;; CHECK: (type $3 (func (param (ref null $struct)) (result i32)))
+
+ ;; CHECK: (func $create (type $1) (param $x i32)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $struct
+ ;; CHECK-NEXT: (i32.const 10)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $substruct
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: (f64.const 3.14159)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $create (param $x i32)
+ (drop
+ (struct.new $struct
+ (i32.const 10) ;; this changed
+ )
+ )
+ (drop
+ (struct.new $substruct
+ (local.get $x) ;; this changed
+ (f64.const 3.14159)
+ )
+ )
+ )
+ ;; CHECK: (func $get (type $3) (param $struct (ref null $struct)) (result i32)
+ ;; CHECK-NEXT: (struct.get $struct 0
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get (param $struct (ref null $struct)) (result i32)
+ ;; We cannot optimize here.
+ (struct.get $struct 0
+ (local.get $struct)
+ )
+ )
+)
+
+;; Almost optimizable, but the field is mutable, so we can't.
+(module
+ ;; CHECK: (type $struct (sub (struct (field (mut i32)))))
+ (type $struct (sub (struct (mut i32))))
+ ;; CHECK: (type $1 (func))
+
+ ;; CHECK: (type $substruct (sub $struct (struct (field (mut i32)) (field f64))))
+ (type $substruct (sub $struct (struct (mut i32) f64)))
+
+ ;; CHECK: (type $3 (func (param (ref null $struct)) (result i32)))
+
+ ;; CHECK: (func $create (type $1)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $struct
+ ;; CHECK-NEXT: (i32.const 10)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $substruct
+ ;; CHECK-NEXT: (i32.const 20)
+ ;; CHECK-NEXT: (f64.const 3.14159)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $create
+ (drop
+ (struct.new $struct
+ (i32.const 10)
+ )
+ )
+ (drop
+ (struct.new $substruct
+ (i32.const 20)
+ (f64.const 3.14159)
+ )
+ )
+ )
+ ;; CHECK: (func $get (type $3) (param $struct (ref null $struct)) (result i32)
+ ;; CHECK-NEXT: (struct.get $struct 0
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get (param $struct (ref null $struct)) (result i32)
+ ;; We cannot optimize here.
+ (struct.get $struct 0
+ (local.get $struct)
+ )
+ )
+)
+
+;; Three types (in a chain) with three values, 10, 20, 30.
+(module
+ ;; CHECK: (type $struct (sub (struct (field i32))))
+ (type $struct (sub (struct i32)))
+ ;; CHECK: (type $substruct (sub $struct (struct (field i32) (field f64))))
+ (type $substruct (sub $struct (struct i32 f64)))
+
+ ;; CHECK: (type $subsubstruct (sub $substruct (struct (field i32) (field f64) (field anyref))))
+ (type $subsubstruct (sub $substruct (struct i32 f64 anyref)))
+
+ ;; CHECK: (type $3 (func))
+
+ ;; CHECK: (type $4 (func (param (ref null $struct)) (result i32)))
+
+ ;; CHECK: (type $5 (func (param (ref null $substruct)) (result i32)))
+
+ ;; CHECK: (func $create (type $3)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $struct
+ ;; CHECK-NEXT: (i32.const 10)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $substruct
+ ;; CHECK-NEXT: (i32.const 20)
+ ;; CHECK-NEXT: (f64.const 3.14159)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $subsubstruct
+ ;; CHECK-NEXT: (i32.const 30)
+ ;; CHECK-NEXT: (f64.const 3.14159)
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $create
+ (drop
+ (struct.new $struct
+ (i32.const 10)
+ )
+ )
+ (drop
+ (struct.new $substruct
+ (i32.const 20)
+ (f64.const 3.14159)
+ )
+ )
+ (drop
+ (struct.new $subsubstruct
+ (i32.const 30)
+ (f64.const 3.14159)
+ (ref.null any)
+ )
+ )
+ )
+ ;; CHECK: (func $get (type $4) (param $struct (ref null $struct)) (result i32)
+ ;; CHECK-NEXT: (struct.get $struct 0
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get (param $struct (ref null $struct)) (result i32)
+ ;; Three types are possible here, with three different values, so we do not
+ ;; optimize.
+ (struct.get $struct 0
+ (local.get $struct)
+ )
+ )
+
+ ;; CHECK: (func $get-sub (type $5) (param $substruct (ref null $substruct)) (result i32)
+ ;; CHECK-NEXT: (select
+ ;; CHECK-NEXT: (i32.const 30)
+ ;; CHECK-NEXT: (i32.const 20)
+ ;; CHECK-NEXT: (ref.test (ref $subsubstruct)
+ ;; CHECK-NEXT: (ref.as_non_null
+ ;; CHECK-NEXT: (local.get $substruct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get-sub (param $substruct (ref null $substruct)) (result i32)
+ ;; Only two types are relevant here, so we do optimize.
+ (struct.get $substruct 0
+ (local.get $substruct)
+ )
+ )
+)
+
+;; Three types with two values, 10, 20, 20.
+(module
+ ;; CHECK: (type $struct (sub (struct (field i32))))
+ (type $struct (sub (struct i32)))
+ ;; CHECK: (type $1 (func))
+
+ ;; CHECK: (type $substruct (sub $struct (struct (field i32) (field f64))))
+ (type $substruct (sub $struct (struct i32 f64)))
+
+ ;; CHECK: (type $subsubstruct (sub $substruct (struct (field i32) (field f64) (field anyref))))
+ (type $subsubstruct (sub $substruct (struct i32 f64 anyref)))
+
+ ;; CHECK: (type $4 (func (param (ref null $struct)) (result i32)))
+
+ ;; CHECK: (func $create (type $1)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $struct
+ ;; CHECK-NEXT: (i32.const 10)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $substruct
+ ;; CHECK-NEXT: (i32.const 20)
+ ;; CHECK-NEXT: (f64.const 3.14159)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $subsubstruct
+ ;; CHECK-NEXT: (i32.const 20)
+ ;; CHECK-NEXT: (f64.const 3.14159)
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $create
+ (drop
+ (struct.new $struct
+ (i32.const 10)
+ )
+ )
+ (drop
+ (struct.new $substruct
+ (i32.const 20)
+ (f64.const 3.14159)
+ )
+ )
+ (drop
+ (struct.new $subsubstruct
+ (i32.const 20) ;; this changed
+ (f64.const 3.14159)
+ (ref.null any)
+ )
+ )
+ )
+ ;; CHECK: (func $get (type $4) (param $struct (ref null $struct)) (result i32)
+ ;; CHECK-NEXT: (struct.get $struct 0
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get (param $struct (ref null $struct)) (result i32)
+ ;; Three types are possible here, but two have the same value, and we can
+ ;; differentiate between them with a test. However, the test would be on
+ ;; $substruct, which is not a final type, so we do not optimize.
+ (struct.get $struct 0
+ (local.get $struct)
+ )
+ )
+)
+
+;; Three types with two values, but non-consecutive: 20, 10, 20.
+(module
+ ;; CHECK: (type $struct (sub (struct (field i32))))
+ (type $struct (sub (struct i32)))
+ ;; CHECK: (type $1 (func))
+
+ ;; CHECK: (type $substruct (sub $struct (struct (field i32) (field f64))))
+ (type $substruct (sub $struct (struct i32 f64)))
+
+ ;; CHECK: (type $subsubstruct (sub $substruct (struct (field i32) (field f64) (field anyref))))
+ (type $subsubstruct (sub $substruct (struct i32 f64 anyref)))
+
+ ;; CHECK: (type $4 (func (param (ref null $struct)) (result i32)))
+
+ ;; CHECK: (func $create (type $1)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $struct
+ ;; CHECK-NEXT: (i32.const 20)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $substruct
+ ;; CHECK-NEXT: (i32.const 10)
+ ;; CHECK-NEXT: (f64.const 3.14159)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $subsubstruct
+ ;; CHECK-NEXT: (i32.const 20)
+ ;; CHECK-NEXT: (f64.const 3.14159)
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $create
+ (drop
+ (struct.new $struct
+ (i32.const 20) ;; this changed
+ )
+ )
+ (drop
+ (struct.new $substruct
+ (i32.const 10) ;; this changed
+ (f64.const 3.14159)
+ )
+ )
+ (drop
+ (struct.new $subsubstruct
+ (i32.const 20)
+ (f64.const 3.14159)
+ (ref.null any)
+ )
+ )
+ )
+ ;; CHECK: (func $get (type $4) (param $struct (ref null $struct)) (result i32)
+ ;; CHECK-NEXT: (struct.get $struct 0
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get (param $struct (ref null $struct)) (result i32)
+ ;; Three types are possible here, and two have the same value, but we still
+ ;; cannot optimize: the chain of types has values A->B->A so there is no
+ ;; ref.test that can differentiate the two sets.
+ (struct.get $struct 0
+ (local.get $struct)
+ )
+ )
+)
+
+;; Three types with two values, 10, 10, 20.
+(module
+ ;; CHECK: (type $struct (sub (struct (field i32))))
+ (type $struct (sub (struct i32)))
+ ;; CHECK: (type $substruct (sub $struct (struct (field i32) (field f64))))
+ (type $substruct (sub $struct (struct i32 f64)))
+
+ ;; CHECK: (type $subsubstruct (sub $substruct (struct (field i32) (field f64) (field anyref))))
+ (type $subsubstruct (sub $substruct (struct i32 f64 anyref)))
+
+ ;; CHECK: (type $3 (func))
+
+ ;; CHECK: (type $4 (func (param (ref null $struct)) (result i32)))
+
+ ;; CHECK: (func $create (type $3)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $struct
+ ;; CHECK-NEXT: (i32.const 10)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $substruct
+ ;; CHECK-NEXT: (i32.const 10)
+ ;; CHECK-NEXT: (f64.const 3.14159)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $subsubstruct
+ ;; CHECK-NEXT: (i32.const 20)
+ ;; CHECK-NEXT: (f64.const 3.14159)
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $create
+ (drop
+ (struct.new $struct
+ (i32.const 10) ;; this changed
+ )
+ )
+ (drop
+ (struct.new $substruct
+ (i32.const 10)
+ (f64.const 3.14159)
+ )
+ )
+ (drop
+ (struct.new $subsubstruct
+ (i32.const 20)
+ (f64.const 3.14159)
+ (ref.null any)
+ )
+ )
+ )
+ ;; CHECK: (func $get (type $4) (param $struct (ref null $struct)) (result i32)
+ ;; CHECK-NEXT: (select
+ ;; CHECK-NEXT: (i32.const 20)
+ ;; CHECK-NEXT: (i32.const 10)
+ ;; CHECK-NEXT: (ref.test (ref $subsubstruct)
+ ;; CHECK-NEXT: (ref.as_non_null
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get (param $struct (ref null $struct)) (result i32)
+ ;; We can differentiate between the first 2 and the last 1 by testing on the
+ ;; last, so we can optimize here.
+ (struct.get $struct 0
+ (local.get $struct)
+ )
+ )
+)
+
+;; Three types with two values and an abstract type.
+(module
+ ;; CHECK: (type $struct (sub (struct (field i32))))
+ (type $struct (sub (struct i32)))
+ ;; CHECK: (type $substruct (sub $struct (struct (field i32) (field f64))))
+ (type $substruct (sub $struct (struct i32 f64)))
+
+ ;; CHECK: (type $subsubstruct (sub $substruct (struct (field i32) (field f64) (field anyref))))
+ (type $subsubstruct (sub $substruct (struct i32 f64 anyref)))
+
+ ;; CHECK: (type $3 (func))
+
+ ;; CHECK: (type $4 (func (param (ref null $struct)) (result i32)))
+
+ ;; CHECK: (func $create (type $3)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $struct
+ ;; CHECK-NEXT: (i32.const 10)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $subsubstruct
+ ;; CHECK-NEXT: (i32.const 30)
+ ;; CHECK-NEXT: (f64.const 3.14159)
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $create
+ (drop
+ (struct.new $struct
+ (i32.const 10)
+ )
+ )
+ ;; We never create $substruct, so it doesn't matter (we use a local to
+ ;; keep it alive).
+ (drop
+ (struct.new $subsubstruct
+ (i32.const 30)
+ (f64.const 3.14159)
+ (ref.null any)
+ )
+ )
+ )
+ ;; CHECK: (func $get (type $4) (param $struct (ref null $struct)) (result i32)
+ ;; CHECK-NEXT: (select
+ ;; CHECK-NEXT: (i32.const 30)
+ ;; CHECK-NEXT: (i32.const 10)
+ ;; CHECK-NEXT: (ref.test (ref $subsubstruct)
+ ;; CHECK-NEXT: (ref.as_non_null
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get (param $struct (ref null $struct)) (result i32)
+ ;; We can optimize since only two types are actually possible.
+ (struct.get $struct 0
+ (local.get $struct)
+ )
+ )
+)
+
+;; Three types with three values, now in a triangle.
+(module
+ ;; CHECK: (type $struct (sub (struct (field i32))))
+ (type $struct (sub (struct i32)))
+ ;; CHECK: (type $1 (func))
+
+ ;; CHECK: (type $substruct.A (sub $struct (struct (field i32) (field f64))))
+ (type $substruct.A (sub $struct (struct i32 f64)))
+
+ ;; CHECK: (type $substruct.B (sub $struct (struct (field i32) (field f64) (field anyref))))
+ (type $substruct.B (sub $struct (struct i32 f64 anyref)))
+
+ ;; CHECK: (type $4 (func (param (ref null $struct)) (result i32)))
+
+ ;; CHECK: (func $create (type $1)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $struct
+ ;; CHECK-NEXT: (i32.const 10)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $substruct.A
+ ;; CHECK-NEXT: (i32.const 20)
+ ;; CHECK-NEXT: (f64.const 3.14159)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $substruct.B
+ ;; CHECK-NEXT: (i32.const -20)
+ ;; CHECK-NEXT: (f64.const 3.14159)
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $create
+ (drop
+ (struct.new $struct
+ (i32.const 10)
+ )
+ )
+ (drop
+ (struct.new $substruct.A
+ (i32.const 20)
+ (f64.const 3.14159)
+ )
+ )
+ (drop
+ (struct.new $substruct.B
+ (i32.const -20)
+ (f64.const 3.14159)
+ (ref.null any)
+ )
+ )
+ )
+ ;; CHECK: (func $get (type $4) (param $struct (ref null $struct)) (result i32)
+ ;; CHECK-NEXT: (struct.get $struct 0
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get (param $struct (ref null $struct)) (result i32)
+ ;; Three types are possible here, with three different values, so we do not
+ ;; optimize.
+ (struct.get $struct 0
+ (local.get $struct)
+ )
+ )
+)
+
+;; Three types in a triangle, with only two values.
+(module
+ ;; CHECK: (type $struct (sub (struct (field i32))))
+ (type $struct (sub (struct i32)))
+ ;; CHECK: (type $1 (func))
+
+ ;; CHECK: (type $substruct.A (sub $struct (struct (field i32) (field f64))))
+ (type $substruct.A (sub $struct (struct i32 f64)))
+
+ ;; CHECK: (type $substruct.B (sub $struct (struct (field i32) (field f64) (field anyref))))
+ (type $substruct.B (sub $struct (struct i32 f64 anyref)))
+
+ ;; CHECK: (type $4 (func (param (ref null $struct)) (result i32)))
+
+ ;; CHECK: (func $create (type $1)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $struct
+ ;; CHECK-NEXT: (i32.const 10)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $substruct.A
+ ;; CHECK-NEXT: (i32.const 20)
+ ;; CHECK-NEXT: (f64.const 3.14159)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $substruct.B
+ ;; CHECK-NEXT: (i32.const 20)
+ ;; CHECK-NEXT: (f64.const 3.14159)
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $create
+ (drop
+ (struct.new $struct
+ (i32.const 10)
+ )
+ )
+ (drop
+ (struct.new $substruct.A
+ (i32.const 20)
+ (f64.const 3.14159)
+ )
+ )
+ (drop
+ (struct.new $substruct.B
+ (i32.const 20) ;; this changed
+ (f64.const 3.14159)
+ (ref.null any)
+ )
+ )
+ )
+ ;; CHECK: (func $get (type $4) (param $struct (ref null $struct)) (result i32)
+ ;; CHECK-NEXT: (struct.get $struct 0
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get (param $struct (ref null $struct)) (result i32)
+ ;; There is no ref.test that can separate the parent from the two children,
+ ;; so we cannot optimize.
+ (struct.get $struct 0
+ (local.get $struct)
+ )
+ )
+)
+
+;; As above, but the singular value is moved.
+(module
+ ;; CHECK: (type $struct (sub (struct (field i32))))
+ (type $struct (sub (struct i32)))
+ ;; CHECK: (type $substruct.A (sub $struct (struct (field i32) (field f64))))
+ (type $substruct.A (sub $struct (struct i32 f64)))
+
+ ;; CHECK: (type $2 (func))
+
+ ;; CHECK: (type $substruct.B (sub $struct (struct (field i32) (field f64) (field anyref))))
+ (type $substruct.B (sub $struct (struct i32 f64 anyref)))
+
+ ;; CHECK: (type $4 (func (param (ref null $struct)) (result i32)))
+
+ ;; CHECK: (func $create (type $2)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $struct
+ ;; CHECK-NEXT: (i32.const 20)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $substruct.A
+ ;; CHECK-NEXT: (i32.const 10)
+ ;; CHECK-NEXT: (f64.const 3.14159)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $substruct.B
+ ;; CHECK-NEXT: (i32.const 20)
+ ;; CHECK-NEXT: (f64.const 3.14159)
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $create
+ (drop
+ (struct.new $struct
+ (i32.const 20) ;; this changed
+ )
+ )
+ (drop
+ (struct.new $substruct.A
+ (i32.const 10) ;; this changed
+ (f64.const 3.14159)
+ )
+ )
+ (drop
+ (struct.new $substruct.B
+ (i32.const 20)
+ (f64.const 3.14159)
+ (ref.null any)
+ )
+ )
+ )
+ ;; CHECK: (func $get (type $4) (param $struct (ref null $struct)) (result i32)
+ ;; CHECK-NEXT: (select
+ ;; CHECK-NEXT: (i32.const 10)
+ ;; CHECK-NEXT: (i32.const 20)
+ ;; CHECK-NEXT: (ref.test (ref $substruct.A)
+ ;; CHECK-NEXT: (ref.as_non_null
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get (param $struct (ref null $struct)) (result i32)
+ ;; We can ref.test on $substruct.A now, and optimize.
+ (struct.get $struct 0
+ (local.get $struct)
+ )
+ )
+)
+
+;; As above, but the singular value is moved again.
+(module
+ ;; CHECK: (type $struct (sub (struct (field i32))))
+ (type $struct (sub (struct i32)))
+ ;; CHECK: (type $substruct.B (sub $struct (struct (field i32) (field f64) (field anyref))))
+
+ ;; CHECK: (type $2 (func))
+
+ ;; CHECK: (type $substruct.A (sub $struct (struct (field i32) (field f64))))
+ (type $substruct.A (sub $struct (struct i32 f64)))
+
+ (type $substruct.B (sub $struct (struct i32 f64 anyref)))
+
+ ;; CHECK: (type $4 (func (param (ref null $struct)) (result i32)))
+
+ ;; CHECK: (func $create (type $2)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $struct
+ ;; CHECK-NEXT: (i32.const 20)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $substruct.A
+ ;; CHECK-NEXT: (i32.const 20)
+ ;; CHECK-NEXT: (f64.const 3.14159)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $substruct.B
+ ;; CHECK-NEXT: (i32.const 10)
+ ;; CHECK-NEXT: (f64.const 3.14159)
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $create
+ (drop
+ (struct.new $struct
+ (i32.const 20)
+ )
+ )
+ (drop
+ (struct.new $substruct.A
+ (i32.const 20) ;; this changed
+ (f64.const 3.14159)
+ )
+ )
+ (drop
+ (struct.new $substruct.B
+ (i32.const 10) ;; this changed
+ (f64.const 3.14159)
+ (ref.null any)
+ )
+ )
+ )
+ ;; CHECK: (func $get (type $4) (param $struct (ref null $struct)) (result i32)
+ ;; CHECK-NEXT: (select
+ ;; CHECK-NEXT: (i32.const 10)
+ ;; CHECK-NEXT: (i32.const 20)
+ ;; CHECK-NEXT: (ref.test (ref $substruct.B)
+ ;; CHECK-NEXT: (ref.as_non_null
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get (param $struct (ref null $struct)) (result i32)
+ ;; We can ref.test on $substruct.B now, and optimize.
+ (struct.get $struct 0
+ (local.get $struct)
+ )
+ )
+)
+
+;; A triangle with an abstract type at the top.
+(module
+ ;; CHECK: (type $struct (sub (struct (field i32))))
+ (type $struct (sub (struct i32)))
+ ;; CHECK: (type $substruct.A (sub $struct (struct (field i32) (field f64))))
+ (type $substruct.A (sub $struct (struct i32 f64)))
+
+ ;; CHECK: (type $2 (func))
+
+ ;; CHECK: (type $substruct.B (sub $struct (struct (field i32) (field f64) (field anyref))))
+ (type $substruct.B (sub $struct (struct i32 f64 anyref)))
+
+ ;; CHECK: (type $4 (func (param (ref null $struct)) (result i32)))
+
+ ;; CHECK: (func $create (type $2)
+ ;; CHECK-NEXT: (local $keepalive (ref $struct))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $substruct.A
+ ;; CHECK-NEXT: (i32.const 20)
+ ;; CHECK-NEXT: (f64.const 3.14159)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $substruct.B
+ ;; CHECK-NEXT: (i32.const 30)
+ ;; CHECK-NEXT: (f64.const 3.14159)
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $create
+ (local $keepalive (ref $struct))
+ ;; $struct is never created.
+ (drop
+ (struct.new $substruct.A
+ (i32.const 20)
+ (f64.const 3.14159)
+ )
+ )
+ (drop
+ (struct.new $substruct.B
+ (i32.const 30)
+ (f64.const 3.14159)
+ (ref.null any)
+ )
+ )
+ )
+ ;; CHECK: (func $get (type $4) (param $struct (ref null $struct)) (result i32)
+ ;; CHECK-NEXT: (select
+ ;; CHECK-NEXT: (i32.const 20)
+ ;; CHECK-NEXT: (i32.const 30)
+ ;; CHECK-NEXT: (ref.test (ref $substruct.A)
+ ;; CHECK-NEXT: (ref.as_non_null
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get (param $struct (ref null $struct)) (result i32)
+ ;; We can optimize here as only two types are non-abstract, and picking
+ ;; between the two siblings is easy.
+ (struct.get $struct 0
+ (local.get $struct)
+ )
+ )
+)
+
+;; A triangle with an abstract type in a sibling.
+(module
+ ;; CHECK: (type $struct (sub (struct (field i32))))
+ (type $struct (sub (struct i32)))
+ ;; CHECK: (type $substruct.B (sub $struct (struct (field i32) (field f64) (field anyref))))
+
+ ;; CHECK: (type $2 (func))
+
+ ;; CHECK: (type $substruct.A (sub $struct (struct (field i32) (field f64))))
+ (type $substruct.A (sub $struct (struct i32 f64)))
+
+ (type $substruct.B (sub $struct (struct i32 f64 anyref)))
+
+ ;; CHECK: (type $4 (func (param (ref null $struct)) (result i32)))
+
+ ;; CHECK: (func $create (type $2)
+ ;; CHECK-NEXT: (local $keepalive (ref $substruct.A))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $struct
+ ;; CHECK-NEXT: (i32.const 10)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $substruct.B
+ ;; CHECK-NEXT: (i32.const 30)
+ ;; CHECK-NEXT: (f64.const 3.14159)
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $create
+ (local $keepalive (ref $substruct.A))
+ (drop
+ (struct.new $struct
+ (i32.const 10)
+ )
+ )
+ ;; $substruct.A is never created.
+ (drop
+ (struct.new $substruct.B
+ (i32.const 30)
+ (f64.const 3.14159)
+ (ref.null any)
+ )
+ )
+ )
+ ;; CHECK: (func $get (type $4) (param $struct (ref null $struct)) (result i32)
+ ;; CHECK-NEXT: (select
+ ;; CHECK-NEXT: (i32.const 30)
+ ;; CHECK-NEXT: (i32.const 10)
+ ;; CHECK-NEXT: (ref.test (ref $substruct.B)
+ ;; CHECK-NEXT: (ref.as_non_null
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get (param $struct (ref null $struct)) (result i32)
+ ;; We can optimize here as only two types are non-abstract, and we can test
+ ;; on the non-abstract sibling.
+ (struct.get $struct 0
+ (local.get $struct)
+ )
+ )
+)
+
+;; A triangle with an abstract type in the other sibling.
+(module
+ ;; CHECK: (type $struct (sub (struct (field i32))))
+ (type $struct (sub (struct i32)))
+ ;; CHECK: (type $substruct.A (sub $struct (struct (field i32) (field f64))))
+ (type $substruct.A (sub $struct (struct i32 f64)))
+
+ ;; CHECK: (type $2 (func))
+
+ ;; CHECK: (type $substruct.B (sub $struct (struct (field i32) (field f64) (field anyref))))
+ (type $substruct.B (sub $struct (struct i32 f64 anyref)))
+
+ ;; CHECK: (type $4 (func (param (ref null $struct)) (result i32)))
+
+ ;; CHECK: (func $create (type $2)
+ ;; CHECK-NEXT: (local $keepalive (ref $substruct.B))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $struct
+ ;; CHECK-NEXT: (i32.const 10)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $substruct.A
+ ;; CHECK-NEXT: (i32.const 20)
+ ;; CHECK-NEXT: (f64.const 3.14159)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $create
+ (local $keepalive (ref $substruct.B))
+ (drop
+ (struct.new $struct
+ (i32.const 10)
+ )
+ )
+ (drop
+ (struct.new $substruct.A
+ (i32.const 20)
+ (f64.const 3.14159)
+ )
+ )
+ ;; $substruct.B is never created.
+ )
+ ;; CHECK: (func $get (type $4) (param $struct (ref null $struct)) (result i32)
+ ;; CHECK-NEXT: (select
+ ;; CHECK-NEXT: (i32.const 20)
+ ;; CHECK-NEXT: (i32.const 10)
+ ;; CHECK-NEXT: (ref.test (ref $substruct.A)
+ ;; CHECK-NEXT: (ref.as_non_null
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get (param $struct (ref null $struct)) (result i32)
+ ;; We can optimize here as only two types are non-abstract, and we can test
+ ;; on the non-abstract sibling.
+ (struct.get $struct 0
+ (local.get $struct)
+ )
+ )
+)
+
+;; Several fields and several news.
+(module
+ ;; CHECK: (type $struct (sub (struct (field i32) (field i64) (field f64) (field f32))))
+ (type $struct (sub (struct i32 i64 f64 f32)))
+ ;; CHECK: (type $substruct (sub $struct (struct (field i32) (field i64) (field f64) (field f32))))
+ (type $substruct (sub $struct (struct i32 i64 f64 f32)))
+
+ ;; CHECK: (type $2 (func))
+
+ ;; CHECK: (type $3 (func (param (ref null $struct)) (result i32)))
+
+ ;; CHECK: (type $4 (func (param (ref null $struct)) (result i64)))
+
+ ;; CHECK: (type $5 (func (param (ref null $struct)) (result f64)))
+
+ ;; CHECK: (type $6 (func (param (ref null $struct)) (result f32)))
+
+ ;; CHECK: (func $create (type $2)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $struct
+ ;; CHECK-NEXT: (i32.const 10)
+ ;; CHECK-NEXT: (i64.const 20)
+ ;; CHECK-NEXT: (f64.const 30.3)
+ ;; CHECK-NEXT: (f32.const 40.400001525878906)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $substruct
+ ;; CHECK-NEXT: (i32.const 10)
+ ;; CHECK-NEXT: (i64.const 22)
+ ;; CHECK-NEXT: (f64.const 36.36)
+ ;; CHECK-NEXT: (f32.const 40.79999923706055)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $substruct
+ ;; CHECK-NEXT: (i32.const 11)
+ ;; CHECK-NEXT: (i64.const 22)
+ ;; CHECK-NEXT: (f64.const 30.3)
+ ;; CHECK-NEXT: (f32.const 40.79999923706055)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $create
+ ;; The first new is for $struct, the last two for $substruct.
+ ;; The first two news agree on field 0; the last two on fields 1&3; and the
+ ;; first and last on field 2. As a result, we can optimize only fields 1&3.
+ ;; field.
+ (drop
+ (struct.new $struct
+ (i32.const 10)
+ (i64.const 20)
+ (f64.const 30.3)
+ (f32.const 40.4)
+ )
+ )
+ (drop
+ (struct.new $substruct
+ (i32.const 10)
+ (i64.const 22)
+ (f64.const 36.36)
+ (f32.const 40.8)
+ )
+ )
+ (drop
+ (struct.new $substruct
+ (i32.const 11)
+ (i64.const 22)
+ (f64.const 30.3)
+ (f32.const 40.8)
+ )
+ )
+ )
+ ;; CHECK: (func $get-0 (type $3) (param $struct (ref null $struct)) (result i32)
+ ;; CHECK-NEXT: (struct.get $struct 0
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get-0 (param $struct (ref null $struct)) (result i32)
+ (struct.get $struct 0
+ (local.get $struct)
+ )
+ )
+
+ ;; CHECK: (func $get-1 (type $4) (param $struct (ref null $struct)) (result i64)
+ ;; CHECK-NEXT: (select
+ ;; CHECK-NEXT: (i64.const 22)
+ ;; CHECK-NEXT: (i64.const 20)
+ ;; CHECK-NEXT: (ref.test (ref $substruct)
+ ;; CHECK-NEXT: (ref.as_non_null
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get-1 (param $struct (ref null $struct)) (result i64)
+ (struct.get $struct 1
+ (local.get $struct)
+ )
+ )
+
+ ;; CHECK: (func $get-2 (type $5) (param $struct (ref null $struct)) (result f64)
+ ;; CHECK-NEXT: (struct.get $struct 2
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get-2 (param $struct (ref null $struct)) (result f64)
+ (struct.get $struct 2
+ (local.get $struct)
+ )
+ )
+
+ ;; CHECK: (func $get-3 (type $6) (param $struct (ref null $struct)) (result f32)
+ ;; CHECK-NEXT: (select
+ ;; CHECK-NEXT: (f32.const 40.79999923706055)
+ ;; CHECK-NEXT: (f32.const 40.400001525878906)
+ ;; CHECK-NEXT: (ref.test (ref $substruct)
+ ;; CHECK-NEXT: (ref.as_non_null
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get-3 (param $struct (ref null $struct)) (result f32)
+ (struct.get $struct 3
+ (local.get $struct)
+ )
+ )
+)
+
+;; Three top-level struct hierarchies.
+(module
+ (rec
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $A (sub (struct (field i32))))
+ (type $A (sub (struct i32)))
+ ;; CHECK: (type $subA (sub $A (struct (field i32) (field f64))))
+ (type $subA (sub $A (struct i32 f64)))
+
+ ;; CHECK: (type $B (sub (struct (field i32))))
+ (type $B (sub (struct i32)))
+
+ ;; CHECK: (type $subB (sub $B (struct (field i32) (field f64))))
+ (type $subB (sub $B (struct i32 f64)))
+
+ ;; CHECK: (type $C (sub (struct (field i32))))
+ (type $C (sub (struct i32)))
+
+ ;; CHECK: (type $subC (sub $C (struct (field i32) (field f64))))
+ (type $subC (sub $C (struct i32 f64)))
+ )
+
+ ;; CHECK: (type $6 (func))
+
+ ;; CHECK: (type $7 (func (param (ref null $A)) (result i32)))
+
+ ;; CHECK: (type $8 (func (param (ref null $B)) (result i32)))
+
+ ;; CHECK: (type $9 (func (param (ref null $C)) (result i32)))
+
+ ;; CHECK: (func $create (type $6)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $A
+ ;; CHECK-NEXT: (i32.const 10)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $subA
+ ;; CHECK-NEXT: (i32.const 20)
+ ;; CHECK-NEXT: (f64.const 3.14159)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $B
+ ;; CHECK-NEXT: (i32.const 30)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $subB
+ ;; CHECK-NEXT: (i32.const 40)
+ ;; CHECK-NEXT: (f64.const 2.61828)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $C
+ ;; CHECK-NEXT: (i32.add
+ ;; CHECK-NEXT: (i32.const 1000)
+ ;; CHECK-NEXT: (i32.const 2000)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $subC
+ ;; CHECK-NEXT: (i32.const 50)
+ ;; CHECK-NEXT: (f64.const 999999.9)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $create
+ (drop
+ (struct.new $A
+ (i32.const 10)
+ )
+ )
+ (drop
+ (struct.new $subA
+ (i32.const 20)
+ (f64.const 3.14159)
+ )
+ )
+ (drop
+ (struct.new $B
+ (i32.const 30)
+ )
+ )
+ (drop
+ (struct.new $subB
+ (i32.const 40)
+ (f64.const 2.61828)
+ )
+ )
+ (drop
+ (struct.new $C
+ ;; Something not constant enough for us to reason about
+ (i32.add
+ (i32.const 1000)
+ (i32.const 2000)
+ )
+ )
+ )
+ (drop
+ (struct.new $subC
+ (i32.const 50)
+ (f64.const 999999.9)
+ )
+ )
+ )
+ ;; CHECK: (func $get-A (type $7) (param $A (ref null $A)) (result i32)
+ ;; CHECK-NEXT: (select
+ ;; CHECK-NEXT: (i32.const 20)
+ ;; CHECK-NEXT: (i32.const 10)
+ ;; CHECK-NEXT: (ref.test (ref $subA)
+ ;; CHECK-NEXT: (ref.as_non_null
+ ;; CHECK-NEXT: (local.get $A)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get-A (param $A (ref null $A)) (result i32)
+ ;; We can optimize here, picking 10 or 20.
+ (struct.get $A 0
+ (local.get $A)
+ )
+ )
+
+ ;; CHECK: (func $get-B (type $8) (param $B (ref null $B)) (result i32)
+ ;; CHECK-NEXT: (select
+ ;; CHECK-NEXT: (i32.const 40)
+ ;; CHECK-NEXT: (i32.const 30)
+ ;; CHECK-NEXT: (ref.test (ref $subB)
+ ;; CHECK-NEXT: (ref.as_non_null
+ ;; CHECK-NEXT: (local.get $B)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get-B (param $B (ref null $B)) (result i32)
+ ;; We can optimize here, picking 30 or 40.
+ (struct.get $B 0
+ (local.get $B)
+ )
+ )
+
+ ;; CHECK: (func $get-C (type $9) (param $C (ref null $C)) (result i32)
+ ;; CHECK-NEXT: (struct.get $C 0
+ ;; CHECK-NEXT: (local.get $C)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $get-C (param $C (ref null $C)) (result i32)
+ ;; We can't optimize here.
+ (struct.get $C 0
+ (local.get $C)
+ )
+ )
+)
diff --git a/test/lit/passes/cfp.wast b/test/lit/passes/cfp.wast
index 0cc1d8c00..c3bc81a1b 100644
--- a/test/lit/passes/cfp.wast
+++ b/test/lit/passes/cfp.wast
@@ -767,6 +767,10 @@
;; Subtyping: Create both a subtype and a supertype, with different constants
;; for the shared field, preventing optimization, as a get of the
;; supertype may receive an instance of the subtype.
+;;
+;; Note that this may be optimized using a ref.test, in --cfp-reftest, but not
+;; in --cfp. This gives us coverage that --cfp does not do the things that
+;; --cfp-reftest does (how --cfp-reftest works is tested in cfp-reftest.wast).
(module
;; CHECK: (type $struct (sub (struct (field i32))))
(type $struct (sub (struct i32)))