Skip to content

Commit a3ac1d9

Browse files
authored
[NFC] Allow SubTypes::iterSubTypes to stop early (#8573)
In ConstantFieldPropagation this is important: we scan subtypes to check if they all have 2 possible values we can `ref.test` between. In the common case there are many values and we can stop early. This makes the pass 4.2x faster on a large Dart testcase, and `-O3` overall 3.5% faster.
1 parent 6780d4b commit a3ac1d9

File tree

6 files changed

+52
-25
lines changed

6 files changed

+52
-25
lines changed

src/ir/possible-contents.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -881,6 +881,7 @@ struct InfoCollector
881881
info.links.push_back({SignatureResultLocation{subType, i},
882882
ResultLocation{getFunction(), i}});
883883
}
884+
return true;
884885
});
885886
}
886887
}
@@ -3282,6 +3283,7 @@ void Flower::readFromData(Type declaredType,
32823283
[&](HeapType type, Index depth) {
32833284
connectDuringFlow(DataLocation{type, fieldIndex},
32843285
coneReadLocation);
3286+
return true;
32853287
});
32863288

32873289
// TODO: we can end up with redundant links here if we see one cone first
@@ -3351,6 +3353,7 @@ void Flower::writeToData(Expression* ref,
33513353
cone.type.getHeapType(), normalizedDepth, [&](HeapType type, Index depth) {
33523354
auto heapLoc = DataLocation{type, fieldIndex};
33533355
updateContents(heapLoc, valueContents);
3356+
return true;
33543357
});
33553358
}
33563359

src/ir/subtypes.h

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -168,15 +168,19 @@ struct SubTypes {
168168

169169
// Efficiently iterate on subtypes of a type, up to a particular depth (depth
170170
// 0 means not to traverse subtypes, etc.). The callback function receives
171-
// (type, depth).
171+
// (type, depth) and returns whether to continue the scan, i.e. if it returns
172+
// false, we stop. Returns the last value returned to it, that is, returns
173+
// true if we did not stop early, and false if we did.
172174
template<typename F>
173-
void iterSubTypes(HeapType type, Index depth, F func) const {
175+
bool iterSubTypes(HeapType type, Index depth, F func) const {
174176
// Start by traversing the type itself.
175-
func(type, 0);
177+
if (!func(type, 0)) {
178+
return false;
179+
}
176180

177181
if (depth == 0) {
178182
// Nothing else to scan.
179-
return;
183+
return true;
180184
}
181185

182186
// getImmediateSubTypes() returns vectors of subtypes, so for efficiency
@@ -201,17 +205,21 @@ struct SubTypes {
201205
auto& currVec = *item.vec;
202206
assert(currDepth <= depth);
203207
for (auto type : currVec) {
204-
func(type, currDepth);
208+
if (!func(type, currDepth)) {
209+
return false;
210+
}
205211
auto* subVec = &getImmediateSubTypes(type);
206212
if (currDepth + 1 <= depth && !subVec->empty()) {
207213
work.push_back({subVec, currDepth + 1});
208214
}
209215
}
210216
}
217+
218+
return true;
211219
}
212220

213221
// As above, but iterate to the maximum depth.
214-
template<typename F> void iterSubTypes(HeapType type, F func) const {
222+
template<typename F> bool iterSubTypes(HeapType type, F func) const {
215223
return iterSubTypes(type, std::numeric_limits<Index>::max(), func);
216224
}
217225

src/passes/ConstantFieldPropagation.cpp

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -328,31 +328,23 @@ struct FunctionOptimizer : public WalkerPass<PostWalker<FunctionOptimizer>> {
328328
} values[2];
329329

330330
// Handle one of the subtypes of the relevant type. We check what value it
331-
// has for the field, and update |values|. If we hit a problem, we mark us
332-
// as having failed.
333-
auto fail = false;
331+
// has for the field, and update |values|. If we hit a problem, we stop
332+
// early.
334333
auto handleType = [&](HeapType type, Index depth) {
335-
if (fail) {
336-
// TODO: Add a mechanism to halt |iterSubTypes| in the middle, as once
337-
// we fail there is no point to further iterating.
338-
return;
339-
}
340-
341334
auto iter = refTestInfos.find({type, Exact});
342335
if (iter == refTestInfos.end()) {
343336
// This type has no allocations, so we can ignore it: it is abstract.
344-
return;
337+
return true;
345338
}
346339

347340
auto value = iter->second[index];
348341
if (!value.hasNoted()) {
349342
// Also abstract and ignorable.
350-
return;
343+
return true;
351344
}
352345
if (!value.isConstant()) {
353346
// The value here is not constant, so give up entirely.
354-
fail = true;
355-
return;
347+
return false;
356348
}
357349

358350
// Consider the constant value compared to previous ones.
@@ -375,14 +367,15 @@ struct FunctionOptimizer : public WalkerPass<PostWalker<FunctionOptimizer>> {
375367
// least, we can do that if there is another iteration: If it's already
376368
// the last, we've failed to find only two values.
377369
if (i == 1) {
378-
fail = true;
379-
return;
370+
return false;
380371
}
381372
}
373+
374+
return true;
382375
};
383-
subTypes.iterSubTypes(refHeapType, handleType);
384376

385-
if (fail) {
377+
// If we stopped early, we hit a problem and failed.
378+
if (!subTypes.iterSubTypes(refHeapType, handleType)) {
386379
return;
387380
}
388381

@@ -677,6 +670,7 @@ struct ConstantFieldPropagation : public Pass {
677670
if (readable[{sub, Exact}][dst.index].combine(val)) {
678671
applyCopiesFrom(sub, Exact, dst.index, val);
679672
}
673+
return true;
680674
});
681675
} else {
682676
// The copy destination is exact, so there are no subtypes to

src/passes/RemoveUnusedModuleElements.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,7 @@ struct Analyzer {
496496
}
497497
}
498498
unreadStructFieldExprMap.erase(subStructField);
499+
return true;
499500
});
500501
}
501502
}

