Skip to content

Commit 10d440d

Browse files
Fix review
1 parent b90aee7 commit 10d440d

File tree

1 file changed

+52
-32
lines changed

1 file changed

+52
-32
lines changed

src/passes/CodeFolding.cpp

Lines changed: 52 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -300,76 +300,96 @@ struct CodeFolding
300300
returnTails.clear();
301301
unoptimizables.clear();
302302
modifieds.clear();
303-
exitingBranchCache.clear();
304-
exitingBranchCachePopulated = false;
303+
hasExitingBranchCache.clear();
305304
if (needEHFixups) {
306305
EHUtils::handleBlockNestedPops(func, *getModule());
307306
}
308307
}
309308
}
310309

311310
private:
312-
// Cache of expressions that have branches exiting to targets defined
313-
// outside them. Populated lazily on first access via hasExitingBranches().
314-
std::unordered_set<Expression*> exitingBranchCache;
315-
bool exitingBranchCachePopulated = false;
311+
// Cache of hasExitingBranches() bool results, populated on demand.
312+
std::unordered_map<Expression*, bool> hasExitingBranchCache;
316313

317314
bool hasExitingBranches(Expression* expr) {
318-
if (!exitingBranchCachePopulated) {
319-
populateExitingBranchCache(getFunction()->body);
320-
exitingBranchCachePopulated = true;
315+
auto it = hasExitingBranchCache.find(expr);
316+
if (it != hasExitingBranchCache.end()) {
317+
return it->second;
321318
}
322-
return exitingBranchCache.count(expr);
319+
return populateExitingBranchCache(expr);
323320
}
324321

325-
// Pre-populate the exiting branch cache for all sub-expressions of root
326-
// in a single O(N) bottom-up walk. After this, exitingBranchCache
327-
// lookups are O(1).
328-
void populateExitingBranchCache(Expression* root) {
322+
// Walk |root| bottom-up computing exiting branches. Name sets are kept
323+
// transiently (moved from children, erased after merge). Only the root's
324+
// bool result is persisted to keep the cache small. Already-cached
325+
// subtrees are skipped via scan().
326+
bool populateExitingBranchCache(Expression* root) {
329327
struct CachePopulator
330328
: public PostWalker<CachePopulator,
331329
UnifiedExpressionVisitor<CachePopulator>> {
332-
std::unordered_set<Expression*>& cache;
333-
// Track unresolved branch targets at each node. We propagate children's
334-
// targets upward: add uses, remove defs. If any remain, the expression
335-
// has exiting branches.
336-
std::unordered_map<Expression*, std::unordered_set<Name>> targetSets;
337-
338-
CachePopulator(std::unordered_set<Expression*>& cache) : cache(cache) {}
330+
std::unordered_map<Expression*, bool>& resultCache;
331+
Expression* root;
332+
bool rootResult = false;
333+
std::unordered_map<Expression*, std::unordered_set<Name>> nameSets;
334+
335+
CachePopulator(std::unordered_map<Expression*, bool>& resultCache,
336+
Expression* root)
337+
: resultCache(resultCache), root(root) {}
338+
339+
static void scan(CachePopulator* self, Expression** currp) {
340+
auto* curr = *currp;
341+
if (self->resultCache.count(curr)) {
342+
return;
343+
}
344+
PostWalker<CachePopulator,
345+
UnifiedExpressionVisitor<CachePopulator>>::scan(self, currp);
346+
}
339347

340348
void visitExpression(Expression* curr) {
341349
std::unordered_set<Name> targets;
342-
// Merge children's target sets into ours (move to avoid copies)
350+
351+
bool childFromPriorWalkHasExiting = false;
343352
ChildIterator children(curr);
344353
for (auto* child : children) {
345-
auto it = targetSets.find(child);
346-
if (it != targetSets.end()) {
354+
auto it = nameSets.find(child);
355+
if (it != nameSets.end()) {
347356
if (targets.empty()) {
348357
targets = std::move(it->second);
349358
} else {
350359
targets.merge(it->second);
351360
}
352-
targetSets.erase(it);
361+
nameSets.erase(it);
362+
} else {
363+
// Conservatively propagate prior-cached children's results.
364+
auto cacheIt = resultCache.find(child);
365+
if (cacheIt != resultCache.end() && cacheIt->second) {
366+
childFromPriorWalkHasExiting = true;
367+
}
353368
}
354369
}
355-
// Add branch uses (names this expression branches to)
370+
356371
BranchUtils::operateOnScopeNameUses(
357372
curr, [&](Name& name) { targets.insert(name); });
358-
// Remove branch defs (names this expression defines as targets)
373+
359374
BranchUtils::operateOnScopeNameDefs(curr, [&](Name& name) {
360375
if (name.is()) {
361376
targets.erase(name);
362377
}
363378
});
364-
bool hasExiting = !targets.empty();
365-
if (hasExiting) {
366-
cache.insert(curr);
367-
targetSets[curr] = std::move(targets);
379+
380+
bool hasExiting = !targets.empty() || childFromPriorWalkHasExiting;
381+
if (!targets.empty()) {
382+
nameSets[curr] = std::move(targets);
383+
}
384+
if (curr == root) {
385+
resultCache[curr] = hasExiting;
386+
rootResult = hasExiting;
368387
}
369388
}
370389
};
371-
CachePopulator populator(exitingBranchCache);
390+
CachePopulator populator(hasExitingBranchCache, root);
372391
populator.walk(root);
392+
return populator.rootResult;
373393
}
374394

375395
// check if we can move a list of items out of another item. we can't do so

0 commit comments

Comments
 (0)