Index: sdk/lib/_internal/compiler/implementation/ssa/builder.dart |
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/builder.dart b/sdk/lib/_internal/compiler/implementation/ssa/builder.dart |
index 44fcda2919853742d0212712ed0b74c2d8ee2de3..9eed786f2c3068cd6b1905f924133cdde5fcf4c6 100644 |
--- a/sdk/lib/_internal/compiler/implementation/ssa/builder.dart |
+++ b/sdk/lib/_internal/compiler/implementation/ssa/builder.dart |
@@ -496,13 +496,17 @@ class LocalsHandler { |
} |
} |
- void beginLoopHeader(Node node, HBasicBlock loopEntry) { |
+ /** |
+ * Create phis at the loop entry for local variables (ready for the values |
+ * from the back edge). Populate the phis with the current values. |
+ */ |
+ void beginLoopHeader(HBasicBlock loopEntry) { |
// Create a copy because we modify the map while iterating over it. |
- Map<Element, HInstruction> saved = |
+ Map<Element, HInstruction> savedDirectLocals = |
new LinkedHashMap<Element, HInstruction>.from(directLocals); |
// Create phis for all elements in the definitions environment. |
- saved.forEach((Element element, HInstruction instruction) { |
+ savedDirectLocals.forEach((Element element, HInstruction instruction) { |
if (isAccessedDirectly(element)) { |
// We know 'this' cannot be modified. |
if (!identical(element, closureData.thisElement)) { |
@@ -539,6 +543,10 @@ class LocalsHandler { |
} |
} |
+ /** |
+ * Goes through the phis created in beginLoopHeader entry and adds the |
+ * input from the back edge (from the current value of directLocals) to them. |
+ */ |
void endLoop(HBasicBlock loopEntry) { |
// If the loop has an aborting body, we don't update the loop |
// phis. |
@@ -586,15 +594,16 @@ class LocalsHandler { |
} |
/** |
- * The current localsHandler is not used for its values, only for its |
- * declared variables. This is a way to exclude local values from the |
- * result when they are no longer in scope. |
- * Returns the new LocalsHandler to use (may not be [this]). |
+ * When control flow merges, this method can be used to merge several |
+ * localsHandlers into a new one using phis. The new localsHandler is |
+ * returned. Unless it is also in the list, the current localsHandler is not |
+ * used for its values, only for its declared variables. This is a way to |
+ * exclude local values from the result when they are no longer in scope. |
*/ |
- LocalsHandler mergeMultiple(List<LocalsHandler> locals, |
+ LocalsHandler mergeMultiple(List<LocalsHandler> localsHandlers, |
HBasicBlock joinBlock) { |
- assert(locals.length > 0); |
- if (locals.length == 1) return locals[0]; |
+ assert(localsHandlers.length > 0); |
+ if (localsHandlers.length == 1) return localsHandlers[0]; |
Map<Element, HInstruction> joinedLocals = |
new LinkedHashMap<Element,HInstruction>(); |
HInstruction thisValue = null; |
@@ -610,8 +619,8 @@ class LocalsHandler { |
thisValue = instruction; |
} |
}); |
- for (LocalsHandler local in locals) { |
- local.directLocals.forEach((Element element, HInstruction instruction) { |
+ for (LocalsHandler handler in localsHandlers) { |
+ handler.directLocals.forEach((Element element, HInstruction instruction) { |
HPhi phi = joinedLocals[element]; |
if (phi != null) { |
phi.addInput(instruction); |
@@ -1736,7 +1745,7 @@ class SsaBuilder extends ResolvedVisitor implements Visitor { |
'length': sourceFile.text.length}); |
} |
return location; |
-} |
+ } |
void visit(Node node) { |
if (node != null) node.accept(this); |
@@ -1782,37 +1791,56 @@ class SsaBuilder extends ResolvedVisitor implements Visitor { |
previousBlock.addSuccessor(loopEntry); |
open(loopEntry); |
- localsHandler.beginLoopHeader(node, loopEntry); |
+ localsHandler.beginLoopHeader(loopEntry); |
return jumpHandler; |
} |
/** |
* Ends the loop: |
- * - creates a new block and adds it as successor to the [branchBlock]. |
+ * - creates a new block and adds it as successor to the [branchBlock] and |
+ * any blocks that end in break. |
* - opens the new block (setting as [current]). |
* - notifies the locals handler that we're exiting a loop. |
+ * [savedLocals] are the locals from the end of the loop condition. |
+ * [branchBlock] is the exit (branching) block of the condition. For the |
+ * while and for loops this is at the top of the loop. For do-while it is |
+ * the end of the body. It is null for degenerate do-while loops that have |
+ * no back edge because they abort (throw/return/break in the body and have |
+ * no continues). |
*/ |
void endLoop(HBasicBlock loopEntry, |
HBasicBlock branchBlock, |
JumpHandler jumpHandler, |
LocalsHandler savedLocals) { |
- if (branchBlock == null && !jumpHandler.hasAnyBreak()) return; |
- |
HBasicBlock loopExitBlock = addNewBlock(); |
- assert(branchBlock == null || branchBlock.successors.length == 1); |
- List<LocalsHandler> breakLocals = <LocalsHandler>[]; |
+ List<LocalsHandler> breakHandlers = <LocalsHandler>[]; |
+ // Collect data for the successors and the phis at each break. |
jumpHandler.forEachBreak((HBreak breakInstruction, LocalsHandler locals) { |
breakInstruction.block.addSuccessor(loopExitBlock); |
- breakLocals.add(locals); |
+ breakHandlers.add(locals); |
}); |
+ // The exit block is a successor of the loop condition if it is reached. |
+ // We don't add the successor in the case of a while/for loop that aborts |
+ // because the caller of endLoop will be wiring up a special empty else |
+ // block instead. |
if (branchBlock != null) { |
branchBlock.addSuccessor(loopExitBlock); |
} |
- open(loopExitBlock); |
+ // Update the phis at the loop entry with the current values of locals. |
localsHandler.endLoop(loopEntry); |
- if (!breakLocals.isEmpty) { |
- breakLocals.add(savedLocals); |
- localsHandler = savedLocals.mergeMultiple(breakLocals, loopExitBlock); |
+ |
+ // Start generating code for the exit block. |
+ open(loopExitBlock); |
+ |
+ // Create a new localsHandler for the loopExitBlock with the correct phis. |
+ if (!breakHandlers.isEmpty) { |
+ if (branchBlock != null) { |
+ // Add the values of the locals at the end of the condition block to |
+ // the phis. These are the values that flow to the exit if the |
+ // condition fails. |
+ breakHandlers.add(savedLocals); |
+ } |
+ localsHandler = savedLocals.mergeMultiple(breakHandlers, loopExitBlock); |
} else { |
localsHandler = savedLocals; |
} |
@@ -1870,6 +1898,9 @@ class SsaBuilder extends ResolvedVisitor implements Visitor { |
SubExpression conditionExpression = |
new SubExpression(conditionBlock, conditionExitBlock); |
+ // Save the values of the local variables at the end of the condition |
+ // block. These are the values that will flow to the loop exit if the |
+ // condition fails. |
LocalsHandler savedLocals = new LocalsHandler.from(localsHandler); |
// The body. |
@@ -1886,30 +1917,30 @@ class SsaBuilder extends ResolvedVisitor implements Visitor { |
SubExpression updateGraph; |
- // Check that the loop has at least one back-edge. |
- if (jumpHandler.hasAnyContinue() || bodyBlock != null) { |
+ bool loopIsDegenerate = !jumpHandler.hasAnyContinue() && bodyBlock == null; |
+ if (!loopIsDegenerate) { |
// Update. |
// We create an update block, even when we are in a while loop. There the |
// update block is the jump-target for continue statements. We could avoid |
// the creation if there is no continue, but for now we always create it. |
HBasicBlock updateBlock = addNewBlock(); |
- List<LocalsHandler> continueLocals = <LocalsHandler>[]; |
+ List<LocalsHandler> continueHandlers = <LocalsHandler>[]; |
jumpHandler.forEachContinue((HContinue instruction, |
LocalsHandler locals) { |
instruction.block.addSuccessor(updateBlock); |
- continueLocals.add(locals); |
+ continueHandlers.add(locals); |
}); |
if (bodyBlock != null) { |
- continueLocals.add(localsHandler); |
+ continueHandlers.add(localsHandler); |
bodyBlock.addSuccessor(updateBlock); |
} |
open(updateBlock); |
localsHandler = |
- continueLocals[0].mergeMultiple(continueLocals, updateBlock); |
+ continueHandlers[0].mergeMultiple(continueHandlers, updateBlock); |
HLabeledBlockInformation labelInfo; |
List<LabelElement> labels = jumpHandler.labels(); |
@@ -1938,10 +1969,9 @@ class SsaBuilder extends ResolvedVisitor implements Visitor { |
// The back-edge completing the cycle. |
updateEndBlock.addSuccessor(conditionBlock); |
updateGraph = new SubExpression(updateBlock, updateEndBlock); |
- } |
- if (jumpHandler.hasAnyContinue() || bodyBlock != null) { |
endLoop(conditionBlock, conditionExitBlock, jumpHandler, savedLocals); |
+ |
conditionBlock.postProcessLoopHeader(); |
HLoopBlockInformation info = |
new HLoopBlockInformation( |
@@ -1958,7 +1988,8 @@ class SsaBuilder extends ResolvedVisitor implements Visitor { |
startBlock.setBlockFlow(info, current); |
loopInfo.loopBlockInformation = info; |
} else { |
- // There is no back edge for the loop, so we turn the code into: |
+ // The body of the for/while loop always aborts, so there is no back edge. |
+ // We turn the code into: |
// if (condition) { |
// body; |
// } else { |
@@ -1970,12 +2001,10 @@ class SsaBuilder extends ResolvedVisitor implements Visitor { |
HBasicBlock elseBlock = addNewBlock(); |
open(elseBlock); |
close(new HGoto()); |
- endLoop(conditionBlock, null, jumpHandler, savedLocals); |
+ // Pass the elseBlock as the branchBlock, because that's the block we go |
+ // to just before leaving the 'loop'. |
+ endLoop(conditionBlock, elseBlock, jumpHandler, savedLocals); |
- // [endLoop] will not create an exit block if there are no |
- // breaks. |
- if (current == null) open(addNewBlock()); |
- elseBlock.addSuccessor(current); |
SubGraph elseGraph = new SubGraph(elseBlock, elseBlock); |
// Remove the loop information attached to the header. |
conditionBlock.loopInformation = null; |
@@ -2099,25 +2128,25 @@ class SsaBuilder extends ResolvedVisitor implements Visitor { |
} |
SubExpression conditionExpression; |
- HBasicBlock conditionEndBlock; |
- if (!isAbortingBody || hasContinues) { |
+ bool loopIsDegenerate = isAbortingBody && !hasContinues; |
+ if (!loopIsDegenerate) { |
HBasicBlock conditionBlock = addNewBlock(); |
- List<LocalsHandler> continueLocals = <LocalsHandler>[]; |
+ List<LocalsHandler> continueHandlers = <LocalsHandler>[]; |
jumpHandler.forEachContinue((HContinue instruction, |
LocalsHandler locals) { |
instruction.block.addSuccessor(conditionBlock); |
- continueLocals.add(locals); |
+ continueHandlers.add(locals); |
}); |
if (!isAbortingBody) { |
bodyExitBlock.addSuccessor(conditionBlock); |
} |
- if (!continueLocals.isEmpty) { |
- if (!isAbortingBody) continueLocals.add(localsHandler); |
+ if (!continueHandlers.isEmpty) { |
+ if (!isAbortingBody) continueHandlers.add(localsHandler); |
localsHandler = |
- savedLocals.mergeMultiple(continueLocals, conditionBlock); |
+ savedLocals.mergeMultiple(continueHandlers, conditionBlock); |
SubGraph bodyGraph = new SubGraph(bodyEntryBlock, bodyExitBlock); |
List<LabelElement> labels = jumpHandler.labels(); |
HSubGraphBlockInformation bodyInfo = |
@@ -2137,7 +2166,7 @@ class SsaBuilder extends ResolvedVisitor implements Visitor { |
visit(node.condition); |
assert(!isAborted()); |
HInstruction conditionInstruction = popBoolified(); |
- conditionEndBlock = close( |
+ HBasicBlock conditionEndBlock = close( |
new HLoopBranch(conditionInstruction, HLoopBranch.DO_WHILE_LOOP)); |
HBasicBlock avoidCriticalEdge = addNewBlock(); |
@@ -2148,10 +2177,9 @@ class SsaBuilder extends ResolvedVisitor implements Visitor { |
conditionExpression = |
new SubExpression(conditionBlock, conditionEndBlock); |
- } |
- endLoop(loopEntryBlock, conditionEndBlock, jumpHandler, localsHandler); |
- if (!isAbortingBody || hasContinues) { |
+ endLoop(loopEntryBlock, conditionEndBlock, jumpHandler, localsHandler); |
+ |
loopEntryBlock.postProcessLoopHeader(); |
SubGraph bodyGraph = new SubGraph(loopEntryBlock, bodyExitBlock); |
HLoopBlockInformation loopBlockInfo = |
@@ -2168,13 +2196,17 @@ class SsaBuilder extends ResolvedVisitor implements Visitor { |
loopEntryBlock.setBlockFlow(loopBlockInfo, current); |
loopInfo.loopBlockInformation = loopBlockInfo; |
} else { |
- // If the loop has no back edge, we remove the loop information |
- // on the header. |
+ // Since the loop has no back edge, we remove the loop information on the |
+ // header. |
loopEntryBlock.loopInformation = null; |
- // If the body of the loop has any break, we attach a |
- // synthesized label to the body. |
if (jumpHandler.hasAnyBreak()) { |
+ // Null branchBlock because the body of the do-while loop always aborts, |
+ // so we never get to the condition. |
+ endLoop(loopEntryBlock, null, jumpHandler, localsHandler); |
+ |
+ // Since the body of the loop has a break, we attach a synthesized label |
+ // to the body. |
SubGraph bodyGraph = new SubGraph(bodyEntryBlock, bodyExitBlock); |
TargetElement target = elements[node]; |
LabelElement label = target.addLabel(null, 'loop'); |
@@ -4077,18 +4109,18 @@ class SsaBuilder extends ResolvedVisitor implements Visitor { |
SubGraph bodyGraph = new SubGraph(entryBlock, lastOpenedBlock); |
HBasicBlock joinBlock = graph.addNewBlock(); |
- List<LocalsHandler> breakLocals = <LocalsHandler>[]; |
+ List<LocalsHandler> breakHandlers = <LocalsHandler>[]; |
handler.forEachBreak((HBreak breakInstruction, LocalsHandler locals) { |
breakInstruction.block.addSuccessor(joinBlock); |
- breakLocals.add(locals); |
+ breakHandlers.add(locals); |
}); |
- bool hasBreak = breakLocals.length > 0; |
+ bool hasBreak = breakHandlers.length > 0; |
if (!isAborted()) { |
goto(current, joinBlock); |
- breakLocals.add(localsHandler); |
+ breakHandlers.add(localsHandler); |
} |
open(joinBlock); |
- localsHandler = beforeLocals.mergeMultiple(breakLocals, joinBlock); |
+ localsHandler = beforeLocals.mergeMultiple(breakHandlers, joinBlock); |
if (hasBreak) { |
// There was at least one reachable break, so the label is needed. |
@@ -4151,25 +4183,25 @@ class SsaBuilder extends ResolvedVisitor implements Visitor { |
// Create merge block for break targets. |
HBasicBlock joinBlock = new HBasicBlock(); |
- List<LocalsHandler> caseLocals = <LocalsHandler>[]; |
+ List<LocalsHandler> caseHandlers = <LocalsHandler>[]; |
jumpHandler.forEachBreak((HBreak instruction, LocalsHandler locals) { |
instruction.block.addSuccessor(joinBlock); |
- caseLocals.add(locals); |
+ caseHandlers.add(locals); |
}); |
if (!isAborted()) { |
// The current flow is only aborted if the switch has a default that |
// aborts (all previous cases must abort, and if there is no default, |
// it's possible to miss all the cases). |
- caseLocals.add(localsHandler); |
+ caseHandlers.add(localsHandler); |
goto(current, joinBlock); |
} |
- if (caseLocals.length != 0) { |
+ if (caseHandlers.length != 0) { |
graph.addBlock(joinBlock); |
open(joinBlock); |
- if (caseLocals.length == 1) { |
- localsHandler = caseLocals[0]; |
+ if (caseHandlers.length == 1) { |
+ localsHandler = caseHandlers[0]; |
} else { |
- localsHandler = savedLocals.mergeMultiple(caseLocals, joinBlock); |
+ localsHandler = savedLocals.mergeMultiple(caseHandlers, joinBlock); |
} |
} else { |
// The joinblock is not used. |
@@ -4290,36 +4322,36 @@ class SsaBuilder extends ResolvedVisitor implements Visitor { |
// Add a join-block if necessary. |
// We create [joinBlock] early, and then go through the cases that might |
// want to jump to it. In each case, if we add [joinBlock] as a successor |
- // of another block, we also add an element to [caseLocals] that is used |
+ // of another block, we also add an element to [caseHandlers] that is used |
// to create the phis in [joinBlock]. |
- // If we never jump to the join block, [caseLocals] will stay empty, and |
+ // If we never jump to the join block, [caseHandlers] will stay empty, and |
// the join block is never added to the graph. |
HBasicBlock joinBlock = new HBasicBlock(); |
- List<LocalsHandler> caseLocals = <LocalsHandler>[]; |
+ List<LocalsHandler> caseHandlers = <LocalsHandler>[]; |
jumpHandler.forEachBreak((HBreak instruction, LocalsHandler locals) { |
instruction.block.addSuccessor(joinBlock); |
- caseLocals.add(locals); |
+ caseHandlers.add(locals); |
}); |
if (!isAborted()) { |
current.close(new HGoto()); |
lastOpenedBlock.addSuccessor(joinBlock); |
- caseLocals.add(localsHandler); |
+ caseHandlers.add(localsHandler); |
} |
if (!hasDefault) { |
// The current flow is only aborted if the switch has a default that |
// aborts (all previous cases must abort, and if there is no default, |
// it's possible to miss all the cases). |
expressionEnd.addSuccessor(joinBlock); |
- caseLocals.add(savedLocals); |
+ caseHandlers.add(savedLocals); |
} |
- assert(caseLocals.length == joinBlock.predecessors.length); |
- if (caseLocals.length != 0) { |
+ assert(caseHandlers.length == joinBlock.predecessors.length); |
+ if (caseHandlers.length != 0) { |
graph.addBlock(joinBlock); |
open(joinBlock); |
- if (caseLocals.length == 1) { |
- localsHandler = caseLocals[0]; |
+ if (caseHandlers.length == 1) { |
+ localsHandler = caseHandlers[0]; |
} else { |
- localsHandler = savedLocals.mergeMultiple(caseLocals, joinBlock); |
+ localsHandler = savedLocals.mergeMultiple(caseHandlers, joinBlock); |
} |
} else { |
// The joinblock is not used. |