@@ -3309,6 +3309,7 @@ class SsaDeadCodeEliminator extends HGraphVisitor implements OptimizationPhase {
33093309 }
33103310 block.forEachPhi (simplifyPhi);
33113311 evacuateTakenBranch (block);
3312+ updateEmptyRegions (block);
33123313 }
33133314
33143315 void simplifyPhi (HPhi phi) {
@@ -3391,24 +3392,20 @@ class SsaDeadCodeEliminator extends HGraphVisitor implements OptimizationPhase {
33913392 // We want to remove an if-then-else diamond when the then- and else-
33923393 // branches are empty and the condition does not control a HPhi. We cannot
33933394 // change the CFG structure so we replace the HIf condition with a
3394- // constant. This may leave the original condition unused. i.e. a
3395- // candidate for being dead code.
3395+ // constant. This may leave the original condition unused and a candidate
3396+ // for being dead code.
33963397 //
33973398 // TODO(http://dartbug.com/29475): Remove empty blocks so that recognizing
33983399 // no-op control flow is trivial.
33993400
3400- List <HBasicBlock > dominated = block.dominatedBlocks;
3401- // Diamond-like control flow dominates the then-, else- and join- blocks.
3402- if (dominated.length != 3 ) return ;
3403- HBasicBlock join = dominated.last;
3404- if (! join.phis.isEmpty) return ; // condition controls a phi.
3405- // Ignore exit block - usually the join in `if (...) return ...`
3406- if (join.isExitBlock ()) return ;
3401+ final thenBlock = block.successors[0 ];
3402+ final (thenContinuation, thenSize) = _emptyRegion[thenBlock]! ;
34073403
3408- final thenSize = measureEmptyInterval (instruction.thenBlock, join);
3409- if (thenSize == null ) return ;
3410- final elseSize = measureEmptyInterval (instruction.elseBlock, join);
3411- if (elseSize == null ) return ;
3404+ final elseBlock = block.successors[1 ];
3405+ final (elseContinuation, elseSize) = _emptyRegion[elseBlock]! ;
3406+
3407+ if (thenContinuation != elseContinuation) return ;
3408+ if (! thenContinuation.phis.isEmpty) return ;
34123409
34133410 // Pick the 'live' branch to be the smallest subgraph.
34143411 bool value = thenSize <= elseSize;
@@ -3417,38 +3414,70 @@ class SsaDeadCodeEliminator extends HGraphVisitor implements OptimizationPhase {
34173414 }
34183415 }
34193416
3420- /// Returns the number of blocks from [start] up to but not including [end] .
3421- /// Returns `null` if any of the blocks is non-empty (other than control
3422- /// flow). Returns `null` if there is an exit from the region other than
3423- /// [end] or via control flow other than HGoto and HIf.
3424- int ? measureEmptyInterval (HBasicBlock start, HBasicBlock end) {
3425- const int maxCount = 10 ;
3426- int count = 0 ;
3427- for (HBasicBlock currentBlock = start; currentBlock != end;) {
3428- final instruction = currentBlock.first;
3429- if (instruction! .next != null ) return null ; // Block not empty.
3430- count++ ;
3431- // K-limit the search for `end`.
3432- if (count > maxCount) return null ;
3433- HBasicBlock ? next;
3434- if (instruction is HGoto ) {
3435- next = currentBlock.successors.single;
3436- } else if (instruction is HIf ) {
3437- // If no-op control flow in the subgraph was simplified, the condition
3438- // will be constant, and point us down the shorter side of the diamond.
3439- final condition = instruction.inputs.single;
3440- if (condition.isConstantTrue ()) {
3441- next = instruction.thenBlock;
3442- } else if (condition.isConstantFalse ()) {
3443- next = instruction.elseBlock;
3417+ /// Map from block to the continuation, and, if all paths to the continuation
3418+ /// are empty, the number of blocks in that region. The block is either (1)
3419+ /// the continuation of the block (a join point of a single-entry, single-exit
3420+ /// region) or (2a) an earlier block if some the path to the continuation is
3421+ /// not empty, or (2b) there is path that avoids the continuation (like a loop
3422+ /// exit or a return). A block `B` that is non-empty has the entry `(B, 0)` . A
3423+ /// block that dominates a region that has an exit or a path that is non-empty
3424+ /// is also marked as `(B, 0)` .
3425+ final Map <HBasicBlock , (HBasicBlock , int )> _emptyRegion = {};
3426+
3427+ void updateEmptyRegions (HBasicBlock block) {
3428+ // Default is to consider this block to be the entry to an non-empty region.
3429+ _emptyRegion[block] = (block, 0 );
3430+
3431+ // To be empty, this block should have nothing except a single terminating
3432+ // HControlFlow instruction.
3433+ final instruction = block.first;
3434+ if (instruction == null || instruction.next != null ) return ;
3435+ if (! block.phis.isEmpty) return ;
3436+
3437+ if (! (instruction is HGoto ||
3438+ instruction is HIf ||
3439+ instruction is HBreak ||
3440+ instruction is HSwitch )) {
3441+ // Other control flow instructions either generate code (e.g. HReturn),
3442+ // or are part of some structure like a loop or try-catch.
3443+ return ;
3444+ }
3445+
3446+ // If all paths from this block reach the same continuation, we have an
3447+ // empty single-entry / single-exit region. Create a new empty interval
3448+ // from this block to the continuation, and extend it to the continuation of
3449+ // the continuation.
3450+
3451+ if (block.successors.isEmpty) return ;
3452+
3453+ int size = 1 ; // Size of empty region includes this block.
3454+
3455+ HBasicBlock ? continuation;
3456+ if (block.successors.length == 1 ) {
3457+ continuation = block.successors.single;
3458+ } else {
3459+ for (final successor in block.successors) {
3460+ if (successor.id < block.id) return ; // Back-edge
3461+ final (successorContinuation, successorSize) = _emptyRegion[successor]! ;
3462+ size += successorSize;
3463+ if (continuation == null ) {
3464+ continuation = successorContinuation;
3465+ } else if (continuation != successorContinuation) {
3466+ // This happens when (1) some successor is not empty, (2) some
3467+ // successor is, or contains, an edge that exits the nested
3468+ // forward-edge single-entry-single-exit regions.
3469+ return ;
34443470 }
34453471 }
3446- if (next == null ) return null ;
3447- // Check [next] is within [start,end) region.
3448- if (next.id < start.id || next.id > end.id) return null ;
3449- currentBlock = next;
34503472 }
3451- return count;
3473+
3474+ if (continuation! .dominator == block) {
3475+ final int continuationSize;
3476+ (continuation, continuationSize) = _emptyRegion[continuation]! ;
3477+ size += continuationSize;
3478+ }
3479+
3480+ _emptyRegion[block] = (continuation, size);
34523481 }
34533482
34543483 /// If [block] is an always-taken branch, move the code from the taken branch
0 commit comments