| ;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. |
| |
| ;; RUN: foreach %s %t wasm-opt --remove-unused-names --cfp -all -S -o - | filecheck %s |
| |
| ;; (remove-unused-names is added to test fallthrough values without a block |
| ;; name getting in the way) |
| |
| (module |
| ;; CHECK: (type $0 (func)) |
| |
| ;; CHECK: (type $struct (struct (field i32))) |
| (type $struct (struct i32)) |
| ;; CHECK: (func $impossible-get (type $0) |
| ;; CHECK-NEXT: (local $struct (ref null $struct)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $impossible-get (local $struct (ref null $struct)) |
| (drop |
| ;; This type is never created, so a get is impossible, and we will trap |
| ;; anyhow. So we can turn this into an unreachable (plus a drop of the |
| ;; reference). |
| (struct.get $struct 0 |
| (local.get $struct) |
| ) |
| ) |
| ) |
| ) |
| |
| (module |
| ;; CHECK: (type $struct (struct (field i64))) |
| (type $struct (struct i64)) |
| ;; CHECK: (type $1 (func (param (ref null $struct)))) |
| |
| ;; CHECK: (func $test (type $1) (param $struct (ref null $struct)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new_default $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i64.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $test (param $struct (ref null $struct)) |
| ;; The only place this type is created is with a default value, and so we |
| ;; can optimize the later get into a constant (plus a drop of the ref). |
| ;; |
| ;; (Note that the allocated reference is dropped here, so it is not actually |
| ;; used anywhere, but this pass does not attempt to trace the paths of |
| ;; references escaping and being stored etc. - it just thinks at the type |
| ;; level.) |
| (drop |
| (struct.new_default $struct) |
| ) |
| (drop |
| (struct.get $struct 0 |
| (local.get $struct) |
| ) |
| ) |
| ) |
| ) |
| |
| (module |
| ;; CHECK: (type $struct (struct (field f32))) |
| (type $struct (struct f32)) |
| ;; CHECK: (type $1 (func (param (ref null $struct)))) |
| |
| ;; CHECK: (func $test (type $1) (param $struct (ref null $struct)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $struct |
| ;; CHECK-NEXT: (f32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result f32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (f32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $test (param $struct (ref null $struct)) |
| ;; The only place this type is created is with a constant value, and so we |
| ;; can optimize the later get into a constant (plus a drop of the ref). |
| (drop |
| (struct.new $struct |
| (f32.const 42) |
| ) |
| ) |
| (drop |
| (struct.get $struct 0 |
| (local.get $struct) |
| ) |
| ) |
| ) |
| ) |
| |
| (module |
| ;; CHECK: (type $struct (struct (field f32))) |
| (type $struct (struct f32)) |
| ;; CHECK: (type $1 (func (param f32 (ref null $struct)))) |
| |
| ;; CHECK: (func $test (type $1) (param $f f32) (param $struct (ref null $struct)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $struct |
| ;; CHECK-NEXT: (local.get $f) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.get $struct 0 |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $test (param $f f32) (param $struct (ref null $struct)) |
| ;; The value given is not a constant, and so we cannot optimize. |
| (drop |
| (struct.new $struct |
| (local.get $f) |
| ) |
| ) |
| (drop |
| (struct.get $struct 0 |
| (local.get $struct) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; Create in one function, get in another. The 10 should be forwarded to the |
| ;; get. |
| (module |
| ;; CHECK: (type $struct (struct (field i32))) |
| (type $struct (struct i32)) |
| ;; CHECK: (type $1 (func)) |
| |
| ;; CHECK: (type $2 (func (param (ref null $struct)))) |
| |
| ;; CHECK: (func $create (type $1) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $struct |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $create |
| (drop |
| (struct.new $struct |
| (i32.const 10) |
| ) |
| ) |
| ) |
| ;; CHECK: (func $get (type $2) (param $struct (ref null $struct)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $get (param $struct (ref null $struct)) |
| (drop |
| (struct.get $struct 0 |
| (local.get $struct) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; As before, but with the order of functions reversed to check for any ordering |
| ;; issues. |
| (module |
| ;; CHECK: (type $struct (struct (field i32))) |
| (type $struct (struct i32)) |
| |
| ;; CHECK: (type $1 (func (param (ref null $struct)))) |
| |
| ;; CHECK: (type $2 (func)) |
| |
| ;; CHECK: (func $get (type $1) (param $struct (ref null $struct)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $get (param $struct (ref null $struct)) |
| (drop |
| (struct.get $struct 0 |
| (local.get $struct) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $create (type $2) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $struct |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $create |
| (drop |
| (struct.new $struct |
| (i32.const 10) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; Different values assigned in the same function, in different struct.news, |
| ;; so we cannot optimize the struct.get away. |
| (module |
| ;; CHECK: (type $struct (struct (field f32))) |
| (type $struct (struct f32)) |
| ;; CHECK: (type $1 (func (param (ref null $struct)))) |
| |
| ;; CHECK: (func $test (type $1) (param $struct (ref null $struct)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $struct |
| ;; CHECK-NEXT: (f32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $struct |
| ;; CHECK-NEXT: (f32.const 1337) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.get $struct 0 |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $test (param $struct (ref null $struct)) |
| (drop |
| (struct.new $struct |
| (f32.const 42) |
| ) |
| ) |
| (drop |
| (struct.new $struct |
| (f32.const 1337) |
| ) |
| ) |
| (drop |
| (struct.get $struct 0 |
| (local.get $struct) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; Different values assigned in different functions, and one is a struct.set. |
| (module |
| ;; CHECK: (type $struct (struct (field (mut f32)))) |
| (type $struct (struct (mut f32))) |
| ;; CHECK: (type $1 (func (param (ref null $struct)))) |
| |
| ;; CHECK: (type $2 (func)) |
| |
| ;; CHECK: (func $create (type $2) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $struct |
| ;; CHECK-NEXT: (f32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $create |
| (drop |
| (struct.new $struct |
| (f32.const 42) |
| ) |
| ) |
| ) |
| ;; CHECK: (func $set (type $1) (param $struct (ref null $struct)) |
| ;; CHECK-NEXT: (struct.set $struct 0 |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: (f32.const 1337) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $set (param $struct (ref null $struct)) |
| (struct.set $struct 0 |
| (local.get $struct) |
| (f32.const 1337) |
| ) |
| ) |
| ;; CHECK: (func $get (type $1) (param $struct (ref null $struct)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.get $struct 0 |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $get (param $struct (ref null $struct)) |
| (drop |
| (struct.get $struct 0 |
| (local.get $struct) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; As the last testcase, but the values happen to coincide, so we can optimize |
| ;; the get into a constant. |
| (module |
| ;; CHECK: (type $struct (struct (field (mut f32)))) |
| (type $struct (struct (mut f32))) |
| ;; CHECK: (type $1 (func (param (ref null $struct)))) |
| |
| ;; CHECK: (type $2 (func)) |
| |
| ;; CHECK: (func $create (type $2) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $struct |
| ;; CHECK-NEXT: (f32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $create |
| (drop |
| (struct.new $struct |
| (f32.const 42) |
| ) |
| ) |
| ) |
| ;; CHECK: (func $set (type $1) (param $struct (ref null $struct)) |
| ;; CHECK-NEXT: (struct.set $struct 0 |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: (f32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $set (param $struct (ref null $struct)) |
| (struct.set $struct 0 |
| (local.get $struct) |
| (f32.const 42) ;; The last testcase had 1337 here. |
| ) |
| ) |
| ;; CHECK: (func $get (type $1) (param $struct (ref null $struct)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result f32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (f32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $get (param $struct (ref null $struct)) |
| (drop |
| (struct.get $struct 0 |
| (local.get $struct) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; Check that we look into the fallthrough value that is assigned. |
| (module |
| ;; CHECK: (type $struct (struct (field (mut f32)))) |
| (type $struct (struct (mut f32))) |
| ;; CHECK: (type $1 (func)) |
| |
| ;; CHECK: (type $2 (func (param i32 (ref null $struct)))) |
| |
| ;; CHECK: (type $3 (func (param (ref null $struct)))) |
| |
| ;; CHECK: (func $create (type $1) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $struct |
| ;; CHECK-NEXT: (block (result f32) |
| ;; CHECK-NEXT: (nop) |
| ;; CHECK-NEXT: (f32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $create |
| (drop |
| (struct.new $struct |
| ;; Fall though a 42 via a block. |
| (block (result f32) |
| (nop) |
| (f32.const 42) |
| ) |
| ) |
| ) |
| ) |
| ;; CHECK: (func $set (type $2) (param $x i32) (param $struct (ref null $struct)) |
| ;; CHECK-NEXT: (struct.set $struct 0 |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: (if (result f32) |
| ;; CHECK-NEXT: (local.get $x) |
| ;; CHECK-NEXT: (then |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (else |
| ;; CHECK-NEXT: (f32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $set (param $x i32) (param $struct (ref null $struct)) |
| (struct.set $struct 0 |
| (local.get $struct) |
| ;; Fall though a 42 via an if. |
| (if (result f32) |
| (local.get $x) |
| (then |
| (unreachable) |
| ) |
| (else |
| (f32.const 42) |
| ) |
| ) |
| ) |
| ) |
| ;; CHECK: (func $get (type $3) (param $struct (ref null $struct)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result f32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (f32.const 42) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $get (param $struct (ref null $struct)) |
| (drop |
| (struct.get $struct 0 |
| (local.get $struct) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; Test a function reference instead of a number. |
| (module |
| ;; CHECK: (type $struct (struct (field funcref))) |
| (type $struct (struct funcref)) |
| ;; CHECK: (type $1 (func (param (ref null $struct)))) |
| |
| ;; CHECK: (elem declare func $test) |
| |
| ;; CHECK: (func $test (type $1) (param $struct (ref null $struct)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $struct |
| ;; CHECK-NEXT: (ref.func $test) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result (ref $1)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.func $test) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $test (param $struct (ref null $struct)) |
| (drop |
| (struct.new $struct |
| (ref.func $test) |
| ) |
| ) |
| (drop |
| (struct.get $struct 0 |
| (local.get $struct) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; Test for unreachable creations, sets, and gets. |
| (module |
| (type $struct (struct (mut i32))) |
| ;; CHECK: (type $0 (func)) |
| |
| ;; CHECK: (func $test (type $0) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block ;; (replaces unreachable StructNew we can't emit) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block ;; (replaces unreachable StructGet we can't emit) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (block ;; (replaces unreachable StructSet we can't emit) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (i32.const 20) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $test |
| (drop |
| (struct.new $struct |
| (unreachable) |
| ) |
| ) |
| (drop |
| (struct.get $struct 0 |
| (unreachable) |
| ) |
| ) |
| (struct.set $struct 0 |
| (unreachable) |
| (i32.const 20) |
| ) |
| ) |
| ) |
| |
| ;; Subtyping: Create a supertype and get a subtype. As we never create a |
| ;; subtype, the get must trap anyhow (the reference it receives can |
| ;; only be null in this closed world). |
| (module |
| ;; CHECK: (type $struct (sub (struct (field i32)))) |
| (type $struct (sub (struct i32))) |
| ;; CHECK: (type $substruct (sub $struct (struct (field i32)))) |
| (type $substruct (sub $struct (struct i32))) |
| |
| ;; CHECK: (type $2 (func)) |
| |
| ;; CHECK: (type $3 (func (param (ref null $substruct)))) |
| |
| ;; CHECK: (func $create (type $2) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $struct |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $create |
| (drop |
| (struct.new $struct |
| (i32.const 10) |
| ) |
| ) |
| ) |
| ;; CHECK: (func $get (type $3) (param $substruct (ref null $substruct)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $substruct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $get (param $substruct (ref null $substruct)) |
| (drop |
| (struct.get $substruct 0 |
| (local.get $substruct) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; As above, but in addition to a new of $struct also add a set. The set could |
| ;; in principle be relevant for the get as far as this pass knows, and so we |
| ;; will optimize the result to the only possible value. (In practice, though, |
| ;; it will trap anyhow.) |
| (module |
| ;; CHECK: (type $struct (sub (struct (field (mut i32))))) |
| (type $struct (sub (struct (mut i32)))) |
| ;; CHECK: (type $substruct (sub $struct (struct (field (mut i32))))) |
| (type $substruct (sub $struct (struct (mut i32)))) |
| |
| ;; CHECK: (type $2 (func (param (ref null $struct)))) |
| |
| ;; CHECK: (type $3 (func (param (ref null $substruct)))) |
| |
| ;; CHECK: (func $create (type $2) (param $struct (ref null $struct)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $struct |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (struct.set $struct 0 |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $create (param $struct (ref null $struct)) |
| (drop |
| (struct.new $struct |
| (i32.const 10) |
| ) |
| ) |
| (struct.set $struct 0 |
| (local.get $struct) |
| (i32.const 10) |
| ) |
| ) |
| ;; CHECK: (func $get (type $3) (param $substruct (ref null $substruct)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $substruct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $get (param $substruct (ref null $substruct)) |
| (drop |
| (struct.get $substruct 0 |
| (local.get $substruct) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; Subtyping: Create a subtype and get a supertype. The get must receive a |
| ;; reference to the subtype (we never create a supertype) and so we |
| ;; can optimize. |
| (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)))) |
| |
| ;; CHECK: (func $create (type $1) |
| ;; 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: ) |
| (func $create |
| (drop |
| (struct.new $substruct |
| (i32.const 10) |
| (f64.const 3.14159) |
| ) |
| ) |
| ) |
| ;; CHECK: (func $get (type $3) (param $struct (ref null $struct)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $get (param $struct (ref null $struct)) |
| (drop |
| (struct.get $struct 0 |
| (local.get $struct) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; Subtyping: Create both a subtype and a supertype, with identical constants |
| ;; for the shared field, and get the supertype. |
| (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)))) |
| |
| ;; 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 10) |
| ;; 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 10) |
| (f64.const 3.14159) |
| ) |
| ) |
| ) |
| ;; CHECK: (func $get (type $3) (param $struct (ref null $struct)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $get (param $struct (ref null $struct)) |
| (drop |
| (struct.get $struct 0 |
| (local.get $struct) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; 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))) |
| ;; 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)))) |
| |
| ;; 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)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.get $struct 0 |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $get (param $struct (ref null $struct)) |
| (drop |
| (struct.get $struct 0 |
| (local.get $struct) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; Subtyping: Create both a subtype and a supertype, with different constants |
| ;; for the shared field, but get from the subtype. The field is |
| ;; shared between the types, but we only create the substruct with |
| ;; one value, so we can optimize. |
| (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 $substruct)))) |
| |
| ;; 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 $substruct (ref null $substruct)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $substruct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 20) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $get (param $substruct (ref null $substruct)) |
| (drop |
| (struct.get $substruct 0 |
| (local.get $substruct) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; As above, but add a set of $struct. The set prevents the optimization. |
| (module |
| ;; CHECK: (type $struct (sub (struct (field (mut i32))))) |
| (type $struct (sub (struct (mut i32)))) |
| |
| ;; CHECK: (type $substruct (sub $struct (struct (field (mut i32)) (field f64)))) |
| (type $substruct (sub $struct (struct (mut i32) f64))) |
| |
| ;; CHECK: (type $2 (func (param (ref null $struct)))) |
| |
| ;; CHECK: (type $3 (func (param (ref null $substruct)))) |
| |
| ;; CHECK: (func $create (type $2) (param $struct (ref null $struct)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $struct |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (struct.set $struct 0 |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; 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 $struct (ref null $struct)) |
| (drop |
| (struct.new $struct |
| (i32.const 10) |
| ) |
| ) |
| (struct.set $struct 0 |
| (local.get $struct) |
| (i32.const 10) |
| ) |
| (drop |
| (struct.new $substruct |
| (i32.const 20) |
| (f64.const 3.14159) |
| ) |
| ) |
| ) |
| ;; CHECK: (func $get (type $3) (param $substruct (ref null $substruct)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.get $substruct 0 |
| ;; CHECK-NEXT: (local.get $substruct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $get (param $substruct (ref null $substruct)) |
| (drop |
| (struct.get $substruct 0 |
| (local.get $substruct) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; Multi-level subtyping, check that we propagate not just to the immediate |
| ;; supertype but all the way as needed. |
| (module |
| ;; CHECK: (type $struct1 (sub (struct (field i32)))) |
| (type $struct1 (sub (struct i32))) |
| |
| ;; CHECK: (type $struct2 (sub $struct1 (struct (field i32) (field f64)))) |
| (type $struct2 (sub $struct1 (struct i32 f64))) |
| |
| ;; CHECK: (type $struct3 (sub $struct2 (struct (field i32) (field f64) (field anyref)))) |
| (type $struct3 (sub $struct2 (struct i32 f64 anyref))) |
| |
| ;; CHECK: (type $3 (func)) |
| |
| ;; CHECK: (type $4 (func (param (ref null $struct1) (ref null $struct2) (ref null $struct3)))) |
| |
| ;; CHECK: (func $create (type $3) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $struct3 |
| ;; 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 $struct3 |
| (i32.const 20) |
| (f64.const 3.14159) |
| (ref.null none) |
| ) |
| ) |
| ) |
| ;; CHECK: (func $get (type $4) (param $struct1 (ref null $struct1)) (param $struct2 (ref null $struct2)) (param $struct3 (ref null $struct3)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $struct1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 20) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $struct2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 20) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $struct2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (f64.const 3.14159) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $struct3) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 20) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $struct3) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (f64.const 3.14159) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result nullref) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $struct3) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.null none) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $get (param $struct1 (ref null $struct1)) (param $struct2 (ref null $struct2)) (param $struct3 (ref null $struct3)) |
| ;; Get field 0 from the $struct1. This can be optimized to a constant |
| ;; since we only ever created an instance of struct3 with a constant there. |
| (drop |
| (struct.get $struct1 0 |
| (local.get $struct1) |
| ) |
| ) |
| ;; Get both fields of $struct2. |
| (drop |
| (struct.get $struct2 0 |
| (local.get $struct2) |
| ) |
| ) |
| (drop |
| (struct.get $struct2 1 |
| (local.get $struct2) |
| ) |
| ) |
| ;; Get all 3 fields of $struct3 |
| (drop |
| (struct.get $struct3 0 |
| (local.get $struct3) |
| ) |
| ) |
| (drop |
| (struct.get $struct3 1 |
| (local.get $struct3) |
| ) |
| ) |
| (drop |
| (struct.get $struct3 2 |
| (local.get $struct3) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; Multi-level subtyping with conflicts. The even-numbered fields will get |
| ;; different values in the sub-most type. Create the top and bottom types, but |
| ;; not the middle one. |
| (module |
| ;; CHECK: (type $struct1 (sub (struct (field i32) (field i32)))) |
| (type $struct1 (sub (struct i32 i32))) |
| |
| ;; CHECK: (type $struct2 (sub $struct1 (struct (field i32) (field i32) (field f64) (field f64)))) |
| (type $struct2 (sub $struct1 (struct i32 i32 f64 f64))) |
| |
| ;; CHECK: (type $struct3 (sub $struct2 (struct (field i32) (field i32) (field f64) (field f64) (field anyref) (field anyref)))) |
| (type $struct3 (sub $struct2 (struct i32 i32 f64 f64 anyref anyref))) |
| |
| ;; CHECK: (type $3 (func (param anyref))) |
| |
| ;; CHECK: (type $4 (func (param (ref null $struct1) (ref null $struct2) (ref null $struct3)))) |
| |
| ;; CHECK: (func $create (type $3) (param $any anyref) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $struct1 |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: (i32.const 20) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $struct3 |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: (i32.const 999) |
| ;; CHECK-NEXT: (f64.const 2.71828) |
| ;; CHECK-NEXT: (f64.const 9.9999999) |
| ;; CHECK-NEXT: (ref.null none) |
| ;; CHECK-NEXT: (local.get $any) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $create (param $any anyref) |
| (drop |
| (struct.new $struct1 |
| (i32.const 10) |
| (i32.const 20) |
| ) |
| ) |
| (drop |
| (struct.new $struct3 |
| (i32.const 10) |
| (i32.const 999) ;; use a different value here |
| (f64.const 2.71828) |
| (f64.const 9.9999999) |
| (ref.null any) |
| (local.get $any) ;; use a non-constant value here, which can never be |
| ;; optimized. |
| ) |
| ) |
| ) |
| ;; CHECK: (func $get (type $4) (param $struct1 (ref null $struct1)) (param $struct2 (ref null $struct2)) (param $struct3 (ref null $struct3)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $struct1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.get $struct1 1 |
| ;; CHECK-NEXT: (local.get $struct1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $struct2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $struct2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 999) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $struct2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (f64.const 2.71828) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $struct2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (f64.const 9.9999999) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $struct3) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $struct3) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 999) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $struct3) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (f64.const 2.71828) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $struct3) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (f64.const 9.9999999) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result nullref) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $struct3) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (ref.null none) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.get $struct3 5 |
| ;; CHECK-NEXT: (local.get $struct3) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $get (param $struct1 (ref null $struct1)) (param $struct2 (ref null $struct2)) (param $struct3 (ref null $struct3)) |
| ;; Get all the fields of all the structs. |
| (drop |
| (struct.get $struct1 0 |
| (local.get $struct1) |
| ) |
| ) |
| (drop |
| (struct.get $struct1 1 |
| (local.get $struct1) |
| ) |
| ) |
| (drop |
| (struct.get $struct2 0 |
| (local.get $struct2) |
| ) |
| ) |
| (drop |
| (struct.get $struct2 1 |
| (local.get $struct2) |
| ) |
| ) |
| (drop |
| (struct.get $struct2 2 |
| (local.get $struct2) |
| ) |
| ) |
| (drop |
| (struct.get $struct2 3 |
| (local.get $struct2) |
| ) |
| ) |
| (drop |
| (struct.get $struct3 0 |
| (local.get $struct3) |
| ) |
| ) |
| (drop |
| (struct.get $struct3 1 |
| (local.get $struct3) |
| ) |
| ) |
| (drop |
| (struct.get $struct3 2 |
| (local.get $struct3) |
| ) |
| ) |
| (drop |
| (struct.get $struct3 3 |
| (local.get $struct3) |
| ) |
| ) |
| (drop |
| (struct.get $struct3 4 |
| (local.get $struct3) |
| ) |
| ) |
| (drop |
| (struct.get $struct3 5 |
| (local.get $struct3) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; Multi-level subtyping with a different value in the middle of the chain. We |
| ;; can only optimize $struct3. |
| (module |
| ;; CHECK: (type $struct1 (sub (struct (field (mut i32))))) |
| (type $struct1 (sub (struct (mut i32)))) |
| ;; CHECK: (type $struct2 (sub $struct1 (struct (field (mut i32)) (field f64)))) |
| (type $struct2 (sub $struct1 (struct (mut i32) f64))) |
| ;; CHECK: (type $struct3 (sub $struct2 (struct (field (mut i32)) (field f64) (field anyref)))) |
| (type $struct3 (sub $struct2 (struct (mut i32) f64 anyref))) |
| |
| ;; CHECK: (type $3 (func)) |
| |
| ;; CHECK: (type $4 (func (param (ref null $struct1) (ref null $struct2) (ref null $struct3)))) |
| |
| ;; CHECK: (func $create (type $3) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $struct1 |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $struct2 |
| ;; CHECK-NEXT: (i32.const 9999) |
| ;; CHECK-NEXT: (f64.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $struct3 |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: (f64.const 0) |
| ;; CHECK-NEXT: (ref.null none) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $create |
| (drop |
| (struct.new $struct1 |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (struct.new $struct2 |
| (i32.const 9999) ;; use a different value here |
| (f64.const 0) |
| ) |
| ) |
| (drop |
| (struct.new $struct3 |
| (i32.const 10) |
| (f64.const 0) |
| (ref.null any) |
| ) |
| ) |
| ) |
| ;; CHECK: (func $get (type $4) (param $struct1 (ref null $struct1)) (param $struct2 (ref null $struct2)) (param $struct3 (ref null $struct3)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.get $struct1 0 |
| ;; CHECK-NEXT: (local.get $struct1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.get $struct2 0 |
| ;; CHECK-NEXT: (local.get $struct2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $struct3) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $get (param $struct1 (ref null $struct1)) (param $struct2 (ref null $struct2)) (param $struct3 (ref null $struct3)) |
| ;; Get field 0 in all the types. |
| (drop |
| (struct.get $struct1 0 |
| (local.get $struct1) |
| ) |
| ) |
| (drop |
| (struct.get $struct2 0 |
| (local.get $struct2) |
| ) |
| ) |
| (drop |
| (struct.get $struct3 0 |
| (local.get $struct3) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; As above, but add not just a new of the middle class with a different value |
| ;; but also a set. That prevents all optimizations. |
| (module |
| |
| ;; CHECK: (type $struct1 (sub (struct (field (mut i32))))) |
| (type $struct1 (sub (struct (mut i32)))) |
| |
| ;; CHECK: (type $struct2 (sub $struct1 (struct (field (mut i32)) (field f64)))) |
| (type $struct2 (sub $struct1 (struct (mut i32) f64))) |
| |
| ;; CHECK: (type $struct3 (sub $struct2 (struct (field (mut i32)) (field f64) (field anyref)))) |
| (type $struct3 (sub $struct2 (struct (mut i32) f64 anyref))) |
| |
| ;; CHECK: (type $3 (func (param (ref null $struct2)))) |
| |
| ;; CHECK: (type $4 (func (param (ref null $struct1) (ref null $struct2) (ref null $struct3)))) |
| |
| ;; CHECK: (func $create (type $3) (param $struct2 (ref null $struct2)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $struct1 |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $struct2 |
| ;; CHECK-NEXT: (i32.const 9999) |
| ;; CHECK-NEXT: (f64.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (struct.set $struct2 0 |
| ;; CHECK-NEXT: (local.get $struct2) |
| ;; CHECK-NEXT: (i32.const 9999) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $struct3 |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: (f64.const 0) |
| ;; CHECK-NEXT: (ref.null none) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $create (param $struct2 (ref null $struct2)) |
| (drop |
| (struct.new $struct1 |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (struct.new $struct2 |
| (i32.const 9999) ;; use a different value here |
| (f64.const 0) |
| ) |
| ) |
| (struct.set $struct2 0 |
| (local.get $struct2) |
| (i32.const 9999) ;; use a different value here |
| ) |
| (drop |
| (struct.new $struct3 |
| (i32.const 10) |
| (f64.const 0) |
| (ref.null any) |
| ) |
| ) |
| ) |
| ;; CHECK: (func $get (type $4) (param $struct1 (ref null $struct1)) (param $struct2 (ref null $struct2)) (param $struct3 (ref null $struct3)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.get $struct1 0 |
| ;; CHECK-NEXT: (local.get $struct1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.get $struct2 0 |
| ;; CHECK-NEXT: (local.get $struct2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.get $struct3 0 |
| ;; CHECK-NEXT: (local.get $struct3) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $get (param $struct1 (ref null $struct1)) (param $struct2 (ref null $struct2)) (param $struct3 (ref null $struct3)) |
| ;; Get field 0 in all the types. |
| (drop |
| (struct.get $struct1 0 |
| (local.get $struct1) |
| ) |
| ) |
| (drop |
| (struct.get $struct2 0 |
| (local.get $struct2) |
| ) |
| ) |
| (drop |
| (struct.get $struct3 0 |
| (local.get $struct3) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; Test for a struct with multiple fields, some of which are constant and hence |
| ;; optimizable, and some not. Also test that some have the same type. |
| (module |
| ;; CHECK: (type $struct (struct (field i32) (field f64) (field i32) (field f64) (field i32))) |
| (type $struct (struct i32 f64 i32 f64 i32)) |
| |
| ;; CHECK: (type $1 (func)) |
| |
| ;; CHECK: (type $2 (func (param (ref null $struct)))) |
| |
| ;; CHECK: (func $create (type $1) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $struct |
| ;; CHECK-NEXT: (i32.eqz |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (f64.const 3.14159) |
| ;; CHECK-NEXT: (i32.const 20) |
| ;; CHECK-NEXT: (f64.abs |
| ;; CHECK-NEXT: (f64.const 2.71828) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 30) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $create |
| (drop |
| (struct.new $struct |
| (i32.eqz (i32.const 10)) ;; not a constant (as far as this pass knows) |
| (f64.const 3.14159) |
| (i32.const 20) |
| (f64.abs (f64.const 2.71828)) ;; not a constant |
| (i32.const 30) |
| ) |
| ) |
| ) |
| ;; CHECK: (func $get (type $2) (param $struct (ref null $struct)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.get $struct 0 |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result f64) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (f64.const 3.14159) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 20) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.get $struct 3 |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 30) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 30) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $get (param $struct (ref null $struct)) |
| (drop |
| (struct.get $struct 0 |
| (local.get $struct) |
| ) |
| ) |
| (drop |
| (struct.get $struct 1 |
| (local.get $struct) |
| ) |
| ) |
| (drop |
| (struct.get $struct 2 |
| (local.get $struct) |
| ) |
| ) |
| (drop |
| (struct.get $struct 3 |
| (local.get $struct) |
| ) |
| ) |
| (drop |
| (struct.get $struct 4 |
| (local.get $struct) |
| ) |
| ) |
| ;; Also test for multiple gets of the same field. |
| (drop |
| (struct.get $struct 4 |
| (local.get $struct) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; Never create A, but have a set to its field. A subtype B has no creates nor |
| ;; sets, and the final subtype C has a create and a get. The set to A should |
| ;; apply to it, preventing optimization. |
| (module |
| ;; CHECK: (type $A (sub (struct (field (mut i32))))) |
| (type $A (sub (struct (mut i32)))) |
| |
| ;; CHECK: (type $B (sub $A (struct (field (mut i32))))) |
| (type $B (sub $A (struct (mut i32)))) |
| |
| ;; CHECK: (type $C (sub $B (struct (field (mut i32))))) |
| (type $C (sub $B (struct (mut i32)))) |
| |
| ;; CHECK: (type $3 (func)) |
| |
| ;; CHECK: (type $4 (func (param (ref $A)))) |
| |
| ;; CHECK: (type $5 (func (param (ref $C)))) |
| |
| ;; CHECK: (func $create (type $3) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $C |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $create |
| (drop |
| (struct.new $C |
| (i32.const 10) |
| ) |
| ) |
| ) |
| ;; CHECK: (func $set (type $4) (param $a (ref $A)) |
| ;; CHECK-NEXT: (struct.set $A 0 |
| ;; CHECK-NEXT: (local.get $a) |
| ;; CHECK-NEXT: (i32.const 20) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $set (param $a (ref $A)) |
| (struct.set $A 0 |
| (local.get $a) |
| (i32.const 20) |
| ) |
| ) |
| ;; CHECK: (func $get (type $5) (param $c (ref $C)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.get $C 0 |
| ;; CHECK-NEXT: (local.get $c) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $get (param $c (ref $C)) |
| (drop |
| (struct.get $C 0 |
| (local.get $c) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; Copies of a field to itself can be ignored. As a result, we can optimize both |
| ;; of the gets here. |
| (module |
| ;; CHECK: (type $struct (struct (field (mut i32)))) |
| (type $struct (struct (mut i32))) |
| |
| ;; CHECK: (type $1 (func (param (ref null $struct) (ref null $struct)))) |
| |
| ;; CHECK: (func $test (type $1) (param $struct (ref null $struct)) (param $other (ref null $struct)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new_default $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (struct.set $struct 0 |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $other) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $test (param $struct (ref null $struct)) (param $other (ref null $struct)) |
| (drop |
| (struct.new_default $struct) |
| ) |
| ;; This copy does not actually introduce any new possible values, and so it |
| ;; remains true that the only possible value is the default. |
| (struct.set $struct 0 |
| (local.get $struct) |
| (struct.get $struct 0 |
| (local.get $struct) |
| ) |
| ) |
| (drop |
| (struct.get $struct 0 |
| (local.get $other) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; Test of a near-copy, of a similar looking field (same index, and same field |
| ;; type) but in a different struct. |
| (module |
| ;; CHECK: (type $struct (struct (field (mut f32)) (field (mut i32)))) |
| (type $struct (struct (mut f32) (mut i32))) |
| ;; CHECK: (type $other (struct (field (mut f64)) (field (mut i32)))) |
| (type $other (struct (mut f64) (mut i32))) |
| |
| ;; CHECK: (type $2 (func (param (ref null $struct) (ref null $other)))) |
| |
| ;; CHECK: (func $test (type $2) (param $struct (ref null $struct)) (param $other (ref null $other)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new_default $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (struct.set $struct 1 |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: (block |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (local.get $other) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (unreachable) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.get $struct 1 |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $test (param $struct (ref null $struct)) (param $other (ref null $other)) |
| (drop |
| (struct.new_default $struct) |
| ) |
| ;; As this is not a copy, we cannot optimize struct.1's get lower down. |
| (struct.set $struct 1 |
| (local.get $struct) |
| (struct.get $other 1 |
| (local.get $other) |
| ) |
| ) |
| (drop |
| (struct.get $struct 1 |
| (local.get $struct) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; Test of a near-copy, of a different index. |
| (module |
| ;; CHECK: (type $struct (struct (field (mut i32)) (field (mut i32)))) |
| (type $struct (struct (mut i32) (mut i32))) |
| |
| ;; CHECK: (type $1 (func (param (ref null $struct)))) |
| |
| ;; CHECK: (func $test (type $1) (param $struct (ref null $struct)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new_default $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (struct.set $struct 0 |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.get $struct 0 |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $test (param $struct (ref null $struct)) |
| (drop |
| (struct.new_default $struct) |
| ) |
| ;; As this is not a copy, we cannot optimize struct.0's get lower down. |
| (struct.set $struct 0 |
| (local.get $struct) |
| (struct.get $struct 1 |
| (local.get $struct) |
| ) |
| ) |
| (drop |
| (struct.get $struct 0 |
| (local.get $struct) |
| ) |
| ) |
| ) |
| ) |
| |
| (module |
| ;; CHECK: (type $struct (struct (field i32))) |
| (type $struct (struct i32)) |
| |
| ;; CHECK: (type $1 (func (param (ref null $struct)))) |
| |
| ;; CHECK: (global $global i32 (i32.const 42)) |
| (global $global i32 (i32.const 42)) |
| |
| ;; CHECK: (func $test (type $1) (param $struct (ref null $struct)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $struct |
| ;; CHECK-NEXT: (global.get $global) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (global.get $global) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $test (param $struct (ref null $struct)) |
| ;; An immutable global is the only thing written to this field, so we can |
| ;; propagate the value to the struct.get and replace it with a global.get. |
| (drop |
| (struct.new $struct |
| (global.get $global) |
| ) |
| ) |
| (drop |
| (struct.get $struct 0 |
| (local.get $struct) |
| ) |
| ) |
| ) |
| ) |
| |
| (module |
| ;; CHECK: (type $struct (struct (field i32))) |
| (type $struct (struct i32)) |
| |
| ;; CHECK: (type $1 (func (param (ref null $struct)))) |
| |
| ;; CHECK: (global $global (mut i32) (i32.const 42)) |
| (global $global (mut i32) (i32.const 42)) |
| |
| ;; CHECK: (func $test (type $1) (param $struct (ref null $struct)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $struct |
| ;; CHECK-NEXT: (global.get $global) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.get $struct 0 |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $test (param $struct (ref null $struct)) |
| ;; As above, but the global is *not* immutable, so we cannot optimize. |
| (drop |
| (struct.new $struct |
| (global.get $global) |
| ) |
| ) |
| (drop |
| (struct.get $struct 0 |
| (local.get $struct) |
| ) |
| ) |
| ) |
| ) |
| |
| (module |
| ;; CHECK: (type $struct (struct (field (mut i32)))) |
| (type $struct (struct (mut i32))) |
| |
| ;; CHECK: (type $1 (func (param (ref null $struct)))) |
| |
| ;; CHECK: (global $global i32 (i32.const 42)) |
| (global $global i32 (i32.const 42)) |
| |
| ;; CHECK: (func $test (type $1) (param $struct (ref null $struct)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $struct |
| ;; CHECK-NEXT: (global.get $global) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (struct.set $struct 0 |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: (global.get $global) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (global.get $global) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $test (param $struct (ref null $struct)) |
| (drop |
| (struct.new $struct |
| (global.get $global) |
| ) |
| ) |
| ;; As above, but there is another set of the field. It is the same, though, |
| ;; so that is fine. Also, the struct's field is now mutable as well to allow |
| ;; that, and that also does not prevent optimization. |
| (struct.set $struct 0 |
| (local.get $struct) |
| (global.get $global) |
| ) |
| (drop |
| (struct.get $struct 0 |
| (local.get $struct) |
| ) |
| ) |
| ) |
| ) |
| |
| (module |
| ;; CHECK: (type $struct (struct (field (mut i32)))) |
| (type $struct (struct (mut i32))) |
| |
| ;; CHECK: (type $1 (func (param (ref null $struct)))) |
| |
| ;; CHECK: (global $global i32 (i32.const 42)) |
| (global $global i32 (i32.const 42)) |
| ;; CHECK: (global $global-2 i32 (i32.const 1337)) |
| (global $global-2 i32 (i32.const 1337)) |
| |
| ;; CHECK: (func $test (type $1) (param $struct (ref null $struct)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $struct |
| ;; CHECK-NEXT: (global.get $global) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (struct.set $struct 0 |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: (global.get $global-2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.get $struct 0 |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $test (param $struct (ref null $struct)) |
| (drop |
| (struct.new $struct |
| (global.get $global) |
| ) |
| ) |
| ;; As above, but set a different global, which prevents optimization. |
| (struct.set $struct 0 |
| (local.get $struct) |
| (global.get $global-2) |
| ) |
| (drop |
| (struct.get $struct 0 |
| (local.get $struct) |
| ) |
| ) |
| ) |
| ) |
| |
| (module |
| ;; CHECK: (type $struct (struct (field (mut i32)))) |
| (type $struct (struct (mut i32))) |
| |
| ;; CHECK: (type $1 (func (param (ref null $struct)))) |
| |
| ;; CHECK: (global $global i32 (i32.const 42)) |
| (global $global i32 (i32.const 42)) |
| ;; CHECK: (global $global-2 i32 (i32.const 1337)) |
| (global $global-2 i32 (i32.const 1337)) |
| |
| ;; CHECK: (func $test (type $1) (param $struct (ref null $struct)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $struct |
| ;; CHECK-NEXT: (global.get $global) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (struct.set $struct 0 |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: (i32.const 1337) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.get $struct 0 |
| ;; CHECK-NEXT: (local.get $struct) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $test (param $struct (ref null $struct)) |
| (drop |
| (struct.new $struct |
| (global.get $global) |
| ) |
| ) |
| ;; As above, but set a constant, which means we are mixing constants with |
| ;; globals, which prevents the optimization. |
| (struct.set $struct 0 |
| (local.get $struct) |
| (i32.const 1337) |
| ) |
| (drop |
| (struct.get $struct 0 |
| (local.get $struct) |
| ) |
| ) |
| ) |
| ) |
| |
| (module |
| ;; Test a global type other than i32. Arrays of structs are a realistic case |
| ;; as they are used to implement itables. |
| |
| ;; CHECK: (type $vtable (struct (field funcref))) |
| (type $vtable (struct funcref)) |
| |
| ;; CHECK: (type $itable (array (ref $vtable))) |
| (type $itable (array (ref $vtable))) |
| |
| ;; CHECK: (type $object (struct (field $itable (ref $itable)))) |
| (type $object (struct (field $itable (ref $itable)))) |
| |
| ;; CHECK: (type $3 (func (param (ref null $object)) (result funcref))) |
| |
| ;; CHECK: (global $global (ref $itable) (array.new_fixed $itable 2 |
| ;; CHECK-NEXT: (struct.new $vtable |
| ;; CHECK-NEXT: (ref.null nofunc) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (struct.new $vtable |
| ;; CHECK-NEXT: (ref.func $test) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: )) |
| (global $global (ref $itable) (array.new_fixed $itable 2 |
| (struct.new $vtable |
| (ref.null func) |
| ) |
| (struct.new $vtable |
| (ref.func $test) |
| ) |
| )) |
| |
| ;; CHECK: (func $test (type $3) (param $object (ref null $object)) (result funcref) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $object |
| ;; CHECK-NEXT: (global.get $global) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (struct.get $vtable 0 |
| ;; CHECK-NEXT: (array.get $itable |
| ;; CHECK-NEXT: (block (result (ref $itable)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $object) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (global.get $global) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $test (param $object (ref null $object)) (result funcref) |
| (drop |
| (struct.new $object |
| (global.get $global) |
| ) |
| ) |
| ;; Realistic usage of an itable: read an item from it, then a func from |
| ;; that, and return the value (all verifying that the types are correct |
| ;; after optimization). Note how after optimization everything is lined up |
| ;; so that precompute-propagate can infer from the global.get the specific |
| ;; object the array.get is on, allowing us to emit a constant value for the |
| ;; outer struct.get in principle. |
| (struct.get $vtable 0 |
| (array.get $itable |
| (struct.get $object $itable |
| (local.get $object) |
| ) |
| (i32.const 1) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; Test we handle packed fields properly. |
| (module |
| (rec |
| ;; CHECK: (type $0 (func)) |
| |
| ;; CHECK: (rec |
| ;; CHECK-NEXT: (type $A_8 (struct (field i8))) |
| (type $A_8 (struct (field i8))) |
| ;; CHECK: (type $A_16 (struct (field i16))) |
| (type $A_16 (struct (field i16))) |
| ;; CHECK: (type $B_16 (struct (field i16))) |
| (type $B_16 (struct (field i16))) |
| ) |
| |
| ;; CHECK: (import "a" "b" (global $g i32)) |
| (import "a" "b" (global $g i32)) |
| |
| ;; CHECK: (func $test (type $0) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (struct.new $A_8 |
| ;; CHECK-NEXT: (i32.const 305419896) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.and |
| ;; CHECK-NEXT: (i32.const 305419896) |
| ;; CHECK-NEXT: (i32.const 255) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (struct.new $A_16 |
| ;; CHECK-NEXT: (i32.const 305419896) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.and |
| ;; CHECK-NEXT: (i32.const 305419896) |
| ;; CHECK-NEXT: (i32.const 65535) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (struct.new $B_16 |
| ;; CHECK-NEXT: (global.get $g) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.and |
| ;; CHECK-NEXT: (global.get $g) |
| ;; CHECK-NEXT: (i32.const 65535) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $test |
| ;; We can infer values here, but must add proper masks, as the inputs get |
| ;; truncated during packing. |
| (drop |
| (struct.get_u $A_8 0 |
| (struct.new $A_8 |
| (i32.const 0x12345678) |
| ) |
| ) |
| ) |
| (drop |
| (struct.get_u $A_16 0 |
| (struct.new $A_16 |
| (i32.const 0x12345678) |
| ) |
| ) |
| ) |
| ;; Also test reading a value from an imported global, which is an unknown |
| ;; value at compile time, but which we know must be masked as well. |
| (drop |
| (struct.get_u $B_16 0 |
| (struct.new $B_16 |
| (global.get $g) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; CHECK: (func $test_signed (type $0) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (struct.new $A_8 |
| ;; CHECK-NEXT: (i32.const 305419896) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.shr_s |
| ;; CHECK-NEXT: (i32.shl |
| ;; CHECK-NEXT: (i32.const 305419896) |
| ;; CHECK-NEXT: (i32.const 24) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 24) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (struct.new $A_16 |
| ;; CHECK-NEXT: (i32.const 305419896) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.shr_s |
| ;; CHECK-NEXT: (i32.shl |
| ;; CHECK-NEXT: (i32.const 305419896) |
| ;; CHECK-NEXT: (i32.const 16) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 16) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (struct.new $B_16 |
| ;; CHECK-NEXT: (global.get $g) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.shr_s |
| ;; CHECK-NEXT: (i32.shl |
| ;; CHECK-NEXT: (global.get $g) |
| ;; CHECK-NEXT: (i32.const 16) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 16) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $test_signed |
| ;; As above, but with signed gets. |
| (drop |
| (struct.get_s $A_8 0 |
| (struct.new $A_8 |
| (i32.const 0x12345678) |
| ) |
| ) |
| ) |
| (drop |
| (struct.get_s $A_16 0 |
| (struct.new $A_16 |
| (i32.const 0x12345678) |
| ) |
| ) |
| ) |
| (drop |
| (struct.get_s $B_16 0 |
| (struct.new $B_16 |
| (global.get $g) |
| ) |
| ) |
| ) |
| ) |
| ) |
| |
| (module |
| (rec |
| ;; CHECK: (rec |
| ;; CHECK-NEXT: (type $A (sub (struct (field (mut i32))))) |
| (type $A (sub (struct (field (mut i32))))) |
| ;; CHECK: (type $B (sub $A (struct (field (mut i32))))) |
| (type $B (sub $A (struct (field (mut i32))))) |
| ) |
| |
| ;; CHECK: (type $2 (func (param i32) (result i32))) |
| |
| ;; CHECK: (func $test (type $2) (param $0 i32) (result i32) |
| ;; CHECK-NEXT: (local $A (ref $A)) |
| ;; CHECK-NEXT: (local $B (ref $B)) |
| ;; CHECK-NEXT: (struct.set $A 0 |
| ;; CHECK-NEXT: (select (result (ref null $A)) |
| ;; CHECK-NEXT: (ref.null none) |
| ;; CHECK-NEXT: (local.tee $B |
| ;; CHECK-NEXT: (struct.new $B |
| ;; CHECK-NEXT: (i32.const 20) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 0) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (struct.get $A 0 |
| ;; CHECK-NEXT: (struct.new $A |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (struct.get $B 0 |
| ;; CHECK-NEXT: (local.get $B) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $test (param $0 i32) (result i32) |
| (local $A (ref $A)) |
| (local $B (ref $B)) |
| ;; This set is part of a copy, as the value is a struct.get. The copy is on |
| ;; $A, but the reference we operate on is an instance of $B, actually. So |
| ;; the value read at the end is in fact 10. That is, this test verifies |
| ;; that we track the copied value even though the copy is on $A but it |
| ;; affects $B. |
| (struct.set $A 0 |
| ;; This select is used to keep the type that reaches the struct.set $A, |
| ;; and not $B, so it looks like a perfect copy of $A->$A. |
| (select (result (ref null $A)) |
| (ref.null $A) |
| (local.tee $B |
| (struct.new $B |
| (i32.const 20) |
| ) |
| ) |
| (i32.const 0) |
| ) |
| (struct.get $A 0 |
| (struct.new $A |
| (i32.const 10) |
| ) |
| ) |
| ) |
| ;; This should not turn into 20, since just based on values flowing to |
| ;; fields we cannot infer that (the value will be 10, but CFP cannot infer |
| ;; that either - that would require tracking references through locals |
| ;; etc.). |
| (struct.get $B 0 |
| (local.get $B) |
| ) |
| ) |
| ) |
| |
| ;; A type with two subtypes. A copy on the parent can affect either child. |
| (module |
| (rec |
| ;; CHECK: (rec |
| ;; CHECK-NEXT: (type $A (sub (struct (field (mut i32))))) |
| (type $A (sub (struct (field (mut i32))))) |
| |
| ;; CHECK: (type $B1 (sub $A (struct (field (mut i32))))) |
| (type $B1 (sub $A (struct (field (mut i32))))) |
| |
| ;; CHECK: (type $B2 (sub $A (struct (field (mut i32))))) |
| (type $B2 (sub $A (struct (field (mut i32))))) |
| ) |
| |
| ;; CHECK: (type $3 (func (param (ref null $A) (ref null $B1) (ref null $B2)))) |
| |
| ;; CHECK: (func $test (type $3) (param $A (ref null $A)) (param $B1 (ref null $B1)) (param $B2 (ref null $B2)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $A |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $B1 |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $B2 |
| ;; CHECK-NEXT: (i32.const 20) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (struct.set $A 0 |
| ;; CHECK-NEXT: (local.get $A) |
| ;; CHECK-NEXT: (struct.get $A 0 |
| ;; CHECK-NEXT: (local.get $A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.get $A 0 |
| ;; CHECK-NEXT: (local.get $A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.get $B1 0 |
| ;; CHECK-NEXT: (local.get $B1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.get $B2 0 |
| ;; CHECK-NEXT: (local.get $B2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $test (param $A (ref null $A)) (param $B1 (ref null $B1)) (param $B2 (ref null $B2)) |
| ;; A and B1 agree on their value in their construction. |
| (drop |
| (struct.new $A |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (struct.new $B1 |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (struct.new $B2 |
| (i32.const 20) ;; this value is different from the others |
| ) |
| ) |
| |
| ;; Copy on $A |
| (struct.set $A 0 |
| (local.get $A) |
| (struct.get $A 0 |
| (local.get $A) |
| ) |
| ) |
| |
| ;; $A might read either child, so we can't infer. |
| (drop |
| (struct.get $A 0 |
| (local.get $A) |
| ) |
| ) |
| ;; $B1 should be only able to read 10, but the copy opens the possibility |
| ;; of 20, so we can't optimize. |
| (drop |
| (struct.get $B1 0 |
| (local.get $B1) |
| ) |
| ) |
| ;; As with $B1, the copy stops us. |
| (drop |
| (struct.get $B2 0 |
| (local.get $B2) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; As above, but now the copy is on B1. |
| (module |
| (rec |
| ;; CHECK: (rec |
| ;; CHECK-NEXT: (type $A (sub (struct (field (mut i32))))) |
| (type $A (sub (struct (field (mut i32))))) |
| |
| ;; CHECK: (type $B1 (sub $A (struct (field (mut i32))))) |
| (type $B1 (sub $A (struct (field (mut i32))))) |
| |
| ;; CHECK: (type $B2 (sub $A (struct (field (mut i32))))) |
| (type $B2 (sub $A (struct (field (mut i32))))) |
| ) |
| |
| ;; CHECK: (type $3 (func (param (ref null $A) (ref null $B1) (ref null $B2)))) |
| |
| ;; CHECK: (func $test (type $3) (param $A (ref null $A)) (param $B1 (ref null $B1)) (param $B2 (ref null $B2)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $A |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $B1 |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $B2 |
| ;; CHECK-NEXT: (i32.const 20) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (struct.set $B1 0 |
| ;; CHECK-NEXT: (local.get $B1) |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $B1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.get $A 0 |
| ;; CHECK-NEXT: (local.get $A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $B1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.get $B2 0 |
| ;; CHECK-NEXT: (local.get $B2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $test (param $A (ref null $A)) (param $B1 (ref null $B1)) (param $B2 (ref null $B2)) |
| (drop |
| (struct.new $A |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (struct.new $B1 |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (struct.new $B2 |
| (i32.const 20) |
| ) |
| ) |
| |
| ;; This changed from $A to $B1. |
| (struct.set $B1 0 |
| (local.get $B1) |
| (struct.get $B1 0 |
| (local.get $B1) |
| ) |
| ) |
| |
| ;; This might still read $B1 or $B2, with different values, so we can't |
| ;; optimize. |
| (drop |
| (struct.get $A 0 |
| (local.get $A) |
| ) |
| ) |
| ;; The copy can only refer to $B1, so we can optimize here. |
| (drop |
| (struct.get $B1 0 |
| (local.get $B1) |
| ) |
| ) |
| ;; The copy can't refer to a $B2, so we can optimize here. TODO (but GUFA |
| ;; can do this) |
| (drop |
| (struct.get $B2 0 |
| (local.get $B2) |
| ) |
| ) |
| ) |
| ) |
| |
| ;; As above, but now the copy is on B2. |
| (module |
| (rec |
| ;; CHECK: (rec |
| ;; CHECK-NEXT: (type $A (sub (struct (field (mut i32))))) |
| (type $A (sub (struct (field (mut i32))))) |
| |
| ;; CHECK: (type $B1 (sub $A (struct (field (mut i32))))) |
| (type $B1 (sub $A (struct (field (mut i32))))) |
| |
| ;; CHECK: (type $B2 (sub $A (struct (field (mut i32))))) |
| (type $B2 (sub $A (struct (field (mut i32))))) |
| ) |
| |
| ;; CHECK: (type $3 (func (param (ref null $A) (ref null $B1) (ref null $B2)))) |
| |
| ;; CHECK: (func $test (type $3) (param $A (ref null $A)) (param $B1 (ref null $B1)) (param $B2 (ref null $B2)) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $A |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $B1 |
| ;; CHECK-NEXT: (i32.const 10) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.new $B2 |
| ;; CHECK-NEXT: (i32.const 20) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (struct.set $B2 0 |
| ;; CHECK-NEXT: (local.get $B2) |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $B2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 20) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.get $A 0 |
| ;; CHECK-NEXT: (local.get $A) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (struct.get $B1 0 |
| ;; CHECK-NEXT: (local.get $B1) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (block (result i32) |
| ;; CHECK-NEXT: (drop |
| ;; CHECK-NEXT: (ref.as_non_null |
| ;; CHECK-NEXT: (local.get $B2) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: (i32.const 20) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| ;; CHECK-NEXT: ) |
| (func $test (param $A (ref null $A)) (param $B1 (ref null $B1)) (param $B2 (ref null $B2)) |
| (drop |
| (struct.new $A |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (struct.new $B1 |
| (i32.const 10) |
| ) |
| ) |
| (drop |
| (struct.new $B2 |
| (i32.const 20) |
| ) |
| ) |
| |
| ;; This changed from $A to $B2. |
| (struct.set $B2 0 |
| (local.get $B2) |
| (struct.get $B2 0 |
| (local.get $B2) |
| ) |
| ) |
| |
| ;; This might still read $B1 or $B2, with different values, so we can't |
| ;; optimize. |
| (drop |
| (struct.get $A 0 |
| (local.get $A) |
| ) |
| ) |
| ;; The copy can't refer to a $B1, so we can optimize here. TODO (but GUFA |
| ;; can do this) |
| (drop |
| (struct.get $B1 0 |
| (local.get $B1) |
| ) |
| ) |
| ;; $B2 is copied to itself, and nothing else, so we can optimize here. |
| (drop |
| (struct.get $B2 0 |
| (local.get $B2) |
| ) |
| ) |
| ) |
| ) |