Chromium Code Reviews| Index: frog/leg/ssa/codegen.dart |
| diff --git a/frog/leg/ssa/codegen.dart b/frog/leg/ssa/codegen.dart |
| index e3412386c8318480078a0f35297013ea52eda4ae..843e05d4edb86e2b7a466c81a8bd7acfbf432eb1 100644 |
| --- a/frog/leg/ssa/codegen.dart |
| +++ b/frog/leg/ssa/codegen.dart |
| @@ -61,6 +61,18 @@ class SsaCodeGeneratorTask extends CompilerTask { |
| typedef void ElementAction(Element element); |
| class SsaCodeGenerator implements HVisitor { |
| + /** |
| + * Current state for generating simple (non-local-control) code. |
| + * It is generated as either statements (indented and ';'-terminated), |
| + * expressions (comma separated) or declarations (also comma separated, |
| + * but expected to be preceeded by a 'var' so it declares its variables); |
| + */ |
| + static final int STATE_STATEMENT = 0; |
| + static final int STATE_FIRST_EXPRESSION = 1; |
| + static final int STATE_FIRST_DECLARATION = 2; |
| + static final int STATE_EXPRESSION = 3; |
| + static final int STATE_DECLARATION = 4; |
| + |
| final Compiler compiler; |
| final WorkItem work; |
| final StringBuffer buffer; |
| @@ -78,11 +90,21 @@ class SsaCodeGenerator implements HVisitor { |
| int indent = 0; |
| int expectedPrecedence = JSPrecedence.STATEMENT_PRECEDENCE; |
| HGraph currentGraph; |
| + /** |
| + * Whether the code-generation should try to generate an expression |
| + * instead of a sequence of statements. |
| + */ |
| + int generationState = STATE_STATEMENT; |
| + /** |
| + * While generating expressions, we can't insert variable declarations. |
| + * Instead we declare them at the end of the function |
| + */ |
| + Link<String> delayedVarDecl = const EmptyLink<String>(); |
| HBasicBlock currentBlock; |
| // Records a block-information that is being handled specially. |
| // Used to break bad recursion. |
| - HLabeledBlockInformation currentBlockInformation; |
| + HBlockInformation currentBlockInformation; |
| // The subgraph is used to delimit traversal for some constructions, e.g., |
| // if branches. |
| SubGraph subGraph; |
| @@ -154,6 +176,17 @@ class SsaCodeGenerator implements HVisitor { |
| subGraph = new SubGraph(graph.entry, graph.exit); |
| beginGraph(graph); |
| visitBasicBlock(graph.entry); |
| + if (!delayedVarDecl.isEmpty()) { |
| + addIndentation(); |
| + buffer.add("var "); |
| + while (true) { |
| + buffer.add(delayedVarDecl.head); |
| + delayedVarDecl = delayedVarDecl.tail; |
| + if (delayedVarDecl.isEmpty()) break; |
| + buffer.add(", "); |
| + } |
| + buffer.add(";\n"); |
| + } |
| endGraph(graph); |
| } |
| @@ -164,6 +197,51 @@ class SsaCodeGenerator implements HVisitor { |
| subGraph = oldSubGraph; |
| } |
| + bool isExpression(SubGraph limits) { |
| + HBasicBlock basicBlock = limits.start; |
| + do { |
| + HInstruction current = basicBlock.first; |
| + while (current != basicBlock.last) { |
| + // E.g, type guards. |
| + if (current.isControlFlow()) { |
| + return false; |
| + } |
| + current = current.next; |
| + } |
| + if (current is HGoto) { |
| + basicBlock = basicBlock.successors[0]; |
| + } else if (current is HConditionalBranch) { |
| + if (generateAtUseSite.contains(current)) { |
| + // Short-circuit logical operator trickery. |
| + // Check the second half, which will continue into the join. |
| + basicBlock = basicBlock.successors[0]; |
| + } else { |
| + // We allow an expression to end on an HIf (a condition expression). |
| + return basicBlock === limits.end; |
| + } |
| + } else { |
| + // Expression-incompatible control flow. |
| + return false; |
| + } |
| + } while (limits.contains(basicBlock)); |
| + return true; |
| + } |
| + |
| + bool isCondition(SubGraph limits) { |
| + return isExpression(limits) && (limits.end.last is HConditionalBranch); |
| + } |
| + |
| + void visitExpressionGraph(SubGraph subGraph) { |
| + int oldState = generationState; |
| + generationState = STATE_FIRST_EXPRESSION; |
| + visitSubGraph(subGraph); |
| + generationState = oldState; |
| + } |
| + |
| + void visitConditionGraph(SubGraph subGraph) { |
| + visitExpressionGraph(subGraph); |
| + } |
| + |
| String temporary(HInstruction instruction) { |
| int id = instruction.id; |
| String name = names[id]; |
| @@ -220,8 +298,54 @@ class SsaCodeGenerator implements HVisitor { |
| buffer.add(')'); |
| } |
| + |
| + |
| + /** |
| + * Whether we are currently generating expressions instead of statements. |
| + * This includes declarations, which are generated as expressions. |
| + */ |
| + bool isGeneratingExpression() { |
| + return generationState != STATE_STATEMENT; |
| + } |
| + |
| + /** |
| + * Whether we are generating a declaration. |
| + */ |
| + bool isGeneratingDeclaration() { |
| + return (generationState == STATE_DECLARATION || |
| + generationState == STATE_FIRST_DECLARATION); |
| + } |
| + |
| + /** |
| + * Called before writing an expression. |
| + * Ensures that expressions are comma spearated. |
| + */ |
| + void addExpressionSeparator() { |
| + if (generationState == STATE_FIRST_DECLARATION) { |
| + generationState = STATE_DECLARATION; |
| + } else if (generationState == STATE_FIRST_EXPRESSION) { |
| + generationState = STATE_EXPRESSION; |
| + } else { |
| + buffer.add(", "); |
| + } |
| + } |
| + |
| + void declareVariable(String variableName) { |
| + if (isGeneratingExpression()) { |
| + buffer.add(variableName); |
| + if (!isGeneratingDeclaration()) { |
| + delayedVarDecl = delayedVarDecl.prepend(variableName); |
| + } |
| + } else { |
| + buffer.add("var "); |
| + buffer.add(variableName); |
| + } |
| + } |
| + |
| void define(HInstruction instruction) { |
| - buffer.add('var ${temporary(instruction)} = '); |
| + String name = temporary(instruction); |
| + declareVariable(name); |
| + buffer.add(" = "); |
| visit(instruction, JSPrecedence.ASSIGNMENT_PRECEDENCE); |
| } |
| @@ -343,7 +467,132 @@ class SsaCodeGenerator implements HVisitor { |
| endExpression(operatorPrecedence.precedence); |
| } |
| - visitBasicBlock(HBasicBlock node) { |
| + // Wraps a loop body in a block to make continues have a target to break |
| + // to (if necessary). |
| + void wrapLoopBodyForContinue(HLoopInformation info) { |
| + TargetElement target = info.target; |
| + if (target !== null && target.isContinueTarget) { |
| + addIndentation(); |
| + for (LabelElement label in info.labels) { |
| + if (label.isContinueTarget) { |
| + writeContinueLabel(label); |
| + buffer.add(":"); |
| + continueAction[label] = continueAsBreak; |
| + } |
| + } |
| + addImplicitContinueLabel(); |
| + buffer.add(":{\n"); |
| + continueAction[info.target] = implicitContinueAsBreak; |
| + indent++; |
| + visitSubGraph(info.body); |
| + indent--; |
| + addIndentation(); |
| + buffer.add("}\n"); |
| + continueAction.remove(info.target); |
| + for (LabelElement label in info.labels) { |
| + if (label.isContinueTarget) { |
| + continueAction.remove(label); |
| + } |
| + } |
| + } else { |
| + // Loop body contains no continues, so we don't need a break target. |
| + visitSubGraph(info.body); |
| + } |
| + } |
| + |
| + bool handleLoop(HBasicBlock node) { |
| + bool success = false; |
| + assert(node.isLoopHeader()); |
| + HLoopInformation info = node.loopInformation; |
| + SubExpression condition = info.condition; |
| + if (isCondition(condition)) { |
| + switch (info.type) { |
| + case HLoopInformation.WHILE_LOOP: |
| + case HLoopInformation.FOR_IN_LOOP: { |
| + addIndentation(); |
| + for (LabelElement label in info.labels) { |
| + writeLabel(label); |
| + buffer.add(":"); |
| + } |
| + bool inlineUpdates = |
| + info.updates !== null && isExpression(info.updates); |
| + if (inlineUpdates) { |
| + buffer.add("for (; "); |
| + visitConditionGraph(condition); |
| + buffer.add("; "); |
| + visitExpressionGraph(info.updates); |
| + buffer.add(") {\n"); |
| + indent++; |
| + // The body might be labeled. Ignore this when recursing on the |
| + // subgraph. |
| + // TODO(lrn): Remove this extra labeling when handling all loops |
| + // using subgraphs. |
| + HBlockInformation oldInfo = currentBlockInformation; |
| + currentBlockInformation = info.body.start.labeledBlockInformation; |
| + visitSubGraph(info.body); |
| + currentBlockInformation = oldInfo; |
| + |
| + indent--; |
| + } else { |
| + buffer.add("while ("); |
| + visitConditionGraph(condition); |
| + buffer.add(") {\n"); |
| + indent++; |
| + wrapLoopBodyForContinue(info); |
| + if (info.updates !== null) visitSubGraph(info.updates); |
| + indent--; |
| + } |
| + addIndentation(); |
| + buffer.add("}\n"); |
| + success = true; |
| + break; |
| + } |
| + case HLoopInformation.FOR_LOOP: { |
| + // TODO(lrn): Find a way to put initialization into the for. |
| + // It's currently handled before we reach the [HLoopInformation]. |
| + addIndentation(); |
| + for (LabelElement label in info.labels) { |
| + if (label.isTarget) { |
| + writeLabel(label); |
| + buffer.add(":"); |
| + } |
| + } |
| + buffer.add("for(;"); |
| + visitConditionGraph(info.condition); |
| + buffer.add(";"); |
| + if (isExpression(info.updates)) { |
| + visitExpressionGraph(info.updates); |
| + buffer.add(") {\n"); |
| + indent++; |
| + |
| + HBlockInformation oldInfo = currentBlockInformation; |
| + currentBlockInformation = info.body.start.labeledBlockInformation; |
| + visitSubGraph(info.body); |
| + currentBlockInformation = oldInfo; |
| + |
| + indent--; |
| + addIndentation(); |
| + buffer.add("}\n"); |
| + } else { |
| + buffer.add(") {\n"); |
| + indent++; |
| + wrapLoopBodyForContinue(info); |
| + visitSubGraph(info.updates); |
| + indent--; |
| + buffer.add("}\n"); |
| + } |
| + success = true; |
| + break; |
| + } |
| + case HLoopInformation.DO_WHILE_LOOP: |
| + // Currently unhandled. |
| + default: |
| + } |
| + } |
| + return success; |
| + } |
| + |
| + void visitBasicBlock(HBasicBlock node) { |
| // Abort traversal if we are leaving the currently active sub-graph. |
| if (!subGraph.contains(node)) return; |
| @@ -353,21 +602,31 @@ class SsaCodeGenerator implements HVisitor { |
| // don't handle it again. |
| if (node.hasLabeledBlockInformation() && |
| node.labeledBlockInformation !== currentBlockInformation) { |
| - HLabeledBlockInformation oldBlockInformation = currentBlockInformation; |
| + HBlockInformation oldBlockInformation = currentBlockInformation; |
| currentBlockInformation = node.labeledBlockInformation; |
| handleLabeledBlock(currentBlockInformation); |
| currentBlockInformation = oldBlockInformation; |
| return; |
| } |
| - currentBlock = node; |
| - |
| - if (node.isLoopHeader()) { |
| - // While loop will be closed by the conditional loop-branch. |
| - // TODO(floitsch): HACK HACK HACK. |
| + if (node.isLoopHeader() && |
| + node.loopInformation !== currentBlockInformation) { |
| + HBlockInformation oldBlockInformation = currentBlockInformation; |
| + currentBlockInformation = node.loopInformation; |
| + bool prettyLoop = handleLoop(node); |
| + currentBlockInformation = oldBlockInformation; |
| + if (prettyLoop) { |
| + visitBasicBlock(node.loopInformation.joinBlock); |
| + return; |
| + } |
| beginLoop(node); |
| } |
| + iterateBasicBlock(node); |
| + } |
| + |
| + void iterateBasicBlock(HBasicBlock node) { |
| + currentBlock = node; |
| HInstruction instruction = node.first; |
| while (instruction != null) { |
| if (instruction === node.last) { |
| @@ -378,15 +637,25 @@ class SsaCodeGenerator implements HVisitor { |
| // In case the phi is being generated by another |
| // instruction. |
| if (isLogicalOperation && isGenerateAtUseSite(phi)) return; |
| - addIndentation(); |
| - if (!temporaryExists(phi)) buffer.add('var '); |
| - buffer.add('${temporary(phi)} = '); |
| + if (isGeneratingExpression()) { |
| + addExpressionSeparator(); |
| + } else { |
| + addIndentation(); |
| + } |
| + if (!temporaryExists(phi)) { |
| + declareVariable(temporary(phi)); |
| + } else { |
| + buffer.add(temporary(phi)); |
| + } |
| + buffer.add(" = "); |
| if (isLogicalOperation) { |
| emitLogicalOperation(phi, logicalOperations[phi]); |
| } else { |
| use(phi.inputs[index], JSPrecedence.ASSIGNMENT_PRECEDENCE); |
| } |
| - buffer.add(';\n'); |
| + if (!isGeneratingExpression()) { |
| + buffer.add(';\n'); |
| + } |
| }); |
| } |
| } |
| @@ -395,9 +664,13 @@ class SsaCodeGenerator implements HVisitor { |
| visit(instruction, JSPrecedence.STATEMENT_PRECEDENCE); |
| return; |
| } else if (!isGenerateAtUseSite(instruction)) { |
| - if (instruction is !HIf && instruction is !HTypeGuard) { |
| + if (instruction is !HIf && instruction is !HTypeGuard && |
| + !isGeneratingExpression()) { |
| addIndentation(); |
| } |
| + if (isGeneratingExpression()) { |
| + addExpressionSeparator(); |
| + } |
| if (instruction.usedBy.isEmpty() |
| || instruction is HTypeGuard |
| || instruction is HCheck) { |
| @@ -406,7 +679,8 @@ class SsaCodeGenerator implements HVisitor { |
| define(instruction); |
| } |
| // Control flow instructions know how to handle ';'. |
| - if (instruction is !HControlFlow && instruction is !HTypeGuard) { |
| + if (instruction is !HControlFlow && instruction is !HTypeGuard && |
| + !isGeneratingExpression()) { |
| buffer.add(';\n'); |
| } |
| } else if (instruction is HIf) { |
| @@ -577,7 +851,9 @@ class SsaCodeGenerator implements HVisitor { |
| assert(currentBlock.successors.length == 1); |
| if (node.label !== null) { |
| LabelElement label = node.label; |
| + buffer.add("/*b*/"); |
|
floitsch
2012/03/29 21:44:11
debug?
Lasse Reichstein Nielsen
2012/03/30 09:37:17
Done.
|
| if (!tryCallAction(continueAction, label)) { |
| + buffer.add("/*a*/"); |
|
floitsch
2012/03/29 21:44:11
ditto.
Lasse Reichstein Nielsen
2012/03/30 09:37:17
Done.
|
| addIndentation(); |
| buffer.add("continue "); |
| writeLabel(label); |
| @@ -626,6 +902,13 @@ class SsaCodeGenerator implements HVisitor { |
| } |
| visitIf(HIf node) { |
| + if (isGeneratingExpression()) { |
| + assert(node.block == subGraph.end); |
| + // We are generating an expression for a condition. |
| + addExpressionSeparator(); |
| + use(node.inputs[0], JSPrecedence.EXPRESSION_PRECEDENCE); |
| + return; |
| + } |
| List<HBasicBlock> dominated = node.block.dominatedBlocks; |
| HIfBlockInformation info = node.blockInformation; |
| startIf(node); |
| @@ -767,17 +1050,22 @@ class SsaCodeGenerator implements HVisitor { |
| } |
| visitFieldSet(HFieldSet node) { |
| + // This method may introduce variable declarations in the JS code. |
| + // If we are generating an expression, those variable declarations |
| + // must be delayed until later. |
| + bool delayDeclaration = false; |
| + String name = JsNames.getValid(node.element.name.slowToString()); |
| if (node.receiver !== null) { |
| beginExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE); |
| use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE); |
| buffer.add('.'); |
| + buffer.add(name); |
| } else { |
| // TODO(ngeoffray): Remove the 'var' once we don't globally box |
| // variables used in a try/catch. |
| - buffer.add('var '); |
| + declareVariable(name); |
| } |
| - String name = JsNames.getValid(node.element.name.slowToString()); |
| - buffer.add(name); |
| + if (delayDeclaration) delayedVarDecl = delayedVarDecl.prepend(name); |
| buffer.add(' = '); |
| use(node.value, JSPrecedence.ASSIGNMENT_PRECEDENCE); |
| if (node.receiver !== null) { |
| @@ -841,6 +1129,15 @@ class SsaCodeGenerator implements HVisitor { |
| } |
| visitLoopBranch(HLoopBranch node) { |
| + if (subGraph !== null && node.block == subGraph.end) { |
| + // We are generating code for a loop condition. |
| + // If doing this as part of a SubGraph traversal, the |
| + // calling code will handle the control flow logic. |
| + if (isGeneratingExpression()) { |
| + use(node.inputs[0], JSPrecedence.EXPRESSION_PRECEDENCE); |
| + } |
| + return; |
| + } |
| HBasicBlock branchBlock = currentBlock; |
| handleLoopCondition(node); |
| List<HBasicBlock> dominated = currentBlock.dominatedBlocks; |
| @@ -1411,6 +1708,8 @@ class SsaUnoptimizedCodeGenerator extends SsaCodeGenerator { |
| } |
| } |
| + bool handleLoop(HBasicBlock node) => false; |
| + |
| void visitTypeGuard(HTypeGuard node) { |
| indent--; |
| addIndentation(); |