diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/lit/help/wasm-opt.test | 3 | ||||
-rw-r--r-- | test/lit/help/wasm2js.test | 3 | ||||
-rw-r--r-- | test/lit/passes/cfp-reftest.wast | 1458 | ||||
-rw-r--r-- | test/lit/passes/cfp.wast | 4 |
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))) |