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

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

Issue 2637483002: Implement switch statement, without the "complex switch statement" (aka switch statement with conti… (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/graph_builder.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 b2fa8379325738c4a08fc91bfdd064e3c6df0de4..9c4f1cbbd2558174ec8bc4f522b06739a3ec8bcd 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -40,6 +40,7 @@ import 'locals_handler.dart';
import 'loop_handler.dart';
import 'nodes.dart';
import 'ssa_branch_builder.dart';
+import 'switch_continue_analysis.dart';
import 'type_builder.dart';
import 'types.dart' show TypeMaskFactory;
@@ -558,6 +559,7 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder {
if (expression is ir.Throw) {
// TODO(sra): Prevent generating a statement when inlining.
_visitThrowExpression(expression.expression);
+ handleInTryStatement();
closeAndGotoExit(new HThrow(pop(), null));
} else {
expression.accept(this);
@@ -611,6 +613,7 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder {
}
}
// TODO(het): Add source information
+ handleInTryStatement();
// TODO(het): Set a return value instead of closing the function when we
// support inlining.
closeAndGotoExit(new HReturn(value, null));
@@ -904,8 +907,7 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder {
HLoopInformation loopInfo = current.loopInformation;
HBasicBlock loopEntryBlock = current;
HBasicBlock bodyEntryBlock = current;
- JumpTarget target = astAdapter.elements
- .getTargetDefinition(astAdapter.getNode(doStatement));
+ JumpTarget target = astAdapter.getJumpTarget(doStatement);
bool hasContinues = target != null && target.isContinueTarget;
if (hasContinues) {
// Add extra block to hang labels on.
@@ -1017,8 +1019,7 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder {
// Since the body of the loop has a break, we attach a synthesized label
// to the body.
SubGraph bodyGraph = new SubGraph(bodyEntryBlock, bodyExitBlock);
- JumpTarget target = astAdapter.elements
- .getTargetDefinition(astAdapter.getNode(doStatement));
+ JumpTarget target = astAdapter.getJumpTarget(doStatement);
LabelDefinition label = target.addLabel(null, 'loop');
label.setBreakTarget();
HLabeledBlockInformation info = new HLabeledBlockInformation(
@@ -1042,6 +1043,18 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder {
visitElse: () => ifStatement.otherwise?.accept(this));
}
+ void handleIf(
+ {ir.Node node,
+ void visitCondition(),
+ void visitThen(),
+ void visitElse(),
+ SourceInformation sourceInformation}) {
+ SsaBranchBuilder branchBuilder = new SsaBranchBuilder(
+ this, compiler, node == null ? node : astAdapter.getNode(node));
+ branchBuilder.handleIf(visitCondition, visitThen, visitElse,
+ sourceInformation: sourceInformation);
+ }
+
@override
void visitAsExpression(ir.AsExpression asExpression) {
asExpression.operand.accept(this);
@@ -1115,30 +1128,56 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder {
handleIf(visitCondition: buildCondition, visitThen: fail);
}
+ /// 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 synthesized loop created for a switch statement with
+ /// continue statements from simple switch statements.
+ JumpHandler createJumpHandler(ir.TreeNode node, {bool isLoopJump: false}) {
+ JumpTarget target = astAdapter.getJumpTarget(node);
+ assert(target is KernelJumpTarget);
+ if (target == null) {
+ // No breaks or continues to this node.
+ return new NullJumpHandler(compiler.reporter);
+ }
+ if (isLoopJump && node is ir.SwitchStatement) {
+ throw 'Kernel Switch Statement handler not yet implemented.';
+ }
+
+ return new JumpHandler(this, target);
+ }
+
@override
void visitBreakStatement(ir.BreakStatement breakStatement) {
assert(!isAborted());
+ handleInTryStatement();
JumpTarget target = astAdapter.getJumpTarget(breakStatement.target);
assert(target != null);
JumpHandler handler = jumpTargets[target];
assert(handler != null);
- handler.generateBreak(handler.labels.first);
+ if (handler.labels.isNotEmpty) {
+ handler.generateBreak(handler.labels.first);
+ } else {
+ handler.generateBreak();
+ }
}
@override
void visitLabeledStatement(ir.LabeledStatement labeledStatement) {
- JumpTarget target = astAdapter.getJumpTarget(labeledStatement);
- JumpHandler handler = new JumpHandler(this, target);
-
ir.Statement body = labeledStatement.body;
if (body is ir.WhileStatement ||
body is ir.DoStatement ||
body is ir.ForStatement ||
- body is ir.ForInStatement) {
- // loops handle breaks on their own
+ body is ir.ForInStatement ||
+ body is ir.SwitchStatement) {
+ // loops and switches handle breaks on their own
body.accept(this);
return;
}
+ JumpHandler handler = createJumpHandler(labeledStatement);
+
LocalsHandler beforeLocals = new LocalsHandler.from(localsHandler);
HBasicBlock newBlock = openNewBlock();
@@ -1168,6 +1207,198 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder {
handler.close();
}
+ /// Loop through the cases in a switch and create a mapping of case
+ /// expressions to constants.
+ Map<ir.Expression, ConstantValue> _buildSwitchCaseConstants(
+ ir.SwitchStatement switchStatement) {
+ Map<ir.Expression, ConstantValue> constants =
+ new Map<ir.Expression, ConstantValue>();
+ for (ir.SwitchCase switchCase in switchStatement.cases) {
+ for (ir.Expression caseExpression in switchCase.expressions) {
+ ConstantValue constant = astAdapter.getConstantFor(caseExpression);
+ constants[caseExpression] = constant;
+ }
+ }
+ return constants;
+ }
+
+ @override
+ void visitContinueSwitchStatement(
+ ir.ContinueSwitchStatement switchStatement) {
+ handleInTryStatement();
+ JumpTarget target = astAdapter.getJumpTarget(switchStatement.target);
+ assert(target != null);
+ JumpHandler handler = jumpTargets[target];
+ assert(handler != null);
+ assert(target.labels.isNotEmpty);
+ handler.generateContinue(target.labels.first);
+ }
+
+ @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;
+ Map<ir.SwitchCase, int> caseIndex = new Map<ir.SwitchCase, int>();
+ int switchIndex = 1;
+ bool hasDefault = false;
+ for (ir.SwitchCase switchCase in switchStatement.cases) {
+ if (SwitchContinueAnalysis.containsContinue(switchCase.body)) {
+ hasContinue = true;
+ }
+ if (switchCase.isDefault) {
+ hasDefault = true;
+ }
+ caseIndex[switchCase] = switchIndex;
+ switchIndex++;
+ }
+
+ JumpHandler jumpHandler = createJumpHandler(switchStatement);
+ if (!hasContinue) {
+ // If the switch statement has no switch cases targeted by continue
+ // statements we encode the switch statement directly.
+ _buildSimpleSwitchStatement(switchStatement, jumpHandler, constants);
+ } else {
+ throw 'Complex switch statement with continue label not implemented yet.';
+ }
+ }
+
+ /// Helper for building switch statements.
+ static bool _isDefaultCase(ir.SwitchCase switchCase) =>
+ switchCase == null || switchCase.isDefault;
+
+ /// 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 buildSwitchCase(ir.SwitchCase switchCase) {
+ switchCase.body.accept(this);
+ }
+
+ handleSwitch(switchStatement, jumpHandler, switchStatement.cases,
+ _isDefaultCase, buildSwitchCase, constants);
+ jumpHandler.close();
+ }
+
+ /// Creates a switch statement.
+ ///
+ /// [jumpHandler] is the [JumpHandler] for the created switch statement.
+ /// [buildSwitchCase] creates the statements for the switch case.
+ void handleSwitch(
+ ir.SwitchStatement switchStatement,
+ JumpHandler jumpHandler,
+ List<ir.SwitchCase> switchCases,
+ bool isDefaultCase(ir.SwitchCase switchCase),
+ void buildSwitchCase(ir.SwitchCase switchCase),
+ Map<ir.Expression, ConstantValue> constantsLookup) {
+ 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;
+ }
+
+ if (switchCases.isEmpty) {
+ return;
+ }
+
+ HSwitch switchInstruction = new HSwitch(<HInstruction>[expression]);
+ HBasicBlock expressionEnd = close(switchInstruction);
+ LocalsHandler savedLocals = localsHandler;
+
+ List<HStatementInformation> statements = <HStatementInformation>[];
+ bool hasDefault = false;
+ for (ir.SwitchCase switchCase in switchCases) {
+ HBasicBlock block = graph.addNewBlock();
+ for (ConstantValue constant in getConstants(switchCase)) {
+ HConstant hConstant = graph.addConstant(constant, closedWorld);
+ switchInstruction.inputs.add(hConstant);
+ hConstant.usedBy.add(switchInstruction);
+ expressionEnd.addSuccessor(block);
+ }
+
+ if (isDefaultCase(switchCase)) {
+ // An HSwitch has n inputs and n+1 successors, the last being the
+ // default case.
+ expressionEnd.addSuccessor(block);
+ hasDefault = true;
+ }
+ open(block);
+ localsHandler = new LocalsHandler.from(savedLocals);
+ buildSwitchCase(switchCase);
+ statements.add(
+ new HSubGraphBlockInformation(new SubGraph(block, lastOpenedBlock)));
+ }
+
+ // 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 [caseHandlers] that is used
+ // to create the phis in [joinBlock].
+ // 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> caseHandlers = <LocalsHandler>[];
+ jumpHandler.forEachBreak((HBreak instruction, LocalsHandler locals) {
+ instruction.block.addSuccessor(joinBlock);
+ caseHandlers.add(locals);
+ });
+ jumpHandler.forEachContinue((HContinue instruction, LocalsHandler locals) {
+ assert(invariant(astAdapter.getNode(switchStatement), false,
+ message: 'Continue cannot target a switch.'));
+ });
+ if (!isAborted()) {
+ current.close(new HGoto());
+ lastOpenedBlock.addSuccessor(joinBlock);
+ caseHandlers.add(localsHandler);
+ }
+ if (!hasDefault) {
+ // Always create a default case, to avoid a critical edge in the
+ // graph.
+ HBasicBlock defaultCase = addNewBlock();
+ expressionEnd.addSuccessor(defaultCase);
+ open(defaultCase);
+ close(new HGoto());
+ defaultCase.addSuccessor(joinBlock);
+ caseHandlers.add(savedLocals);
+ statements.add(new HSubGraphBlockInformation(
+ new SubGraph(defaultCase, defaultCase)));
+ }
+ assert(caseHandlers.length == joinBlock.predecessors.length);
+ if (caseHandlers.length != 0) {
+ graph.addBlock(joinBlock);
+ open(joinBlock);
+ if (caseHandlers.length == 1) {
+ localsHandler = caseHandlers[0];
+ } else {
+ localsHandler = savedLocals.mergeMultiple(caseHandlers, joinBlock);
+ }
+ } else {
+ // The joinblock is not used.
+ joinBlock = null;
+ }
+
+ HSubExpressionBlockInformation expressionInfo =
+ new HSubExpressionBlockInformation(
+ new SubExpression(expressionStart, expressionEnd));
+ expressionStart.setBlockFlow(
+ new HSwitchBlockInformation(
+ expressionInfo, statements, jumpHandler.target, jumpHandler.labels),
+ joinBlock);
+
+ jumpHandler.close();
+ }
+
@override
void visitConditionalExpression(ir.ConditionalExpression conditional) {
SsaBranchBuilder brancher = new SsaBranchBuilder(this, compiler);
@@ -2326,6 +2557,7 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder {
void visitThrow(ir.Throw throwNode) {
_visitThrowExpression(throwNode.expression);
if (isReachable) {
+ handleInTryStatement();
push(new HThrowExpression(pop(), null));
isReachable = false;
}
@@ -2362,6 +2594,7 @@ class KernelSsaBuilder extends ir.Visitor with GraphBuilder {
compiler.reporter.internalError(astAdapter.getNode(rethrowNode),
'rethrowableException should not be null.');
}
+ handleInTryStatement();
SourceInformation sourceInformation = null;
closeAndGotoExit(new HThrow(exception, sourceInformation, isRethrow: true));
// ir.Rethrow is an expression so we need to push a value - a constant with
@@ -2446,6 +2679,10 @@ class TryCatchFinallyBuilder {
HLocalValue exception;
KernelSsaBuilder kernelBuilder;
+ /// True if the code surrounding this try statement was also part of a
+ /// try/catch/finally statement.
+ bool previouslyInTryStatement;
+
SubGraph bodyGraph;
SubGraph catchGraph;
SubGraph finallyGraph;
@@ -2463,6 +2700,8 @@ class TryCatchFinallyBuilder {
originalSavedLocals = new LocalsHandler.from(kernelBuilder.localsHandler);
enterBlock = kernelBuilder.openNewBlock();
kernelBuilder.close(tryInstruction);
+ previouslyInTrySequence = kernelBuilder.inTryStatement;
+ kernelBuilder.inTryStatement = true;
startTryBlock = kernelBuilder.graph.addNewBlock();
kernelBuilder.open(startTryBlock);
@@ -2644,5 +2883,6 @@ class TryCatchFinallyBuilder {
kernelBuilder.wrapStatementGraph(catchGraph),
kernelBuilder.wrapStatementGraph(finallyGraph)),
exitBlock);
+ kernelBuilder.inTryStatement = previouslyInTrySequence;
}
}
« no previous file with comments | « pkg/compiler/lib/src/ssa/builder.dart ('k') | pkg/compiler/lib/src/ssa/graph_builder.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698