Index: lib/compiler/implementation/ssa/codegen.dart |
diff --git a/lib/compiler/implementation/ssa/codegen.dart b/lib/compiler/implementation/ssa/codegen.dart |
index 4ef4e02014b33084545e371663981db6515c2cf1..c97a88ec2ce1825540627a406aa3bf6e4eaa8c92 100644 |
--- a/lib/compiler/implementation/ssa/codegen.dart |
+++ b/lib/compiler/implementation/ssa/codegen.dart |
@@ -435,6 +435,18 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { |
return newName(id, name); |
} |
+ // TODO(floitsch): share more code with [temporary]. |
+ String freshTemporary() { |
+ String prefix = TEMPORARY_PREFIX; |
+ String name = '${prefix}${prefixes[prefix]++}'; |
+ while (usedNames.contains(name)) { |
+ name = '${prefix}${prefixes[prefix]++}'; |
+ } |
+ String result = JsNames.getValid(name); |
+ usedNames.add(result); |
+ return result; |
+ } |
+ |
bool temporaryExists(HInstruction instruction) { |
return names.containsKey(instruction.id); |
} |
@@ -909,50 +921,144 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { |
iterateBasicBlock(node); |
} |
+ /** Generates the assignments for all phis of successors blocks. */ |
+ void assignPhisOfAllSuccessors(HBasicBlock node) { |
+ Map<HInstruction, String> temporaryNamesOfPhis = null; |
+ |
+ /** |
+ * Generates the assignment [canonicalPhi] = [value]. |
+ * |
+ * If the [canonicalPhi] has a temporary name (in [temporaryNamesOfPhis]) |
+ * then the temporary is assigned instead of the [canonicalPhi]. However, |
+ * when the left and right-hand side are equal ([:canonicalPhi === value:]) |
+ * then the [canonicalPhi] is assigned with the temporary. |
Lasse Reichstein Nielsen
2012/05/03 12:19:49
Still a slightly odd "modal" function. Acceptable,
floitsch
2012/05/03 12:27:23
It could be split, but there is quite some boiler-
|
+ */ |
+ void generateAssignment(HPhi canonicalPhi, HInstruction value) { |
+ if (isGeneratingExpression()) { |
+ addExpressionSeparator(); |
+ } else { |
+ addIndentation(); |
+ } |
+ if (temporaryNamesOfPhis !== null && |
+ canonicalPhi !== value && |
+ temporaryNamesOfPhis.containsKey(canonicalPhi)) { |
+ // This is the assignment to the temporary. |
+ declareVariable(temporaryNamesOfPhis[canonicalPhi]); |
+ } else if (!temporaryExists(canonicalPhi)) { |
+ declareVariable(temporary(canonicalPhi)); |
+ } else { |
+ buffer.add(temporary(canonicalPhi)); |
+ } |
+ buffer.add(" = "); |
+ bool isLogicalOperation = logicalOperations.containsKey(canonicalPhi); |
+ if (isLogicalOperation) { |
+ emitLogicalOperation(canonicalPhi, logicalOperations[canonicalPhi]); |
+ } else if (canonicalPhi === value) { |
+ buffer.add(temporaryNamesOfPhis[value]); |
+ } else { |
+ use(value, JSPrecedence.ASSIGNMENT_PRECEDENCE); |
+ } |
+ if (!isGeneratingExpression()) { |
+ buffer.add(';\n'); |
+ } |
+ } |
+ |
+ // Assignments are delayed so that we don't overwrite phis that might |
+ // be used as inputs. |
+ // TODO(floitsch): improve phi assignments. Currently we introduce |
+ // way too many temporary variables. |
+ Map<HPhi, HInstruction> phiAssignments = new Map<HPhi, HInstruction>(); |
+ |
+ for (HBasicBlock successor in node.successors) { |
+ int index = successor.predecessors.indexOf(node); |
+ successor.forEachPhi((HPhi phi) { |
+ bool isLogicalOperation = logicalOperations.containsKey(phi); |
+ // In case the phi is being generated by another |
+ // instruction. |
+ if (isLogicalOperation && isGenerateAtUseSite(phi)) return; |
+ HPhi canonicalPhi = phiEquivalence.getRepresentative(phi); |
+ assert(!isLogicalOperation || canonicalPhi === phi); |
+ HInstruction input = phi.inputs[index]; |
+ if (input is HPhi) { |
+ input = phiEquivalence.getRepresentative(input); |
+ // If we use the same variable, we don't need to create an |
+ // assignment. |
+ if (input === canonicalPhi) { |
+ assert(!isLogicalOperation); |
+ return; |
+ } |
+ } |
+ phiAssignments[canonicalPhi] = input; |
+ }); |
+ } |
+ |
+ Set<HPhi> inputPhis = new Set<HPhi>(); |
+ List<HPhi> phis = <HPhi>[]; |
+ |
+ /** |
+ * Transitively collects the phis that are used when emitting the [input] |
+ * and adds them to [inputPhis]. Does not add phis that are equal to the |
+ * [targetPhi] or are not in the [phiAssignments] map. |
+ */ |
+ void collectInputPhis(HInstruction input, HPhi targetPhi) { |
+ if (input is HPhi) { |
+ HPhi canonicalPhi = phiEquivalence.getRepresentative(input); |
+ // Self-updates are ok. |
+ if (canonicalPhi !== targetPhi && |
+ phiAssignments.containsKey(canonicalPhi)) { |
+ inputPhis.add(canonicalPhi); |
+ } |
+ } else if (isGenerateAtUseSite(input)) { |
+ for (HInstruction inputOfInput in input.inputs) { |
+ collectInputPhis(inputOfInput, targetPhi); |
+ } |
+ } |
+ } |
+ |
+ phiAssignments.forEach((HPhi targetPhi, HInstruction input) { |
+ phis.add(targetPhi); |
+ collectInputPhis(input, targetPhi); |
+ }); |
+ |
+ if (inputPhis.isEmpty()) { |
+ phiAssignments.forEach(generateAssignment); |
+ } else { |
+ // Emit the phiX=phiY assignments, taking care to break cycles. |
+ // Example program: |
+ // var x = 499; |
+ // var y = 99; |
+ // while (x != 99) { |
+ // var tmp = x; // <=== The tmp variable is removed in Ssa-form. |
+ // x = y; |
+ // y = tmp; |
+ // } |
+ // |
+ temporaryNamesOfPhis = new HashMap<HInstruction, String>(); |
+ // For phis that are used as inputs simply *always* allocate a |
+ // temporary. |
+ for (HPhi phi in phis) { |
+ if (inputPhis.contains(phi)) { |
+ String temporaryPhi = freshTemporary(); |
+ temporaryNamesOfPhis[phi] = temporaryPhi; |
+ } |
+ // The assignPhi uses temporary variables for the left-hand side if |
+ // they exist. |
+ generateAssignment(phi, phiAssignments[phi]); |
+ } |
+ // Finally assign the input phis. |
+ for (HPhi phi in inputPhis) { |
+ // The assignPhi special-cases phi assignments to itself and recognizes |
+ // it as assignment from the temporary variable to the actual phi. |
+ generateAssignment(phi, phi); |
+ } |
+ } |
+ } |
+ |
void iterateBasicBlock(HBasicBlock node) { |
HInstruction instruction = node.first; |
while (instruction != null) { |
if (instruction === node.last) { |
- for (HBasicBlock successor in node.successors) { |
- int index = successor.predecessors.indexOf(node); |
- successor.forEachPhi((HPhi phi) { |
- bool isLogicalOperation = logicalOperations.containsKey(phi); |
- // In case the phi is being generated by another |
- // instruction. |
- if (isLogicalOperation && isGenerateAtUseSite(phi)) return; |
- HPhi canonicalPhi = phiEquivalence.getRepresentative(phi); |
- HInstruction input = phi.inputs[index]; |
- if (input is HPhi) { |
- HPhi inputPhi = input; |
- HPhi canonicalInput = phiEquivalence.getRepresentative(inputPhi); |
- // If we use the same variable, we don't need to create an |
- // assignment. |
- if (canonicalInput === canonicalPhi) { |
- assert(!isLogicalOperation); |
- return; |
- } |
- } |
- if (isGeneratingExpression()) { |
- addExpressionSeparator(); |
- } else { |
- addIndentation(); |
- } |
- if (!temporaryExists(canonicalPhi)) { |
- declareVariable(temporary(canonicalPhi)); |
- } else { |
- buffer.add(temporary(canonicalPhi)); |
- } |
- buffer.add(" = "); |
- if (isLogicalOperation) { |
- emitLogicalOperation(phi, logicalOperations[phi]); |
- } else { |
- use(phi.inputs[index], JSPrecedence.ASSIGNMENT_PRECEDENCE); |
- } |
- if (!isGeneratingExpression()) { |
- buffer.add(';\n'); |
- } |
- }); |
- } |
+ assignPhisOfAllSuccessors(node); |
} |
if (instruction is HGoto || instruction is HExit || instruction is HTry) { |