Index: pkg/compiler/lib/src/ssa/optimize.dart |
diff --git a/pkg/compiler/lib/src/ssa/optimize.dart b/pkg/compiler/lib/src/ssa/optimize.dart |
index f3d934f18c4c48c49a45d3368b3507d855e02b5d..4b5679b33c1a4fe9666c5feaa8d8b5992a7500c2 100644 |
--- a/pkg/compiler/lib/src/ssa/optimize.dart |
+++ b/pkg/compiler/lib/src/ssa/optimize.dart |
@@ -1459,6 +1459,7 @@ class SsaDeadCodeEliminator extends HGraphVisitor implements OptimizationPhase { |
final ClosedWorld closedWorld; |
final SsaOptimizerTask optimizer; |
+ HGraph _graph; |
SsaLiveBlockAnalyzer analyzer; |
Map<HInstruction, bool> trivialDeadStoreReceivers = |
new Maplet<HInstruction, bool>(); |
@@ -1596,15 +1597,17 @@ class SsaDeadCodeEliminator extends HGraphVisitor implements OptimizationPhase { |
} |
void visitGraph(HGraph graph) { |
+ _graph = graph; |
analyzer = new SsaLiveBlockAnalyzer(graph, closedWorld, optimizer); |
analyzer.analyze(); |
visitPostDominatorTree(graph); |
- cleanPhis(graph); |
+ cleanPhis(); |
} |
void visitBasicBlock(HBasicBlock block) { |
bool isDeadBlock = analyzer.isDeadBlock(block); |
block.isLive = !isDeadBlock; |
+ simplifyControlFlow(block); |
// Start from the last non-control flow instruction in the block. |
HInstruction instruction = block.last.previous; |
while (instruction != null) { |
@@ -1619,11 +1622,89 @@ class SsaDeadCodeEliminator extends HGraphVisitor implements OptimizationPhase { |
} |
instruction = previous; |
} |
+ block.forEachPhi(simplifyPhi); |
+ } |
+ |
+ void simplifyPhi(HPhi phi) { |
+ // If the phi is of the form `phi(x, HTypeKnown(x))`, it does not strengthen |
+ // `x`. We can replace the phi with `x` to potentially make the HTypeKnown |
+ // refinement node dead and potentially make a HIf control no HPhis. |
+ |
+ // TODO(sra): Implement version of this test that works for a subgraph of |
+ // HPhi nodes. |
+ HInstruction base = null; |
+ bool seenUnrefinedBase = false; |
+ for (HInstruction input in phi.inputs) { |
+ HInstruction value = input; |
+ while (value is HTypeKnown) { |
+ HTypeKnown refinement = value; |
+ value = refinement.checkedInput; |
+ } |
+ if (value == input) seenUnrefinedBase = true; |
+ base ??= value; |
+ if (base != value) return; |
+ } |
+ |
+ if (seenUnrefinedBase) { |
+ HBasicBlock block = phi.block; |
+ block.rewrite(phi, base); |
+ block.removePhi(phi); |
+ } |
+ } |
+ |
+ void simplifyControlFlow(HBasicBlock block) { |
+ HControlFlow instruction = block.last; |
+ if (instruction is HIf) { |
+ HInstruction condition = instruction.condition; |
+ if (condition.isConstant()) return; |
+ |
+ // We want to remove an if-then-else diamond when the then- and else- |
+ // branches are empty and the condition does not control a HPhi. We cannot |
+ // change the CFG structure so we replace the HIf condition with a |
+ // constant. This may leave the original condition unused. i.e. a |
+ // candidate for being dead code. |
+ |
+ List<HBasicBlock> dominated = block.dominatedBlocks; |
+ // Diamond-like control flow dominates the then-, else- and join- blocks. |
+ if (dominated.length != 3) return; |
+ HBasicBlock join = dominated.last; |
+ if (!join.phis.isEmpty) return; // condition controls a phi. |
+ // Ignore exit block - usually the join in `if (...) return ...` |
+ if (join.isExitBlock()) return; |
+ |
+ int thenSize = measureEmptyInterval(instruction.thenBlock, join); |
+ if (thenSize == null) return; |
+ int elseSize = measureEmptyInterval(instruction.elseBlock, join); |
+ if (elseSize == null) return; |
+ |
+ // Pick the 'live' branch to be the smallest subgraph. |
+ bool value = thenSize <= elseSize; |
+ HInstruction newCondition = _graph.addConstantBool(value, closedWorld); |
+ instruction.inputs[0] = newCondition; |
+ condition.usedBy.remove(instruction); |
+ newCondition.usedBy.add(instruction); |
+ } |
+ } |
+ |
+ /// Returns the number of blocks from [start] up to but not including [end]. |
+ /// Returns `null` if any of the blocks is non-empty (other than control |
+ /// flow). Returns `null` if there is an exit from the region other than |
+ /// [end] or via control flow other than HGoto and HIf. |
+ int measureEmptyInterval(HBasicBlock start, HBasicBlock end) { |
+ if (start.first != start.last) return null; // start is not empty. |
+ // Do a simple single-block region test first. |
+ if (start.last is HGoto && |
+ start.successors.length == 1 && |
+ start.successors.single == end) { |
+ return 1; |
+ } |
+ // TODO(sra): Implement fuller test. |
+ return null; |
} |
- void cleanPhis(HGraph graph) { |
+ void cleanPhis() { |
L: |
- for (HBasicBlock block in graph.blocks) { |
+ for (HBasicBlock block in _graph.blocks) { |
List<HBasicBlock> predecessors = block.predecessors; |
// Zap all inputs to phis that correspond to dead blocks. |
block.forEachPhi((HPhi phi) { |