Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1024)

Unified Diff: frog/leg/ssa/builder.dart

Issue 9421035: Support break and labeled statements. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Removed unused var. Better trace output. Created 8 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: frog/leg/ssa/builder.dart
diff --git a/frog/leg/ssa/builder.dart b/frog/leg/ssa/builder.dart
index c74c887a0f4ef499225484b326e0b25dc78ff54e..dcf86b14910183184233271582cdafe8b0a55642 100644
--- a/frog/leg/ssa/builder.dart
+++ b/frog/leg/ssa/builder.dart
@@ -482,7 +482,7 @@ class LocalsHandler {
if (scopeData == null) return;
if (scopeData.hasBoxedLoopVariables()) {
updateCaptureBox(scopeData.boxElement, scopeData.boxedLoopVariables);
- }
+ }
}
void endLoop(HBasicBlock loopEntry) {
@@ -493,6 +493,15 @@ class LocalsHandler {
});
}
+ /**
+ * Merge [otherLocals] into this locals handler, creating phi-nodes when
+ * there is a conflict.
+ * If a phi node is necessary, it will use the otherLocals instruction as the
+ * first input, and this handler's instruction as the second.
+ * NOTICE: This means that the predecessor corresponding to [otherLocals]
+ * should be the first predecessor of the current block, and the one
+ * corresponding to this locals handler should be the second.
+ */
void mergeWith(LocalsHandler otherLocals, HBasicBlock joinBlock) {
// If an element is in one map but not the other we can safely
// ignore it. It means that a variable was declared in the
@@ -521,6 +530,86 @@ class LocalsHandler {
}
}
+
+// Represents a single break instruction.
+class BreakHandlerEntry {
+ final HBreak breakInstruction;
+ final LocalsHandler locals;
+ BreakHandlerEntry(this.breakInstruction, this.locals);
+}
+
+interface BreakHandler default BreakHandlerImpl {
+ BreakHandler(Map<StatementElement, BreakHandler> breakTargets);
+ void addTarget(StatementElement element);
+ void addBreak(HBreak breakInstruction);
+ void forEachBreak(Function action);
+ void close();
+ List<SourceString> labels();
+}
+
+// Inert break handler used to avoid null checks when a loop isn't
+// used as the target of a break, and therefore doesn't need a break
+// handler associated with it.
+class NullBreakHandler implements BreakHandler {
+ const NullBreakHandler();
+ void addTarget(StatementElement element) { unreachable(); }
+ void addBreak(HBreak breakInstruction) { unreachable() }
+ void forEachBreak(Function ignored) { }
+ void close() { }
+ List<SourceString> labels() => const <SourceString>[];
+}
+
+// Records breaks until a target block is available.
+// Breaks are always forward jumps.
+class BreakHandlerImpl implements BreakHandler{
+ final Map<StatementElement, BreakHandler> breakTargets;
+ final List<StatementElement> elements;
+ final List<BreakHandlerEntry> breaks;
+ bool closed = false;
+ BreakHandlerImpl(this.breakTargets)
floitsch 2012/02/20 19:01:54 Instead of passing in breakTargets take the builde
Lasse Reichstein Nielsen 2012/02/21 13:53:56 Will do.
+ : elements = <StatementElement>[],
+ breaks = <BreakHandlerEntry>[];
+
+ void addTarget(StatementElement element) {
+ assert(breakTargets[element] === null);
+ elements.add(element);
+ breakTargets[element] = this;
+ }
+
+ void addBreak(HBreak breakInstruction,
+ LocalsHandler locals) {
+ breaks.add(new BreakHandlerEntry(breakInstruction, locals));
+ }
+
+ void forEachBreak(Function action) {
+ for (BreakHandlerEntry entry in breaks) {
+ action(entry.breakInstruction, entry.locals);
+ }
+ }
+
+ void close() {
+ assert(!closed);
+ closed = true;
+ // The mapping from StatementElement to BreakHandler is no longer needed.
+ for (StatementElement element in elements) {
+ assert(breakTargets[element] === this);
+ breakTargets.remove(element);
+ }
+ }
+
+ List<SourceString> labels() {
+ List<SourceString> result = null;
+ for (StatementElement element in elements) {
+ SourceString name = element.name;
+ if (!name.isEmpty()) {
+ if (result === null) result = <SourceString>[];
+ result.add(name);
+ }
+ }
+ return result === null ? const <SourceString>[] : result;
+ }
+}
+
class SsaBuilder implements Visitor {
final Compiler compiler;
TreeElements elements;
@@ -530,6 +619,8 @@ class SsaBuilder implements Visitor {
HGraph graph;
LocalsHandler localsHandler;
+ Map<StatementElement, BreakHandler> breakTargets;
+
// We build the Ssa graph by simulating a stack machine.
List<HInstruction> stack;
@@ -544,7 +635,8 @@ class SsaBuilder implements Visitor {
methodInterceptionEnabled = true,
elements = work.resolutionTree,
graph = new HGraph(),
- stack = new List<HInstruction>() {
+ stack = new List<HInstruction>(),
+ breakTargets = new Map<StatementElement, BreakHandler>() {
localsHandler = new LocalsHandler(this);
}
@@ -847,15 +939,16 @@ class SsaBuilder implements Visitor {
* is closed with an [HGoto] and replaced by the newly created block.
* Also notifies the locals handler that we're entering a loop.
*/
- void beginLoopHeader(Node node) {
+ BreakHandler beginLoopHeader(Node node) {
floitsch 2012/02/20 19:01:54 Instead of returning a BreakHandler make the Break
Lasse Reichstein Nielsen 2012/02/21 13:53:56 Done. Break handlers are now a list, and since the
assert(!isAborted());
HBasicBlock previousBlock = close(new HGoto());
-
- HBasicBlock loopEntry = graph.addNewLoopHeaderBlock();
+ BreakHandler breakHandler = getLoopBreakHandler(node);
+ HBasicBlock loopEntry = graph.addNewLoopHeaderBlock(breakHandler.labels());
previousBlock.addSuccessor(loopEntry);
open(loopEntry);
localsHandler.beginLoopHeader(node, loopEntry);
+ return breakHandler;
}
/**
@@ -864,12 +957,21 @@ class SsaBuilder implements Visitor {
* - opens the new block (setting as [current]).
* - notifies the locals handler that we're exiting a loop.
*/
- void endLoop(HBasicBlock loopEntry, HBasicBlock branchBlock) {
+ void endLoop(HBasicBlock loopEntry,
+ HBasicBlock branchBlock,
+ BreakHandler breakHandler) {
HBasicBlock loopExitBlock = addNewBlock();
assert(branchBlock.successors.length == 1);
branchBlock.addSuccessor(loopExitBlock);
open(loopExitBlock);
localsHandler.endLoop(loopEntry);
+ breakHandler.forEachBreak((HBreak breakInstruction, LocalsHandler locals) {
+ HBasicBlock joinBlock = addNewBlock();
+ breakInstruction.block.addSuccessor(joinBlock);
+ goto(current, joinBlock);
+ open(joinBlock);
+ localsHandler.mergeWith(locals, joinBlock);
+ });
}
// For while loops, initializer and update are null.
@@ -899,7 +1001,7 @@ class SsaBuilder implements Visitor {
}
assert(!isAborted());
- beginLoopHeader(loop);
+ BreakHandler breakHandler = beginLoopHeader(loop);
HBasicBlock conditionBlock = current;
// The condition.
@@ -944,7 +1046,7 @@ class SsaBuilder implements Visitor {
updateBlock.addSuccessor(conditionBlock);
conditionBlock.postProcessLoopHeader();
- endLoop(conditionBlock, conditionExitBlock);
+ endLoop(conditionBlock, conditionExitBlock, breakHandler);
localsHandler = savedLocals;
}
@@ -962,7 +1064,7 @@ class SsaBuilder implements Visitor {
visitDoWhile(DoWhile node) {
localsHandler.startLoop(node);
- beginLoopHeader(node);
+ BreakHandler breakHandler = beginLoopHeader(node);
HBasicBlock loopEntryBlock = current;
localsHandler.enterLoopBody(node);
@@ -984,7 +1086,7 @@ class SsaBuilder implements Visitor {
conditionBlock.addSuccessor(loopEntryBlock); // The back-edge.
loopEntryBlock.postProcessLoopHeader();
- endLoop(loopEntryBlock, conditionBlock);
+ endLoop(loopEntryBlock, conditionBlock, breakHandler);
}
visitFunctionExpression(FunctionExpression node) {
@@ -1881,13 +1983,42 @@ class SsaBuilder implements Visitor {
}
visitBreakStatement(BreakStatement node) {
- compiler.unimplemented('SsaBuilder.visitBreakStatement', node: node);
+ work.allowSpeculativeOptimization = false;
+ assert(!isAborted());
+ StatementElement target = elements[node];
+ assert(target !== null);
+ BreakHandler handler = breakTargets[target];
+ assert(handler !== null);
+ LocalsHandler savedLocals = new LocalsHandler.from(localsHandler);
+ HBreak breakInstruction;
+ if (node.target === null) {
+ breakInstruction = new HBreak();
+ } else {
+ breakInstruction = new HBreak(node.target.source);
+ }
+ close(breakInstruction);
+ handler.addBreak(breakInstruction, savedLocals);
}
visitContinueStatement(ContinueStatement node) {
compiler.unimplemented('SsaBuilder.visitContinueStatement', node: node);
}
+ BreakHandler getLoopBreakHandler(Loop node) {
+ StatementElement element = elements[node];
+ BreakHandler handler;
+ if (currentBreakHandler === null) {
+ if (element === null) return const NullBreakHandler();
+ handler = new BreakHandler(breakTargets);
+ } else {
+ handler = currentBreakHandler;
+ currentBreakHandler = null;
+ if (element === null) return handler;
+ }
+ handler.addTarget(element);
+ return handler;
+ }
+
visitForInStatement(ForInStatement node) {
// Generate a structure equivalent to:
// Iterator<E> $iter = <iterable>.iterator()
@@ -1910,7 +2041,7 @@ class SsaBuilder implements Visitor {
selector, iteratorName, false, inputs);
add(iterator);
- beginLoopHeader(node);
+ BreakHandler breakHandler = beginLoopHeader(node);
HBasicBlock conditionBlock = current;
// The condition.
@@ -1960,12 +2091,61 @@ class SsaBuilder implements Visitor {
updateBlock.addSuccessor(conditionBlock);
conditionBlock.postProcessLoopHeader();
- endLoop(conditionBlock, conditionExitBlock);
+ endLoop(conditionBlock, conditionExitBlock, breakHandler);
localsHandler = savedLocals;
+ breakHandler.close();
}
+ BreakHandler currentBreakHandler = null;
+
visitLabelledStatement(LabelledStatement node) {
- compiler.unimplemented('SsaBuilder.visitLabelledStatement', node: node);
+ BreakHandler handler = null;
+ Node currentNode = node;
+ do {
+ StatementElement element = elements[currentNode];
+ if (element !== null && element.isBreakTarget) {
+ if (handler === null) handler = new BreakHandler(breakTargets);
+ handler.addTarget(element);
+ }
+ currentNode = currentNode.statement;
+ } while (currentNode is LabelledStatement);
+ if (handler === null) {
+ // The labels are not break targets.
+ visit(currentNode);
+ } else if (currentNode is Loop || currentNode is SwitchStatement) {
+ // The labels apply to that statement, and will be handled there.
+ currentBreakHandler = handler;
floitsch 2012/02/20 19:01:54 This looks like work that the resolver should have
Lasse Reichstein Nielsen 2012/02/21 13:53:56 I agree, and agreed when doing the resolution, but
floitsch 2012/02/22 13:17:21 What I wanted: a LabelElement that points to its A
Lasse Reichstein Nielsen 2012/02/22 14:12:24 Touche. I'll consider redoing this in a different
+ visit(currentNode);
+ assert(currentBreakHandler === null);
+ } else {
+ // Introduce a new basic block.
+ HBasicBlock entryBlock = graph.addNewBlock();
+ goto(current, entryBlock);
+ open(entryBlock);
+ visit(currentNode);
+ // Always create at least one join block, even if there turns out to be
+ // no breaks anyway.
floitsch 2012/02/20 19:01:54 How can this be possible? I thought that the handl
Lasse Reichstein Nielsen 2012/02/21 13:53:56 Some analysis might have turned a break into dead
floitsch 2012/02/22 13:17:21 As long as we don't have such an optimization, ass
Lasse Reichstein Nielsen 2012/02/22 14:12:24 As discussed offline, the break could be dead code
+ HBasicBlock firstJoinBlock = graph.addNewBlock();
+ bool isFirst = true;
+ handler.forEachBreak((HBreak breakInstruction, LocalsHandler locals) {
+ HBasicBlock joinBlock = isFirst ? firstJoinBlock : graph.addNewBlock();
+ isFirst = false;
+ breakInstruction.block.addSuccessor(joinBlock);
+ if (!isAborted()) {
+ goto(current, joinBlock);
+ }
+ open(joinBlock);
+ localsHandler.mergeWith(locals, joinBlock);
+ });
+ HLabeledBlockInformation blockInfo =
+ new HLabeledBlockInformation(entryBlock, firstJoinBlock,
+ handler.labels());
+ handler.close();
+ // Mark both entry and exit with the information. You can
+ // tell which one is which by comparing with blockInfo.start/end.
+ entryBlock.labeledBlockInformation = blockInfo;
+ firstJoinBlock.labeledBlockInformation = blockInfo;
+ }
}
visitLiteralMap(LiteralMap node) {
« no previous file with comments | « frog/leg/ssa/bailout.dart ('k') | frog/leg/ssa/codegen.dart » ('j') | frog/leg/ssa/codegen.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698