From 004d33f35cc6e5326f75bdd06ca3d4be3bc89f2b Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 16 Jan 2026 11:52:30 -0800 Subject: [PATCH 1/6] fix --- src/ir/possible-contents.cpp | 9 +++++++++ src/ir/subtypes.h | 7 +++++++ 2 files changed, 16 insertions(+) diff --git a/src/ir/possible-contents.cpp b/src/ir/possible-contents.cpp index 24e42cf1c42..e1a49a81d87 100644 --- a/src/ir/possible-contents.cpp +++ b/src/ir/possible-contents.cpp @@ -2243,6 +2243,9 @@ struct Flower { // For a non-full cone, we also reduce the depth as much as possible, so it is // equal to the maximum depth of an existing subtype. Index getNormalizedConeDepth(Type type, Index depth) { + // A max depth must be in the map (otherwise we would use the default 0, + // making it exact, almost certainly incorrectly). + assert(maxDepths.count(type.getHeapType())); return std::min(depth, maxDepths[type.getHeapType()]); } @@ -2639,6 +2642,12 @@ bool Flower::updateContents(LocationIndex locationIndex, // more later (we compute that at the end), so use a temp out var for that. bool worthSendingMoreTemp = true; filterExpressionContents(newContents, *exprLoc, worthSendingMoreTemp); + +#if defined(POSSIBLE_CONTENTS_DEBUG) && POSSIBLE_CONTENTS_DEBUG >= 2 + std::cout << " post-filtered exprLoc:\n"; + newContents.dump(std::cout, &wasm); + std::cout << '\n'; +#endif } else if (auto* globalLoc = std::get_if(&location)) { // Generic filtering. We do this both before and after. filterGlobalContents(newContents, *globalLoc); diff --git a/src/ir/subtypes.h b/src/ir/subtypes.h index 5c654ceb7be..296fc42228d 100644 --- a/src/ir/subtypes.h +++ b/src/ir/subtypes.h @@ -135,6 +135,7 @@ struct SubTypes { basicDepth = std::max(basicDepth, depths[type] + 1); } + // Fill in the basic types themselves. for (auto share : {Unshared, Shared}) { depths[HeapTypes::eq.getBasic(share)] = std::max(depths[HeapTypes::struct_.getBasic(share)], @@ -142,6 +143,12 @@ struct SubTypes { 1; depths[HeapTypes::any.getBasic(share)] = depths[HeapTypes::eq.getBasic(share)] + 1; + + depths[HeapTypes::i31.getBasic(share)] = 0; + + // Extern has string as a subtype. + depths[HeapTypes::ext.getBasic(share)] = 1; + depths[HeapTypes::string.getBasic(share)] = 0; } return depths; From 575b77a2b83fafafdba1e0742f6278186b554c0d Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 16 Jan 2026 11:53:43 -0800 Subject: [PATCH 2/6] prep --- test/lit/passes/gufa-refs.wast | 45 ++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/test/lit/passes/gufa-refs.wast b/test/lit/passes/gufa-refs.wast index 9907fd90c23..505b630dbf3 100644 --- a/test/lit/passes/gufa-refs.wast +++ b/test/lit/passes/gufa-refs.wast @@ -6198,3 +6198,48 @@ ) ) ) + + + + +;; maek nice + +(module + (type $0 (func (result externref))) + + (export "func_294_invoker" (func $3)) + + (func $2 (type $0) (result externref) + (string.const "") + ) + (func $3 + (call $9 + (ref.func $2) + ) + ) + (func $5 (type $0) (result externref) + (extern.convert_any + (ref.i31 + (i32.const 0) + ) + ) + ) + (func $9 (param $0 (ref $0)) + ;; This reference suggests that $0 may contain either $2 (from the call + ;; above) or $5, which means the return can be a string or an externalized + ;; i31. The cast may succeed, or it may fail. We should not be confused by + ;; the combination of PossibleContents(string) and PossibleContents(ext i31) - + ;; that combination is "anything", not "nothing". + (drop + (ref.func $5) + ) + (drop + (ref.cast (ref string) + (call_ref $0 + (local.get $0) + ) + ) + ) + ) +) + From 7d8fb1b268f06b2b0ec467ccefd7c63243288239 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 16 Jan 2026 12:07:41 -0800 Subject: [PATCH 3/6] fix --- src/ir/possible-contents.cpp | 5 ++-- test/gtest/type-builder.cpp | 3 +++ test/lit/passes/gufa-refs.wast | 45 ---------------------------------- 3 files changed, 6 insertions(+), 47 deletions(-) diff --git a/src/ir/possible-contents.cpp b/src/ir/possible-contents.cpp index e1a49a81d87..f07a42acf97 100644 --- a/src/ir/possible-contents.cpp +++ b/src/ir/possible-contents.cpp @@ -2243,10 +2243,11 @@ struct Flower { // For a non-full cone, we also reduce the depth as much as possible, so it is // equal to the maximum depth of an existing subtype. Index getNormalizedConeDepth(Type type, Index depth) { + auto iter = maxDepths.find(type.getHeapType()); // A max depth must be in the map (otherwise we would use the default 0, // making it exact, almost certainly incorrectly). - assert(maxDepths.count(type.getHeapType())); - return std::min(depth, maxDepths[type.getHeapType()]); + assert(iter != maxDepths.end()); + return std::min(depth, iter->second); } void normalizeConeType(PossibleContents& cone) { diff --git a/test/gtest/type-builder.cpp b/test/gtest/type-builder.cpp index da7f4c60f7c..52f5b220a3c 100644 --- a/test/gtest/type-builder.cpp +++ b/test/gtest/type-builder.cpp @@ -1604,6 +1604,9 @@ TEST_F(TypeTest, TestMaxStructDepths) { EXPECT_EQ(maxDepths[HeapType::struct_], Index(2)); EXPECT_EQ(maxDepths[HeapType::eq], Index(3)); EXPECT_EQ(maxDepths[HeapType::any], Index(4)); + EXPECT_EQ(maxDepths[HeapType::i31], Index(0)); + EXPECT_EQ(maxDepths[HeapType::ext], Index(1)); + EXPECT_EQ(maxDepths[HeapType::string], Index(0)); } TEST_F(TypeTest, TestMaxArrayDepths) { diff --git a/test/lit/passes/gufa-refs.wast b/test/lit/passes/gufa-refs.wast index 505b630dbf3..9907fd90c23 100644 --- a/test/lit/passes/gufa-refs.wast +++ b/test/lit/passes/gufa-refs.wast @@ -6198,48 +6198,3 @@ ) ) ) - - - - -;; maek nice - -(module - (type $0 (func (result externref))) - - (export "func_294_invoker" (func $3)) - - (func $2 (type $0) (result externref) - (string.const "") - ) - (func $3 - (call $9 - (ref.func $2) - ) - ) - (func $5 (type $0) (result externref) - (extern.convert_any - (ref.i31 - (i32.const 0) - ) - ) - ) - (func $9 (param $0 (ref $0)) - ;; This reference suggests that $0 may contain either $2 (from the call - ;; above) or $5, which means the return can be a string or an externalized - ;; i31. The cast may succeed, or it may fail. We should not be confused by - ;; the combination of PossibleContents(string) and PossibleContents(ext i31) - - ;; that combination is "anything", not "nothing". - (drop - (ref.func $5) - ) - (drop - (ref.cast (ref string) - (call_ref $0 - (local.get $0) - ) - ) - ) - ) -) - From c70df5a57c939083ea8c0746168bc968a965e94f Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 16 Jan 2026 13:03:28 -0800 Subject: [PATCH 4/6] exn --- src/ir/subtypes.h | 2 ++ test/gtest/type-builder.cpp | 1 + 2 files changed, 3 insertions(+) diff --git a/src/ir/subtypes.h b/src/ir/subtypes.h index 296fc42228d..d627a2e4fdb 100644 --- a/src/ir/subtypes.h +++ b/src/ir/subtypes.h @@ -145,10 +145,12 @@ struct SubTypes { depths[HeapTypes::eq.getBasic(share)] + 1; depths[HeapTypes::i31.getBasic(share)] = 0; + depths[HeapTypes::exn.getBasic(share)] = 0; // Extern has string as a subtype. depths[HeapTypes::ext.getBasic(share)] = 1; depths[HeapTypes::string.getBasic(share)] = 0; + } return depths; diff --git a/test/gtest/type-builder.cpp b/test/gtest/type-builder.cpp index 52f5b220a3c..3c52115cce5 100644 --- a/test/gtest/type-builder.cpp +++ b/test/gtest/type-builder.cpp @@ -1605,6 +1605,7 @@ TEST_F(TypeTest, TestMaxStructDepths) { EXPECT_EQ(maxDepths[HeapType::eq], Index(3)); EXPECT_EQ(maxDepths[HeapType::any], Index(4)); EXPECT_EQ(maxDepths[HeapType::i31], Index(0)); + EXPECT_EQ(maxDepths[HeapType::exn], Index(0)); EXPECT_EQ(maxDepths[HeapType::ext], Index(1)); EXPECT_EQ(maxDepths[HeapType::string], Index(0)); } From 37b07bc383f721dd8d14156942314647c93172ef Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 16 Jan 2026 15:46:20 -0800 Subject: [PATCH 5/6] fix --- src/ir/subtypes.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/ir/subtypes.h b/src/ir/subtypes.h index d627a2e4fdb..fa949e992c2 100644 --- a/src/ir/subtypes.h +++ b/src/ir/subtypes.h @@ -135,7 +135,7 @@ struct SubTypes { basicDepth = std::max(basicDepth, depths[type] + 1); } - // Fill in the basic types themselves. + // Fill in the other basic types. for (auto share : {Unshared, Shared}) { depths[HeapTypes::eq.getBasic(share)] = std::max(depths[HeapTypes::struct_.getBasic(share)], @@ -151,6 +151,11 @@ struct SubTypes { depths[HeapTypes::ext.getBasic(share)] = 1; depths[HeapTypes::string.getBasic(share)] = 0; + depths[HeapTypes::none.getBasic(share)] = 0; + depths[HeapTypes::noext.getBasic(share)] = 0; + depths[HeapTypes::nofunc.getBasic(share)] = 0; + depths[HeapTypes::nocont.getBasic(share)] = 0; + depths[HeapTypes::noexn.getBasic(share)] = 0; } return depths; From e6fa617f77f0c702c8e6515fd04cca1f486d5794 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 16 Jan 2026 16:01:09 -0800 Subject: [PATCH 6/6] test --- test/gtest/type-builder.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/gtest/type-builder.cpp b/test/gtest/type-builder.cpp index 3c52115cce5..d7ad960b185 100644 --- a/test/gtest/type-builder.cpp +++ b/test/gtest/type-builder.cpp @@ -1608,6 +1608,11 @@ TEST_F(TypeTest, TestMaxStructDepths) { EXPECT_EQ(maxDepths[HeapType::exn], Index(0)); EXPECT_EQ(maxDepths[HeapType::ext], Index(1)); EXPECT_EQ(maxDepths[HeapType::string], Index(0)); + EXPECT_EQ(maxDepths[HeapType::none], Index(0)); + EXPECT_EQ(maxDepths[HeapType::noext], Index(0)); + EXPECT_EQ(maxDepths[HeapType::nofunc], Index(0)); + EXPECT_EQ(maxDepths[HeapType::nocont], Index(0)); + EXPECT_EQ(maxDepths[HeapType::noexn], Index(0)); } TEST_F(TypeTest, TestMaxArrayDepths) {