blob: c2ac66a579363f11280ff730ed477bce86c2b85b [file] [log] [blame] [edit]
;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
;; RUN: foreach %s %t wasm-opt --gto --closed-world -all -S -o - | filecheck %s
(module
;; A struct with a field that is never read or written, so it can be
;; removed.
;; CHECK: (rec
;; CHECK-NEXT: (type $struct (sub (struct)))
(type $struct (sub (struct (field (mut funcref)))))
;; CHECK: (type $1 (func (param (ref $struct))))
;; CHECK: (func $func (type $1) (param $x (ref $struct))
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
(func $func (param $x (ref $struct))
)
)
(module
;; A write does not keep a field from being removed.
;; CHECK: (rec
;; CHECK-NEXT: (type $struct (sub (struct)))
(type $struct (sub (struct (field (mut funcref)))))
;; CHECK: (type $1 (func (param (ref $struct))))
;; CHECK: (func $func (type $1) (param $x (ref $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (block (result (ref $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.null nofunc)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $func (param $x (ref $struct))
;; The fields of this set will be dropped, as we do not need to perform
;; the write.
(struct.set $struct 0
(local.get $x)
(ref.null func)
)
)
)
(module
;; A new does not keep a field from being removed.
;; CHECK: (rec
;; CHECK-NEXT: (type $struct (sub (struct)))
(type $struct (sub (struct (field (mut funcref)))))
;; CHECK: (type $1 (func (param (ref $struct))))
;; CHECK: (func $func (type $1) (param $x (ref $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new_default $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $func (param $x (ref $struct))
;; The fields in this new will be removed.
(drop
(struct.new $struct
(ref.null func)
)
)
)
)
(module
;; A new_default does not keep a field from being removed.
;; CHECK: (rec
;; CHECK-NEXT: (type $struct (sub (struct)))
(type $struct (sub (struct (field (mut funcref)))))
;; CHECK: (type $1 (func (param (ref $struct))))
;; CHECK: (func $func (type $1) (param $x (ref $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new_default $struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $func (param $x (ref $struct))
;; The fields in this new will be removed.
(drop
(struct.new_default $struct
)
)
)
)
(module
;; A read *does* keep a field from being removed.
;; CHECK: (rec
;; CHECK-NEXT: (type $struct (sub (struct (field funcref))))
(type $struct (sub (struct (field (mut funcref)))))
;; CHECK: (type $1 (func (param (ref $struct))))
;; CHECK: (func $func (type $1) (param $x (ref $struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $struct 0
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $func (param $x (ref $struct))
(drop
(struct.get $struct 0
(local.get $x)
)
)
)
)
(module
;; Different struct types with different situations: some fields are read,
;; some written, and some both. (Note that this also tests the interaction
;; of removing with the immutability inference that --gto does.)
;; A struct with all fields marked mutable.
;; CHECK: (rec
;; CHECK-NEXT: (type $imm-struct (sub (struct (field $rw i32) (field $rw-2 i32))))
;; CHECK: (type $1 (func (param (ref $imm-struct))))
;; CHECK: (type $mut-struct (sub (struct (field $r i32) (field $rw (mut i32)) (field $r-2 i32) (field $rw-2 (mut i32)))))
(type $mut-struct (sub (struct (field $r (mut i32)) (field $w (mut i32)) (field $rw (mut i32)) (field $r-2 (mut i32)) (field $w-2 (mut i32)) (field $rw-2 (mut i32)))))
;; A similar struct but with all fields marked immutable, and the only
;; writes are from during creation (so all fields are at least writeable).
(type $imm-struct (sub (struct (field $w i32) (field $rw i32) (field $w-2 i32) (field $rw-2 i32))))
;; CHECK: (type $3 (func (param (ref $mut-struct))))
;; CHECK: (func $func-mut (type $3) (param $x (ref $mut-struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $mut-struct $r
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (block (result (ref $mut-struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.set $mut-struct $rw
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $mut-struct $rw
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $mut-struct $r-2
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (block (result (ref $mut-struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.set $mut-struct $rw-2
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (i32.const 3)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $mut-struct $rw-2
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $func-mut (param $x (ref $mut-struct))
;; $r is only read
(drop
(struct.get $mut-struct $r
(local.get $x)
)
)
;; $w is only written
(struct.set $mut-struct $w
(local.get $x)
(i32.const 0)
)
;; $rw is both
(struct.set $mut-struct $rw
(local.get $x)
(i32.const 1)
)
(drop
(struct.get $mut-struct $rw
(local.get $x)
)
)
;; The same, for the $*-2 fields
(drop
(struct.get $mut-struct $r-2
(local.get $x)
)
)
(struct.set $mut-struct $w-2
(local.get $x)
(i32.const 2)
)
(struct.set $mut-struct $rw-2
(local.get $x)
(i32.const 3)
)
(drop
(struct.get $mut-struct $rw-2
(local.get $x)
)
)
)
;; CHECK: (func $func-imm (type $1) (param $x (ref $imm-struct))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.new $imm-struct
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: (i32.const 3)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $imm-struct $rw
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $imm-struct $rw-2
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $func-imm (param $x (ref $imm-struct))
;; create an instance
(drop
(struct.new $imm-struct
(i32.const 0)
(i32.const 1)
(i32.const 2)
(i32.const 3)
)
)
;; $rw and $rw-2 are also read
(drop
(struct.get $imm-struct $rw
(local.get $x)
)
)
(drop
(struct.get $imm-struct $rw-2
(local.get $x)
)
)
)
)
(module
;; A vtable-like structure created in a global location. Only some of the
;; fields are accessed.
;; CHECK: (rec
;; CHECK-NEXT: (type $0 (func))
;; CHECK: (type $vtable (sub (struct (field $v1 funcref) (field $v2 funcref))))
(type $vtable (sub (struct (field $v0 funcref) (field $v1 funcref) (field $v2 funcref) (field $v3 funcref) (field $v4 funcref))))
;; CHECK: (global $vtable (ref $vtable) (struct.new $vtable
;; CHECK-NEXT: (ref.func $func-1)
;; CHECK-NEXT: (ref.func $func-2)
;; CHECK-NEXT: ))
(global $vtable (ref $vtable) (struct.new $vtable
(ref.func $func-0)
(ref.func $func-1)
(ref.func $func-2)
(ref.func $func-3)
(ref.func $func-4)
))
;; CHECK: (func $test (type $0)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $vtable $v1
;; CHECK-NEXT: (global.get $vtable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $vtable $v2
;; CHECK-NEXT: (global.get $vtable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test
;; To differ from previous tests, do not read the very first field.
(drop
(struct.get $vtable 1
(global.get $vtable)
)
)
;; To differ from previous tests, do reads in two adjacent fields.
(drop
(struct.get $vtable 2
(global.get $vtable)
)
)
;; To differ from previous tests, do not read the very last field, and the
;; one before it.
)
;; CHECK: (func $func-0 (type $0)
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
(func $func-0)
;; CHECK: (func $func-1 (type $0)
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
(func $func-1)
;; CHECK: (func $func-2 (type $0)
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
(func $func-2)
;; CHECK: (func $func-3 (type $0)
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
(func $func-3)
;; CHECK: (func $func-4 (type $0)
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
(func $func-4)
)
(module
;; Similar to the above, but with different types in each field, to verify
;; that we emit valid code and are not confused by the names being right
;; by coincidence.
;; CHECK: (rec
;; CHECK-NEXT: (type $0 (func))
;; CHECK: (type $vtable (sub (struct (field $v1 i64) (field $v2 f32))))
(type $vtable (sub (struct (field $v0 i32) (field $v1 i64) (field $v2 f32) (field $v3 f64) (field $v4 anyref))))
;; CHECK: (global $vtable (ref $vtable) (struct.new $vtable
;; CHECK-NEXT: (i64.const 1)
;; CHECK-NEXT: (f32.const 2.200000047683716)
;; CHECK-NEXT: ))
(global $vtable (ref $vtable) (struct.new $vtable
(i32.const 0)
(i64.const 1)
(f32.const 2.2)
(f64.const 3.3)
(ref.null none)
))
;; CHECK: (func $test (type $0)
;; CHECK-NEXT: (local $i64 i64)
;; CHECK-NEXT: (local $f32 f32)
;; CHECK-NEXT: (local.set $i64
;; CHECK-NEXT: (struct.get $vtable $v1
;; CHECK-NEXT: (global.get $vtable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $f32
;; CHECK-NEXT: (struct.get $vtable $v2
;; CHECK-NEXT: (global.get $vtable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test
(local $i64 i64)
(local $f32 f32)
(local.set $i64
(struct.get $vtable 1
(global.get $vtable)
)
)
(local.set $f32
(struct.get $vtable 2
(global.get $vtable)
)
)
)
)
(module
;; A new with side effects
;; CHECK: (rec
;; CHECK-NEXT: (type $0 (func (param i32) (result (ref any))))
;; CHECK: (type $1 (func (param i32) (result f64)))
;; CHECK: (type $2 (func (param i32) (result i32)))
;; CHECK: (type $3 (func (param (ref any))))
;; CHECK: (type $4 (func))
;; CHECK: (type $struct (struct (field i32)))
(type $struct (struct i32 f64 (ref any)))
;; CHECK: (type $6 (func (param (ref any) (ref null $struct))))
;; CHECK: (global $imm-i32 i32 (i32.const 1234))
(global $imm-i32 i32 (i32.const 1234))
;; CHECK: (global $mut-i32 (mut i32) (i32.const 5678))
(global $mut-i32 (mut i32) (i32.const 5678))
;; CHECK: (func $gets (type $6) (param $x (ref any)) (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 $gets (param $x (ref any)) (param $struct (ref null $struct))
;; Gets to keep certain fields alive.
(drop
(struct.get $struct 0
(local.get $struct)
)
)
)
;; CHECK: (func $new-side-effect (type $4)
;; CHECK-NEXT: (local $0 i32)
;; CHECK-NEXT: (local $1 f64)
;; CHECK-NEXT: (local $2 (ref any))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result (ref $struct))
;; CHECK-NEXT: (local.set $0
;; CHECK-NEXT: (call $helper0
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (call $helper1
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (call $helper2
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $new-side-effect
;; The 2nd&3rd fields here will be removed, since those fields have no
;; reads. They have side effects, though, so the operands will be saved in
;; locals. Note that one of the fields is non-nullable, and we need to use a
;; nullable local for it.
(drop
(struct.new $struct
(call $helper0 (i32.const 0))
(call $helper1 (i32.const 1))
(call $helper2 (i32.const 2))
)
)
)
;; CHECK: (func $new-side-effect-global-imm (type $4)
;; CHECK-NEXT: (local $0 f64)
;; CHECK-NEXT: (local $1 (ref any))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result (ref $struct))
;; CHECK-NEXT: (local.set $0
;; CHECK-NEXT: (call $helper1
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (call $helper2
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (global.get $imm-i32)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $new-side-effect-global-imm
;; As above, the 2nd&3rd fields here will be removed. The first field does
;; a global.get, which has effects, but those effects do not interact with
;; anything else (since it is an immutable global), so we do not need a
;; local for it.
(drop
(struct.new $struct
(global.get $imm-i32)
(call $helper1 (i32.const 0))
(call $helper2 (i32.const 1))
)
)
)
;; CHECK: (func $new-side-effect-global-mut (type $4)
;; CHECK-NEXT: (local $0 i32)
;; CHECK-NEXT: (local $1 f64)
;; CHECK-NEXT: (local $2 (ref any))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result (ref $struct))
;; CHECK-NEXT: (local.set $0
;; CHECK-NEXT: (global.get $mut-i32)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (call $helper1
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (call $helper2
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $new-side-effect-global-mut
;; As above, but the global is mutable, so we will use a local: the calls
;; might alter that global, in theory.
(drop
(struct.new $struct
(global.get $mut-i32)
(call $helper1 (i32.const 0))
(call $helper2 (i32.const 1))
)
)
)
;; CHECK: (func $new-unreachable (type $4)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block ;; (replaces unreachable StructNew we can't emit)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (call $helper2
;; CHECK-NEXT: (i32.const 3)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $new-unreachable
;; Another case with side effects. We stop at the unreachable param before
;; it, however.
(drop
(struct.new $struct
(i32.const 2)
(unreachable)
(call $helper2 (i32.const 3))
)
)
)
;; CHECK: (func $new-side-effect-in-kept (type $3) (param $any (ref any))
;; CHECK-NEXT: (local $1 i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result (ref $struct))
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (call $helper0
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.new $struct
;; CHECK-NEXT: (local.get $1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $new-side-effect-in-kept (param $any (ref any))
;; Side effects appear in fields that we do *not* remove. We do not need to
;; use locals here, but for simplicity we do, and rely on later opts.
(drop
(struct.new $struct
(call $helper0 (i32.const 0))
(f64.const 3.14159)
(local.get $any)
)
)
)
;; CHECK: (func $helper0 (type $2) (param $x i32) (result i32)
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
(func $helper0 (param $x i32) (result i32)
(unreachable)
)
;; CHECK: (func $helper1 (type $1) (param $x i32) (result f64)
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
(func $helper1 (param $x i32) (result f64)
(unreachable)
)
;; CHECK: (func $helper2 (type $0) (param $x i32) (result (ref any))
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
(func $helper2 (param $x i32) (result (ref any))
(unreachable)
)
)
;; We can remove fields if they are only used in subtypes, because we can
;; reorder the fields in the super and re-add them in the sub, appending on top
;; of the now-shorter super.
(module
;; CHECK: (rec
;; CHECK-NEXT: (type $parent (sub (struct (field i64))))
(type $parent (sub (struct (field i32) (field i64) (field f32) (field f64))))
;; CHECK: (type $child (sub $parent (struct (field i64) (field i32) (field f32) (field f64) (field anyref))))
(type $child (sub $parent (struct (field i32) (field i64) (field f32) (field f64) (field anyref))))
;; CHECK: (type $2 (func (param (ref $parent) (ref $child))))
;; CHECK: (func $func (type $2) (param $x (ref $parent)) (param $y (ref $child))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $parent 0
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $child 1
;; CHECK-NEXT: (local.get $y)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $child 2
;; CHECK-NEXT: (local.get $y)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $child 3
;; CHECK-NEXT: (local.get $y)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $child 4
;; CHECK-NEXT: (local.get $y)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $func (param $x (ref $parent)) (param $y (ref $child))
;; The parent has fields 0, 1, 2, 3 and the child adds 4.
;; Use only field 1 in the parent, and all the rest in the child. We can
;; reorder field 1 to the start of the parent (flipping its position with
;; field 0) and then remove all the fields but the now-first. The child
;; keeps all fields, but is reordered.
(drop (struct.get $parent 1 (local.get $x)))
(drop (struct.get $child 0 (local.get $y)))
(drop (struct.get $child 2 (local.get $y)))
(drop (struct.get $child 3 (local.get $y)))
(drop (struct.get $child 4 (local.get $y)))
)
)
(module
;; CHECK: (rec
;; CHECK-NEXT: (type $parent (sub (struct (field i64) (field (mut f32)))))
(type $parent (sub (struct (field (mut i32)) (field (mut i64)) (field (mut f32)) (field (mut f64)))))
;; CHECK: (type $child (sub $parent (struct (field i64) (field (mut f32)) (field i32) (field f64) (field anyref))))
(type $child (sub $parent (struct (field (mut i32)) (field (mut i64)) (field (mut f32)) (field (mut f64)) (field (mut anyref)))))
;; CHECK: (type $2 (func (param (ref $parent) (ref $child))))
;; CHECK: (func $func (type $2) (param $x (ref $parent)) (param $y (ref $child))
;; CHECK-NEXT: (struct.set $parent 1
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (f32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $parent 0
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $child 2
;; CHECK-NEXT: (local.get $y)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $child 1
;; CHECK-NEXT: (local.get $y)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $child 3
;; CHECK-NEXT: (local.get $y)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $child 4
;; CHECK-NEXT: (local.get $y)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $func (param $x (ref $parent)) (param $y (ref $child))
;; As above, but add a write in the parent of field 2. That prevents us from
;; removing it from the parent.
(struct.set $parent 2 (local.get $x) (f32.const 0))
(drop (struct.get $parent 1 (local.get $x)))
(drop (struct.get $child 0 (local.get $y)))
(drop (struct.get $child 2 (local.get $y)))
(drop (struct.get $child 3 (local.get $y)))
(drop (struct.get $child 4 (local.get $y)))
)
)
(module
;; CHECK: (rec
;; CHECK-NEXT: (type $parent (sub (struct (field i64) (field (mut f32)))))
(type $parent (sub (struct (field (mut i32)) (field (mut i64)) (field (mut f32)) (field (mut f64)))))
;; CHECK: (type $child (sub $parent (struct (field i64) (field (mut f32)) (field i32) (field anyref))))
(type $child (sub $parent (struct (field (mut i32)) (field (mut i64)) (field (mut f32)) (field (mut f64)) (field (mut anyref)))))
;; CHECK: (type $2 (func (param (ref $parent) (ref $child))))
;; CHECK: (func $func (type $2) (param $x (ref $parent)) (param $y (ref $child))
;; CHECK-NEXT: (struct.set $parent 1
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (f32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $parent 0
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $child 2
;; CHECK-NEXT: (local.get $y)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $child 1
;; CHECK-NEXT: (local.get $y)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $child 3
;; CHECK-NEXT: (local.get $y)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $func (param $x (ref $parent)) (param $y (ref $child))
;; As above, but now we remove fields in the child as well: 3 is not used.
(struct.set $parent 2 (local.get $x) (f32.const 0))
(drop (struct.get $parent 1 (local.get $x)))
(drop (struct.get $child 0 (local.get $y)))
(drop (struct.get $child 2 (local.get $y)))
;; the read of 3 was removed here.
(drop (struct.get $child 4 (local.get $y)))
)
)
;; A parent with two children, and there are only reads of the parent. Those
;; reads might be of data of either child, of course (as a refernce to the
;; parent might point to them), so we cannot optimize here.
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $parent (sub (struct (field i32))))
(type $parent (sub (struct (field i32))))
;; CHECK: (type $child1 (sub $parent (struct (field i32))))
(type $child1 (sub $parent (struct (field i32))))
;; CHECK: (type $child2 (sub $parent (struct (field i32))))
(type $child2 (sub $parent (struct (field i32))))
)
;; CHECK: (type $3 (func (param (ref $parent) (ref $child1) (ref $child2))))
;; CHECK: (func $func (type $3) (param $parent (ref $parent)) (param $child1 (ref $child1)) (param $child2 (ref $child2))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $parent 0
;; CHECK-NEXT: (local.get $parent)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $func (param $parent (ref $parent)) (param $child1 (ref $child1)) (param $child2 (ref $child2))
(drop (struct.get $parent 0 (local.get $parent)))
)
)
;; As above, but now the read is just of one child. We can remove the field
;; from the parent and the other child.
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $parent (sub (struct)))
(type $parent (sub (struct (field i32))))
;; CHECK: (type $child2 (sub $parent (struct)))
;; CHECK: (type $child1 (sub $parent (struct (field i32))))
(type $child1 (sub $parent (struct (field i32))))
(type $child2 (sub $parent (struct (field i32))))
)
;; CHECK: (type $3 (func (param (ref $parent) (ref $child1) (ref $child2))))
;; CHECK: (func $func (type $3) (param $parent (ref $parent)) (param $child1 (ref $child1)) (param $child2 (ref $child2))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $child1 0
;; CHECK-NEXT: (local.get $child1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $func (param $parent (ref $parent)) (param $child1 (ref $child1)) (param $child2 (ref $child2))
(drop (struct.get $child1 0 (local.get $child1)))
)
)
(module
;; CHECK: (rec
;; CHECK-NEXT: (type $0 (func (result (ref $"{mut:i8}"))))
;; CHECK: (type $1 (func (result i32)))
;; CHECK: (type $2 (func))
;; CHECK: (type $"{mut:i8}" (sub (struct)))
(type $"{mut:i8}" (sub (struct (field (mut i8)))))
;; CHECK: (type $4 (func (param (ref null $"{mut:i8}"))))
;; CHECK: (func $unreachable-set (type $4) (param $"{mut:i8}" (ref null $"{mut:i8}"))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (block (result (ref null $"{mut:i8}"))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (call $helper-i32)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.get $"{mut:i8}")
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $unreachable-set (param $"{mut:i8}" (ref null $"{mut:i8}"))
;; The struct type has no reads, so we want to remove all of the sets of it.
;; This struct.set will trap on null, but first the call must run. When we
;; optimize here we should be careful to not emit something with different
;; ordering (naively emitting ref.as_non_null on the reference would trap
;; before the call, so we must reorder).
(struct.set $"{mut:i8}" 0
(local.get $"{mut:i8}")
(call $helper-i32)
)
)
;; CHECK: (func $unreachable-set-2 (type $4) (param $"{mut:i8}" (ref null $"{mut:i8}"))
;; CHECK-NEXT: (block $block
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (block
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $"{mut:i8}")
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (br $block)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $unreachable-set-2 (param $"{mut:i8}" (ref null $"{mut:i8}"))
;; As above, but the side effects now are a br. Again, the br must happen
;; before the trap (in fact, the br will skip the trap here).
(block $block
(struct.set $"{mut:i8}" 0
(local.get $"{mut:i8}")
(br $block)
)
)
)
;; CHECK: (func $unreachable-set-2b (type $4) (param $"{mut:i8}" (ref null $"{mut:i8}"))
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (block
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $"{mut:i8}")
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $unreachable-set-2b (param $"{mut:i8}" (ref null $"{mut:i8}"))
;; As above, but with an unreachable instead of a br. We add a nop here so
;; that we are inside of a block, and then validation would fail if we do
;; not keep the type of the replacement for the struct.set identical to the
;; struct.set. That is, the type must remain unreachable.
(nop)
(struct.set $"{mut:i8}" 0
(local.get $"{mut:i8}")
(unreachable)
)
)
;; CHECK: (func $unreachable-set-3 (type $2)
;; CHECK-NEXT: (local $0 (ref $"{mut:i8}"))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (block (result (ref $"{mut:i8}"))
;; CHECK-NEXT: (local.set $0
;; CHECK-NEXT: (call $helper-ref)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (call $helper-i32)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $unreachable-set-3
;; As above, but now we have side effects in both children.
(block
(struct.set $"{mut:i8}" 0
(call $helper-ref)
(call $helper-i32)
)
)
)
;; CHECK: (func $helper-i32 (type $1) (result i32)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
(func $helper-i32 (result i32)
(i32.const 1)
)
;; CHECK: (func $helper-ref (type $0) (result (ref $"{mut:i8}"))
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
(func $helper-ref (result (ref $"{mut:i8}"))
(unreachable)
)
)
(module
;; CHECK: (rec
;; CHECK-NEXT: (type $struct (sub (struct)))
(type $struct (sub (struct (field anyref) (field i32) (field f32) (field f64))))
;; CHECK: (type $1 (func (result (ref $struct))))
;; CHECK: (func $func (type $1) (result (ref $struct))
;; CHECK-NEXT: (local $0 (ref $struct))
;; CHECK-NEXT: (local $1 f64)
;; CHECK-NEXT: (local.set $0
;; CHECK-NEXT: (call $func)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $1
;; CHECK-NEXT: (block (result f64)
;; CHECK-NEXT: (if
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: (then
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (f64.const 30)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (struct.new_default $struct)
;; CHECK-NEXT: )
(func $func (result (ref $struct))
;; The fields can be removed here, but the effects must be preserved before
;; the struct.new. The consts in the middle can vanish entirely.
(struct.new $struct
(call $func)
(i32.const 10)
(f32.const 20)
(block (result f64)
(if
(i32.const 0)
(then
(unreachable)
)
)
(f64.const 30)
)
)
)
)
;; A parent with two children, with fields used in various combinations.
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $A (sub (struct (field i64) (field eqref) (field nullref))))
(type $A (sub (struct (field i32) (field i64) (field f32) (field f64) (field anyref) (field eqref) (field nullref))))
;; CHECK: (type $C (sub $A (struct (field i64) (field eqref) (field nullref) (field f64) (field anyref))))
(type $C (sub $A (struct (field i32) (field i64) (field f32) (field f64) (field anyref) (field eqref) (field nullref))))
;; CHECK: (type $B (sub $A (struct (field i64) (field eqref) (field nullref) (field f32) (field anyref))))
(type $B (sub $A (struct (field i32) (field i64) (field f32) (field f64) (field anyref) (field eqref) (field nullref))))
)
;; CHECK: (type $3 (func (param anyref)))
;; CHECK: (func $func (type $3) (param $x anyref)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $A 0
;; CHECK-NEXT: (ref.cast (ref $A)
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $B 3
;; CHECK-NEXT: (ref.cast (ref $B)
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $C 3
;; CHECK-NEXT: (ref.cast (ref $C)
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $B 4
;; CHECK-NEXT: (ref.cast (ref $B)
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $C 4
;; CHECK-NEXT: (ref.cast (ref $C)
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $A 1
;; CHECK-NEXT: (ref.cast (ref $A)
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $C 1
;; CHECK-NEXT: (ref.cast (ref $C)
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $A 2
;; CHECK-NEXT: (ref.cast (ref $A)
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $B 2
;; CHECK-NEXT: (ref.cast (ref $B)
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $func (param $x anyref)
;; Field 0 (i32) is used nowhere.
;; Field 1 (i64) is used only in $A.
;; Field 2 (f32) is used only in $B.
;; Field 3 (f64) is used only in $C.
;; Field 4 (anyref) is used only in $B and $C.
;; Field 5 (eqref) is used only in $A and $C.
;; Field 6 (nullref) is used only in $A and $B.
;; As a result:
;; * A can keep only fields 1, 5, 6 (i64, eqref, nullref).
;; * B keeps A's fields, and appends 2, 4 (f32, anyref).
;; * C keeps A's fields, and appends 3, 4 (f64, anyref).
(drop (struct.get $A 1 (ref.cast (ref $A) (local.get $x))))
(drop (struct.get $B 2 (ref.cast (ref $B) (local.get $x))))
(drop (struct.get $C 3 (ref.cast (ref $C) (local.get $x))))
(drop (struct.get $B 4 (ref.cast (ref $B) (local.get $x))))
(drop (struct.get $C 4 (ref.cast (ref $C) (local.get $x))))
(drop (struct.get $A 5 (ref.cast (ref $A) (local.get $x))))
(drop (struct.get $C 5 (ref.cast (ref $C) (local.get $x))))
(drop (struct.get $A 6 (ref.cast (ref $A) (local.get $x))))
(drop (struct.get $B 6 (ref.cast (ref $B) (local.get $x))))
)
)
;; As above, but instead of $A having children $B, $C, now they are a chain,
;; $A :> $B :> $C
;; $C must now also include $B's fields (specifically field 2, the f32).
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $A (sub (struct (field i64) (field eqref) (field nullref))))
(type $A (sub (struct (field i32) (field i64) (field f32) (field f64) (field anyref) (field eqref) (field nullref))))
;; CHECK: (type $B (sub $A (struct (field i64) (field eqref) (field nullref) (field f32) (field anyref))))
(type $B (sub $A (struct (field i32) (field i64) (field f32) (field f64) (field anyref) (field eqref) (field nullref))))
;; CHECK: (type $C (sub $B (struct (field i64) (field eqref) (field nullref) (field f32) (field anyref) (field f64))))
(type $C (sub $B (struct (field i32) (field i64) (field f32) (field f64) (field anyref) (field eqref) (field nullref))))
)
;; CHECK: (type $3 (func (param anyref)))
;; CHECK: (func $func (type $3) (param $x anyref)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $A 0
;; CHECK-NEXT: (ref.cast (ref $A)
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $B 3
;; CHECK-NEXT: (ref.cast (ref $B)
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $C 5
;; CHECK-NEXT: (ref.cast (ref $C)
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $B 4
;; CHECK-NEXT: (ref.cast (ref $B)
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $C 4
;; CHECK-NEXT: (ref.cast (ref $C)
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $A 1
;; CHECK-NEXT: (ref.cast (ref $A)
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $C 1
;; CHECK-NEXT: (ref.cast (ref $C)
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $A 2
;; CHECK-NEXT: (ref.cast (ref $A)
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $B 2
;; CHECK-NEXT: (ref.cast (ref $B)
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $func (param $x anyref)
;; Same uses as before.
(drop (struct.get $A 1 (ref.cast (ref $A) (local.get $x))))
(drop (struct.get $B 2 (ref.cast (ref $B) (local.get $x))))
(drop (struct.get $C 3 (ref.cast (ref $C) (local.get $x))))
(drop (struct.get $B 4 (ref.cast (ref $B) (local.get $x))))
(drop (struct.get $C 4 (ref.cast (ref $C) (local.get $x))))
(drop (struct.get $A 5 (ref.cast (ref $A) (local.get $x))))
(drop (struct.get $C 5 (ref.cast (ref $C) (local.get $x))))
(drop (struct.get $A 6 (ref.cast (ref $A) (local.get $x))))
(drop (struct.get $B 6 (ref.cast (ref $B) (local.get $x))))
)
)
;; The parent $A is an empty struct, with nothing to remove. See we do not error
;; here.
(module
;; CHECK: (type $A (sub (struct)))
(type $A (sub (struct)))
;; CHECK: (type $B (sub $A (struct)))
(type $B (sub $A (struct)))
;; CHECK: (type $2 (func (param (ref $B))))
;; CHECK: (func $func (type $2) (param $x (ref $B))
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
(func $func (param $x (ref $B))
;; Use $B in a param to keep it alive, and lead us to process it and $A.
)
)
;; As above, but now $B has fields to remove.
(module
;; CHECK: (rec
;; CHECK-NEXT: (type $A (sub (struct)))
(type $A (sub (struct)))
;; CHECK: (type $B (sub $A (struct)))
(type $B (sub $A (struct (field i32) (field i64))))
;; CHECK: (type $2 (func (param (ref $B))))
;; CHECK: (func $func (type $2) (param $x (ref $B))
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
(func $func (param $x (ref $B))
)
)
;; As above, but now $B's fields are used.
(module
;; CHECK: (type $A (sub (struct)))
(type $A (sub (struct)))
;; CHECK: (type $B (sub $A (struct (field i32) (field i64))))
(type $B (sub $A (struct (field i32) (field i64))))
;; CHECK: (type $2 (func (param (ref $B))))
;; CHECK: (func $func (type $2) (param $x (ref $B))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $B 0
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $B 1
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $func (param $x (ref $B))
(drop (struct.get $B 0 (local.get $x)))
(drop (struct.get $B 1 (local.get $x)))
)
)
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $A (sub (struct (field i32))))
(type $A (sub (struct (field i32))))
;; CHECK: (type $B (sub $A (struct (field i32))))
(type $B (sub $A (struct (field i32) (field f64))))
)
;; CHECK: (type $2 (func))
;; CHECK: (func $test (type $2)
;; CHECK-NEXT: (local $x (ref null $A))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $A 0
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $B 0
;; CHECK-NEXT: (ref.cast (ref $B)
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test
(local $x (ref null $A))
;; We cannot remove anything from $A, but we can from $B. That $A is
;; unchanged should not confuse us.
(drop
(struct.get $A 0
(local.get $x)
)
)
;; $B reads field 0, but not its new field 1.
(drop
(struct.get $B 0
(ref.cast (ref $B)
(local.get $x)
)
)
)
)
)
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $A (sub (struct)))
(type $A (sub (struct (field i32))))
;; CHECK: (type $B (sub $A (struct (field i32) (field f64))))
(type $B (sub $A (struct (field i32) (field f64))))
)
;; CHECK: (type $2 (func))
;; CHECK: (func $test (type $2)
;; CHECK-NEXT: (local $x (ref null $B))
;; CHECK-NEXT: (local.set $x
;; CHECK-NEXT: (struct.new $B
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: (f64.const 3.14159)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $B 0
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (struct.get $B 1
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test
(local $x (ref null $B))
;; We can remove everything from $A, but nothing from $B. That $A changes
;; entirely, and $B changes not at all, should not cause any errors.
(local.set $x
(struct.new $B
(i32.const 42)
(f64.const 3.14159)
)
)
(drop
(struct.get $B 0
(local.get $x)
)
)
(drop
(struct.get $B 1
(local.get $x)
)
)
)
)