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

Unified Diff: sdk/lib/_internal/compiler/implementation/ssa/builder.dart

Issue 14969004: Implement continue for switch. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 7 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: 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 487104cdabd11bc8281f7e5876f429521c42401d..2a4baf415a8f32dc1a765594c52c5ceec6fdd3dd 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/builder.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/builder.dart
@@ -533,7 +533,7 @@ class LocalsHandler {
}
}
- void enterLoopUpdates(Loop node) {
+ void enterLoopUpdates(Node node) {
// If there are declared boxed loop variables then the updates might have
// access to the box and we must switch to a new box before executing the
// updates.
@@ -736,6 +736,9 @@ class TargetJumpHandler implements JumpHandler {
continueInstruction = new HContinue(target);
} else {
continueInstruction = new HContinue.toLabel(label);
+ // Switch case continue statements must be handled by the
+ // [SwitchCaseJumpHandler].
+ assert(label.target.statement is! SwitchCase);
}
LocalsHandler locals = new LocalsHandler.from(builder.localsHandler);
builder.close(continueInstruction);
@@ -783,6 +786,86 @@ class TargetJumpHandler implements JumpHandler {
}
}
+/// Special [JumpHandler] implementation used to handle continue statements
+/// targeting switch cases.
+class SwitchCaseJumpHandler extends TargetJumpHandler {
+ /// Map from switch case targets to indices used to encode the flow of the
+ /// switch case loop.
+ final Map<TargetElement, int> targetIndexMap = new Map<TargetElement, int>();
+
+ SwitchCaseJumpHandler(SsaBuilder builder,
+ TargetElement target,
+ SwitchStatement node)
+ : super(builder, target) {
+ // The switch case indices must match those computed in
+ // [SsaBuilder.visitSwitchStatement].
+ int switchIndex = 1;
ngeoffray 2013/05/14 07:08:51 Explain why you start with 1.
Johnni Winther 2013/05/17 07:03:04 Done.
+ for (SwitchCase switchCase in node.cases) {
+ for (Node labelOrCase in switchCase.labelsAndCases) {
+ Node label = labelOrCase.asLabel();
+ if (label != null) {
+ LabelElement labelElement = builder.elements[label];
+ if (labelElement != null) {
+ if (labelElement.isContinueTarget) {
ngeoffray 2013/05/14 07:08:51 I prever avoiding nested if and do if (labelElemen
Johnni Winther 2013/05/17 07:03:04 Done.
+ TargetElement continueTarget = labelElement.target;
+ targetIndexMap[continueTarget] = switchIndex;
+ assert(builder.jumpTargets[continueTarget] == null);
+ builder.jumpTargets[continueTarget] = this;
+ }
+ }
+ }
+ }
+ switchIndex++;
+ }
+ }
+
+ void generateBreak([LabelElement label]) {
+ if (label == null) {
+ // Creates a special break instruction for the synthetic loop generated
+ // for a switch statement with continue statements. See
+ // [SsaBuilder.visitSwitchStatement] for detail.
+
+ HInstruction breakInstruction =
+ new HBreak(target, breakSwitchContinueLoop: true);
+ LocalsHandler locals = new LocalsHandler.from(builder.localsHandler);
+ builder.close(breakInstruction);
+ jumps.add(new JumpHandlerEntry(breakInstruction, locals));
+ } else {
+ super.generateBreak(label);
+ }
+ }
+
+ void generateContinue([LabelElement label]) {
+ if (label != null && targetIndexMap.containsKey(label.target)) {
ngeoffray 2013/05/14 07:08:51 It would read nicer with a helper method whose nam
Johnni Winther 2013/05/17 07:03:04 Done.
+ // Creates the special instructions 'label = i; continue l;' used in
+ // switch statements with continue statements. See
+ // [SsaBuilder.visitSwitchStatement] for detail.
+
+ assert(label != null);
+ HInstruction value = builder.graph.addConstantInt(
+ targetIndexMap[label.target],
+ builder.constantSystem);
+ builder.localsHandler.updateLocal(target, value);
+
+ assert(label.target.labels.contains(label));
+ HInstruction continueInstruction = new HContinue(target);
+ LocalsHandler locals = new LocalsHandler.from(builder.localsHandler);
+ builder.close(continueInstruction);
+ jumps.add(new JumpHandlerEntry(continueInstruction, locals));
+ } else {
+ super.generateContinue(label);
+ }
+ }
+
+ void close() {
+ // The mapping from TargetElement to JumpHandler is no longer needed.
+ for (TargetElement target in targetIndexMap.keys) {
+ builder.jumpTargets.remove(target);
+ }
+ super.close();
+ }
+}
+
class SsaBuilder extends ResolvedVisitor implements Visitor {
final SsaBuilderTask builder;
final JavaScriptBackend backend;
@@ -1888,7 +1971,7 @@ class SsaBuilder extends ResolvedVisitor implements Visitor {
assert(!isAborted());
HBasicBlock previousBlock = close(new HGoto());
- JumpHandler jumpHandler = createJumpHandler(node);
+ JumpHandler jumpHandler = createJumpHandler(node, isLoopJump: true);
HBasicBlock loopEntry = graph.addNewLoopHeaderBlock(
jumpHandler.target,
jumpHandler.labels());
@@ -4127,13 +4210,22 @@ class SsaBuilder extends ResolvedVisitor implements Visitor {
* Creates a [JumpHandler] for a statement. The node must be a jump
* target. If there are no breaks or continues targeting the statement,
* a special "null handler" is returned.
+ *
+ * [isLoopJump] is [:true:] when the jump handler is for a loop. This is used
+ * to distinguish the synthetized loop created for a switch statement with
+ * continue statements from simple switch statements.
*/
- JumpHandler createJumpHandler(Statement node) {
+ JumpHandler createJumpHandler(Statement node, {bool isLoopJump}) {
TargetElement element = elements[node];
if (element == null || !identical(element.statement, node)) {
// No breaks or continues to this node.
return new NullJumpHandler(compiler);
}
+ if (isLoopJump && node is SwitchStatement) {
+ // Create a special jump handler for loops created for switch statements
+ // with continue statements.
+ return new SwitchCaseJumpHandler(this, element, node);
+ }
return new JumpHandler(this, element);
}
@@ -4273,8 +4365,129 @@ class SsaBuilder extends ResolvedVisitor implements Visitor {
}
visitSwitchStatement(SwitchStatement node) {
- if (tryBuildConstantSwitch(node)) return;
+ // The switch case indices must match those computed in
+ // [SwitchCaseJumpHandler].
+ bool hasContinue = false;
+ Map<SwitchCase, int> caseIndex = new Map<SwitchCase, int>();
+ int switchIndex = 1;
+ bool hasDefault = false;
+ for (SwitchCase switchCase in node.cases) {
+ for (Node labelOrCase in switchCase.labelsAndCases) {
+ Node label = labelOrCase.asLabel();
+ if (label != null) {
+ LabelElement labelElement = elements[label];
+ if (labelElement != null && labelElement.isContinueTarget) {
+ hasContinue = true;
+ }
+ }
+ }
+ if (switchCase.isDefaultCase) {
+ hasDefault = true;
+ }
+ caseIndex[switchCase] = switchIndex;
+ switchIndex++;
+ }
+ if (!hasContinue) {
+ // If the switch statement has no switch cases targeted by continue
+ // statements we encode the switch statement directly.
+ void buildSwitchCase(SwitchCase node) {
+ visit(node.statements);
+ }
+ buildSwitchStatement(node, buildSwitchCase);
+ } else {
+ // If the switch statement has switch cases targeted by continue
+ // statements we create the following encoding:
+ //
+ // switch (e) {
+ // l_1: case e0: s_1; break;
+ // l_2: case e1: s_2; continue l_i;
+ // ...
+ // l_n: default: s_n; continue l_j;
+ // }
+ //
+ // is encoded as
+ //
+ // var target;
+ // switch (e) {
+ // case e1: target = 1; break;
+ // case e2: target = 2; break;
+ // ...
+ // default: target = n; break;
+ // }
+ // l: while (true) {
+ // switch (target) {
+ // case 1: s_1; break l;
+ // case 2: s_2; target = i; continue l;
+ // ...
+ // case n: s_n; target = j; continue l;
+ // }
+ // }
+
+ TargetElement switchTarget = elements[node];
+ HInstruction initialValue = graph.addConstantNull(constantSystem);
+ localsHandler.updateLocal(switchTarget, initialValue);
+ void buildSwitchCase(SwitchCase switchCase) {
+ // Generate 'target = i; break;' for switch case i.
+ int index = caseIndex[switchCase];
+ HInstruction value = graph.addConstantInt(index, constantSystem);
+ localsHandler.updateLocal(switchTarget, value);
+ jumpTargets[switchTarget].generateBreak();
+ }
+ buildSwitchStatement(node, buildSwitchCase);
+
+ HInstruction buildCondition() =>
+ graph.addConstantBool(true, constantSystem);
+ void buildSwitch() {
+ HInstruction buildExpression() {
+ return localsHandler.readLocal(switchTarget);
+ }
+ Iterable<Constant> getConstants(SwitchCase switchCase) {
+ return <Constant>[constantSystem.createInt(caseIndex[switchCase])];
+ }
+ void buildSwitchCase(SwitchCase switchCase) {
+ visit(switchCase.statements);
+ if (!isAborted()) {
+ // Ensure that we break the loop if the case falls through. (This
+ // is only possible for the last case.)
+ jumpTargets[switchTarget].generateBreak();
+ }
+ }
+ // Pass a [NullJumpHandler] because the target for the contained break
+ // is not the generated switch statement but instead the loop generated
+ // in the call to [handleLoop] below.
+ handleSwitch(
+ new NullJumpHandler(compiler),
+ buildExpression, node, getConstants,
+ (_) => false, // No case is default.
+ buildSwitchCase);
+ }
+
+ void buildLoop() {
+ handleLoop(node,
+ () {},
+ buildCondition,
+ () {},
+ buildSwitch);
+ }
+
+ if (hasDefault) {
+ buildLoop();
+ } else {
+ // If the switch statement has no default case, surround the loop with
+ // a test of the target.
+ void buildCondition() {
+ push(createForeign('#', HType.BOOLEAN,
+ [localsHandler.readLocal(switchTarget)]));
+ }
+ handleIf(node, buildCondition, buildLoop, () => {});
+ }
+ }
+ }
+
+ buildSwitchStatement(SwitchStatement node,
+ void buildSwitchCase(SwitchCase switchCase)) {
+ if (tryBuildConstantSwitch(node, buildSwitchCase)) return;
LocalsHandler savedLocals = new LocalsHandler.from(localsHandler);
HBasicBlock startBlock = openNewBlock();
visit(node.expression);
@@ -4284,9 +4497,9 @@ class SsaBuilder extends ResolvedVisitor implements Visitor {
}
Link<Node> cases = node.cases.nodes;
- JumpHandler jumpHandler = createJumpHandler(node);
+ JumpHandler jumpHandler = createJumpHandler(node, isLoopJump: false);
- buildSwitchCases(cases, expression);
+ buildSwitchCases(jumpHandler, cases, expression, buildSwitchCase);
HBasicBlock lastBlock = lastOpenedBlock;
@@ -4324,7 +4537,8 @@ class SsaBuilder extends ResolvedVisitor implements Visitor {
jumpHandler.close();
}
- bool tryBuildConstantSwitch(SwitchStatement node) {
+ bool tryBuildConstantSwitch(SwitchStatement node,
+ void buildSwitchCase(SwitchCase switchCase)) {
Map<CaseMatch, Constant> constants = new Map<CaseMatch, Constant>();
// First check whether all case expressions are compile-time constants,
// and all have the same type that doesn't override operator==.
@@ -4362,9 +4576,6 @@ class SsaBuilder extends ResolvedVisitor implements Visitor {
}
}
constants[labelOrCase] = constant;
- } else {
- compiler.reportWarning(node, "Unsupported: Labels on cases");
- failure = true;
}
}
}
@@ -4372,20 +4583,56 @@ class SsaBuilder extends ResolvedVisitor implements Visitor {
return false;
}
+ JumpHandler jumpHandler = createJumpHandler(node, isLoopJump: false);
+ HInstruction buildExpression() {
+ visit(node.expression);
+ return pop();
+ }
+ Iterable<Constant> getConstants(SwitchCase switchCase) {
+ List<Constant> constantList = <Constant>[];
+ for (Node labelOrCase in switchCase.labelsAndCases) {
+ if (labelOrCase is CaseMatch) {
+ constantList.add(constants[labelOrCase]);
+ }
+ }
+ return constantList;
+ }
+ handleSwitch(jumpHandler, buildExpression, node,
+ getConstants,
+ (SwitchCase switchCase) => switchCase.isDefaultCase,
+ buildSwitchCase);
+ jumpHandler.close();
+ return true;
+ }
+
+ /**
+ * Creates a switch statement [node].
ngeoffray 2013/05/14 07:08:51 Do you mean a [HSwitch] ?
Johnni Winther 2013/05/17 07:03:04 A 'for' was missing. Now it doesn't apply.
+ *
+ * [jumpHandler] is the [JumpHandler] for the created switch statement.
+ * [buildExpression] creates the switch expression.
+ * [getConstants] returns the set of constants for a switch case.
+ * [buildSwitchCase] creates the statements for the switch case.
+ */
+ void handleSwitch(JumpHandler jumpHandler,
+ HInstruction buildExpression(),
+ SwitchStatement node,
+ Iterable<Constant> getConstants(SwitchCase switchCase),
+ bool isDefaultCase(SwitchCase switchCase),
+ void buildSwitchCase(SwitchCase switchCase)) {
+ Map<CaseMatch, Constant> constants = new Map<CaseMatch, Constant>();
+
// TODO(ngeoffray): Handle switch-instruction in bailout code.
work.allowSpeculativeOptimization = false;
// Then build a switch structure.
HBasicBlock expressionStart = openNewBlock();
- visit(node.expression);
- HInstruction expression = pop();
+ HInstruction expression = buildExpression();
if (node.cases.isEmpty) {
- return true;
+ return;
}
HBasicBlock expressionEnd = current;
HSwitch switchInstruction = new HSwitch(<HInstruction>[expression]);
HBasicBlock expressionBlock = close(switchInstruction);
- JumpHandler jumpHandler = createJumpHandler(node);
LocalsHandler savedLocals = localsHandler;
List<List<Constant>> matchExpressions = <List<Constant>>[];
@@ -4398,19 +4645,16 @@ class SsaBuilder extends ResolvedVisitor implements Visitor {
SwitchCase switchCase = caseIterator.next();
List<Constant> caseConstants = <Constant>[];
HBasicBlock block = graph.addNewBlock();
- for (Node labelOrCase in switchCase.labelsAndCases) {
- if (labelOrCase is CaseMatch) {
- Constant constant = constants[labelOrCase];
- caseConstants.add(constant);
- HConstant hConstant = graph.addConstant(constant);
- switchInstruction.inputs.add(hConstant);
- hConstant.usedBy.add(switchInstruction);
- expressionBlock.addSuccessor(block);
- }
+ for (Constant constant in getConstants(switchCase)) {
+ caseConstants.add(constant);
+ HConstant hConstant = graph.addConstant(constant);
+ switchInstruction.inputs.add(hConstant);
+ hConstant.usedBy.add(switchInstruction);
+ expressionBlock.addSuccessor(block);
}
matchExpressions.add(caseConstants);
- if (switchCase.isDefaultCase) {
+ if (isDefaultCase(switchCase)) {
// An HSwitch has n inputs and n+1 successors, the last being the
// default case.
expressionBlock.addSuccessor(block);
@@ -4418,7 +4662,7 @@ class SsaBuilder extends ResolvedVisitor implements Visitor {
}
open(block);
localsHandler = new LocalsHandler.from(savedLocals);
- visit(switchCase.statements);
+ buildSwitchCase(switchCase);
if (!isAborted() && caseIterator.hasNext) {
pushInvokeHelper0(getFallThroughErrorElement, HType.UNKNOWN);
HInstruction error = pop();
@@ -4441,6 +4685,10 @@ class SsaBuilder extends ResolvedVisitor implements Visitor {
instruction.block.addSuccessor(joinBlock);
caseHandlers.add(locals);
});
+ jumpHandler.forEachContinue((HContinue instruction, LocalsHandler locals) {
ngeoffray 2013/05/14 07:08:51 If that's a simple switch, those continues should
Johnni Winther 2013/05/17 07:03:04 It should not happen: If simple, no continue shoul
+ instruction.block.addSuccessor(joinBlock);
+ caseHandlers.add(locals);
+ });
if (!isAborted()) {
current.close(new HGoto());
lastOpenedBlock.addSuccessor(joinBlock);
@@ -4480,7 +4728,6 @@ class SsaBuilder extends ResolvedVisitor implements Visitor {
joinBlock);
jumpHandler.close();
- return true;
}
bool nonPrimitiveTypeOverridesEquals(Constant constant) {
@@ -4520,7 +4767,9 @@ class SsaBuilder extends ResolvedVisitor implements Visitor {
// Recursively build an if/else structure to match the cases.
- void buildSwitchCases(Link<Node> cases, HInstruction expression,
+ void buildSwitchCases(JumpHandler jumpHandler,
+ Link<Node> cases, HInstruction expression,
+ void buildSwitchCase(SwitchCase switchCase),
[int encounteredCaseTypes = 0]) {
final int NO_TYPE = 0;
final int INT_TYPE = 1;
@@ -4532,7 +4781,7 @@ class SsaBuilder extends ResolvedVisitor implements Visitor {
// Called for the statements on all but the last case block.
// Ensures that a user expecting a fallthrough gets an error.
void visitStatementsAndAbort() {
- visit(node.statements);
+ buildSwitchCase(node);
if (!isAborted()) {
compiler.reportWarning(node, 'Missing break at end of switch case');
Element element =
@@ -4557,7 +4806,7 @@ class SsaBuilder extends ResolvedVisitor implements Visitor {
compiler.internalError("Case with no expression and not default",
node: node);
}
- visit(node.statements);
+ buildSwitchCase(node);
// This must be the final case (otherwise "default" would be invalid),
// so we don't need to check for fallthrough.
return;
@@ -4605,18 +4854,19 @@ class SsaBuilder extends ResolvedVisitor implements Visitor {
// TODO(lrn): Stop performing tests when all expressions are compile-time
// constant strings or integers.
handleIf(node, () { buildTests(labelsAndCases); }, (){}, null);
- visit(node.statements);
+ buildSwitchCase(node);
} else {
if (cases.tail.isEmpty) {
handleIf(node,
() { buildTests(labelsAndCases); },
- () { visit(node.statements); },
+ () { buildSwitchCase(node); },
null);
} else {
handleIf(node,
() { buildTests(labelsAndCases); },
() { visitStatementsAndAbort(); },
- () { buildSwitchCases(cases.tail, expression,
+ () { buildSwitchCases(jumpHandler, cases.tail, expression,
+ buildSwitchCase,
encounteredCaseTypes); });
}
}

Powered by Google App Engine
This is Rietveld 408576698