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