@@ -301,7 +301,6 @@ struct CodeFolding
301301 unoptimizables.clear ();
302302 modifieds.clear ();
303303 exitingBranchCache.clear ();
304- bodyCachePopulated = false ;
305304 if (needEHFixups) {
306305 EHUtils::handleBlockNestedPops (func, *getModule ());
307306 }
@@ -400,41 +399,6 @@ struct CodeFolding
400399 // inside that item
401400 bool canMove (const std::vector<Expression*>& items, Expression* outOf) {
402401 auto allTargets = BranchUtils::getBranchTargets (outOf);
403- bool hasTry = false ;
404- bool hasTryTable = false ;
405- if (getModule ()->features .hasExceptionHandling ()) {
406- hasTry = FindAll<Try>(outOf).has ();
407- hasTryTable = FindAll<TryTable>(outOf).has ();
408- }
409- return canMoveImpl (items, allTargets, hasTry, hasTryTable);
410- }
411-
412- // Cached data for the function body, computed on demand to avoid repeated
413- // O(N) tree walks in optimizeTerminatingTails.
414- BranchUtils::NameSet bodyBranchTargets;
415- bool bodyHasTry = false ;
416- bool bodyHasTryTable = false ;
417- bool bodyCachePopulated = false ;
418-
419- // Like canMove, but uses precomputed branch targets and Try/TryTable
420- // presence. This avoids repeated O(N) tree walks when outOf is the
421- // function body and canMove is called multiple times.
422- bool canMoveWithCachedBodyInfo (const std::vector<Expression*>& items) {
423- if (!bodyCachePopulated) {
424- bodyBranchTargets = BranchUtils::getBranchTargets (getFunction ()->body );
425- if (getModule ()->features .hasExceptionHandling ()) {
426- bodyHasTry = FindAll<Try>(getFunction ()->body ).has ();
427- bodyHasTryTable = FindAll<TryTable>(getFunction ()->body ).has ();
428- }
429- bodyCachePopulated = true ;
430- }
431- return canMoveImpl (items, bodyBranchTargets, bodyHasTry, bodyHasTryTable);
432- }
433-
434- bool canMoveImpl (const std::vector<Expression*>& items,
435- const BranchUtils::NameSet& allTargets,
436- bool hasTry,
437- bool hasTryTable) {
438402 for (auto * item : items) {
439403 auto exiting = BranchUtils::getExitingBranches (item);
440404 std::vector<Name> intersection;
@@ -465,7 +429,8 @@ struct CodeFolding
465429 // conservative approximation because there can be cases that
466430 // 'try'/'try_table' is within the expression that may throw so it is
467431 // safe to take the expression out.
468- if (effects.throws () && (hasTry || hasTryTable)) {
432+ if (effects.throws () &&
433+ (FindAll<Try>(outOf).has () || FindAll<TryTable>(outOf).has ())) {
469434 return false ;
470435 }
471436 }
@@ -667,10 +632,19 @@ struct CodeFolding
667632 // equal in the last num items, so we can merge there, but we look for
668633 // deeper merges first.
669634 // returns whether we optimized something.
670- bool optimizeTerminatingTails (std::vector<Tail>& tails, Index num = 0 ) {
635+ bool optimizeTerminatingTails (std::vector<Tail>& tails,
636+ Index num = 0 ,
637+ BranchUtils::NameSet* bodyTargets = nullptr ) {
671638 if (tails.size () < 2 ) {
672639 return false ;
673640 }
641+ // Compute body branch targets once and share across recursive calls to
642+ // avoid repeated O(N) tree walks.
643+ BranchUtils::NameSet localBodyTargets;
644+ if (!bodyTargets) {
645+ localBodyTargets = BranchUtils::getBranchTargets (getFunction ()->body );
646+ bodyTargets = &localBodyTargets;
647+ }
674648 // remove things that are untoward and cannot be optimized
675649 tails.erase (
676650 std::remove_if (tails.begin (),
@@ -731,8 +705,36 @@ struct CodeFolding
731705 // can be removed, though
732706 cost += WORTH_ADDING_BLOCK_TO_REMOVE_THIS_MUCH;
733707 // if we cannot merge to the end, then we definitely need 2 blocks,
734- // and a branch
735- if (!canMoveWithCachedBodyInfo (items)) {
708+ // and a branch. Use the pre-computed bodyTargets to avoid repeated
709+ // O(N) getBranchTargets calls.
710+ auto * body = getFunction ()->body ;
711+ bool canMoveItems = [&]() {
712+ for (auto * item : items) {
713+ auto exiting = BranchUtils::getExitingBranches (item);
714+ std::vector<Name> intersection;
715+ std::set_intersection (bodyTargets->begin (),
716+ bodyTargets->end (),
717+ exiting.begin (),
718+ exiting.end (),
719+ std::back_inserter (intersection));
720+ if (intersection.size () > 0 ) {
721+ return false ;
722+ }
723+ if (getModule ()->features .hasExceptionHandling ()) {
724+ EffectAnalyzer effects (getPassOptions (), *getModule (), item);
725+ if (effects.danglingPop ) {
726+ return false ;
727+ }
728+ if (effects.throws () &&
729+ (FindAll<Try>(body).has () ||
730+ FindAll<TryTable>(body).has ())) {
731+ return false ;
732+ }
733+ }
734+ }
735+ return true ;
736+ }();
737+ if (!canMoveItems) {
736738 cost += 1 + WORTH_ADDING_BLOCK_TO_REMOVE_THIS_MUCH;
737739 // TODO: to do this, we need to maintain a map of element=>parent,
738740 // so that we can insert the new blocks in the right place
@@ -828,7 +830,7 @@ struct CodeFolding
828830 // as the changes may influence us. we leave further opts to further
829831 // passes (as this is rare in practice, it's generally not a perf
830832 // issue, but TODO optimize)
831- if (optimizeTerminatingTails (explore, num + 1 )) {
833+ if (optimizeTerminatingTails (explore, num + 1 , bodyTargets )) {
832834 return true ;
833835 }
834836 }
0 commit comments