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

Unified Diff: pkg/compiler/lib/src/ssa/builder_kernel.dart

Issue 2648443004: Implement complex switch statement (switch with continue). (Closed)
Patch Set: . Created 3 years, 11 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
« no previous file with comments | « pkg/compiler/lib/src/ssa/builder.dart ('k') | pkg/compiler/lib/src/ssa/jump_handler.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/compiler/lib/src/ssa/builder_kernel.dart
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart
index 223889ecc1e916ceedd510536196d5b16904cec4..8dd05aed6f751f95a0596d3ad1101acbd4ec69da 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -1143,7 +1143,7 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder {
return new NullJumpHandler(compiler.reporter);
}
if (isLoopJump && node is ir.SwitchStatement) {
- throw 'Kernel Switch Statement handler not yet implemented.';
+ return new KernelSwitchCaseJumpHandler(this, target, node, astAdapter);
}
return new JumpHandler(this, target);
@@ -1236,9 +1236,6 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder {
@override
void visitSwitchStatement(ir.SwitchStatement switchStatement) {
- Map<ir.Expression, ConstantValue> constants =
- _buildSwitchCaseConstants(switchStatement);
-
// The switch case indices must match those computed in
// [KernelSwitchCaseJumpHandler].
bool hasContinue = false;
@@ -1260,9 +1257,10 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder {
if (!hasContinue) {
// If the switch statement has no switch cases targeted by continue
// statements we encode the switch statement directly.
- _buildSimpleSwitchStatement(switchStatement, jumpHandler, constants);
+ _buildSimpleSwitchStatement(switchStatement, jumpHandler);
} else {
- throw 'Complex switch statement with continue label not implemented yet.';
+ _buildComplexSwitchStatement(
+ switchStatement, jumpHandler, caseIndex, hasDefault);
}
}
@@ -1270,43 +1268,192 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder {
static bool _isDefaultCase(ir.SwitchCase switchCase) =>
switchCase == null || switchCase.isDefault;
+ /// Helper for building switch statements.
+ HInstruction _buildExpression(ir.SwitchStatement switchStatement) {
+ switchStatement.expression.accept(this);
+ return pop();
+ }
+
+ /// Helper method for creating the list of constants that make up the
+ /// switch case branches.
+ List<ConstantValue> _getSwitchConstants(
+ ir.SwitchStatement parentSwitch, ir.SwitchCase switchCase) {
+ Map<ir.Expression, ConstantValue> constantsLookup =
+ _buildSwitchCaseConstants(parentSwitch);
+ List<ConstantValue> constantList = <ConstantValue>[];
+ if (switchCase != null) {
+ for (var expression in switchCase.expressions) {
+ constantList.add(constantsLookup[expression]);
+ }
+ }
+ return constantList;
+ }
+
/// Builds a simple switch statement which does not handle uses of continue
/// statements to labeled switch cases.
- void _buildSimpleSwitchStatement(ir.SwitchStatement switchStatement,
- JumpHandler jumpHandler, Map<ir.Expression, ConstantValue> constants) {
+ void _buildSimpleSwitchStatement(
+ ir.SwitchStatement switchStatement, JumpHandler jumpHandler) {
void buildSwitchCase(ir.SwitchCase switchCase) {
switchCase.body.accept(this);
}
- handleSwitch(switchStatement, jumpHandler, switchStatement.cases,
- _isDefaultCase, buildSwitchCase, constants);
+ _handleSwitch(
+ switchStatement,
+ jumpHandler,
+ _buildExpression,
+ switchStatement.cases,
+ _getSwitchConstants,
+ _isDefaultCase,
+ buildSwitchCase);
jumpHandler.close();
}
+ /// Builds a switch statement that can handle arbitrary uses of continue
+ /// statements to labeled switch cases.
+ void _buildComplexSwitchStatement(
+ ir.SwitchStatement switchStatement,
+ JumpHandler jumpHandler,
+ Map<ir.SwitchCase, int> caseIndex,
+ bool hasDefault) {
+ // 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;
+ // }
+ // }
+ //
+ // This is because JS does not have this same "continue label" semantics so
+ // we encode it in the form of a state machine.
+
+ JumpTarget switchTarget = astAdapter.getJumpTarget(switchStatement.parent);
+ localsHandler.updateLocal(switchTarget, graph.addConstantNull(closedWorld));
+
+ var switchCases = switchStatement.cases;
+ if (!hasDefault) {
+ // Use null as the marker for a synthetic default clause.
+ // The synthetic default is added because otherwise there would be no
+ // good place to give a default value to the local.
+ switchCases = new List<ir.SwitchCase>.from(switchCases);
+ switchCases.add(null);
+ }
+
+ void buildSwitchCase(ir.SwitchCase switchCase) {
+ if (switchCase != null) {
+ // Generate 'target = i; break;' for switch case i.
+ int index = caseIndex[switchCase];
+ HInstruction value = graph.addConstantInt(index, closedWorld);
+ localsHandler.updateLocal(switchTarget, value);
+ } else {
+ // Generate synthetic default case 'target = null; break;'.
+ HInstruction nullValue = graph.addConstantNull(closedWorld);
+ localsHandler.updateLocal(switchTarget, nullValue);
+ }
+ jumpTargets[switchTarget].generateBreak();
+ }
+
+ _handleSwitch(switchStatement, jumpHandler, _buildExpression, switchCases,
+ _getSwitchConstants, _isDefaultCase, buildSwitchCase);
+ jumpHandler.close();
+
+ HInstruction buildCondition() => graph.addConstantBool(true, closedWorld);
+
+ void buildSwitch() {
+ HInstruction buildExpression(ir.SwitchStatement notUsed) {
+ return localsHandler.readLocal(switchTarget);
+ }
+
+ List<ConstantValue> getConstants(
+ ir.SwitchStatement parentSwitch, ir.SwitchCase switchCase) {
+ return <ConstantValue>[
+ backend.constantSystem.createInt(caseIndex[switchCase])
+ ];
+ }
+
+ void buildSwitchCase(ir.SwitchCase switchCase) {
+ switchCase.body.accept(this);
+ 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(
+ switchStatement, // nor is buildExpression.
+ new NullJumpHandler(compiler.reporter),
+ buildExpression,
+ switchStatement.cases,
+ getConstants,
+ (_) => false, // No case is default.
+ buildSwitchCase);
+ }
+
+ void buildLoop() {
+ loopHandler.handleLoop(
+ switchStatement, () {}, buildCondition, () {}, buildSwitch);
+ }
+
+ if (hasDefault) {
+ buildLoop();
+ } else {
+ // If the switch statement has no default case, surround the loop with
+ // a test of the target. So:
+ // `if (target) while (true) ...` If there's no default case, target is
+ // null, so we don't drop into the while loop.
+ void buildCondition() {
+ js.Template code = js.js.parseForeignJS('#');
+ push(new HForeignCode(
+ code, commonMasks.boolType, [localsHandler.readLocal(switchTarget)],
+ nativeBehavior: native.NativeBehavior.PURE));
+ }
+
+ handleIf(
+ node: switchStatement,
+ visitCondition: buildCondition,
+ visitThen: buildLoop,
+ visitElse: () => {});
+ }
+ }
+
/// Creates a switch statement.
///
/// [jumpHandler] is the [JumpHandler] for the created switch statement.
/// [buildSwitchCase] creates the statements for the switch case.
- void handleSwitch(
+ void _handleSwitch(
ir.SwitchStatement switchStatement,
JumpHandler jumpHandler,
+ HInstruction buildExpression(ir.SwitchStatement statement),
List<ir.SwitchCase> switchCases,
+ List<ConstantValue> getConstants(
+ ir.SwitchStatement parentSwitch, ir.SwitchCase switchCase),
bool isDefaultCase(ir.SwitchCase switchCase),
- void buildSwitchCase(ir.SwitchCase switchCase),
- Map<ir.Expression, ConstantValue> constantsLookup) {
+ void buildSwitchCase(ir.SwitchCase switchCase)) {
HBasicBlock expressionStart = openNewBlock();
- switchStatement.expression.accept(this);
- HInstruction expression = pop();
-
- List<ConstantValue> getConstants(ir.SwitchCase switchCase) {
- List<ConstantValue> constantList = <ConstantValue>[];
- if (switchCase != null) {
- for (var expression in switchCase.expressions) {
- constantList.add(constantsLookup[expression]);
- }
- }
- return constantList;
- }
+ HInstruction expression = buildExpression(switchStatement);
if (switchCases.isEmpty) {
return;
@@ -1320,7 +1467,8 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder {
bool hasDefault = false;
for (ir.SwitchCase switchCase in switchCases) {
HBasicBlock block = graph.addNewBlock();
- for (ConstantValue constant in getConstants(switchCase)) {
+ for (ConstantValue constant
+ in getConstants(switchStatement, switchCase)) {
HConstant hConstant = graph.addConstant(constant, closedWorld);
switchInstruction.inputs.add(hConstant);
hConstant.usedBy.add(switchInstruction);
« no previous file with comments | « pkg/compiler/lib/src/ssa/builder.dart ('k') | pkg/compiler/lib/src/ssa/jump_handler.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698