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; |