@@ -136,9 +136,10 @@ struct CodeFolding
136136 modifieds; // modified code should not be processed
137137 // again, wait for next pass
138138
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_;
139+ // Cache of expressions that have branches exiting to targets defined
140+ // outside them. Populated lazily on first access via PostWalker.
141+ std::unordered_set<Expression*> exitingBranchCache_;
142+ bool exitingBranchCachePopulated_ = false ;
142143
143144 // walking
144145
@@ -305,28 +306,39 @@ struct CodeFolding
305306 unoptimizables.clear ();
306307 modifieds.clear ();
307308 exitingBranchCache_.clear ();
309+ exitingBranchCachePopulated_ = false ;
308310 if (needEHFixups) {
309311 EHUtils::handleBlockNestedPops (func, *getModule ());
310312 }
311313 }
312314 }
313315
314316private:
317+ // Check if an expression has branches that exit to targets defined outside
318+ // it. The cache is populated lazily on first call using a PostWalker for
319+ // efficient bottom-up traversal.
320+ bool hasExitingBranches (Expression* expr) {
321+ if (!exitingBranchCachePopulated_) {
322+ populateExitingBranchCache (getFunction ()->body );
323+ exitingBranchCachePopulated_ = true ;
324+ }
325+ return exitingBranchCache_.count (expr);
326+ }
327+
315328 // Pre-populate the exiting branch cache for all sub-expressions of root
316329 // in a single O(N) bottom-up walk. After this, exitingBranchCache_
317330 // lookups are O(1).
318331 void populateExitingBranchCache (Expression* root) {
319332 struct CachePopulator
320333 : public PostWalker<CachePopulator,
321334 UnifiedExpressionVisitor<CachePopulator>> {
322- std::unordered_map <Expression*, bool >& cache;
335+ std::unordered_set <Expression*>& cache;
323336 // Track unresolved branch targets at each node. We propagate children's
324337 // targets upward: add uses, remove defs. If any remain, the expression
325338 // has exiting branches.
326339 std::unordered_map<Expression*, std::unordered_set<Name>> targetSets;
327340
328- CachePopulator (std::unordered_map<Expression*, bool >& cache)
329- : cache(cache) {}
341+ CachePopulator (std::unordered_set<Expression*>& cache) : cache(cache) {}
330342
331343 void visitExpression (Expression* curr) {
332344 std::unordered_set<Name> targets;
@@ -353,8 +365,8 @@ struct CodeFolding
353365 }
354366 });
355367 bool hasExiting = !targets.empty ();
356- cache[curr] = hasExiting;
357368 if (hasExiting) {
369+ cache.insert (curr);
358370 targetSets[curr] = std::move (targets);
359371 }
360372 }
@@ -606,11 +618,6 @@ struct CodeFolding
606618 if (tails.size () < 2 ) {
607619 return false ;
608620 }
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- }
614621 // remove things that are untoward and cannot be optimized
615622 tails.erase (
616623 std::remove_if (tails.begin (),
@@ -699,7 +706,7 @@ struct CodeFolding
699706 // TODO: this should not be a problem in
700707 // *non*-terminating tails, but
701708 // double-verify that
702- if (exitingBranchCache_[ newItem] ) {
709+ if (hasExitingBranches ( newItem) ) {
703710 return true ;
704711 }
705712 return false ;
0 commit comments