src/tools/fuzzing/heap-types.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,8 +1000,10 @@ void Inhabitator::markNullable(FieldPos field) {
10001000
// this extra `index` variable once we have C++20. It's a workaround for
10011001
// lambdas being unable to capture structured bindings.
10021002
const size_t index = idx;
1003-
subtypes.iterSubTypes(
1004-
curr, [&](HeapType type, Index) { nullables.insert({type, index}); });
1003+
subtypes.iterSubTypes(curr, [&](HeapType type, Index) {
1004+
nullables.insert({type, index});
1005+
return true;
1006+
});
10051007
break;
10061008
}
10071009
}

test/gtest/type-builder.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1695,6 +1695,7 @@ TEST_F(TypeTest, TestIterSubTypes) {
16951695
HeapType A, B, C, D;
16961696
{
16971697
TypeBuilder builder(4);
1698+
builder.createRecGroup(0, 4);
16981699
builder[0].setOpen() = Struct();
16991700
builder[1].setOpen().subTypeOf(builder[0]) = Struct();
17001701
builder[2].setOpen().subTypeOf(builder[0]) = Struct();
@@ -1717,6 +1718,7 @@ TEST_F(TypeTest, TestIterSubTypes) {
17171718
TypeDepths ret;
17181719
subTypes.iterSubTypes(type, depth, [&](HeapType subType, Index depth) {
17191720
ret.insert({subType, depth});
1721+
return true;
17201722
});
17211723
return ret;
17221724
};
@@ -1729,6 +1731,23 @@ TEST_F(TypeTest, TestIterSubTypes) {
17291731
EXPECT_EQ(getSubTypes(C, 0), TypeDepths({{C, 0}}));
17301732
EXPECT_EQ(getSubTypes(C, 1), TypeDepths({{C, 0}, {D, 1}}));
17311733
EXPECT_EQ(getSubTypes(C, 2), TypeDepths({{C, 0}, {D, 1}}));
1734+
1735+
// When the iteration function returns |false|, we stop.
1736+
int count = 0;
1737+
subTypes.iterSubTypes(A, 3, [&](HeapType subType, Index depth) {
1738+
count++;
1739+
// Stop after the second increment.
1740+
return count != 2;
1741+
});
1742+
EXPECT_EQ(count, 2);
1743+
1744+
// If we return true, we iterate through all four.
1745+
count = 0;
1746+
subTypes.iterSubTypes(A, 3, [&](HeapType subType, Index depth) {
1747+
count++;
1748+
return true;
1749+
});
1750+
EXPECT_EQ(count, 4);
17321751
}
17331752

17341753
// Test supertypes

0 commit comments

Comments
 (0)