Skip to content

Commit 66dff99

Browse files
avoid O(N^2) exiting-branch checks in CodeFolding
1 parent ce7f869 commit 66dff99

File tree

1 file changed

+63
-3
lines changed

1 file changed

+63
-3
lines changed

src/passes/CodeFolding.cpp

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
#include "ir/effects.h"
6464
#include "ir/eh-utils.h"
6565
#include "ir/find_all.h"
66+
#include "ir/iteration.h"
6667
#include "ir/label-utils.h"
6768
#include "ir/utils.h"
6869
#include "pass.h"
@@ -135,6 +136,10 @@ struct CodeFolding
135136
modifieds; // modified code should not be processed
136137
// again, wait for next pass
137138

139+
// Cache for hasExitingBranches results. Populated by a single bottom-up
140+
// walk to avoid O(N^2) repeated tree traversals on nested blocks.
141+
std::unordered_map<Expression*, bool> exitingBranchCache_;
142+
138143
// walking
139144

140145
void visitExpression(Expression* curr) {
@@ -299,13 +304,65 @@ struct CodeFolding
299304
returnTails.clear();
300305
unoptimizables.clear();
301306
modifieds.clear();
307+
exitingBranchCache_.clear();
302308
if (needEHFixups) {
303309
EHUtils::handleBlockNestedPops(func, *getModule());
304310
}
305311
}
306312
}
307313

308314
private:
315+
// Pre-populate the exiting branch cache for all sub-expressions of root
316+
// in a single O(N) bottom-up walk. After this, exitingBranchCache_
317+
// lookups are O(1).
318+
void populateExitingBranchCache(Expression* root) {
319+
struct CachePopulator
320+
: public PostWalker<CachePopulator,
321+
UnifiedExpressionVisitor<CachePopulator>> {
322+
std::unordered_map<Expression*, bool>& cache;
323+
// Track unresolved branch targets at each node. We propagate children's
324+
// targets upward: add uses, remove defs. If any remain, the expression
325+
// has exiting branches.
326+
std::unordered_map<Expression*, std::unordered_set<Name>> targetSets;
327+
328+
CachePopulator(std::unordered_map<Expression*, bool>& cache)
329+
: cache(cache) {}
330+
331+
void visitExpression(Expression* curr) {
332+
std::unordered_set<Name> targets;
333+
// Merge children's target sets into ours (move to avoid copies)
334+
ChildIterator children(curr);
335+
for (auto* child : children) {
336+
auto it = targetSets.find(child);
337+
if (it != targetSets.end()) {
338+
if (targets.empty()) {
339+
targets = std::move(it->second);
340+
} else {
341+
targets.merge(it->second);
342+
}
343+
targetSets.erase(it);
344+
}
345+
}
346+
// Add branch uses (names this expression branches to)
347+
BranchUtils::operateOnScopeNameUses(
348+
curr, [&](Name& name) { targets.insert(name); });
349+
// Remove branch defs (names this expression defines as targets)
350+
BranchUtils::operateOnScopeNameDefs(curr, [&](Name& name) {
351+
if (name.is()) {
352+
targets.erase(name);
353+
}
354+
});
355+
bool hasExiting = !targets.empty();
356+
cache[curr] = hasExiting;
357+
if (hasExiting) {
358+
targetSets[curr] = std::move(targets);
359+
}
360+
}
361+
};
362+
CachePopulator populator(exitingBranchCache_);
363+
populator.walk(root);
364+
}
365+
309366
// check if we can move a list of items out of another item. we can't do so
310367
// if one of the items has a branch to something inside outOf that is not
311368
// inside that item
@@ -549,6 +606,11 @@ struct CodeFolding
549606
if (tails.size() < 2) {
550607
return false;
551608
}
609+
// Pre-populate the cache once at the top level so all subsequent
610+
// exitingBranchCache_ lookups are O(1).
611+
if (num == 0) {
612+
populateExitingBranchCache(getFunction()->body);
613+
}
552614
// remove things that are untoward and cannot be optimized
553615
tails.erase(
554616
std::remove_if(tails.begin(),
@@ -637,9 +699,7 @@ struct CodeFolding
637699
// TODO: this should not be a problem in
638700
// *non*-terminating tails, but
639701
// double-verify that
640-
if (EffectAnalyzer(
641-
getPassOptions(), *getModule(), newItem)
642-
.hasExternalBreakTargets()) {
702+
if (exitingBranchCache_[newItem]) {
643703
return true;
644704
}
645705
return false;

0 commit comments

Comments
 (0)