From 463c92bb809b4c2d3d853bf5b54b265e0b16aa42 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 24 Oct 2025 13:19:35 -0700 Subject: [PATCH 1/5] work --- src/ir/possible-contents.cpp | 18 ++++++++++++++++ test/lit/passes/gufa-closed-open.wast | 31 +++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/src/ir/possible-contents.cpp b/src/ir/possible-contents.cpp index b9f2213fc9a..64579d53f9b 100644 --- a/src/ir/possible-contents.cpp +++ b/src/ir/possible-contents.cpp @@ -2413,6 +2413,24 @@ Flower::Flower(Module& wasm, const PassOptions& options) } } + // In open world, public heap types may be written to from the outside. + if (!options.closedWorld) { + for (auto type : ModuleUtils::getPublicHeapTypes(wasm)) { + if (type.isStruct()) { + auto& fields = type.getStruct().fields; + for (Index i = 0; i < fields.size(); i++) { + roots[DataLocation{type, i}] = PossibleContents::fromType(fields[i].type); + } + if (auto desc = type.getDescriptorType()) { + auto descType = Type(*desc, Nullable, Inexact); + roots[DataLocation{type, DataLocation::DescriptorIndex}] = PossibleContents::fromType(descType); + } + } else if (type.isArray()) { + roots[DataLocation{type, 0}] = PossibleContents::fromType(type.getArray().element.type); + } + } + } + #ifdef POSSIBLE_CONTENTS_DEBUG std::cout << "struct phase\n"; #endif diff --git a/test/lit/passes/gufa-closed-open.wast b/test/lit/passes/gufa-closed-open.wast index 689ee194f85..6188b61dd36 100644 --- a/test/lit/passes/gufa-closed-open.wast +++ b/test/lit/passes/gufa-closed-open.wast @@ -66,3 +66,34 @@ ) ) ) + +;; Import a struct in a global. +(module + ;; OPEND: (type $A (struct (field i32))) + ;; CLOSE: (type $A (struct (field i32))) + (type $A (struct (field i32))) + + ;; OPEND: (type $1 (func (result i32))) + + ;; OPEND: (import "primary" "global" (global $import (ref null $A))) + ;; CLOSE: (type $1 (func (result i32))) + + ;; CLOSE: (import "primary" "global" (global $import (ref null $A))) + (import "primary" "global" (global $import (ref null $A))) + + ;; OPEND: (func $read (type $1) (result i32) + ;; OPEND-NEXT: (unreachable) + ;; OPEND-NEXT: ) + ;; CLOSE: (func $read (type $1) (result i32) + ;; CLOSE-NEXT: (unreachable) + ;; CLOSE-NEXT: ) + (func $read (result i32) + ;; In closed world, we can assume no work happens on this struct outside, so + ;; it can only be null. In open world, we can do nothing here. + (struct.get $A 0 + (global.get $import) + ) + ) +) + +;; TODO: array and desc From 47d849a3509fff67ba85f316f5f033aaff12c567 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 24 Oct 2025 13:19:51 -0700 Subject: [PATCH 2/5] fix --- test/lit/passes/gufa-closed-open.wast | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/lit/passes/gufa-closed-open.wast b/test/lit/passes/gufa-closed-open.wast index 6188b61dd36..fc6fda8ad94 100644 --- a/test/lit/passes/gufa-closed-open.wast +++ b/test/lit/passes/gufa-closed-open.wast @@ -82,7 +82,9 @@ (import "primary" "global" (global $import (ref null $A))) ;; OPEND: (func $read (type $1) (result i32) - ;; OPEND-NEXT: (unreachable) + ;; OPEND-NEXT: (struct.get $A 0 + ;; OPEND-NEXT: (global.get $import) + ;; OPEND-NEXT: ) ;; OPEND-NEXT: ) ;; CLOSE: (func $read (type $1) (result i32) ;; CLOSE-NEXT: (unreachable) From bcb244c8f51a978ad921e158804b07d99574fbca Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 24 Oct 2025 13:23:34 -0700 Subject: [PATCH 3/5] work --- test/lit/passes/gufa-closed-open.wast | 70 ++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/test/lit/passes/gufa-closed-open.wast b/test/lit/passes/gufa-closed-open.wast index fc6fda8ad94..0269a0e39bc 100644 --- a/test/lit/passes/gufa-closed-open.wast +++ b/test/lit/passes/gufa-closed-open.wast @@ -98,4 +98,72 @@ ) ) -;; TODO: array and desc +;; Import an array. +(module + ;; OPEND: (type $A (array (mut i32))) + ;; CLOSE: (type $A (array (mut i32))) + (type $A (array (mut i32))) + + ;; OPEND: (type $1 (func (result i32))) + + ;; OPEND: (import "primary" "global" (global $import (ref null $A))) + ;; CLOSE: (type $1 (func (result i32))) + + ;; CLOSE: (import "primary" "global" (global $import (ref null $A))) + (import "primary" "global" (global $import (ref null $A))) + + ;; OPEND: (func $read (type $1) (result i32) + ;; OPEND-NEXT: (array.get $A + ;; OPEND-NEXT: (global.get $import) + ;; OPEND-NEXT: (i32.const 0) + ;; OPEND-NEXT: ) + ;; OPEND-NEXT: ) + ;; CLOSE: (func $read (type $1) (result i32) + ;; CLOSE-NEXT: (unreachable) + ;; CLOSE-NEXT: ) + (func $read (result i32) + ;; We do not optimize in open world. + (array.get $A + (global.get $import) + (i32.const 0) + ) + ) +) + +;; Import a struct with a descriptor. +(module + (rec + ;; OPEND: (rec + ;; OPEND-NEXT: (type $A (sub (descriptor $A.desc (struct)))) + ;; CLOSE: (rec + ;; CLOSE-NEXT: (type $A (sub (descriptor $A.desc (struct)))) + (type $A (sub (descriptor $A.desc (struct)))) + ;; OPEND: (type $A.desc (sub (describes $A (struct)))) + ;; CLOSE: (type $A.desc (sub (describes $A (struct)))) + (type $A.desc (sub (describes $A (struct)))) + ) + + ;; OPEND: (type $2 (func (result anyref))) + + ;; OPEND: (import "primary" "global" (global $import (ref null $A))) + ;; CLOSE: (type $2 (func (result anyref))) + + ;; CLOSE: (import "primary" "global" (global $import (ref null $A))) + (import "primary" "global" (global $import (ref null $A))) + + ;; OPEND: (func $read (type $2) (result anyref) + ;; OPEND-NEXT: (ref.get_desc $A + ;; OPEND-NEXT: (global.get $import) + ;; OPEND-NEXT: ) + ;; OPEND-NEXT: ) + ;; CLOSE: (func $read (type $2) (result anyref) + ;; CLOSE-NEXT: (unreachable) + ;; CLOSE-NEXT: ) + (func $read (result anyref) + ;; We do not optimize in open world. + (ref.get_desc $A + (global.get $import) + ) + ) +) + From 8c861c993d992faf730197a7d9589a979cba1dd9 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 24 Oct 2025 13:23:43 -0700 Subject: [PATCH 4/5] format --- src/ir/possible-contents.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/ir/possible-contents.cpp b/src/ir/possible-contents.cpp index 64579d53f9b..90df9ee1db5 100644 --- a/src/ir/possible-contents.cpp +++ b/src/ir/possible-contents.cpp @@ -2419,14 +2419,17 @@ Flower::Flower(Module& wasm, const PassOptions& options) if (type.isStruct()) { auto& fields = type.getStruct().fields; for (Index i = 0; i < fields.size(); i++) { - roots[DataLocation{type, i}] = PossibleContents::fromType(fields[i].type); + roots[DataLocation{type, i}] = + PossibleContents::fromType(fields[i].type); } if (auto desc = type.getDescriptorType()) { auto descType = Type(*desc, Nullable, Inexact); - roots[DataLocation{type, DataLocation::DescriptorIndex}] = PossibleContents::fromType(descType); + roots[DataLocation{type, DataLocation::DescriptorIndex}] = + PossibleContents::fromType(descType); } } else if (type.isArray()) { - roots[DataLocation{type, 0}] = PossibleContents::fromType(type.getArray().element.type); + roots[DataLocation{type, 0}] = + PossibleContents::fromType(type.getArray().element.type); } } } From 13c07e4e622b0006616bbbd6089a9b01f7476002 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 24 Oct 2025 13:48:16 -0700 Subject: [PATCH 5/5] Update existing test. Does not interfere with the point there --- test/lit/passes/gufa-cast-all-exact.wast | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/test/lit/passes/gufa-cast-all-exact.wast b/test/lit/passes/gufa-cast-all-exact.wast index 0a03f8930bd..f9a3450a72f 100644 --- a/test/lit/passes/gufa-cast-all-exact.wast +++ b/test/lit/passes/gufa-cast-all-exact.wast @@ -120,10 +120,22 @@ (import "" "" (global $exact (ref (exact $foo)))) ;; CHECK: (func $get (type $1) (result i32) - ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: (struct.get $foo 0 + ;; CHECK-NEXT: (select (result (ref null (exact $foo))) + ;; CHECK-NEXT: (global.get $exact) + ;; CHECK-NEXT: (ref.null none) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; NO_CD: (func $get (type $1) (result i32) - ;; NO_CD-NEXT: (unreachable) + ;; NO_CD-NEXT: (struct.get $foo 0 + ;; NO_CD-NEXT: (select (result (ref null (exact $foo))) + ;; NO_CD-NEXT: (global.get $exact) + ;; NO_CD-NEXT: (ref.null none) + ;; NO_CD-NEXT: (i32.const 0) + ;; NO_CD-NEXT: ) + ;; NO_CD-NEXT: ) ;; NO_CD-NEXT: ) (func $get (result i32) ;; Regression test for a bug where exactness was not preserved when combining @@ -220,20 +232,18 @@ (import "" "" (global $null-exact (ref null (exact $foo)))) ;; CHECK: (func $as-non-null (type $1) (result i32) - ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.get $foo 0 ;; CHECK-NEXT: (ref.as_non_null ;; CHECK-NEXT: (global.get $null-exact) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) ;; NO_CD: (func $as-non-null (type $1) (result i32) - ;; NO_CD-NEXT: (drop + ;; NO_CD-NEXT: (struct.get $foo 0 ;; NO_CD-NEXT: (ref.as_non_null ;; NO_CD-NEXT: (global.get $null-exact) ;; NO_CD-NEXT: ) ;; NO_CD-NEXT: ) - ;; NO_CD-NEXT: (unreachable) ;; NO_CD-NEXT: ) (func $as-non-null (result i32) (struct.get $foo 0