diff options
author | Alon Zakai <azakai@google.com> | 2023-02-01 10:01:08 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-02-01 10:01:08 -0800 |
commit | 71b4ba4711189a26b699061e5b2af665ccc93114 (patch) | |
tree | 7fb045fe4b3889c08e4ac3153b4a4d384099f751 /test | |
parent | a6f4b00141c994c744c6cb148d3ae60f271977f6 (diff) | |
download | binaryen-71b4ba4711189a26b699061e5b2af665ccc93114.tar.gz binaryen-71b4ba4711189a26b699061e5b2af665ccc93114.tar.bz2 binaryen-71b4ba4711189a26b699061e5b2af665ccc93114.zip |
RemoveUnusedModuleElements: Optimize unread struct.new fields (#5445)
This is similar to the existing optimization for function references: a RefFunc is
just a reference, and we do not consider the function actually used unless some
CallRef can actually call it. Here we optimize StructNew fields by tracking if they
are read. Consider this object:
(struct.new $object
(global.get $vtable)
)
Before this PR, when we saw that StructNew we'd make that global used, and
process all of its contents, which look like this:
(global $vtable
(struct.new $object
(ref.func $foo)
)
)
With this PR we track which fields on the struct types are even read. If they
are not then we do not add uses of the things they refer to. In this example,
the global vtable would be referenced, but not used, and likewise the RefFunc
inside it.
The technical way we handle this is to walk StructNews carefully: when we see
a field that has been read, we can just read it normally, but if it has not been
read then we note it on the side, and only process it later if we see a read.
Diffstat (limited to 'test')
-rw-r--r-- | test/lit/passes/remove-unused-module-elements-refs.wast | 1223 |
1 files changed, 1223 insertions, 0 deletions
diff --git a/test/lit/passes/remove-unused-module-elements-refs.wast b/test/lit/passes/remove-unused-module-elements-refs.wast index f0b90a7bb..7d430b021 100644 --- a/test/lit/passes/remove-unused-module-elements-refs.wast +++ b/test/lit/passes/remove-unused-module-elements-refs.wast @@ -571,3 +571,1226 @@ (func $target-keep-2 (type $A) ) ) + +;; Test reachability of struct fields in globals. Only fields that have actual +;; reads need to be processed. +(module + ;; CHECK: (type $void (func)) + ;; OPEN_WORLD: (type $void (func)) + (type $void (func)) + + ;; CHECK: (type $vtable (struct (field (ref $void)) (field (ref $void)))) + ;; OPEN_WORLD: (type $vtable (struct (field (ref $void)) (field (ref $void)))) + (type $vtable (struct_subtype (field (ref $void)) (field (ref $void)) data)) + + ;; CHECK: (global $vtable (ref $vtable) (struct.new $vtable + ;; CHECK-NEXT: (ref.func $a) + ;; CHECK-NEXT: (ref.func $b) + ;; CHECK-NEXT: )) + ;; OPEN_WORLD: (global $vtable (ref $vtable) (struct.new $vtable + ;; OPEN_WORLD-NEXT: (ref.func $a) + ;; OPEN_WORLD-NEXT: (ref.func $b) + ;; OPEN_WORLD-NEXT: )) + (global $vtable (ref $vtable) (struct.new $vtable + (ref.func $a) + (ref.func $b) + )) + + (global $vtable-2 (ref $vtable) (struct.new $vtable + (ref.func $c) + (ref.func $d) + )) + + ;; CHECK: (export "export" (func $export)) + + ;; CHECK: (func $export (type $void) + ;; CHECK-NEXT: (call $b) + ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (export "export" (func $export)) + + ;; OPEN_WORLD: (func $export (type $void) + ;; OPEN_WORLD-NEXT: (call $b) + ;; OPEN_WORLD-NEXT: ) + (func $export (export "export") + ;; Call $b but not $a or $c + (call $b) + ) + + ;; CHECK: (func $a (type $void) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (func $a (type $void) + ;; OPEN_WORLD-NEXT: (call_ref $void + ;; OPEN_WORLD-NEXT: (struct.get $vtable 0 + ;; OPEN_WORLD-NEXT: (global.get $vtable) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + (func $a (type $void) + ;; $a calls field #0 in the vtable. + ;; + ;; Even though $a is in the vtable, it is dead, since the vtable is alive + ;; but there is no live read of field #0 - the only read is in here, which + ;; is basically an unreachable cycle that we can collect. We can empty out + ;; this function since it is dead, but we cannot remove it entirely due to + ;; the ref in the vtable. + ;; + ;; (In open world, however, we cannot do this, as we must assume reads of + ;; struct fields can occur outside of our view. That is, the vtable could be + ;; sent somewhere that reads field #0, which would make $a live.) + (call_ref $void + (struct.get $vtable 0 + (global.get $vtable) + ) + ) + ) + + ;; CHECK: (func $b (type $void) + ;; CHECK-NEXT: (call_ref $void + ;; CHECK-NEXT: (struct.get $vtable 1 + ;; CHECK-NEXT: (global.get $vtable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (func $b (type $void) + ;; OPEN_WORLD-NEXT: (call_ref $void + ;; OPEN_WORLD-NEXT: (struct.get $vtable 1 + ;; OPEN_WORLD-NEXT: (global.get $vtable) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + (func $b (type $void) + ;; $b calls field #1 in the vtable. + ;; + ;; As $b is called from the export, this function is not dead. + (call_ref $void + (struct.get $vtable 1 + (global.get $vtable) + ) + ) + ) + + (func $c (type $void) + ;; $c is parallel to $a, but using vtable-2, which has no other references, + ;; so this is dead like $a, and can be removed entirely. + (call_ref $void + (struct.get $vtable 0 + (global.get $vtable-2) + ) + ) + ) + + (func $d (type $void) + ;; $d is parallel to $b, but using vtable-2, which has no other references. + ;; This is dead, even though the struct type + index have a use (due to the + ;; other vtable) - there is no use of vtable-2 (except from unreachable + ;; places like here), so this cannot be reached. + (call_ref $void + (struct.get $vtable 0 + (global.get $vtable-2) + ) + ) + ) +) + +;; Test struct.news not in globals. +(module + ;; CHECK: (type $void (func)) + ;; OPEN_WORLD: (type $void (func)) + (type $void (func)) + + ;; CHECK: (type $vtable (struct (field (ref $void)) (field (ref $void)))) + ;; OPEN_WORLD: (type $vtable (struct (field (ref $void)) (field (ref $void)))) + (type $vtable (struct_subtype (field (ref $void)) (field (ref $void)) data)) + + ;; CHECK: (type $struct (struct (field (ref $vtable)) (field (ref $vtable)) (field (ref $vtable)) (field (ref $vtable)))) + ;; OPEN_WORLD: (type $struct (struct (field (ref $vtable)) (field (ref $vtable)) (field (ref $vtable)) (field (ref $vtable)))) + (type $struct (struct_subtype (field (ref $vtable)) (field (ref $vtable)) (field (ref $vtable)) (field (ref $vtable)) data)) + + ;; CHECK: (global $vtable (ref $vtable) (struct.new $vtable + ;; CHECK-NEXT: (ref.func $a) + ;; CHECK-NEXT: (ref.func $b) + ;; CHECK-NEXT: )) + ;; OPEN_WORLD: (global $vtable (ref $vtable) (struct.new $vtable + ;; OPEN_WORLD-NEXT: (ref.func $a) + ;; OPEN_WORLD-NEXT: (ref.func $b) + ;; OPEN_WORLD-NEXT: )) + (global $vtable (ref $vtable) (struct.new $vtable + (ref.func $a) + (ref.func $b) + )) + + ;; CHECK: (elem declare func $c $d $e $f $g $h $void) + + ;; CHECK: (export "func" (func $func)) + + ;; CHECK: (func $func (type $void) + ;; CHECK-NEXT: (local $ref (ref $struct)) + ;; CHECK-NEXT: (local $vtable (ref $vtable)) + ;; CHECK-NEXT: (local.set $ref + ;; CHECK-NEXT: (struct.new $struct + ;; CHECK-NEXT: (global.get $vtable) + ;; CHECK-NEXT: (struct.new $vtable + ;; CHECK-NEXT: (ref.func $c) + ;; CHECK-NEXT: (ref.func $d) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.tee $vtable + ;; CHECK-NEXT: (struct.new $vtable + ;; CHECK-NEXT: (ref.func $e) + ;; CHECK-NEXT: (ref.func $f) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.new $vtable + ;; CHECK-NEXT: (ref.func $g) + ;; CHECK-NEXT: (ref.func $h) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (block ;; (replaces something unreachable we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $struct 0 + ;; CHECK-NEXT: (local.get $ref) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $struct 1 + ;; CHECK-NEXT: (local.get $ref) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $struct 2 + ;; CHECK-NEXT: (local.get $ref) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $vtable 1 + ;; CHECK-NEXT: (local.get $vtable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call_ref $void + ;; CHECK-NEXT: (ref.func $void) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (elem declare func $c $d $e $f $g $h $void) + + ;; OPEN_WORLD: (export "func" (func $func)) + + ;; OPEN_WORLD: (func $func (type $void) + ;; OPEN_WORLD-NEXT: (local $ref (ref $struct)) + ;; OPEN_WORLD-NEXT: (local $vtable (ref $vtable)) + ;; OPEN_WORLD-NEXT: (local.set $ref + ;; OPEN_WORLD-NEXT: (struct.new $struct + ;; OPEN_WORLD-NEXT: (global.get $vtable) + ;; OPEN_WORLD-NEXT: (struct.new $vtable + ;; OPEN_WORLD-NEXT: (ref.func $c) + ;; OPEN_WORLD-NEXT: (ref.func $d) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: (local.tee $vtable + ;; OPEN_WORLD-NEXT: (struct.new $vtable + ;; OPEN_WORLD-NEXT: (ref.func $e) + ;; OPEN_WORLD-NEXT: (ref.func $f) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: (struct.new $vtable + ;; OPEN_WORLD-NEXT: (ref.func $g) + ;; OPEN_WORLD-NEXT: (ref.func $h) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: (drop + ;; OPEN_WORLD-NEXT: (block ;; (replaces something unreachable we can't emit) + ;; OPEN_WORLD-NEXT: (drop + ;; OPEN_WORLD-NEXT: (unreachable) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: (drop + ;; OPEN_WORLD-NEXT: (unreachable) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: (unreachable) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: (drop + ;; OPEN_WORLD-NEXT: (struct.get $struct 0 + ;; OPEN_WORLD-NEXT: (local.get $ref) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: (drop + ;; OPEN_WORLD-NEXT: (struct.get $struct 1 + ;; OPEN_WORLD-NEXT: (local.get $ref) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: (drop + ;; OPEN_WORLD-NEXT: (struct.get $struct 2 + ;; OPEN_WORLD-NEXT: (local.get $ref) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: (drop + ;; OPEN_WORLD-NEXT: (struct.get $vtable 1 + ;; OPEN_WORLD-NEXT: (local.get $vtable) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: (call_ref $void + ;; OPEN_WORLD-NEXT: (ref.func $void) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + (func $func (export "func") + (local $ref (ref $struct)) + (local $vtable (ref $vtable)) + + (local.set $ref + (struct.new $struct + ;; Init one field using the global vtable. + (global.get $vtable) + ;; Init another field using a vtable we create here - a nested + ;; struct.new inside this one. + (struct.new $vtable + (ref.func $c) + (ref.func $d) + ) + ;; Another nested one, but now there is a side effect. Everything here + ;; is considered to escape due to that. + (local.tee $vtable + (struct.new $vtable + (ref.func $e) + (ref.func $f) + ) + ) + ;; Another nested one. This field will not be read. + (struct.new $vtable + (ref.func $g) + (ref.func $h) + ) + ) + ) + + ;; Test that we do not assert on an unreachable struct.new. + (drop + (struct.new $vtable + (unreachable) + (unreachable) + ) + ) + + ;; Read from all fields of $struct except for the last. + (drop + (struct.get $struct 0 + (local.get $ref) + ) + ) + (drop + (struct.get $struct 1 + (local.get $ref) + ) + ) + (drop + (struct.get $struct 2 + (local.get $ref) + ) + ) + + ;; Read from field #1 of the vtable type, but not #0. + (drop + (struct.get $vtable 1 + (local.get $vtable) + ) + ) + + ;; Call something of type void so we don't eliminate them all instantly. + (call_ref $void + (ref.func $void) + ) + ) + + ;; CHECK: (func $void (type $void) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (func $void (type $void) + ;; OPEN_WORLD-NEXT: (nop) + ;; OPEN_WORLD-NEXT: ) + (func $void (type $void) + ;; Helper function. This is reached via a call_ref. + ) + + ;; CHECK: (func $a (type $void) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (func $a (type $void) + ;; OPEN_WORLD-NEXT: (nop) + ;; OPEN_WORLD-NEXT: ) + (func $a (type $void) + ;; This is unreachable (in closed world) since a reference to it only exists + ;; in field #0 of the vtable type, which is never read from. + ) + + ;; CHECK: (func $b (type $void) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (func $b (type $void) + ;; OPEN_WORLD-NEXT: (nop) + ;; OPEN_WORLD-NEXT: ) + (func $b (type $void) + ;; This is reachable. It is in field #1, which is read, and the global + ;; vtable is also read, and the type $void is call_reffed. + ) + + ;; CHECK: (func $c (type $void) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (func $c (type $void) + ;; OPEN_WORLD-NEXT: (nop) + ;; OPEN_WORLD-NEXT: ) + (func $c (type $void) + ;; Like $a, this is unreachable. That it is in a nested struct.new, and not + ;; in a global, does not matter. + ) + + ;; CHECK: (func $d (type $void) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (func $d (type $void) + ;; OPEN_WORLD-NEXT: (nop) + ;; OPEN_WORLD-NEXT: ) + (func $d (type $void) + ;; Like $b, this is reachable. That it is in a nested struct.new, and not + ;; in a global, does not matter. + ) + + ;; CHECK: (func $e (type $void) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (func $e (type $void) + ;; OPEN_WORLD-NEXT: (nop) + ;; OPEN_WORLD-NEXT: ) + (func $e (type $void) + ;; Side effects on the struct field are not enough to make this reachable: + ;; there is a tee on the struct.new we are in, but field #0 is still not + ;; read from the relevant struct. + ) + + ;; CHECK: (func $f (type $void) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (func $f (type $void) + ;; OPEN_WORLD-NEXT: (nop) + ;; OPEN_WORLD-NEXT: ) + (func $f (type $void) + ;; Like $b, this is reachable (the tee does not matter). + ) + + ;; CHECK: (func $g (type $void) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (func $g (type $void) + ;; OPEN_WORLD-NEXT: (nop) + ;; OPEN_WORLD-NEXT: ) + (func $g (type $void) + ;; This is in a struct written to a field that is never read in $struct, so + ;; it is unreachable. + ) + + ;; CHECK: (func $h (type $void) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (func $h (type $void) + ;; OPEN_WORLD-NEXT: (nop) + ;; OPEN_WORLD-NEXT: ) + (func $h (type $void) + ;; This is in a struct written to a field that is never read in $struct, so + ;; it is unreachable. + ) +) + +;; Test side effects causing a value to "leak." +(module + ;; CHECK: (type $void (func)) + ;; OPEN_WORLD: (type $void (func)) + (type $void (func)) + + ;; CHECK: (type $vtable (struct (field (ref $void)) (field (ref $void)))) + ;; OPEN_WORLD: (type $vtable (struct (field (ref $void)) (field (ref $void)))) + (type $vtable (struct_subtype (field (ref $void)) (field (ref $void)) data)) + + ;; CHECK: (elem declare func $a $b $void) + + ;; CHECK: (export "func" (func $func)) + + ;; CHECK: (func $func (type $void) + ;; CHECK-NEXT: (local $vtable (ref $vtable)) + ;; CHECK-NEXT: (local $void (ref $void)) + ;; CHECK-NEXT: (local.set $vtable + ;; CHECK-NEXT: (struct.new $vtable + ;; CHECK-NEXT: (ref.func $a) + ;; CHECK-NEXT: (local.tee $void + ;; CHECK-NEXT: (ref.func $b) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call_ref $void + ;; CHECK-NEXT: (ref.func $void) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (elem declare func $a $b $void) + + ;; OPEN_WORLD: (export "func" (func $func)) + + ;; OPEN_WORLD: (func $func (type $void) + ;; OPEN_WORLD-NEXT: (local $vtable (ref $vtable)) + ;; OPEN_WORLD-NEXT: (local $void (ref $void)) + ;; OPEN_WORLD-NEXT: (local.set $vtable + ;; OPEN_WORLD-NEXT: (struct.new $vtable + ;; OPEN_WORLD-NEXT: (ref.func $a) + ;; OPEN_WORLD-NEXT: (local.tee $void + ;; OPEN_WORLD-NEXT: (ref.func $b) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: (call_ref $void + ;; OPEN_WORLD-NEXT: (ref.func $void) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + (func $func (export "func") + (local $vtable (ref $vtable)) + (local $void (ref $void)) + + ;; Init one field using a tee, and one normally. + (local.set $vtable + (struct.new $vtable + (ref.func $a) + (local.tee $void + (ref.func $b) + ) + ) + ) + + ;; Call the type so it is reachable. + (call_ref $void + (ref.func $void) + ) + ) + + ;; CHECK: (func $void (type $void) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (func $void (type $void) + ;; OPEN_WORLD-NEXT: (nop) + ;; OPEN_WORLD-NEXT: ) + (func $void (type $void) + ;; Helper function. This is reached via a call_ref. + ) + + ;; CHECK: (func $a (type $void) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (func $a (type $void) + ;; OPEN_WORLD-NEXT: (nop) + ;; OPEN_WORLD-NEXT: ) + (func $a (type $void) + ;; This is unreachable (in closed world) because we have no reads from the + ;; struct field it is written in. + ) + + ;; CHECK: (func $b (type $void) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (func $b (type $void) + ;; OPEN_WORLD-NEXT: (nop) + ;; OPEN_WORLD-NEXT: ) + (func $b (type $void) + ;; The local.tee makes this reachable: the value is not known to only reside + ;; in the struct field, so we must assume it can be used even if the struct + ;; field is not. + ) +) + +;; Cycles. +(module + (rec + ;; CHECK: (type $vtable-func (func (param (ref $vtable)))) + ;; OPEN_WORLD: (type $vtable-func (func (param (ref $vtable)))) + (type $vtable-func (func (param (ref $vtable)))) + ;; CHECK: (type $vtable (struct (field (ref $vtable-func)) (field (ref $vtable-func)))) + ;; OPEN_WORLD: (type $vtable (struct (field (ref $vtable-func)) (field (ref $vtable-func)))) + (type $vtable (struct_subtype (field (ref $vtable-func)) (field (ref $vtable-func)) data)) + ) + + ;; CHECK: (type $none_=>_none (func)) + + ;; CHECK: (elem declare func $a $b $c $d) + + ;; CHECK: (export "func" (func $func)) + + ;; CHECK: (func $func (type $none_=>_none) + ;; CHECK-NEXT: (call_ref $vtable-func + ;; CHECK-NEXT: (struct.new $vtable + ;; CHECK-NEXT: (ref.func $a) + ;; CHECK-NEXT: (ref.func $b) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (struct.get $vtable 0 + ;; CHECK-NEXT: (struct.new $vtable + ;; CHECK-NEXT: (ref.func $a) + ;; CHECK-NEXT: (ref.func $b) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $vtable 0 + ;; CHECK-NEXT: (struct.new $vtable + ;; CHECK-NEXT: (ref.func $c) + ;; CHECK-NEXT: (ref.func $d) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (type $none_=>_none (func)) + + ;; OPEN_WORLD: (elem declare func $a $b $c $d) + + ;; OPEN_WORLD: (export "func" (func $func)) + + ;; OPEN_WORLD: (func $func (type $none_=>_none) + ;; OPEN_WORLD-NEXT: (call_ref $vtable-func + ;; OPEN_WORLD-NEXT: (struct.new $vtable + ;; OPEN_WORLD-NEXT: (ref.func $a) + ;; OPEN_WORLD-NEXT: (ref.func $b) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: (struct.get $vtable 0 + ;; OPEN_WORLD-NEXT: (struct.new $vtable + ;; OPEN_WORLD-NEXT: (ref.func $a) + ;; OPEN_WORLD-NEXT: (ref.func $b) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: (drop + ;; OPEN_WORLD-NEXT: (struct.get $vtable 0 + ;; OPEN_WORLD-NEXT: (struct.new $vtable + ;; OPEN_WORLD-NEXT: (ref.func $c) + ;; OPEN_WORLD-NEXT: (ref.func $d) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + (func $func (export "func") + ;; Read field 0, and call it. + (call_ref $vtable-func + (struct.new $vtable + (ref.func $a) + (ref.func $b) + ) + (struct.get $vtable 0 + ;; Duplicate the first vtable. + (struct.new $vtable + (ref.func $a) + (ref.func $b) + ) + ) + ) + + ;; Again, read field #0. No need to call it here (the call before makes the + ;; type used). + (drop + (struct.get $vtable 0 + ;; Make a new vtable with new funcs. + (struct.new $vtable + (ref.func $c) + (ref.func $d) + ) + ) + ) + ) + + ;; CHECK: (func $a (type $vtable-func) (param $vtable (ref $vtable)) + ;; CHECK-NEXT: (call_ref $vtable-func + ;; CHECK-NEXT: (local.get $vtable) + ;; CHECK-NEXT: (struct.get $vtable 0 + ;; CHECK-NEXT: (local.get $vtable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (func $a (type $vtable-func) (param $vtable (ref $vtable)) + ;; OPEN_WORLD-NEXT: (call_ref $vtable-func + ;; OPEN_WORLD-NEXT: (local.get $vtable) + ;; OPEN_WORLD-NEXT: (struct.get $vtable 0 + ;; OPEN_WORLD-NEXT: (local.get $vtable) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + (func $a (type $vtable-func) (param $vtable (ref $vtable)) + ;; $a calls $a or $c (using field #0). + ;; $a is reached from $func, so it is reachable. + (call_ref $vtable-func + (local.get $vtable) + (struct.get $vtable 0 + (local.get $vtable) + ) + ) + ) + + ;; CHECK: (func $b (type $vtable-func) (param $vtable (ref $vtable)) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (func $b (type $vtable-func) (param $vtable (ref $vtable)) + ;; OPEN_WORLD-NEXT: (call_ref $vtable-func + ;; OPEN_WORLD-NEXT: (local.get $vtable) + ;; OPEN_WORLD-NEXT: (struct.get $vtable 1 + ;; OPEN_WORLD-NEXT: (local.get $vtable) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + (func $b (type $vtable-func) (param $vtable (ref $vtable)) + ;; $b calls $b or $d (using field #1). + ;; But $b is not reached from $func, so it remains unreachable in closed + ;; world. + (call_ref $vtable-func + (local.get $vtable) + (struct.get $vtable 1 + (local.get $vtable) + ) + ) + ) + + ;; CHECK: (func $c (type $vtable-func) (param $vtable (ref $vtable)) + ;; CHECK-NEXT: (call_ref $vtable-func + ;; CHECK-NEXT: (local.get $vtable) + ;; CHECK-NEXT: (struct.get $vtable 0 + ;; CHECK-NEXT: (local.get $vtable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (func $c (type $vtable-func) (param $vtable (ref $vtable)) + ;; OPEN_WORLD-NEXT: (call_ref $vtable-func + ;; OPEN_WORLD-NEXT: (local.get $vtable) + ;; OPEN_WORLD-NEXT: (struct.get $vtable 0 + ;; OPEN_WORLD-NEXT: (local.get $vtable) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + (func $c (type $vtable-func) (param $vtable (ref $vtable)) + ;; $c forms a cycle with $a. + (call_ref $vtable-func + (local.get $vtable) + (struct.get $vtable 0 + (local.get $vtable) + ) + ) + ) + + ;; CHECK: (func $d (type $vtable-func) (param $vtable (ref $vtable)) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (func $d (type $vtable-func) (param $vtable (ref $vtable)) + ;; OPEN_WORLD-NEXT: (call_ref $vtable-func + ;; OPEN_WORLD-NEXT: (local.get $vtable) + ;; OPEN_WORLD-NEXT: (struct.get $vtable 1 + ;; OPEN_WORLD-NEXT: (local.get $vtable) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + (func $d (type $vtable-func) (param $vtable (ref $vtable)) + ;; $d forms a cycle with $b. + (call_ref $vtable-func + (local.get $vtable) + (struct.get $vtable 1 + (local.get $vtable) + ) + ) + ) +) + +;; Subtyping of struct reads. +(module + ;; CHECK: (type $void (func)) + ;; OPEN_WORLD: (type $void (func)) + (type $void (func)) + + ;; CHECK: (type $struct (struct (field funcref))) + ;; OPEN_WORLD: (type $struct (struct (field funcref))) + (type $struct (struct (field funcref))) + + ;; CHECK: (type $substruct (struct_subtype (field funcref) $struct)) + ;; OPEN_WORLD: (type $substruct (struct_subtype (field funcref) $struct)) + (type $substruct (struct_subtype (field funcref) $struct)) + + ;; CHECK: (type $subsubstruct (struct_subtype (field funcref) $substruct)) + ;; OPEN_WORLD: (type $subsubstruct (struct_subtype (field funcref) $substruct)) + (type $subsubstruct (struct_subtype (field funcref) $substruct)) + + ;; CHECK: (global $g (ref $struct) (struct.new $struct + ;; CHECK-NEXT: (ref.func $f) + ;; CHECK-NEXT: )) + ;; OPEN_WORLD: (global $g (ref $struct) (struct.new $struct + ;; OPEN_WORLD-NEXT: (ref.func $f) + ;; OPEN_WORLD-NEXT: )) + (global $g (ref $struct) (struct.new $struct + (ref.func $f) + )) + + ;; CHECK: (global $subg (ref $substruct) (struct.new $substruct + ;; CHECK-NEXT: (ref.func $subf) + ;; CHECK-NEXT: )) + ;; OPEN_WORLD: (global $subg (ref $substruct) (struct.new $substruct + ;; OPEN_WORLD-NEXT: (ref.func $subf) + ;; OPEN_WORLD-NEXT: )) + (global $subg (ref $substruct) (struct.new $substruct + (ref.func $subf) + )) + + ;; CHECK: (global $subsubg (ref $subsubstruct) (struct.new $subsubstruct + ;; CHECK-NEXT: (ref.func $subsubf) + ;; CHECK-NEXT: )) + ;; OPEN_WORLD: (global $subsubg (ref $subsubstruct) (struct.new $subsubstruct + ;; OPEN_WORLD-NEXT: (ref.func $subsubf) + ;; OPEN_WORLD-NEXT: )) + (global $subsubg (ref $subsubstruct) (struct.new $subsubstruct + (ref.func $subsubf) + )) + + ;; CHECK: (elem declare func $func) + + ;; CHECK: (export "func" (func $func)) + + ;; CHECK: (func $func (type $void) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $g) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $subg) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $subsubg) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $substruct 0 + ;; CHECK-NEXT: (block (result (ref $substruct)) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call_ref $void + ;; CHECK-NEXT: (ref.func $func) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (elem declare func $func) + + ;; OPEN_WORLD: (export "func" (func $func)) + + ;; OPEN_WORLD: (func $func (type $void) + ;; OPEN_WORLD-NEXT: (drop + ;; OPEN_WORLD-NEXT: (global.get $g) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: (drop + ;; OPEN_WORLD-NEXT: (global.get $subg) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: (drop + ;; OPEN_WORLD-NEXT: (global.get $subsubg) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: (drop + ;; OPEN_WORLD-NEXT: (struct.get $substruct 0 + ;; OPEN_WORLD-NEXT: (block (result (ref $substruct)) + ;; OPEN_WORLD-NEXT: (unreachable) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: (call_ref $void + ;; OPEN_WORLD-NEXT: (ref.func $func) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + (func $func (export "func") + ;; Refer to the globals. + (drop + (global.get $g) + ) + (drop + (global.get $subg) + ) + (drop + (global.get $subsubg) + ) + + ;; Read from $substruct's field, but not its super or subtypes. + (drop + (struct.get $substruct 0 + (block (result (ref $substruct)) + (unreachable) + ) + ) + ) + + ;; Call the function type to allow functions to be used. + (call_ref $void + (ref.func $func) + ) + ) + + (func $void (type $void) + ;; Helper function. This is reached via a call_ref. + ) + + ;; CHECK: (func $f (type $void) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (func $f (type $void) + ;; OPEN_WORLD-NEXT: (nop) + ;; OPEN_WORLD-NEXT: ) + (func $f (type $void) + ;; This is unreachable in closed world. The global it is in has a reference + ;; but the struct there has no reads of its field. + ) + + ;; CHECK: (func $subf (type $void) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (func $subf (type $void) + ;; OPEN_WORLD-NEXT: (nop) + ;; OPEN_WORLD-NEXT: ) + (func $subf (type $void) + ;; There is a read of $substruct's field, which makes this reachable. + ) + + ;; CHECK: (func $subsubf (type $void) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (func $subsubf (type $void) + ;; OPEN_WORLD-NEXT: (nop) + ;; OPEN_WORLD-NEXT: ) + (func $subsubf (type $void) + ;; There is a read of $substruct's field, which may read from any subtype, + ;; which makes this reachable. + ) +) + +;; Test references to global function references. +(module + ;; CHECK: (type $void (func)) + ;; OPEN_WORLD: (type $void (func)) + (type $void (func)) + + ;; CHECK: (type $A (struct (field funcref))) + ;; OPEN_WORLD: (type $A (struct (field funcref))) + (type $A (struct (field funcref))) + + ;; CHECK: (global $g1 (ref func) (ref.func $f1)) + ;; OPEN_WORLD: (global $g1 (ref func) (ref.func $f1)) + (global $g1 (ref func) (ref.func $f1)) + + ;; CHECK: (global $g2 (ref func) (ref.func $f2)) + ;; OPEN_WORLD: (global $g2 (ref func) (ref.func $f2)) + (global $g2 (ref func) (ref.func $f2)) + + ;; CHECK: (elem declare func $func) + + ;; CHECK: (export "func" (func $func)) + + ;; CHECK: (func $func (type $void) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (global.get $g1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $A + ;; CHECK-NEXT: (global.get $g2) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call_ref $void + ;; CHECK-NEXT: (ref.func $func) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (elem declare func $func) + + ;; OPEN_WORLD: (export "func" (func $func)) + + ;; OPEN_WORLD: (func $func (type $void) + ;; OPEN_WORLD-NEXT: (drop + ;; OPEN_WORLD-NEXT: (global.get $g1) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: (drop + ;; OPEN_WORLD-NEXT: (struct.new $A + ;; OPEN_WORLD-NEXT: (global.get $g2) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: (call_ref $void + ;; OPEN_WORLD-NEXT: (ref.func $func) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + (func $func (export "func") + ;; Refer to $g1 directly. + (drop + (global.get $g1) + ) + ;; Refer to $g2 from a struct field that is never read. + (drop + (struct.new $A + (global.get $g2) + ) + ) + + ;; Call the function type to allow functions to be used. + (call_ref $void + (ref.func $func) + ) + ) + + (func $void (type $void) + ;; Helper function. This is reached via a call_ref. + ) + + ;; CHECK: (func $f1 (type $void) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (func $f1 (type $void) + ;; OPEN_WORLD-NEXT: (nop) + ;; OPEN_WORLD-NEXT: ) + (func $f1 (type $void) + ;; The global containing this function's reference is used. + ) + + ;; CHECK: (func $f2 (type $void) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (func $f2 (type $void) + ;; OPEN_WORLD-NEXT: (nop) + ;; OPEN_WORLD-NEXT: ) + (func $f2 (type $void) + ;; This is unreachable in closed world as the global is referred to from a + ;; struct field that is never read from. + ) +) + +;; As above, but now the globals are struct.news. +(module + ;; CHECK: (type $A (struct (field funcref))) + + ;; CHECK: (type $void (func)) + ;; OPEN_WORLD: (type $A (struct (field funcref))) + + ;; OPEN_WORLD: (type $void (func)) + (type $void (func)) + + (type $A (struct (field funcref))) + + ;; CHECK: (type $B (struct (field (ref $A)))) + ;; OPEN_WORLD: (type $B (struct (field (ref $A)))) + (type $B (struct (field (ref $A)))) + + ;; CHECK: (global $g (ref $A) (struct.new $A + ;; CHECK-NEXT: (ref.func $f) + ;; CHECK-NEXT: )) + ;; OPEN_WORLD: (global $g (ref $A) (struct.new $A + ;; OPEN_WORLD-NEXT: (ref.func $f) + ;; OPEN_WORLD-NEXT: )) + (global $g (ref $A) (struct.new $A + (ref.func $f) + )) + + ;; CHECK: (elem declare func $func) + + ;; CHECK: (export "func" (func $func)) + + ;; CHECK: (func $func (type $void) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $B + ;; CHECK-NEXT: (global.get $g) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call_ref $void + ;; CHECK-NEXT: (ref.func $func) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $A 0 + ;; CHECK-NEXT: (block (result (ref $A)) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (elem declare func $func) + + ;; OPEN_WORLD: (export "func" (func $func)) + + ;; OPEN_WORLD: (func $func (type $void) + ;; OPEN_WORLD-NEXT: (drop + ;; OPEN_WORLD-NEXT: (struct.new $B + ;; OPEN_WORLD-NEXT: (global.get $g) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: (call_ref $void + ;; OPEN_WORLD-NEXT: (ref.func $func) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: (drop + ;; OPEN_WORLD-NEXT: (struct.get $A 0 + ;; OPEN_WORLD-NEXT: (block (result (ref $A)) + ;; OPEN_WORLD-NEXT: (unreachable) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + (func $func (export "func") + ;; Refer to $g from a struct field that is never read. + (drop + (struct.new $B + (global.get $g) + ) + ) + + ;; Call the function type to allow functions to be used. + (call_ref $void + (ref.func $func) + ) + + ;; Read $A's field. + (drop + (struct.get $A 0 + (block (result (ref $A)) + (unreachable) + ) + ) + ) + ) + + (func $void (type $void) + ;; Helper function. This is reached via a call_ref. + ) + + ;; CHECK: (func $f (type $void) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (func $f (type $void) + ;; OPEN_WORLD-NEXT: (nop) + ;; OPEN_WORLD-NEXT: ) + (func $f (type $void) + ;; This is unreachable in closed world since $B's field is not read, so the + ;; global it is in is only referenced and not used. + ) +) + +;; As above, but read $B's field. Now $f is reachable. +(module + ;; CHECK: (type $A (struct (field funcref))) + + ;; CHECK: (type $void (func)) + ;; OPEN_WORLD: (type $A (struct (field funcref))) + + ;; OPEN_WORLD: (type $void (func)) + (type $void (func)) + + (type $A (struct (field funcref))) + + ;; CHECK: (type $B (struct (field (ref $A)))) + ;; OPEN_WORLD: (type $B (struct (field (ref $A)))) + (type $B (struct (field (ref $A)))) + + ;; CHECK: (global $g (ref $A) (struct.new $A + ;; CHECK-NEXT: (ref.func $f) + ;; CHECK-NEXT: )) + ;; OPEN_WORLD: (global $g (ref $A) (struct.new $A + ;; OPEN_WORLD-NEXT: (ref.func $f) + ;; OPEN_WORLD-NEXT: )) + (global $g (ref $A) (struct.new $A + (ref.func $f) + )) + + ;; CHECK: (elem declare func $func) + + ;; CHECK: (export "func" (func $func)) + + ;; CHECK: (func $func (type $void) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.new $B + ;; CHECK-NEXT: (global.get $g) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (call_ref $void + ;; CHECK-NEXT: (ref.func $func) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $A 0 + ;; CHECK-NEXT: (block (result (ref $A)) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $B 0 + ;; CHECK-NEXT: (block (result (ref $B)) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (elem declare func $func) + + ;; OPEN_WORLD: (export "func" (func $func)) + + ;; OPEN_WORLD: (func $func (type $void) + ;; OPEN_WORLD-NEXT: (drop + ;; OPEN_WORLD-NEXT: (struct.new $B + ;; OPEN_WORLD-NEXT: (global.get $g) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: (call_ref $void + ;; OPEN_WORLD-NEXT: (ref.func $func) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: (drop + ;; OPEN_WORLD-NEXT: (struct.get $A 0 + ;; OPEN_WORLD-NEXT: (block (result (ref $A)) + ;; OPEN_WORLD-NEXT: (unreachable) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: (drop + ;; OPEN_WORLD-NEXT: (struct.get $B 0 + ;; OPEN_WORLD-NEXT: (block (result (ref $B)) + ;; OPEN_WORLD-NEXT: (unreachable) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + ;; OPEN_WORLD-NEXT: ) + (func $func (export "func") + (drop + (struct.new $B + (global.get $g) + ) + ) + + (call_ref $void + (ref.func $func) + ) + + (drop + (struct.get $A 0 + (block (result (ref $A)) + (unreachable) + ) + ) + ) + + ;; The change in this testcase is to read $B's field. + (drop + (struct.get $B 0 + (block (result (ref $B)) + (unreachable) + ) + ) + ) + ) + + (func $void (type $void) + ;; Helper function. This is reached via a call_ref. + ) + + ;; CHECK: (func $f (type $void) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + ;; OPEN_WORLD: (func $f (type $void) + ;; OPEN_WORLD-NEXT: (nop) + ;; OPEN_WORLD-NEXT: ) + (func $f (type $void) + ) +) |