Skip to content

Commit 733935f

Browse files
Fix review
1 parent b90aee7 commit 733935f

File tree

1 file changed

+65
-31
lines changed

1 file changed

+65
-31
lines changed

src/passes/CodeFolding.cpp

Lines changed: 65 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
//
5757

5858
#include <iterator>
59+
#include <optional>
5960
#include <unordered_map>
6061
#include <unordered_set>
6162

@@ -301,75 +302,108 @@ struct CodeFolding
301302
unoptimizables.clear();
302303
modifieds.clear();
303304
exitingBranchCache.clear();
304-
exitingBranchCachePopulated = false;
305305
if (needEHFixups) {
306306
EHUtils::handleBlockNestedPops(func, *getModule());
307307
}
308308
}
309309
}
310310

311311
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;
312+
// Cache of exiting branch names, populated on demand. Only queried roots
313+
// are stored. nullopt means no exiting branches; a set holds the names.
314+
std::unordered_map<Expression*, std::optional<std::unordered_set<Name>>>
315+
exitingBranchCache;
316316

317317
bool hasExitingBranches(Expression* expr) {
318-
if (!exitingBranchCachePopulated) {
319-
populateExitingBranchCache(getFunction()->body);
320-
exitingBranchCachePopulated = true;
318+
auto it = exitingBranchCache.find(expr);
319+
if (it != exitingBranchCache.end()) {
320+
return it->second.has_value();
321321
}
322-
return exitingBranchCache.count(expr);
322+
return populateExitingBranchCache(expr);
323323
}
324324

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) {
325+
// Walk |root| bottom-up computing exiting branches. Name sets are kept
326+
// transiently (moved from children, erased after merge). Only the root's
327+
// name set is persisted. Already-cached subtrees are skipped via scan(),
328+
// and their cached names are merged in precisely.
329+
bool populateExitingBranchCache(Expression* root) {
329330
struct CachePopulator
330331
: public PostWalker<CachePopulator,
331332
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) {}
333+
std::unordered_map<Expression*, std::optional<std::unordered_set<Name>>>&
334+
resultCache;
335+
Expression* root;
336+
bool rootResult = false;
337+
std::unordered_map<Expression*, std::unordered_set<Name>> nameSets;
338+
339+
CachePopulator(std::unordered_map<
340+
Expression*,
341+
std::optional<std::unordered_set<Name>>>& resultCache,
342+
Expression* root)
343+
: resultCache(resultCache), root(root) {}
344+
345+
static void scan(CachePopulator* self, Expression** currp) {
346+
auto* curr = *currp;
347+
if (self->resultCache.count(curr)) {
348+
return;
349+
}
350+
PostWalker<CachePopulator,
351+
UnifiedExpressionVisitor<CachePopulator>>::scan(self, currp);
352+
}
339353

340354
void visitExpression(Expression* curr) {
341355
std::unordered_set<Name> targets;
342-
// Merge children's target sets into ours (move to avoid copies)
356+
343357
ChildIterator children(curr);
344358
for (auto* child : children) {
345-
auto it = targetSets.find(child);
346-
if (it != targetSets.end()) {
359+
auto it = nameSets.find(child);
360+
if (it != nameSets.end()) {
347361
if (targets.empty()) {
348362
targets = std::move(it->second);
349363
} else {
350364
targets.merge(it->second);
351365
}
352-
targetSets.erase(it);
366+
nameSets.erase(it);
367+
} else {
368+
// Child was skipped by scan() — merge its cached names.
369+
auto cacheIt = resultCache.find(child);
370+
if (cacheIt != resultCache.end() && cacheIt->second) {
371+
if (targets.empty()) {
372+
targets = *cacheIt->second;
373+
} else {
374+
targets.insert(cacheIt->second->begin(),
375+
cacheIt->second->end());
376+
}
377+
}
353378
}
354379
}
355-
// Add branch uses (names this expression branches to)
380+
356381
BranchUtils::operateOnScopeNameUses(
357382
curr, [&](Name& name) { targets.insert(name); });
358-
// Remove branch defs (names this expression defines as targets)
383+
359384
BranchUtils::operateOnScopeNameDefs(curr, [&](Name& name) {
360385
if (name.is()) {
361386
targets.erase(name);
362387
}
363388
});
364-
bool hasExiting = !targets.empty();
365-
if (hasExiting) {
366-
cache.insert(curr);
367-
targetSets[curr] = std::move(targets);
389+
390+
if (!targets.empty()) {
391+
nameSets[curr] = std::move(targets);
392+
}
393+
if (curr == root) {
394+
auto it = nameSets.find(curr);
395+
if (it != nameSets.end()) {
396+
resultCache[curr] = std::move(it->second);
397+
rootResult = true;
398+
} else {
399+
resultCache[curr] = std::nullopt;
400+
}
368401
}
369402
}
370403
};
371-
CachePopulator populator(exitingBranchCache);
404+
CachePopulator populator(exitingBranchCache, root);
372405
populator.walk(root);
406+
return populator.rootResult;
373407
}
374408

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

0 commit comments

Comments
 (0)