From 2d540252e638be647a40cd15557305903cdc1f28 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 3 Apr 2026 16:25:16 -0700 Subject: [PATCH 1/6] go --- src/ir/possible-contents.cpp | 2 ++ src/ir/subtypes.h | 11 ++++++++--- src/passes/ConstantFieldPropagation.cpp | 16 +++++++--------- src/passes/RemoveUnusedModuleElements.cpp | 1 + src/tools/fuzzing/heap-types.cpp | 2 +- 5 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/ir/possible-contents.cpp b/src/ir/possible-contents.cpp index 10b01f986dc..f7b5212406a 100644 --- a/src/ir/possible-contents.cpp +++ b/src/ir/possible-contents.cpp @@ -3282,6 +3282,7 @@ void Flower::readFromData(Type declaredType, [&](HeapType type, Index depth) { connectDuringFlow(DataLocation{type, fieldIndex}, coneReadLocation); + return true; }); // TODO: we can end up with redundant links here if we see one cone first @@ -3351,6 +3352,7 @@ void Flower::writeToData(Expression* ref, cone.type.getHeapType(), normalizedDepth, [&](HeapType type, Index depth) { auto heapLoc = DataLocation{type, fieldIndex}; updateContents(heapLoc, valueContents); + return true; }); } diff --git a/src/ir/subtypes.h b/src/ir/subtypes.h index ae3a27878f5..57640818684 100644 --- a/src/ir/subtypes.h +++ b/src/ir/subtypes.h @@ -168,11 +168,14 @@ struct SubTypes { // Efficiently iterate on subtypes of a type, up to a particular depth (depth // 0 means not to traverse subtypes, etc.). The callback function receives - // (type, depth). + // (type, depth) and returns whether to continue the scan (i.e. if it returns + // false, we stop). template void iterSubTypes(HeapType type, Index depth, F func) const { // Start by traversing the type itself. - func(type, 0); + if (!func(type, 0)) { + return; + } if (depth == 0) { // Nothing else to scan. @@ -201,7 +204,9 @@ struct SubTypes { auto& currVec = *item.vec; assert(currDepth <= depth); for (auto type : currVec) { - func(type, currDepth); + if (!func(type, currDepth)) { + return; + } auto* subVec = &getImmediateSubTypes(type); if (currDepth + 1 <= depth && !subVec->empty()) { work.push_back({subVec, currDepth + 1}); diff --git a/src/passes/ConstantFieldPropagation.cpp b/src/passes/ConstantFieldPropagation.cpp index e2b75e88de8..3fa64ccdd23 100644 --- a/src/passes/ConstantFieldPropagation.cpp +++ b/src/passes/ConstantFieldPropagation.cpp @@ -332,27 +332,23 @@ struct FunctionOptimizer : public WalkerPass> { // as having failed. auto fail = false; auto handleType = [&](HeapType type, Index depth) { - if (fail) { - // TODO: Add a mechanism to halt |iterSubTypes| in the middle, as once - // we fail there is no point to further iterating. - return; - } + assert(!fail); auto iter = refTestInfos.find({type, Exact}); if (iter == refTestInfos.end()) { // This type has no allocations, so we can ignore it: it is abstract. - return; + return true; } auto value = iter->second[index]; if (!value.hasNoted()) { // Also abstract and ignorable. - return; + return true; } if (!value.isConstant()) { // The value here is not constant, so give up entirely. fail = true; - return; + return false; } // Consider the constant value compared to previous ones. @@ -376,9 +372,11 @@ struct FunctionOptimizer : public WalkerPass> { // the last, we've failed to find only two values. if (i == 1) { fail = true; - return; + return false; } } + + return true; }; subTypes.iterSubTypes(refHeapType, handleType); diff --git a/src/passes/RemoveUnusedModuleElements.cpp b/src/passes/RemoveUnusedModuleElements.cpp index 34418009cf4..8bba990af8d 100644 --- a/src/passes/RemoveUnusedModuleElements.cpp +++ b/src/passes/RemoveUnusedModuleElements.cpp @@ -496,6 +496,7 @@ struct Analyzer { } } unreadStructFieldExprMap.erase(subStructField); + return true; }); } } diff --git a/src/tools/fuzzing/heap-types.cpp b/src/tools/fuzzing/heap-types.cpp index 426a9b76a9c..23a594c3c0c 100644 --- a/src/tools/fuzzing/heap-types.cpp +++ b/src/tools/fuzzing/heap-types.cpp @@ -1001,7 +1001,7 @@ void Inhabitator::markNullable(FieldPos field) { // lambdas being unable to capture structured bindings. const size_t index = idx; subtypes.iterSubTypes( - curr, [&](HeapType type, Index) { nullables.insert({type, index}); }); + curr, [&](HeapType type, Index) { nullables.insert({type, index}); return true; }); break; } } From 88b936306526fa674cdd69d9b4d76712439e67df Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 3 Apr 2026 16:27:01 -0700 Subject: [PATCH 2/6] go --- src/ir/possible-contents.cpp | 1 + src/passes/ConstantFieldPropagation.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/ir/possible-contents.cpp b/src/ir/possible-contents.cpp index f7b5212406a..4dc6da9c9d9 100644 --- a/src/ir/possible-contents.cpp +++ b/src/ir/possible-contents.cpp @@ -881,6 +881,7 @@ struct InfoCollector info.links.push_back({SignatureResultLocation{subType, i}, ResultLocation{getFunction(), i}}); } + return true; }); } } diff --git a/src/passes/ConstantFieldPropagation.cpp b/src/passes/ConstantFieldPropagation.cpp index 3fa64ccdd23..fd8f82672b8 100644 --- a/src/passes/ConstantFieldPropagation.cpp +++ b/src/passes/ConstantFieldPropagation.cpp @@ -675,6 +675,7 @@ struct ConstantFieldPropagation : public Pass { if (readable[{sub, Exact}][dst.index].combine(val)) { applyCopiesFrom(sub, Exact, dst.index, val); } + return true; }); } else { // The copy destination is exact, so there are no subtypes to From d0e7d48dbc83178d15c6d3f3994ad4f999240e10 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 3 Apr 2026 16:29:42 -0700 Subject: [PATCH 3/6] format --- src/tools/fuzzing/heap-types.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/tools/fuzzing/heap-types.cpp b/src/tools/fuzzing/heap-types.cpp index 23a594c3c0c..e2b6b552623 100644 --- a/src/tools/fuzzing/heap-types.cpp +++ b/src/tools/fuzzing/heap-types.cpp @@ -1000,8 +1000,10 @@ void Inhabitator::markNullable(FieldPos field) { // this extra `index` variable once we have C++20. It's a workaround for // lambdas being unable to capture structured bindings. const size_t index = idx; - subtypes.iterSubTypes( - curr, [&](HeapType type, Index) { nullables.insert({type, index}); return true; }); + subtypes.iterSubTypes(curr, [&](HeapType type, Index) { + nullables.insert({type, index}); + return true; + }); break; } } From 732848ed37758b8d5e926fdf7385c113bf329c50 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 3 Apr 2026 16:34:59 -0700 Subject: [PATCH 4/6] fix --- test/gtest/type-builder.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/gtest/type-builder.cpp b/test/gtest/type-builder.cpp index a92f10d3ee0..bb1a4b9f963 100644 --- a/test/gtest/type-builder.cpp +++ b/test/gtest/type-builder.cpp @@ -1717,6 +1717,7 @@ TEST_F(TypeTest, TestIterSubTypes) { TypeDepths ret; subTypes.iterSubTypes(type, depth, [&](HeapType subType, Index depth) { ret.insert({subType, depth}); + return true; }); return ret; }; @@ -1729,6 +1730,23 @@ TEST_F(TypeTest, TestIterSubTypes) { EXPECT_EQ(getSubTypes(C, 0), TypeDepths({{C, 0}})); EXPECT_EQ(getSubTypes(C, 1), TypeDepths({{C, 0}, {D, 1}})); EXPECT_EQ(getSubTypes(C, 2), TypeDepths({{C, 0}, {D, 1}})); + + // When the iteration function returns |false|, we stop. + int count = 0; + subTypes.iterSubTypes(A, 3, [&](HeapType subType, Index depth) { + count++; + // Stop after the second increment. + return count != 2; + }); + EXPECT_EQ(count, 2); + + // If we return true, we iterate through all four. + count = 0; + subTypes.iterSubTypes(A, 3, [&](HeapType subType, Index depth) { + count++; + return true; + }); + EXPECT_EQ(count, 4); } // Test supertypes From afe81f6868a052e7dd3b8dcf1be477c158bfccab Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 3 Apr 2026 16:41:56 -0700 Subject: [PATCH 5/6] yikes --- test/gtest/type-builder.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/gtest/type-builder.cpp b/test/gtest/type-builder.cpp index bb1a4b9f963..e6ce3bce61b 100644 --- a/test/gtest/type-builder.cpp +++ b/test/gtest/type-builder.cpp @@ -1695,6 +1695,7 @@ TEST_F(TypeTest, TestIterSubTypes) { HeapType A, B, C, D; { TypeBuilder builder(4); + builder.createRecGroup(0, 4); builder[0].setOpen() = Struct(); builder[1].setOpen().subTypeOf(builder[0]) = Struct(); builder[2].setOpen().subTypeOf(builder[0]) = Struct(); From 5d0306a2801c86fdfe02920689480d59bc9e5b94 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 6 Apr 2026 11:52:10 -0700 Subject: [PATCH 6/6] feedback:simplify --- src/ir/subtypes.h | 17 ++++++++++------- src/passes/ConstantFieldPropagation.cpp | 13 ++++--------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/ir/subtypes.h b/src/ir/subtypes.h index 57640818684..912d61f878d 100644 --- a/src/ir/subtypes.h +++ b/src/ir/subtypes.h @@ -168,18 +168,19 @@ struct SubTypes { // Efficiently iterate on subtypes of a type, up to a particular depth (depth // 0 means not to traverse subtypes, etc.). The callback function receives - // (type, depth) and returns whether to continue the scan (i.e. if it returns - // false, we stop). + // (type, depth) and returns whether to continue the scan, i.e. if it returns + // false, we stop. Returns the last value returned to it, that is, returns + // true if we did not stop early, and false if we did. template - void iterSubTypes(HeapType type, Index depth, F func) const { + bool iterSubTypes(HeapType type, Index depth, F func) const { // Start by traversing the type itself. if (!func(type, 0)) { - return; + return false; } if (depth == 0) { // Nothing else to scan. - return; + return true; } // getImmediateSubTypes() returns vectors of subtypes, so for efficiency @@ -205,7 +206,7 @@ struct SubTypes { assert(currDepth <= depth); for (auto type : currVec) { if (!func(type, currDepth)) { - return; + return false; } auto* subVec = &getImmediateSubTypes(type); if (currDepth + 1 <= depth && !subVec->empty()) { @@ -213,10 +214,12 @@ struct SubTypes { } } } + + return true; } // As above, but iterate to the maximum depth. - template void iterSubTypes(HeapType type, F func) const { + template bool iterSubTypes(HeapType type, F func) const { return iterSubTypes(type, std::numeric_limits::max(), func); } diff --git a/src/passes/ConstantFieldPropagation.cpp b/src/passes/ConstantFieldPropagation.cpp index fd8f82672b8..0063a8d3a69 100644 --- a/src/passes/ConstantFieldPropagation.cpp +++ b/src/passes/ConstantFieldPropagation.cpp @@ -328,12 +328,9 @@ struct FunctionOptimizer : public WalkerPass> { } values[2]; // Handle one of the subtypes of the relevant type. We check what value it - // has for the field, and update |values|. If we hit a problem, we mark us - // as having failed. - auto fail = false; + // has for the field, and update |values|. If we hit a problem, we stop + // early. auto handleType = [&](HeapType type, Index depth) { - assert(!fail); - auto iter = refTestInfos.find({type, Exact}); if (iter == refTestInfos.end()) { // This type has no allocations, so we can ignore it: it is abstract. @@ -347,7 +344,6 @@ struct FunctionOptimizer : public WalkerPass> { } if (!value.isConstant()) { // The value here is not constant, so give up entirely. - fail = true; return false; } @@ -371,16 +367,15 @@ struct FunctionOptimizer : public WalkerPass> { // least, we can do that if there is another iteration: If it's already // the last, we've failed to find only two values. if (i == 1) { - fail = true; return false; } } return true; }; - subTypes.iterSubTypes(refHeapType, handleType); - if (fail) { + // If we stopped early, we hit a problem and failed. + if (!subTypes.iterSubTypes(refHeapType, handleType)) { return; }