| Index: lib/compiler/implementation/ssa/builder.dart
|
| diff --git a/lib/compiler/implementation/ssa/builder.dart b/lib/compiler/implementation/ssa/builder.dart
|
| index 3675c3807a66c8d2a433d51f3ba34e3b0fcf6bf6..1f18b7cb03240de0922772074dfe335a7caeecaa 100644
|
| --- a/lib/compiler/implementation/ssa/builder.dart
|
| +++ b/lib/compiler/implementation/ssa/builder.dart
|
| @@ -2921,6 +2921,8 @@ class SsaBuilder extends ResolvedVisitor implements Visitor {
|
| }
|
|
|
| visitSwitchStatement(SwitchStatement node) {
|
| + if (tryBuildConstantSwitch(node)) return;
|
| +
|
| LocalsHandler savedLocals = new LocalsHandler.from(localsHandler);
|
| HBasicBlock startBlock = openNewBlock();
|
| visit(node.expression);
|
| @@ -2928,8 +2930,8 @@ class SsaBuilder extends ResolvedVisitor implements Visitor {
|
| if (node.cases.isEmpty()) {
|
| return;
|
| }
|
| - Link<Node> cases = node.cases.nodes;
|
|
|
| + Link<Node> cases = node.cases.nodes;
|
| JumpHandler jumpHandler = createJumpHandler(node);
|
|
|
| buildSwitchCases(cases, expression);
|
| @@ -2970,10 +2972,141 @@ class SsaBuilder extends ResolvedVisitor implements Visitor {
|
| jumpHandler.close();
|
| }
|
|
|
| + bool tryBuildConstantSwitch(SwitchStatement node) {
|
| + Map<CaseMatch, Constant> constants = new Map<CaseMatch, Constant>();
|
| + // First check whether all case expressions are compile-time constants.
|
| + for (SwitchCase switchCase in node.cases) {
|
| + for (Node labelOrCase in switchCase.labelsAndCases) {
|
| + if (labelOrCase is CaseMatch) {
|
| + CaseMatch match = labelOrCase;
|
| + Constant constant =
|
| + compiler.constantHandler.tryCompileNodeWithDefinitions(
|
| + match.expression, elements);
|
| + if (constant === null) return false;
|
| + constants[labelOrCase] = constant;
|
| + } else {
|
| + // We don't handle labels yet.
|
| + return false;
|
| + }
|
| + }
|
| + }
|
| + // 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();
|
| + if (node.cases.isEmpty()) {
|
| + return true;
|
| + }
|
| + 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>>[];
|
| + List<HStatementInformation> statements = <HStatementInformation>[];
|
| + bool hasDefault = false;
|
| + Element getFallThroughErrorElement =
|
| + compiler.findHelper(const SourceString("getFallThroughError"));
|
| + Iterator<Node> caseIterator = node.cases.iterator();
|
| + while (caseIterator.hasNext()) {
|
| + 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);
|
| + }
|
| + }
|
| + matchExpressions.add(caseConstants);
|
| +
|
| + if (switchCase.isDefaultCase) {
|
| + // An HSwitch has n inputs and n+1 successors, the last being the
|
| + // default case.
|
| + expressionBlock.addSuccessor(block);
|
| + hasDefault = true;
|
| + }
|
| + open(block);
|
| + localsHandler = new LocalsHandler.from(savedLocals);
|
| + visit(switchCase.statements);
|
| + if (!isAborted() && caseIterator.hasNext()) {
|
| + push(new HStatic(getFallThroughErrorElement));
|
| + HInstruction error = new HInvokeStatic(
|
| + Selector.INVOCATION_0, <HInstruction>[pop()]);
|
| + add(error);
|
| + close(new HThrow(error));
|
| + }
|
| + 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 [caseLocals] that is used
|
| + // to create the phis in [joinBlock].
|
| + // If we never jump to the join block, [caseLocals] will stay empty, and
|
| + // the join block is never added to the graph.
|
| + HBasicBlock joinBlock = new HBasicBlock();
|
| + List<LocalsHandler> caseLocals = <LocalsHandler>[];
|
| + jumpHandler.forEachBreak((HBreak instruction, LocalsHandler locals) {
|
| + instruction.block.addSuccessor(joinBlock);
|
| + caseLocals.add(locals);
|
| + });
|
| + if (!isAborted()) {
|
| + current.close(new HGoto());
|
| + lastOpenedBlock.addSuccessor(joinBlock);
|
| + caseLocals.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);
|
| + }
|
| + assert(caseLocals.length == joinBlock.predecessors.length);
|
| + if (caseLocals.length != 0) {
|
| + graph.addBlock(joinBlock);
|
| + open(joinBlock);
|
| + if (caseLocals.length == 1) {
|
| + localsHandler = caseLocals[0];
|
| + } else {
|
| + localsHandler = savedLocals.mergeMultiple(caseLocals, joinBlock);
|
| + }
|
| + } else {
|
| + // The joinblock is not used.
|
| + joinBlock = null;
|
| + }
|
| +
|
| + HSubExpressionBlockInformation expressionInfo =
|
| + new HSubExpressionBlockInformation(new SubExpression(expressionStart,
|
| + expressionEnd));
|
| + expressionStart.setBlockFlow(
|
| + new HSwitchBlockInformation(expressionInfo,
|
| + matchExpressions,
|
| + statements,
|
| + hasDefault,
|
| + jumpHandler.target,
|
| + jumpHandler.labels()),
|
| + joinBlock);
|
| +
|
| + jumpHandler.close();
|
| + return true;
|
| + }
|
| +
|
|
|
| // Recursively build an if/else structure to match the cases.
|
| - buildSwitchCases(Link<Node> cases, HInstruction expression,
|
| - [int encounteredCaseTypes = 0]) {
|
| + void buildSwitchCases(Link<Node> cases, HInstruction expression,
|
| + [int encounteredCaseTypes = 0]) {
|
| final int NO_TYPE = 0;
|
| final int INT_TYPE = 1;
|
| final int STRING_TYPE = 2;
|
|
|