Chromium Code Reviews| Index: lib/compiler/implementation/ssa/builder.dart |
| diff --git a/lib/compiler/implementation/ssa/builder.dart b/lib/compiler/implementation/ssa/builder.dart |
| index 53aacdd1fe8c8884ba66ad2fce96aac676ae5162..395a634f5099a5c376c1586e2674793fc0888c75 100644 |
| --- a/lib/compiler/implementation/ssa/builder.dart |
| +++ b/lib/compiler/implementation/ssa/builder.dart |
| @@ -2941,6 +2941,8 @@ class SsaBuilder implements Visitor { |
| } |
| visitSwitchStatement(SwitchStatement node) { |
| + if (tryBuildConstantSwitch(node)) return; |
| + |
| LocalsHandler savedLocals = new LocalsHandler.from(localsHandler); |
| HBasicBlock startBlock = openNewBlock(); |
| visit(node.expression); |
| @@ -2948,8 +2950,8 @@ class SsaBuilder 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); |
| @@ -2990,10 +2992,134 @@ class SsaBuilder 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. |
|
Lasse Reichstein Nielsen
2012/06/06 09:29:31
I hope it's simple if you know how :)
|
| + 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; |
| + for (Iterator<Node> iterator = node.cases.iterator(); iterator.hasNext();) { |
|
floitsch
2012/06/06 11:50:26
I would prefer a while loop.
Lasse Reichstein Nielsen
2012/06/06 13:01:21
Done.
|
| + SwitchCase switchCase = iterator.next(); |
| + List<Constant> caseConstants = <Constant>[]; |
| + HBasicBlock block = graph.addNewBlock(); |
| + for (Node labelOrCase in switchCase.labelsAndCases) { |
| + if (labelOrCase is CaseMatch) { |
|
floitsch
2012/06/06 11:50:26
At the moment an assert would work too here, no?
Lasse Reichstein Nielsen
2012/06/06 13:01:21
No, we still parse the labels, we just don't do an
floitsch
2012/06/06 14:32:01
But isn't there a "return false" in line 3009 for
Lasse Reichstein Nielsen
2012/06/07 12:03:05
True, there is. Let's not make the assumption in m
|
| + 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() && iterator.hasNext()) { |
| + compiler.reportWarning(node, 'Missing break at end of switch case'); |
|
ngeoffray
2012/06/06 12:03:46
I don't think the codegen should emit a warning.
Lasse Reichstein Nielsen
2012/06/06 13:01:21
True. Something earlier should do that.
I'll remo
|
| + Element element = |
| + compiler.findHelper(const SourceString("getFallThroughError")); |
|
ngeoffray
2012/06/06 12:03:46
Maybe move that call out of the loop?
Lasse Reichstein Nielsen
2012/06/06 13:01:21
Reasonable. Done.
|
| + push(new HStatic(element)); |
| + 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. |
| + HBasicBlock joinBlock = new HBasicBlock(); |
| + List<LocalsHandler> caseLocals = <LocalsHandler>[]; |
| + jumpHandler.forEachBreak((HBreak instruction, LocalsHandler locals) { |
| + instruction.block.addSuccessor(joinBlock); |
| + caseLocals.add(locals); |
| + }); |
| + if (!isAborted()) { |
|
ngeoffray
2012/06/06 12:03:46
Should this check be in the one line 3090? You're
Lasse Reichstein Nielsen
2012/06/06 13:01:21
If this test succeedes, it adds an element to case
|
| + current.close(new HGoto()); |
| + lastOpenedBlock.addSuccessor(joinBlock); |
| + caseLocals.add(localsHandler); |
| + } |
| + if (!hasDefault) { |
|
ngeoffray
2012/06/06 12:03:46
ditto
Lasse Reichstein Nielsen
2012/06/06 13:01:21
And ditto.
|
| + // 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); |
| + } |
| + 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; |