mirror of
https://github.com/dart-lang/sdk
synced 2024-09-20 00:11:50 +00:00
Implement constant switch as JS switch.
This introduces a block-structure for constant switches. Non-constant switches still use the old if-else-if implementation. Review URL: https://chromiumcodereview.appspot.com//10544024 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@8331 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
parent
931f116dfe
commit
745cb6085f
|
@ -2913,6 +2913,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);
|
||||
|
@ -2920,8 +2922,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);
|
||||
|
@ -2962,10 +2964,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;
|
||||
|
|
|
@ -633,6 +633,45 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool visitSwitchInfo(HSwitchBlockInformation info) {
|
||||
bool isExpression = isJSExpression(info.expression);
|
||||
if (!isExpression) {
|
||||
generateStatements(info.expression);
|
||||
}
|
||||
addIndentation();
|
||||
for (LabelElement label in info.labels) {
|
||||
if (label.isTarget) {
|
||||
writeLabel(label);
|
||||
buffer.add(":");
|
||||
}
|
||||
}
|
||||
addIndented("switch (");
|
||||
if (isExpression) {
|
||||
generateExpression(info.expression);
|
||||
} else {
|
||||
use(info.expression.conditionExpression,
|
||||
JSPrecedence.EXPRESSION_PRECEDENCE);
|
||||
}
|
||||
buffer.add(") {\n");
|
||||
indent++;
|
||||
for (int i = 0; i < info.matchExpressions.length; i++) {
|
||||
for (Constant constant in info.matchExpressions[i]) {
|
||||
addIndented("case ");
|
||||
generateConstant(constant);
|
||||
buffer.add(":\n");
|
||||
}
|
||||
if (i == info.matchExpressions.length - 1 && info.hasDefault) {
|
||||
addIndented("default:\n");
|
||||
}
|
||||
indent++;
|
||||
generateStatements(info.statements[i]);
|
||||
indent--;
|
||||
}
|
||||
indent--;
|
||||
addIndented("}\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool visitSequenceInfo(HStatementSequenceInformation info) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1565,28 +1604,33 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
|
|||
endExpression(JSPrecedence.MEMBER_PRECEDENCE);
|
||||
}
|
||||
|
||||
visitConstant(HConstant node) {
|
||||
assert(isGenerateAtUseSite(node));
|
||||
void generateConstant(Constant constant) {
|
||||
// TODO(floitsch): the compile-time constant handler and the codegen
|
||||
// need to work together to avoid the parenthesis. See r4928 for an
|
||||
// implementation that still dealt with precedence.
|
||||
ConstantHandler handler = compiler.constantHandler;
|
||||
String name = handler.getNameForConstant(node.constant);
|
||||
String name = handler.getNameForConstant(constant);
|
||||
if (name === null) {
|
||||
assert(!node.constant.isObject());
|
||||
if (node.constant.isNum()
|
||||
assert(!constant.isObject());
|
||||
if (constant.isNum()
|
||||
&& expectedPrecedence == JSPrecedence.MEMBER_PRECEDENCE) {
|
||||
buffer.add('(');
|
||||
handler.writeConstant(buffer, node.constant);
|
||||
handler.writeConstant(buffer, constant);
|
||||
buffer.add(')');
|
||||
} else {
|
||||
handler.writeConstant(buffer, node.constant);
|
||||
handler.writeConstant(buffer, constant);
|
||||
}
|
||||
} else {
|
||||
buffer.add(compiler.namer.CURRENT_ISOLATE);
|
||||
buffer.add(".");
|
||||
buffer.add(name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
visitConstant(HConstant node) {
|
||||
assert(isGenerateAtUseSite(node));
|
||||
generateConstant(node.constant);
|
||||
}
|
||||
|
||||
visitLoopBranch(HLoopBranch node) {
|
||||
|
@ -1794,6 +1838,10 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
|
|||
buffer.add(text);
|
||||
}
|
||||
|
||||
void visitSwitch(HSwitch node) {
|
||||
// Switches are handled using [visitSwitchInfo].
|
||||
}
|
||||
|
||||
void visitStatic(HStatic node) {
|
||||
world.registerStaticUse(node.element);
|
||||
buffer.add(compiler.namer.isolateAccess(node.element));
|
||||
|
|
|
@ -52,6 +52,7 @@ interface HVisitor<R> {
|
|||
R visitStatic(HStatic node);
|
||||
R visitStaticStore(HStaticStore node);
|
||||
R visitSubtract(HSubtract node);
|
||||
R visitSwitch(HSwitch node);
|
||||
R visitThis(HThis node);
|
||||
R visitThrow(HThrow node);
|
||||
R visitTruncatingDivide(HTruncatingDivide node);
|
||||
|
@ -300,6 +301,7 @@ class HBaseVisitor extends HGraphVisitor implements HVisitor {
|
|||
visitShiftRight(HShiftRight node) => visitBinaryBitOp(node);
|
||||
visitShiftLeft(HShiftLeft node) => visitBinaryBitOp(node);
|
||||
visitSubtract(HSubtract node) => visitBinaryArithmetic(node);
|
||||
visitSwitch(HSwitch node) => visitControlFlow(node);
|
||||
visitStatic(HStatic node) => visitInstruction(node);
|
||||
visitStaticStore(HStaticStore node) => visitInstruction(node);
|
||||
visitThis(HThis node) => visitParameterValue(node);
|
||||
|
@ -333,7 +335,7 @@ class SubExpression extends SubGraph {
|
|||
/** Find the condition expression if this sub-expression is a condition. */
|
||||
HInstruction get conditionExpression() {
|
||||
HInstruction last = end.last;
|
||||
if (last is HConditionalBranch) return last.inputs[0];
|
||||
if (last is HConditionalBranch || last is HSwitch) return last.inputs[0];
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -1478,6 +1480,26 @@ class HSubtract extends HBinaryArithmetic {
|
|||
bool dataEquals(HInstruction other) => true;
|
||||
}
|
||||
|
||||
/**
|
||||
* An [HSwitch] instruction has one input for the incoming
|
||||
* value, and one input per constant that it can switch on.
|
||||
* Its block has one successor per constant, and one for the default.
|
||||
* If the switch didn't have a default case, the last successor is
|
||||
* the join block.
|
||||
*/
|
||||
class HSwitch extends HControlFlow {
|
||||
HSwitch(List<HInstruction> inputs) : super(inputs);
|
||||
|
||||
HConstant constant(int index) => inputs[index + 1];
|
||||
HInstruction get expression() => inputs[0];
|
||||
|
||||
HBasicBlock get defaultTarget() => block.successors.last();
|
||||
|
||||
accept(HVisitor visitor) => visitor.visitSwitch(this);
|
||||
|
||||
String toString() => "HSwitch cases = $inputs";
|
||||
}
|
||||
|
||||
class HTruncatingDivide extends HBinaryArithmetic {
|
||||
HTruncatingDivide(HStatic target, HInstruction left, HInstruction right)
|
||||
: super(target, left, right);
|
||||
|
@ -2308,6 +2330,7 @@ interface HStatementInformationVisitor {
|
|||
bool visitLoopInfo(HLoopBlockInformation info);
|
||||
bool visitIfInfo(HIfBlockInformation info);
|
||||
bool visitTryInfo(HTryBlockInformation info);
|
||||
bool visitSwitchInfo(HSwitchBlockInformation info);
|
||||
bool visitSequenceInfo(HStatementSequenceInformation info);
|
||||
// Pseudo-structure embedding a dominator-based traversal into
|
||||
// the block-structure traversal. This will eventually go away.
|
||||
|
@ -2503,3 +2526,33 @@ class HTryBlockInformation implements HStatementInformation {
|
|||
bool accept(HStatementInformationVisitor visitor) =>
|
||||
visitor.visitTryInfo(this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
class HSwitchBlockInformation implements HStatementInformation {
|
||||
final HExpressionInformation expression;
|
||||
final List<List<Constant>> matchExpressions;
|
||||
final List<HStatementInformation> statements;
|
||||
// If the switch has a default, it's the last statement block, which
|
||||
// may or may not have other expresions.
|
||||
final bool hasDefault;
|
||||
final TargetElement target;
|
||||
final List<LabelElement> labels;
|
||||
|
||||
HSwitchBlockInformation(this.expression,
|
||||
this.matchExpressions,
|
||||
this.statements,
|
||||
this.hasDefault,
|
||||
this.target,
|
||||
this.labels);
|
||||
|
||||
HBasicBlock get start() => expression.start;
|
||||
HBasicBlock get end() {
|
||||
// We don't create a switch block if there are no cases.
|
||||
assert(!statements.isEmpty());
|
||||
return statements.last().end;
|
||||
}
|
||||
|
||||
bool accept(HStatementInformationVisitor visitor) =>
|
||||
visitor.visitSwitchInfo(this);
|
||||
}
|
||||
|
|
|
@ -383,6 +383,22 @@ class HInstructionStringifier implements HVisitor<String> {
|
|||
|
||||
String visitSubtract(HSubtract node) => visitInvokeStatic(node);
|
||||
|
||||
String visitSwitch(HSwitch node) {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.add("Switch: (");
|
||||
buf.add(temporaryId(node.inputs[0]));
|
||||
buf.add(") ");
|
||||
for (int i = 1; i < node.inputs.length; i++) {
|
||||
buf.add(temporaryId(node.inputs[i]));
|
||||
buf.add(": B");
|
||||
buf.add(node.block.successors[i - 1].id);
|
||||
buf.add(", ");
|
||||
}
|
||||
buf.add("default: B");
|
||||
buf.add(node.block.successors.last().id);
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
String visitThis(HThis node) => "this";
|
||||
|
||||
String visitThrow(HThrow node) => "Throw ${temporaryId(node.inputs[0])}";
|
||||
|
|
|
@ -63,7 +63,6 @@ generic_test: Fail # cannot resolve type T
|
|||
get_set_syntax_test/none: Fail # Parsing of pseudo keywords.
|
||||
getters_setters_test: Fail # Internal Error: SsaBuilder.visitIdentifier on non-this
|
||||
implicit_scope_test: Fail # duplicate definition of a="bar"
|
||||
label_test: Fail # switch cases are not implemented
|
||||
library_prefixes_test: Fail # other is not a type
|
||||
local_function_test: Fail # TypeError: Object #<Closure> has no method '$call$2' (bad exception mapping).
|
||||
many_generic_instanceof_test: Fail # cannot resolve type T
|
||||
|
|
Loading…
Reference in a new issue