Chromium Code Reviews| Index: lib/compiler/implementation/ssa/codegen.dart | 
| =================================================================== | 
| --- lib/compiler/implementation/ssa/codegen.dart (revision 8089) | 
| +++ lib/compiler/implementation/ssa/codegen.dart (working copy) | 
| @@ -141,23 +141,19 @@ | 
| static final int TYPE_EXPRESSION = 1; | 
| static final int TYPE_DECLARATION = 2; | 
| - static final String TEMPORARY_PREFIX = 't'; | 
| - | 
| final JavaScriptBackend backend; | 
| final WorkItem work; | 
| final StringBuffer buffer; | 
| final String parameters; | 
| - final Map<Element, String> parameterNames; | 
| - final Map<int, String> names; | 
| - final Set<String> usedNames; | 
| - final Set<HInstruction> declaredInstructions; | 
| - final Map<String, int> prefixes; | 
| + final Set<String> declaredVariables; | 
| 
 
Lasse Reichstein Nielsen
2012/05/31 08:24:56
Can you add a doc-comment describing what this mea
 
ngeoffray
2012/05/31 08:48:22
Done.
 
 | 
| final Set<HInstruction> generateAtUseSite; | 
| final Map<HPhi, String> logicalOperations; | 
| final Map<Element, ElementAction> breakAction; | 
| final Map<Element, ElementAction> continueAction; | 
| - final Equivalence<HPhi> phiEquivalence; | 
| + final List<String> delayedVariablesDeclaration; | 
| + final Map<Element, String> parameterNames; | 
| + VariableNames variableNames; | 
| 
 
Lasse Reichstein Nielsen
2012/05/31 08:24:56
And this one too, to explain the difference from t
 
ngeoffray
2012/05/31 08:48:22
Done.
 
 | 
| Element equalsNullElement; | 
| Element boolifiedEqualsNullElement; | 
| @@ -174,7 +170,6 @@ | 
| * While generating expressions, we can't insert variable declarations. | 
| * Instead we declare them at the end of the function | 
| 
 
floitsch
2012/05/30 18:45:03
Move this comment too.
 
ngeoffray
2012/05/31 08:12:46
Done.
 
 | 
| */ | 
| - Link<String> delayedVarDecl = const EmptyLink<String>(); | 
| HBasicBlock currentBlock; | 
| // Records a block-information that is being handled specially. | 
| @@ -197,25 +192,15 @@ | 
| this.work, | 
| this.parameters, | 
| this.parameterNames) | 
| - : names = new Map<int, String>(), | 
| - prefixes = new Map<String, int>(), | 
| - usedNames = new Set<String>(), | 
| - declaredInstructions = new Set<HInstruction>(), | 
| + : declaredVariables = new Set<String>(), | 
| + delayedVariablesDeclaration = new List<String>(), | 
| buffer = new StringBuffer(), | 
| generateAtUseSite = new Set<HInstruction>(), | 
| logicalOperations = new Map<HPhi, String>(), | 
| breakAction = new Map<Element, ElementAction>(), | 
| continueAction = new Map<Element, ElementAction>(), | 
| - phiEquivalence = new Equivalence<HPhi>(), | 
| unsignedShiftPrecedences = JSPrecedence.binary['>>>'] { | 
| - for (final name in parameterNames.getValues()) { | 
| - prefixes[name] = 0; | 
| - } | 
| - | 
| - // Create a namespace for temporaries. | 
| - prefixes[TEMPORARY_PREFIX] = 0; | 
| - | 
| Interceptors interceptors = backend.builder.interceptors; | 
| equalsNullElement = interceptors.getEqualsNullInterceptor(); | 
| boolifiedEqualsNullElement = | 
| @@ -258,7 +243,16 @@ | 
| new SsaInstructionMerger(generateAtUseSite).visitGraph(graph); | 
| new SsaConditionMerger(generateAtUseSite, | 
| logicalOperations).visitGraph(graph); | 
| - new PhiEquivalator(phiEquivalence, logicalOperations).analyzeGraph(graph); | 
| + SsaLiveIntervalBuilder intervalBuilder = | 
| 
 
floitsch
2012/05/30 18:45:03
new SsaLiveIntervalBuilder(compiler).visitGraph(gr
 
ngeoffray
2012/05/31 08:12:46
No, I need the object to take its liveInstructions
 
Lasse Reichstein Nielsen
2012/05/31 08:24:56
It's also used below.
 
 | 
| + new SsaLiveIntervalBuilder(compiler); | 
| + intervalBuilder.visitGraph(graph); | 
| + SsaVariableAllocator allocator = new SsaVariableAllocator( | 
| + compiler, | 
| + intervalBuilder.liveInstructions, | 
| + intervalBuilder.liveIntervals, | 
| + generateAtUseSite); | 
| + allocator.visitGraph(graph); | 
| + variableNames = allocator.names; | 
| } | 
| visitGraph(HGraph graph) { | 
| @@ -268,14 +262,9 @@ | 
| subGraph = new SubGraph(graph.entry, graph.exit); | 
| beginGraph(graph); | 
| visitBasicBlock(graph.entry); | 
| - if (!delayedVarDecl.isEmpty()) { | 
| + if (!delayedVariablesDeclaration.isEmpty()) { | 
| addIndented("var "); | 
| - while (true) { | 
| - buffer.add(delayedVarDecl.head); | 
| - delayedVarDecl = delayedVarDecl.tail; | 
| - if (delayedVarDecl.isEmpty()) break; | 
| - buffer.add(", "); | 
| - } | 
| + buffer.add(Strings.join(delayedVariablesDeclaration, ', ')); | 
| buffer.add(";\n"); | 
| } | 
| endGraph(graph); | 
| @@ -413,60 +402,6 @@ | 
| generateExpression(condition); | 
| } | 
| - String temporary(HInstruction instruction) { | 
| - int id = instruction.id; | 
| - String name = names[id]; | 
| - if (name !== null) return name; | 
| - | 
| - String prefix = TEMPORARY_PREFIX; | 
| - if (instruction.sourceElement !== null) { | 
| - Element element = instruction.sourceElement; | 
| - if (element !== null && !element.name.isEmpty()) { | 
| - prefix = element.name.slowToString(); | 
| - // Special case the variable named [TEMPORARY_PREFIX] to allow | 
| - // keeping its name. | 
| - if (prefix == TEMPORARY_PREFIX && !usedNames.contains(prefix)) { | 
| - return newName(id, prefix); | 
| - } | 
| - // If we've never seen that prefix before, try to use it | 
| - // directly. | 
| - if (!prefixes.containsKey(prefix)) { | 
| - // Make sure the variable name does not conflict with our mangling. | 
| - while (usedNames.contains(prefix)) { | 
| - prefix = '${prefix}_'; | 
| - } | 
| - prefixes[prefix] = 0; | 
| - return newName(id, prefix); | 
| - } | 
| - } | 
| - } | 
| - | 
| - name = '${prefix}${prefixes[prefix]++}'; | 
| - while (usedNames.contains(name)) { | 
| - name = '${prefix}${prefixes[prefix]++}'; | 
| - } | 
| - 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; | 
| - } | 
| - | 
| - String newName(int id, String name) { | 
| - String result = JsNames.getValid(name); | 
| - names[id] = result; | 
| - usedNames.add(result); | 
| - return result; | 
| - } | 
| - | 
| /** | 
| * Only visits the arguments starting at inputs[HInvoke.ARGUMENTS_OFFSET]. | 
| */ | 
| @@ -502,8 +437,7 @@ | 
| */ | 
| void addExpressionSeparator() { | 
| if (generationState == STATE_FIRST_DECLARATION) { | 
| - buffer.add("var "); | 
| - generationState = STATE_DECLARATION; | 
| + return; | 
| 
 
kasperl
2012/05/31 05:26:03
Maybe change the structure so that you check for S
 
ngeoffray
2012/05/31 08:12:46
Done.
 
Lasse Reichstein Nielsen
2012/05/31 08:24:56
Why this change?
Where do you change the state to
 
ngeoffray
2012/05/31 08:48:22
In declareVariable line 454. I need this change be
 
 | 
| } else if (generationState == STATE_FIRST_EXPRESSION) { | 
| generationState = STATE_EXPRESSION; | 
| } else { | 
| @@ -511,59 +445,35 @@ | 
| } | 
| } | 
| + bool isVariableDeclared(String variableName) { | 
| + return declaredVariables.contains(variableName); | 
| + } | 
| + | 
| void declareVariable(String variableName) { | 
| if (isGeneratingExpression()) { | 
| - buffer.add(variableName); | 
| - if (!isGeneratingDeclaration()) { | 
| - delayedVarDecl = delayedVarDecl.prepend(variableName); | 
| + if (generationState == STATE_FIRST_DECLARATION) { | 
| + generationState = STATE_DECLARATION; | 
| + if (!isVariableDeclared(variableName)) { | 
| + declaredVariables.add(variableName); | 
| + buffer.add("var "); | 
| 
 
floitsch
2012/05/30 18:45:03
The 'var' is emitted conditionally. This means tha
 
ngeoffray
2012/05/31 08:12:46
After the first declaration we must go into STATE_
 
 | 
| + } | 
| + } else if (!isGeneratingDeclaration() | 
| + && !isVariableDeclared(variableName)) { | 
| + delayedVariablesDeclaration.add(variableName); | 
| } | 
| - } else { | 
| + } else if (!isVariableDeclared(variableName)) { | 
| + declaredVariables.add(variableName); | 
| buffer.add("var "); | 
| - buffer.add(variableName); | 
| } | 
| + buffer.add(variableName); | 
| } | 
| void declareInstruction(HInstruction instruction) { | 
| - declaredInstructions.add(instruction); | 
| - String name = temporary(instruction); | 
| - declareVariable(name); | 
| + declareVariable(variableNames.getName(instruction)); | 
| } | 
| - bool needsNewVariable(HInstruction instruction) { | 
| - bool needsVar = !instruction.usedBy.isEmpty(); | 
| - if (needsVar && instruction is HCheck) { | 
| - HCheck check = instruction; | 
| - HInstruction input = check.checkedInput; | 
| - // We only need a new var if [input] is generated at use site | 
| - // but is not a trivial code motion invariant instruction like | 
| - // for parameters or this. | 
| - // | 
| - // For example: | 
| - // Foo a = this; | 
| - // print(a); | 
| - // print(a); | 
| - // | 
| - // In checked mode no new variable is needed. | 
| - // FooTypeCheck(this); | 
| - // print(this); | 
| - // print(this); | 
| - // | 
| - // But for this example: | 
| - // Foo a = foo(); | 
| - // print(a); | 
| - // print(a); | 
| - // | 
| - // We need a new variable: | 
| - // var a = FooTypeCheck(foo()); | 
| - // print(a); | 
| - // print(a); | 
| - needsVar = isGenerateAtUseSite(input) && !input.isCodeMotionInvariant(); | 
| - } | 
| - return needsVar; | 
| - } | 
| - | 
| void define(HInstruction instruction) { | 
| - if (needsNewVariable(instruction)) { | 
| + if (instruction is !HCheck && variableNames.hasName(instruction)) { | 
| declareInstruction(instruction); | 
| buffer.add(" = "); | 
| visit(instruction, JSPrecedence.ASSIGNMENT_PRECEDENCE); | 
| @@ -573,70 +483,13 @@ | 
| } | 
| void use(HInstruction argument, int expectedPrecedenceForArgument) { | 
| - if (argument is HCheck) { | 
| - HCheck instruction = argument; | 
| - HInstruction input = instruction.checkedInput; | 
| - if (isGenerateAtUseSite(argument) && isGenerateAtUseSite(input)) { | 
| - // If both instructions can be generated at use site, we can | 
| - // just visit [argument]. | 
| - // | 
| - // For example: | 
| - // Foo a = foo(); | 
| - // print(a); | 
| - // | 
| - // In checked mode will turn into: | 
| - // print(FooTypeCheck(foo())); | 
| - visit(argument, expectedPrecedenceForArgument); | 
| - } else if (isGenerateAtUseSite(input)) { | 
| - // Get the real input of that checked instruction. | 
| - while (input is HCheck) { | 
| - HCheck check = input; | 
| - input = check.checkedInput; | 
| - } | 
| - | 
| - // If [argument] cannot be generated at use site, but [input] | 
| - // can, use the temporary of [argument]. A code motion | 
| - // invariant instruction does not have a temporary, so we just | 
| - // | 
| - // For example: | 
| - // Foo a = foo(); | 
| - // print(a); | 
| - // print(a); | 
| - // | 
| - // In checked mode will turn into: | 
| - // var a = FooTypeCheck(foo()); | 
| - // print(a); | 
| - // print(a); | 
| - // | 
| - // Note that in case the input is code motion invariant, like | 
| - // for parameters or this, we just need to visit it, since | 
| - // there is no temporary for such instruction. | 
| - if (input.isCodeMotionInvariant()) { | 
| - visit(input, expectedPrecedenceForArgument); | 
| - } else { | 
| - buffer.add(temporary(argument)); | 
| - } | 
| - } else { | 
| - // Otherwise we just use [input]. [argument] has already been | 
| - // emitted, and we just need the temporary of [input]. | 
| - // | 
| - // For example: | 
| - // var a = foo(); | 
| - // print(a); | 
| - // Foo b = a; | 
| - // print(b); | 
| - // | 
| - // In checked mode will turn into: | 
| - // var a = foo(); | 
| - // print(a); | 
| - // FooTypeCheck(a); | 
| - // print(a); | 
| - use(input, expectedPrecedenceForArgument); | 
| - } | 
| - } else if (isGenerateAtUseSite(argument)) { | 
| + if (isGenerateAtUseSite(argument)) { | 
| visit(argument, expectedPrecedenceForArgument); | 
| + } else if (argument is HCheck) { | 
| + HCheck check = argument; | 
| + use(argument.checkedInput, expectedPrecedenceForArgument); | 
| } else { | 
| - buffer.add(temporary(argument)); | 
| + buffer.add(variableNames.getName(argument)); | 
| } | 
| } | 
| @@ -724,7 +577,7 @@ | 
| if (info.catchBlock !== null) { | 
| // Printing the catch part. | 
| HParameterValue exception = info.catchVariable; | 
| - String name = temporary(exception); | 
| + String name = variableNames.getName(exception); | 
| parameterNames[exception.element] = name; | 
| buffer.add(' catch ($name) {\n'); | 
| indent++; | 
| @@ -1025,135 +878,117 @@ | 
| iterateBasicBlock(node); | 
| } | 
| - /** Generates the assignments for all phis of successors blocks. */ | 
| - void assignPhisOfAllSuccessors(HBasicBlock node) { | 
| - Map<HInstruction, String> temporaryNamesOfPhis = null; | 
| + void emitAssignment(String source, String destination) { | 
| + if (isGeneratingExpression()) { | 
| + addExpressionSeparator(); | 
| + } else { | 
| + addIndentation(); | 
| + } | 
| + declareVariable(destination); | 
| + buffer.add(' = $source'); | 
| + if (!isGeneratingExpression()) { | 
| + buffer.add(';\n'); | 
| + } | 
| + } | 
| - /** | 
| - * 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. | 
| - */ | 
| - void generateAssignment(HPhi canonicalPhi, HInstruction value) { | 
| - if (isGeneratingExpression()) { | 
| - addExpressionSeparator(); | 
| - } else { | 
| - addIndentation(); | 
| + /** | 
| + * Sequentialize a list of parallel copies. | 
| 
 
Lasse Reichstein Nielsen
2012/05/31 08:24:56
Describe which criteria are being used to do the o
 
ngeoffray
2012/05/31 08:48:22
Added the 'conceptually'. As discussed, kept the '
 
 | 
| + */ | 
| + void sequentializeCopies(List<Copy> copies) { | 
| + // Map to keep track of the current location of a variable. | 
| + Map<String, String> currentLocation = new Map<String, String>(); | 
| 
 
Lasse Reichstein Nielsen
2012/05/31 08:24:56
What is a "location" here?
 
ngeoffray
2012/05/31 08:48:22
It is a variable. It really is "the variable that
 
 | 
| + | 
| + // Map to keep track of the initial value of a variable. | 
| + Map<String, String> initialValue = new Map<String, String>(); | 
| + | 
| + // List of variables to assign a value. | 
| + List<String> todo = <String>[]; | 
| 
 
Lasse Reichstein Nielsen
2012/05/31 08:24:56
"todo" -> "worklist".
 
ngeoffray
2012/05/31 08:48:22
Done.
 
 | 
| + | 
| + // List of variables that we can assign a value to (ie are not | 
| 
 
Lasse Reichstein Nielsen
2012/05/31 08:24:56
Describe which criteria are being used to do the o
 
ngeoffray
2012/05/31 08:48:22
Done.
 
 | 
| + // being used anymore). | 
| + List<String> ready = <String>[]; | 
| + | 
| + // Prune [copies] by removing self-copies. | 
| + List<Copy> prunedCopies = <Copy>[]; | 
| + for (Copy copy in copies) { | 
| + String sourceName = variableNames.getName(copy.source); | 
| + String destinationName = variableNames.getName(copy.destination); | 
| + if (sourceName != destinationName) { | 
| + prunedCopies.add(new Copy(sourceName, destinationName)); | 
| } | 
| - if (temporaryNamesOfPhis !== null && | 
| - canonicalPhi !== value && | 
| - temporaryNamesOfPhis.containsKey(canonicalPhi)) { | 
| - // This is the assignment to the temporary. | 
| - declareVariable(temporaryNamesOfPhis[canonicalPhi]); | 
| - } else if (!declaredInstructions.contains(canonicalPhi)) { | 
| - declareInstruction(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'); | 
| - } | 
| } | 
| + copies = prunedCopies; | 
| - // 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; | 
| - }); | 
| + // For each copy, set the current location of the source to | 
| + // itself, and the initial value of the destination to the source. | 
| + // Add the destination to the list of copies to make. | 
| + for (Copy copy in copies) { | 
| + currentLocation[copy.source] = copy.source; | 
| + initialValue[copy.destination] = copy.source; | 
| + todo.add(copy.destination); | 
| } | 
| - Set<HPhi> inputPhis = new Set<HPhi>(); | 
| - List<HPhi> phis = <HPhi>[]; | 
| + // For each copy, if the destination does not have a current | 
| + // location, then we can safely assign to it. | 
| + for (Copy copy in copies) { | 
| + if (currentLocation[copy.destination] === null) { | 
| + ready.add(copy.destination); | 
| + } | 
| + } | 
| - /** | 
| - * 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); | 
| + while (!todo.isEmpty()) { | 
| + while (!ready.isEmpty()) { | 
| + String destination = ready.removeLast(); | 
| + String source = initialValue[destination]; | 
| + // Since [source] might have been updated, use the current | 
| + // location of [source] | 
| + String copy = currentLocation[source]; | 
| + emitAssignment(copy, destination); | 
| 
 
floitsch
2012/05/30 18:45:03
I find it easier to read if the arguments are inve
 
ngeoffray
2012/05/31 08:12:46
Done.
 
 | 
| + // Now [destination] is the current location of [source]. | 
| + currentLocation[source] = destination; | 
| + // If [source] hasn't been updated and needs to have a value, | 
| + // add it to the list of variables that can be updated. Copies | 
| + // of [source] will now use [destination]. | 
| + if (source == copy && initialValue[source] !== null) { | 
| + ready.add(source); | 
| } | 
| - } else if (isGenerateAtUseSite(input)) { | 
| - for (HInstruction inputOfInput in input.inputs) { | 
| - collectInputPhis(inputOfInput, targetPhi); | 
| - } | 
| } | 
| + | 
| + // Check if we have a cycle. | 
| + String current = todo.removeLast(); | 
| + // If [current] is the source of a copy, and the current | 
| + // location of its initial value is not itself, then there is a | 
| + // cycle that we break by using a temporary name. | 
| + if (currentLocation[current] !== null | 
| 
 
floitsch
2012/05/30 18:45:03
Is this test necessary?
Clearly: current == curre
 
ngeoffray
2012/05/31 08:12:46
Yes, it is. Because I don't remove a variable that
 
 | 
| + && current != currentLocation[initialValue[current]]) { | 
| + String tempName = VariableNames.SWAP_TEMP; | 
| + emitAssignment(current, tempName); | 
| + currentLocation[current] = tempName; | 
| + // [current] can now be safely updated. Copies of [current] | 
| + // will now use [tempName]. | 
| + ready.add(current); | 
| + } | 
| } | 
| + } | 
| - phiAssignments.forEach((HPhi targetPhi, HInstruction input) { | 
| - phis.add(targetPhi); | 
| - collectInputPhis(input, targetPhi); | 
| - }); | 
| + void assignPhisOfSuccessors(HBasicBlock node) { | 
| + CopyHandler handler = variableNames.getCopies(node); | 
| + if (handler == null) return; | 
| - 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]); | 
| + sequentializeCopies(handler.copies); | 
| + | 
| + for (Copy copy in handler.assignments) { | 
| + if (isGeneratingExpression()) { | 
| + addExpressionSeparator(); | 
| + } else { | 
| + addIndentation(); | 
| } | 
| - // 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); | 
| + declareVariable(variableNames.getName(copy.destination)); | 
| + buffer.add(' = '); | 
| + use(copy.source, JSPrecedence.ASSIGNMENT_PRECEDENCE); | 
| + if (!isGeneratingExpression()) { | 
| + buffer.add(';\n'); | 
| } | 
| } | 
| } | 
| @@ -1162,7 +997,7 @@ | 
| HInstruction instruction = node.first; | 
| while (instruction != null) { | 
| if (instruction === node.last) { | 
| - assignPhisOfAllSuccessors(node); | 
| + assignPhisOfSuccessors(node); | 
| } | 
| if (isGenerateAtUseSite(instruction)) { | 
| @@ -1591,11 +1426,6 @@ | 
| buffer.add('.'); | 
| buffer.add(name); | 
| } else { | 
| - // TODO(ngeoffray): Remove the 'var' once we don't globally box | 
| - // variables used in a try/catch. | 
| - if (!isGeneratingExpression()) { | 
| - buffer.add('var '); | 
| - } | 
| use(node.receiver, JSPrecedence.EXPRESSION_PRECEDENCE); | 
| } | 
| buffer.add(' = '); | 
| @@ -1744,11 +1574,7 @@ | 
| visitParameterValue(HParameterValue node) { | 
| assert(isGenerateAtUseSite(node)); | 
| - if (parameterNames[node.element] == null) { | 
| - buffer.add(temporary(node)); | 
| - } else { | 
| - buffer.add(parameterNames[node.element]); | 
| - } | 
| + buffer.add(variableNames.getName(node)); | 
| } | 
| visitPhi(HPhi node) { | 
| @@ -1756,8 +1582,7 @@ | 
| if (operation !== null) { | 
| emitLogicalOperation(node, operation); | 
| } else { | 
| - HPhi canonicalPhi = phiEquivalence.getRepresentative(node); | 
| - buffer.add('${temporary(canonicalPhi)}'); | 
| + buffer.add('${variableNames.getName(node)}'); | 
| } | 
| } | 
| @@ -2480,7 +2305,7 @@ | 
| int i = 0; | 
| for (HInstruction input in node.inputs) { | 
| HInstruction instruction = unwrap(input); | 
| - setup.add(' ${temporary(instruction)} = env$i;\n'); | 
| + setup.add(' ${variableNames.getName(instruction)} = env$i;\n'); | 
| i++; | 
| } | 
| if (i > maxBailoutParameters) maxBailoutParameters = i; |