OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 class SsaCodeGeneratorTask extends CompilerTask { | 5 class SsaCodeGeneratorTask extends CompilerTask { |
6 final JavaScriptBackend backend; | 6 final JavaScriptBackend backend; |
7 SsaCodeGeneratorTask(JavaScriptBackend backend) | 7 SsaCodeGeneratorTask(JavaScriptBackend backend) |
8 : this.backend = backend, | 8 : this.backend = backend, |
9 super(backend.compiler); | 9 super(backend.compiler); |
10 String get name() => 'SSA code generator'; | 10 String get name() => 'SSA code generator'; |
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
134 * or possibly several comma-separated expressions. | 134 * or possibly several comma-separated expressions. |
135 * - [TYPE_DECLARATION] means that the graph can be generated as an | 135 * - [TYPE_DECLARATION] means that the graph can be generated as an |
136 * expression, and that it only generates expressions of the form | 136 * expression, and that it only generates expressions of the form |
137 * variable = expression | 137 * variable = expression |
138 * which are also valid as parts of a "var" declaration. | 138 * which are also valid as parts of a "var" declaration. |
139 */ | 139 */ |
140 static final int TYPE_STATEMENT = 0; | 140 static final int TYPE_STATEMENT = 0; |
141 static final int TYPE_EXPRESSION = 1; | 141 static final int TYPE_EXPRESSION = 1; |
142 static final int TYPE_DECLARATION = 2; | 142 static final int TYPE_DECLARATION = 2; |
143 | 143 |
144 static final String TEMPORARY_PREFIX = 't'; | |
145 | |
146 final JavaScriptBackend backend; | 144 final JavaScriptBackend backend; |
147 final WorkItem work; | 145 final WorkItem work; |
148 final StringBuffer buffer; | 146 final StringBuffer buffer; |
149 final String parameters; | 147 final String parameters; |
150 | 148 |
151 final Map<Element, String> parameterNames; | |
152 final Map<int, String> names; | |
153 final Set<String> usedNames; | |
154 final Set<HInstruction> declaredInstructions; | |
155 final Map<String, int> prefixes; | |
156 final Set<HInstruction> generateAtUseSite; | 149 final Set<HInstruction> generateAtUseSite; |
157 final Map<HPhi, String> logicalOperations; | 150 final Map<HPhi, String> logicalOperations; |
158 final Map<Element, ElementAction> breakAction; | 151 final Map<Element, ElementAction> breakAction; |
159 final Map<Element, ElementAction> continueAction; | 152 final Map<Element, ElementAction> continueAction; |
160 final Equivalence<HPhi> phiEquivalence; | 153 final Map<Element, String> parameterNames; |
| 154 |
| 155 /** |
| 156 * Contains the names of the instructions, as well as the parallel |
| 157 * copies to perform on block transitioning. |
| 158 */ |
| 159 VariableNames variableNames; |
| 160 |
| 161 /** |
| 162 * While generating expressions, we can't insert variable declarations. |
| 163 * Instead we declare them at the end of the function |
| 164 */ |
| 165 final List<String> delayedVariablesDeclaration; |
| 166 |
| 167 /** |
| 168 * Set of variables that have already been declared. |
| 169 */ |
| 170 final Set<String> declaredVariables; |
161 | 171 |
162 Element equalsNullElement; | 172 Element equalsNullElement; |
163 Element boolifiedEqualsNullElement; | 173 Element boolifiedEqualsNullElement; |
164 int indent = 0; | 174 int indent = 0; |
165 int expectedPrecedence = JSPrecedence.STATEMENT_PRECEDENCE; | 175 int expectedPrecedence = JSPrecedence.STATEMENT_PRECEDENCE; |
166 JSBinaryOperatorPrecedence unsignedShiftPrecedences; | 176 JSBinaryOperatorPrecedence unsignedShiftPrecedences; |
167 HGraph currentGraph; | 177 HGraph currentGraph; |
168 /** | 178 /** |
169 * Whether the code-generation should try to generate an expression | 179 * Whether the code-generation should try to generate an expression |
170 * instead of a sequence of statements. | 180 * instead of a sequence of statements. |
171 */ | 181 */ |
172 int generationState = STATE_STATEMENT; | 182 int generationState = STATE_STATEMENT; |
173 /** | |
174 * While generating expressions, we can't insert variable declarations. | |
175 * Instead we declare them at the end of the function | |
176 */ | |
177 Link<String> delayedVarDecl = const EmptyLink<String>(); | |
178 HBasicBlock currentBlock; | 183 HBasicBlock currentBlock; |
179 | 184 |
180 // Records a block-information that is being handled specially. | 185 // Records a block-information that is being handled specially. |
181 // Used to break bad recursion. | 186 // Used to break bad recursion. |
182 HBlockInformation currentBlockInformation; | 187 HBlockInformation currentBlockInformation; |
183 // The subgraph is used to delimit traversal for some constructions, e.g., | 188 // The subgraph is used to delimit traversal for some constructions, e.g., |
184 // if branches. | 189 // if branches. |
185 SubGraph subGraph; | 190 SubGraph subGraph; |
186 | 191 |
187 LibraryElement get currentLibrary() => work.element.getLibrary(); | 192 LibraryElement get currentLibrary() => work.element.getLibrary(); |
188 Compiler get compiler() => backend.compiler; | 193 Compiler get compiler() => backend.compiler; |
189 NativeEmitter get nativeEmitter() => backend.emitter.nativeEmitter; | 194 NativeEmitter get nativeEmitter() => backend.emitter.nativeEmitter; |
190 Enqueuer get world() => backend.compiler.enqueuer.codegen; | 195 Enqueuer get world() => backend.compiler.enqueuer.codegen; |
191 | 196 |
192 bool isGenerateAtUseSite(HInstruction instruction) { | 197 bool isGenerateAtUseSite(HInstruction instruction) { |
193 return generateAtUseSite.contains(instruction); | 198 return generateAtUseSite.contains(instruction); |
194 } | 199 } |
195 | 200 |
196 SsaCodeGenerator(this.backend, | 201 SsaCodeGenerator(this.backend, |
197 this.work, | 202 this.work, |
198 this.parameters, | 203 this.parameters, |
199 this.parameterNames) | 204 this.parameterNames) |
200 : names = new Map<int, String>(), | 205 : declaredVariables = new Set<String>(), |
201 prefixes = new Map<String, int>(), | 206 delayedVariablesDeclaration = new List<String>(), |
202 usedNames = new Set<String>(), | |
203 declaredInstructions = new Set<HInstruction>(), | |
204 buffer = new StringBuffer(), | 207 buffer = new StringBuffer(), |
205 generateAtUseSite = new Set<HInstruction>(), | 208 generateAtUseSite = new Set<HInstruction>(), |
206 logicalOperations = new Map<HPhi, String>(), | 209 logicalOperations = new Map<HPhi, String>(), |
207 breakAction = new Map<Element, ElementAction>(), | 210 breakAction = new Map<Element, ElementAction>(), |
208 continueAction = new Map<Element, ElementAction>(), | 211 continueAction = new Map<Element, ElementAction>(), |
209 phiEquivalence = new Equivalence<HPhi>(), | |
210 unsignedShiftPrecedences = JSPrecedence.binary['>>>'] { | 212 unsignedShiftPrecedences = JSPrecedence.binary['>>>'] { |
211 | 213 |
212 for (final name in parameterNames.getValues()) { | |
213 prefixes[name] = 0; | |
214 } | |
215 | |
216 // Create a namespace for temporaries. | |
217 prefixes[TEMPORARY_PREFIX] = 0; | |
218 | |
219 Interceptors interceptors = backend.builder.interceptors; | 214 Interceptors interceptors = backend.builder.interceptors; |
220 equalsNullElement = interceptors.getEqualsNullInterceptor(); | 215 equalsNullElement = interceptors.getEqualsNullInterceptor(); |
221 boolifiedEqualsNullElement = | 216 boolifiedEqualsNullElement = |
222 interceptors.getBoolifiedVersionOf(equalsNullElement); | 217 interceptors.getBoolifiedVersionOf(equalsNullElement); |
223 } | 218 } |
224 | 219 |
225 abstract visitTypeGuard(HTypeGuard node); | 220 abstract visitTypeGuard(HTypeGuard node); |
226 | 221 |
227 abstract beginGraph(HGraph graph); | 222 abstract beginGraph(HGraph graph); |
228 abstract endGraph(HGraph graph); | 223 abstract endGraph(HGraph graph); |
(...skipping 22 matching lines...) Expand all Loading... |
251 void endExpression(int precedence) { | 246 void endExpression(int precedence) { |
252 if (precedence < expectedPrecedence) { | 247 if (precedence < expectedPrecedence) { |
253 buffer.add(')'); | 248 buffer.add(')'); |
254 } | 249 } |
255 } | 250 } |
256 | 251 |
257 void preGenerateMethod(HGraph graph) { | 252 void preGenerateMethod(HGraph graph) { |
258 new SsaInstructionMerger(generateAtUseSite).visitGraph(graph); | 253 new SsaInstructionMerger(generateAtUseSite).visitGraph(graph); |
259 new SsaConditionMerger(generateAtUseSite, | 254 new SsaConditionMerger(generateAtUseSite, |
260 logicalOperations).visitGraph(graph); | 255 logicalOperations).visitGraph(graph); |
261 new PhiEquivalator(phiEquivalence, logicalOperations).analyzeGraph(graph); | 256 SsaLiveIntervalBuilder intervalBuilder = |
| 257 new SsaLiveIntervalBuilder(compiler); |
| 258 intervalBuilder.visitGraph(graph); |
| 259 SsaVariableAllocator allocator = new SsaVariableAllocator( |
| 260 compiler, |
| 261 intervalBuilder.liveInstructions, |
| 262 intervalBuilder.liveIntervals, |
| 263 generateAtUseSite); |
| 264 allocator.visitGraph(graph); |
| 265 variableNames = allocator.names; |
262 } | 266 } |
263 | 267 |
264 visitGraph(HGraph graph) { | 268 visitGraph(HGraph graph) { |
265 preGenerateMethod(graph); | 269 preGenerateMethod(graph); |
266 currentGraph = graph; | 270 currentGraph = graph; |
267 indent++; // We are already inside a function. | 271 indent++; // We are already inside a function. |
268 subGraph = new SubGraph(graph.entry, graph.exit); | 272 subGraph = new SubGraph(graph.entry, graph.exit); |
269 beginGraph(graph); | 273 beginGraph(graph); |
270 visitBasicBlock(graph.entry); | 274 visitBasicBlock(graph.entry); |
271 if (!delayedVarDecl.isEmpty()) { | 275 if (!delayedVariablesDeclaration.isEmpty()) { |
272 addIndented("var "); | 276 addIndented("var "); |
273 while (true) { | 277 buffer.add(Strings.join(delayedVariablesDeclaration, ', ')); |
274 buffer.add(delayedVarDecl.head); | |
275 delayedVarDecl = delayedVarDecl.tail; | |
276 if (delayedVarDecl.isEmpty()) break; | |
277 buffer.add(", "); | |
278 } | |
279 buffer.add(";\n"); | 278 buffer.add(";\n"); |
280 } | 279 } |
281 endGraph(graph); | 280 endGraph(graph); |
282 } | 281 } |
283 | 282 |
284 void visitSubGraph(SubGraph newSubGraph) { | 283 void visitSubGraph(SubGraph newSubGraph) { |
285 SubGraph oldSubGraph = subGraph; | 284 SubGraph oldSubGraph = subGraph; |
286 subGraph = newSubGraph; | 285 subGraph = newSubGraph; |
287 visitBasicBlock(subGraph.start); | 286 visitBasicBlock(subGraph.start); |
288 subGraph = oldSubGraph; | 287 subGraph = oldSubGraph; |
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
406 int oldState = generationState; | 405 int oldState = generationState; |
407 generationState = STATE_FIRST_DECLARATION; | 406 generationState = STATE_FIRST_DECLARATION; |
408 visitSubGraph(expressionSubGraph.subExpression); | 407 visitSubGraph(expressionSubGraph.subExpression); |
409 generationState = oldState; | 408 generationState = oldState; |
410 } | 409 } |
411 | 410 |
412 void generateCondition(HBlockInformation condition) { | 411 void generateCondition(HBlockInformation condition) { |
413 generateExpression(condition); | 412 generateExpression(condition); |
414 } | 413 } |
415 | 414 |
416 String temporary(HInstruction instruction) { | |
417 int id = instruction.id; | |
418 String name = names[id]; | |
419 if (name !== null) return name; | |
420 | |
421 String prefix = TEMPORARY_PREFIX; | |
422 if (instruction.sourceElement !== null) { | |
423 Element element = instruction.sourceElement; | |
424 if (element !== null && !element.name.isEmpty()) { | |
425 prefix = element.name.slowToString(); | |
426 // Special case the variable named [TEMPORARY_PREFIX] to allow | |
427 // keeping its name. | |
428 if (prefix == TEMPORARY_PREFIX && !usedNames.contains(prefix)) { | |
429 return newName(id, prefix); | |
430 } | |
431 // If we've never seen that prefix before, try to use it | |
432 // directly. | |
433 if (!prefixes.containsKey(prefix)) { | |
434 // Make sure the variable name does not conflict with our mangling. | |
435 while (usedNames.contains(prefix)) { | |
436 prefix = '${prefix}_'; | |
437 } | |
438 prefixes[prefix] = 0; | |
439 return newName(id, prefix); | |
440 } | |
441 } | |
442 } | |
443 | |
444 name = '${prefix}${prefixes[prefix]++}'; | |
445 while (usedNames.contains(name)) { | |
446 name = '${prefix}${prefixes[prefix]++}'; | |
447 } | |
448 return newName(id, name); | |
449 } | |
450 | |
451 // TODO(floitsch): share more code with [temporary]. | |
452 String freshTemporary() { | |
453 String prefix = TEMPORARY_PREFIX; | |
454 String name = '${prefix}${prefixes[prefix]++}'; | |
455 while (usedNames.contains(name)) { | |
456 name = '${prefix}${prefixes[prefix]++}'; | |
457 } | |
458 String result = JsNames.getValid(name); | |
459 usedNames.add(result); | |
460 return result; | |
461 } | |
462 | |
463 String newName(int id, String name) { | |
464 String result = JsNames.getValid(name); | |
465 names[id] = result; | |
466 usedNames.add(result); | |
467 return result; | |
468 } | |
469 | |
470 /** | 415 /** |
471 * Only visits the arguments starting at inputs[HInvoke.ARGUMENTS_OFFSET]. | 416 * Only visits the arguments starting at inputs[HInvoke.ARGUMENTS_OFFSET]. |
472 */ | 417 */ |
473 void visitArguments(List<HInstruction> inputs) { | 418 void visitArguments(List<HInstruction> inputs) { |
474 assert(inputs.length >= HInvoke.ARGUMENTS_OFFSET); | 419 assert(inputs.length >= HInvoke.ARGUMENTS_OFFSET); |
475 buffer.add('('); | 420 buffer.add('('); |
476 for (int i = HInvoke.ARGUMENTS_OFFSET; i < inputs.length; i++) { | 421 for (int i = HInvoke.ARGUMENTS_OFFSET; i < inputs.length; i++) { |
477 if (i != HInvoke.ARGUMENTS_OFFSET) buffer.add(', '); | 422 if (i != HInvoke.ARGUMENTS_OFFSET) buffer.add(', '); |
478 use(inputs[i], JSPrecedence.ASSIGNMENT_PRECEDENCE); | 423 use(inputs[i], JSPrecedence.ASSIGNMENT_PRECEDENCE); |
479 } | 424 } |
(...skipping 14 matching lines...) Expand all Loading... |
494 bool isGeneratingDeclaration() { | 439 bool isGeneratingDeclaration() { |
495 return (generationState == STATE_DECLARATION || | 440 return (generationState == STATE_DECLARATION || |
496 generationState == STATE_FIRST_DECLARATION); | 441 generationState == STATE_FIRST_DECLARATION); |
497 } | 442 } |
498 | 443 |
499 /** | 444 /** |
500 * Called before writing an expression. | 445 * Called before writing an expression. |
501 * Ensures that expressions are comma spearated. | 446 * Ensures that expressions are comma spearated. |
502 */ | 447 */ |
503 void addExpressionSeparator() { | 448 void addExpressionSeparator() { |
504 if (generationState == STATE_FIRST_DECLARATION) { | 449 if (generationState == STATE_FIRST_EXPRESSION) { |
505 buffer.add("var "); | |
506 generationState = STATE_DECLARATION; | |
507 } else if (generationState == STATE_FIRST_EXPRESSION) { | |
508 generationState = STATE_EXPRESSION; | 450 generationState = STATE_EXPRESSION; |
509 } else { | 451 } else if (generationState != STATE_FIRST_DECLARATION) { |
510 buffer.add(", "); | 452 buffer.add(", "); |
511 } | 453 } |
| 454 // If the state is [STATE_FIRST_DECLARATION] the potential |
| 455 // declaration of the variable will be done by the instruction. |
| 456 } |
| 457 |
| 458 bool isVariableDeclared(String variableName) { |
| 459 return declaredVariables.contains(variableName); |
512 } | 460 } |
513 | 461 |
514 void declareVariable(String variableName) { | 462 void declareVariable(String variableName) { |
515 if (isGeneratingExpression()) { | 463 if (isGeneratingExpression()) { |
516 buffer.add(variableName); | 464 if (generationState == STATE_FIRST_DECLARATION) { |
517 if (!isGeneratingDeclaration()) { | 465 generationState = STATE_DECLARATION; |
518 delayedVarDecl = delayedVarDecl.prepend(variableName); | 466 if (!isVariableDeclared(variableName)) { |
| 467 declaredVariables.add(variableName); |
| 468 buffer.add("var "); |
| 469 } |
| 470 } else if (!isGeneratingDeclaration() |
| 471 && !isVariableDeclared(variableName)) { |
| 472 delayedVariablesDeclaration.add(variableName); |
519 } | 473 } |
520 } else { | 474 } else if (!isVariableDeclared(variableName)) { |
| 475 declaredVariables.add(variableName); |
521 buffer.add("var "); | 476 buffer.add("var "); |
522 buffer.add(variableName); | |
523 } | 477 } |
| 478 buffer.add(variableName); |
524 } | 479 } |
525 | 480 |
526 void declareInstruction(HInstruction instruction) { | 481 void declareInstruction(HInstruction instruction) { |
527 declaredInstructions.add(instruction); | 482 declareVariable(variableNames.getName(instruction)); |
528 String name = temporary(instruction); | |
529 declareVariable(name); | |
530 } | |
531 | |
532 bool needsNewVariable(HInstruction instruction) { | |
533 bool needsVar = !instruction.usedBy.isEmpty(); | |
534 if (needsVar && instruction is HCheck) { | |
535 HCheck check = instruction; | |
536 HInstruction input = check.checkedInput; | |
537 // We only need a new var if [input] is generated at use site | |
538 // but is not a trivial code motion invariant instruction like | |
539 // for parameters or this. | |
540 // | |
541 // For example: | |
542 // Foo a = this; | |
543 // print(a); | |
544 // print(a); | |
545 // | |
546 // In checked mode no new variable is needed. | |
547 // FooTypeCheck(this); | |
548 // print(this); | |
549 // print(this); | |
550 // | |
551 // But for this example: | |
552 // Foo a = foo(); | |
553 // print(a); | |
554 // print(a); | |
555 // | |
556 // We need a new variable: | |
557 // var a = FooTypeCheck(foo()); | |
558 // print(a); | |
559 // print(a); | |
560 needsVar = isGenerateAtUseSite(input) && !input.isCodeMotionInvariant(); | |
561 } | |
562 return needsVar; | |
563 } | 483 } |
564 | 484 |
565 void define(HInstruction instruction) { | 485 void define(HInstruction instruction) { |
566 if (needsNewVariable(instruction)) { | 486 if (instruction is !HCheck && variableNames.hasName(instruction)) { |
567 declareInstruction(instruction); | 487 declareInstruction(instruction); |
568 buffer.add(" = "); | 488 buffer.add(" = "); |
569 visit(instruction, JSPrecedence.ASSIGNMENT_PRECEDENCE); | 489 visit(instruction, JSPrecedence.ASSIGNMENT_PRECEDENCE); |
570 } else { | 490 } else { |
571 visit(instruction, JSPrecedence.STATEMENT_PRECEDENCE); | 491 visit(instruction, JSPrecedence.STATEMENT_PRECEDENCE); |
572 } | 492 } |
573 } | 493 } |
574 | 494 |
575 void use(HInstruction argument, int expectedPrecedenceForArgument) { | 495 void use(HInstruction argument, int expectedPrecedenceForArgument) { |
576 if (argument is HCheck) { | 496 if (isGenerateAtUseSite(argument)) { |
577 HCheck instruction = argument; | |
578 HInstruction input = instruction.checkedInput; | |
579 if (isGenerateAtUseSite(argument) && isGenerateAtUseSite(input)) { | |
580 // If both instructions can be generated at use site, we can | |
581 // just visit [argument]. | |
582 // | |
583 // For example: | |
584 // Foo a = foo(); | |
585 // print(a); | |
586 // | |
587 // In checked mode will turn into: | |
588 // print(FooTypeCheck(foo())); | |
589 visit(argument, expectedPrecedenceForArgument); | |
590 } else if (isGenerateAtUseSite(input)) { | |
591 // Get the real input of that checked instruction. | |
592 while (input is HCheck) { | |
593 HCheck check = input; | |
594 input = check.checkedInput; | |
595 } | |
596 | |
597 // If [argument] cannot be generated at use site, but [input] | |
598 // can, use the temporary of [argument]. A code motion | |
599 // invariant instruction does not have a temporary, so we just | |
600 // | |
601 // For example: | |
602 // Foo a = foo(); | |
603 // print(a); | |
604 // print(a); | |
605 // | |
606 // In checked mode will turn into: | |
607 // var a = FooTypeCheck(foo()); | |
608 // print(a); | |
609 // print(a); | |
610 // | |
611 // Note that in case the input is code motion invariant, like | |
612 // for parameters or this, we just need to visit it, since | |
613 // there is no temporary for such instruction. | |
614 if (input.isCodeMotionInvariant()) { | |
615 visit(input, expectedPrecedenceForArgument); | |
616 } else { | |
617 buffer.add(temporary(argument)); | |
618 } | |
619 } else { | |
620 // Otherwise we just use [input]. [argument] has already been | |
621 // emitted, and we just need the temporary of [input]. | |
622 // | |
623 // For example: | |
624 // var a = foo(); | |
625 // print(a); | |
626 // Foo b = a; | |
627 // print(b); | |
628 // | |
629 // In checked mode will turn into: | |
630 // var a = foo(); | |
631 // print(a); | |
632 // FooTypeCheck(a); | |
633 // print(a); | |
634 use(input, expectedPrecedenceForArgument); | |
635 } | |
636 } else if (isGenerateAtUseSite(argument)) { | |
637 visit(argument, expectedPrecedenceForArgument); | 497 visit(argument, expectedPrecedenceForArgument); |
| 498 } else if (argument is HCheck) { |
| 499 HCheck check = argument; |
| 500 use(argument.checkedInput, expectedPrecedenceForArgument); |
638 } else { | 501 } else { |
639 buffer.add(temporary(argument)); | 502 buffer.add(variableNames.getName(argument)); |
640 } | 503 } |
641 } | 504 } |
642 | 505 |
643 visit(HInstruction node, int expectedPrecedenceForNode) { | 506 visit(HInstruction node, int expectedPrecedenceForNode) { |
644 int oldPrecedence = this.expectedPrecedence; | 507 int oldPrecedence = this.expectedPrecedence; |
645 this.expectedPrecedence = expectedPrecedenceForNode; | 508 this.expectedPrecedence = expectedPrecedenceForNode; |
646 node.accept(this); | 509 node.accept(this); |
647 this.expectedPrecedence = oldPrecedence; | 510 this.expectedPrecedence = oldPrecedence; |
648 } | 511 } |
649 | 512 |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
717 | 580 |
718 bool visitTryInfo(HTryBlockInformation info) { | 581 bool visitTryInfo(HTryBlockInformation info) { |
719 addIndented("try {\n"); | 582 addIndented("try {\n"); |
720 indent++; | 583 indent++; |
721 generateStatements(info.body); | 584 generateStatements(info.body); |
722 indent--; | 585 indent--; |
723 addIndented("}"); | 586 addIndented("}"); |
724 if (info.catchBlock !== null) { | 587 if (info.catchBlock !== null) { |
725 // Printing the catch part. | 588 // Printing the catch part. |
726 HParameterValue exception = info.catchVariable; | 589 HParameterValue exception = info.catchVariable; |
727 String name = temporary(exception); | 590 String name = variableNames.getName(exception); |
728 parameterNames[exception.element] = name; | 591 parameterNames[exception.element] = name; |
729 buffer.add(' catch ($name) {\n'); | 592 buffer.add(' catch ($name) {\n'); |
730 indent++; | 593 indent++; |
731 generateStatements(info.catchBlock); | 594 generateStatements(info.catchBlock); |
732 parameterNames.remove(exception.element); | 595 parameterNames.remove(exception.element); |
733 indent--; | 596 indent--; |
734 addIndented('}'); | 597 addIndented('}'); |
735 } | 598 } |
736 if (info.finallyBlock != null) { | 599 if (info.finallyBlock != null) { |
737 buffer.add(" finally {\n"); | 600 buffer.add(" finally {\n"); |
(...skipping 280 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1018 return; | 881 return; |
1019 } | 882 } |
1020 // Flow based traversal. | 883 // Flow based traversal. |
1021 if (node.isLoopHeader() && | 884 if (node.isLoopHeader() && |
1022 node.loopInformation.loopBlockInformation !== currentBlockInformation) { | 885 node.loopInformation.loopBlockInformation !== currentBlockInformation) { |
1023 beginLoop(node); | 886 beginLoop(node); |
1024 } | 887 } |
1025 iterateBasicBlock(node); | 888 iterateBasicBlock(node); |
1026 } | 889 } |
1027 | 890 |
1028 /** Generates the assignments for all phis of successors blocks. */ | 891 void emitAssignment(String destination, String source) { |
1029 void assignPhisOfAllSuccessors(HBasicBlock node) { | 892 if (isGeneratingExpression()) { |
1030 Map<HInstruction, String> temporaryNamesOfPhis = null; | 893 addExpressionSeparator(); |
| 894 } else { |
| 895 addIndentation(); |
| 896 } |
| 897 declareVariable(destination); |
| 898 buffer.add(' = $source'); |
| 899 if (!isGeneratingExpression()) { |
| 900 buffer.add(';\n'); |
| 901 } |
| 902 } |
1031 | 903 |
1032 /** | 904 /** |
1033 * Generates the assignment [canonicalPhi] = [value]. | 905 * Sequentialize a list of conceptually parallel copies. Parallel |
1034 * | 906 * copies may contain cycles, that this method breaks. |
1035 * If the [canonicalPhi] has a temporary name (in [temporaryNamesOfPhis]) | 907 */ |
1036 * then the temporary is assigned instead of the [canonicalPhi]. However, | 908 void sequentializeCopies(List<Copy> copies) { |
1037 * when the left and right-hand side are equal ([:canonicalPhi === value:]) | 909 // Map to keep track of the current location (ie the variable that |
1038 * then the [canonicalPhi] is assigned with the temporary. | 910 // holds the initial value) of a variable. |
1039 */ | 911 Map<String, String> currentLocation = new Map<String, String>(); |
1040 void generateAssignment(HPhi canonicalPhi, HInstruction value) { | 912 |
| 913 // Map to keep track of the initial value of a variable. |
| 914 Map<String, String> initialValue = new Map<String, String>(); |
| 915 |
| 916 // List of variables to assign a value. |
| 917 List<String> worklist = <String>[]; |
| 918 |
| 919 // List of variables that we can assign a value to (ie are not |
| 920 // being used anymore). |
| 921 List<String> ready = <String>[]; |
| 922 |
| 923 // Prune [copies] by removing self-copies. |
| 924 List<Copy> prunedCopies = <Copy>[]; |
| 925 for (Copy copy in copies) { |
| 926 String sourceName = variableNames.getName(copy.source); |
| 927 String destinationName = variableNames.getName(copy.destination); |
| 928 if (sourceName != destinationName) { |
| 929 prunedCopies.add(new Copy(sourceName, destinationName)); |
| 930 } |
| 931 } |
| 932 copies = prunedCopies; |
| 933 |
| 934 |
| 935 // For each copy, set the current location of the source to |
| 936 // itself, and the initial value of the destination to the source. |
| 937 // Add the destination to the list of copies to make. |
| 938 for (Copy copy in copies) { |
| 939 currentLocation[copy.source] = copy.source; |
| 940 initialValue[copy.destination] = copy.source; |
| 941 worklist.add(copy.destination); |
| 942 } |
| 943 |
| 944 // For each copy, if the destination does not have a current |
| 945 // location, then we can safely assign to it. |
| 946 for (Copy copy in copies) { |
| 947 if (currentLocation[copy.destination] === null) { |
| 948 ready.add(copy.destination); |
| 949 } |
| 950 } |
| 951 |
| 952 while (!worklist.isEmpty()) { |
| 953 while (!ready.isEmpty()) { |
| 954 String destination = ready.removeLast(); |
| 955 String source = initialValue[destination]; |
| 956 // Since [source] might have been updated, use the current |
| 957 // location of [source] |
| 958 String copy = currentLocation[source]; |
| 959 emitAssignment(destination, copy); |
| 960 // Now [destination] is the current location of [source]. |
| 961 currentLocation[source] = destination; |
| 962 // If [source] hasn't been updated and needs to have a value, |
| 963 // add it to the list of variables that can be updated. Copies |
| 964 // of [source] will now use [destination]. |
| 965 if (source == copy && initialValue[source] !== null) { |
| 966 ready.add(source); |
| 967 } |
| 968 } |
| 969 |
| 970 // Check if we have a cycle. |
| 971 String current = worklist.removeLast(); |
| 972 // If [current] is used as a source, and the assignment has been |
| 973 // done, we are done with this variable. Otherwise there is a |
| 974 // cycle that we break by using a temporary name. |
| 975 if (currentLocation[current] !== null |
| 976 && current != currentLocation[initialValue[current]]) { |
| 977 String tempName = VariableNames.SWAP_TEMP; |
| 978 emitAssignment(tempName, current); |
| 979 currentLocation[current] = tempName; |
| 980 // [current] can now be safely updated. Copies of [current] |
| 981 // will now use [tempName]. |
| 982 ready.add(current); |
| 983 } |
| 984 } |
| 985 } |
| 986 |
| 987 void assignPhisOfSuccessors(HBasicBlock node) { |
| 988 CopyHandler handler = variableNames.getCopyHandler(node); |
| 989 if (handler == null) return; |
| 990 |
| 991 sequentializeCopies(handler.copies); |
| 992 |
| 993 for (Copy copy in handler.assignments) { |
1041 if (isGeneratingExpression()) { | 994 if (isGeneratingExpression()) { |
1042 addExpressionSeparator(); | 995 addExpressionSeparator(); |
1043 } else { | 996 } else { |
1044 addIndentation(); | 997 addIndentation(); |
1045 } | 998 } |
1046 if (temporaryNamesOfPhis !== null && | 999 declareVariable(variableNames.getName(copy.destination)); |
1047 canonicalPhi !== value && | 1000 buffer.add(' = '); |
1048 temporaryNamesOfPhis.containsKey(canonicalPhi)) { | 1001 use(copy.source, JSPrecedence.ASSIGNMENT_PRECEDENCE); |
1049 // This is the assignment to the temporary. | |
1050 declareVariable(temporaryNamesOfPhis[canonicalPhi]); | |
1051 } else if (!declaredInstructions.contains(canonicalPhi)) { | |
1052 declareInstruction(canonicalPhi); | |
1053 } else { | |
1054 buffer.add(temporary(canonicalPhi)); | |
1055 } | |
1056 buffer.add(" = "); | |
1057 bool isLogicalOperation = logicalOperations.containsKey(canonicalPhi); | |
1058 if (isLogicalOperation) { | |
1059 emitLogicalOperation(canonicalPhi, logicalOperations[canonicalPhi]); | |
1060 } else if (canonicalPhi === value) { | |
1061 buffer.add(temporaryNamesOfPhis[value]); | |
1062 } else { | |
1063 use(value, JSPrecedence.ASSIGNMENT_PRECEDENCE); | |
1064 } | |
1065 if (!isGeneratingExpression()) { | 1002 if (!isGeneratingExpression()) { |
1066 buffer.add(';\n'); | 1003 buffer.add(';\n'); |
1067 } | 1004 } |
1068 } | 1005 } |
1069 | |
1070 // Assignments are delayed so that we don't overwrite phis that might | |
1071 // be used as inputs. | |
1072 // TODO(floitsch): improve phi assignments. Currently we introduce | |
1073 // way too many temporary variables. | |
1074 Map<HPhi, HInstruction> phiAssignments = new Map<HPhi, HInstruction>(); | |
1075 | |
1076 for (HBasicBlock successor in node.successors) { | |
1077 int index = successor.predecessors.indexOf(node); | |
1078 successor.forEachPhi((HPhi phi) { | |
1079 bool isLogicalOperation = logicalOperations.containsKey(phi); | |
1080 // In case the phi is being generated by another | |
1081 // instruction. | |
1082 if (isLogicalOperation && isGenerateAtUseSite(phi)) return; | |
1083 HPhi canonicalPhi = phiEquivalence.getRepresentative(phi); | |
1084 assert(!isLogicalOperation || canonicalPhi === phi); | |
1085 HInstruction input = phi.inputs[index]; | |
1086 if (input is HPhi) { | |
1087 input = phiEquivalence.getRepresentative(input); | |
1088 // If we use the same variable, we don't need to create an | |
1089 // assignment. | |
1090 if (input === canonicalPhi) { | |
1091 assert(!isLogicalOperation); | |
1092 return; | |
1093 } | |
1094 } | |
1095 phiAssignments[canonicalPhi] = input; | |
1096 }); | |
1097 } | |
1098 | |
1099 Set<HPhi> inputPhis = new Set<HPhi>(); | |
1100 List<HPhi> phis = <HPhi>[]; | |
1101 | |
1102 /** | |
1103 * Transitively collects the phis that are used when emitting the [input] | |
1104 * and adds them to [inputPhis]. Does not add phis that are equal to the | |
1105 * [targetPhi] or are not in the [phiAssignments] map. | |
1106 */ | |
1107 void collectInputPhis(HInstruction input, HPhi targetPhi) { | |
1108 if (input is HPhi) { | |
1109 HPhi canonicalPhi = phiEquivalence.getRepresentative(input); | |
1110 // Self-updates are ok. | |
1111 if (canonicalPhi !== targetPhi && | |
1112 phiAssignments.containsKey(canonicalPhi)) { | |
1113 inputPhis.add(canonicalPhi); | |
1114 } | |
1115 } else if (isGenerateAtUseSite(input)) { | |
1116 for (HInstruction inputOfInput in input.inputs) { | |
1117 collectInputPhis(inputOfInput, targetPhi); | |
1118 } | |
1119 } | |
1120 } | |
1121 | |
1122 phiAssignments.forEach((HPhi targetPhi, HInstruction input) { | |
1123 phis.add(targetPhi); | |
1124 collectInputPhis(input, targetPhi); | |
1125 }); | |
1126 | |
1127 if (inputPhis.isEmpty()) { | |
1128 phiAssignments.forEach(generateAssignment); | |
1129 } else { | |
1130 // Emit the phiX=phiY assignments, taking care to break cycles. | |
1131 // Example program: | |
1132 // var x = 499; | |
1133 // var y = 99; | |
1134 // while (x != 99) { | |
1135 // var tmp = x; // <=== The tmp variable is removed in Ssa-form. | |
1136 // x = y; | |
1137 // y = tmp; | |
1138 // } | |
1139 // | |
1140 temporaryNamesOfPhis = new HashMap<HInstruction, String>(); | |
1141 // For phis that are used as inputs simply *always* allocate a | |
1142 // temporary. | |
1143 for (HPhi phi in phis) { | |
1144 if (inputPhis.contains(phi)) { | |
1145 String temporaryPhi = freshTemporary(); | |
1146 temporaryNamesOfPhis[phi] = temporaryPhi; | |
1147 } | |
1148 // The assignPhi uses temporary variables for the left-hand side if | |
1149 // they exist. | |
1150 generateAssignment(phi, phiAssignments[phi]); | |
1151 } | |
1152 // Finally assign the input phis. | |
1153 for (HPhi phi in inputPhis) { | |
1154 // The assignPhi special-cases phi assignments to itself and recognizes | |
1155 // it as assignment from the temporary variable to the actual phi. | |
1156 generateAssignment(phi, phi); | |
1157 } | |
1158 } | |
1159 } | 1006 } |
1160 | 1007 |
1161 void iterateBasicBlock(HBasicBlock node) { | 1008 void iterateBasicBlock(HBasicBlock node) { |
1162 HInstruction instruction = node.first; | 1009 HInstruction instruction = node.first; |
1163 while (instruction != null) { | 1010 while (instruction != null) { |
1164 if (instruction === node.last) { | 1011 if (instruction === node.last) { |
1165 assignPhisOfAllSuccessors(node); | 1012 assignPhisOfSuccessors(node); |
1166 } | 1013 } |
1167 | 1014 |
1168 if (isGenerateAtUseSite(instruction)) { | 1015 if (isGenerateAtUseSite(instruction)) { |
1169 if (instruction is HIf) { | 1016 if (instruction is HIf) { |
1170 HIf hif = instruction; | 1017 HIf hif = instruction; |
1171 // The "if" is implementing part of a logical expression. | 1018 // The "if" is implementing part of a logical expression. |
1172 // Skip directly forward to to its latest successor, since everything | 1019 // Skip directly forward to to its latest successor, since everything |
1173 // in-between must also be generateAtUseSite. | 1020 // in-between must also be generateAtUseSite. |
1174 assert(hif.trueBranch.id < hif.falseBranch.id); | 1021 assert(hif.trueBranch.id < hif.falseBranch.id); |
1175 visitBasicBlock(hif.falseBranch); | 1022 visitBasicBlock(hif.falseBranch); |
(...skipping 408 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1584 visitFieldSet(HFieldSet node) { | 1431 visitFieldSet(HFieldSet node) { |
1585 String name; | 1432 String name; |
1586 if (!node.isFromActivation()) { | 1433 if (!node.isFromActivation()) { |
1587 name = | 1434 name = |
1588 compiler.namer.instanceFieldName(currentLibrary, node.name); | 1435 compiler.namer.instanceFieldName(currentLibrary, node.name); |
1589 beginExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE); | 1436 beginExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE); |
1590 use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE); | 1437 use(node.receiver, JSPrecedence.MEMBER_PRECEDENCE); |
1591 buffer.add('.'); | 1438 buffer.add('.'); |
1592 buffer.add(name); | 1439 buffer.add(name); |
1593 } else { | 1440 } else { |
1594 // TODO(ngeoffray): Remove the 'var' once we don't globally box | |
1595 // variables used in a try/catch. | |
1596 if (!isGeneratingExpression()) { | |
1597 buffer.add('var '); | |
1598 } | |
1599 use(node.receiver, JSPrecedence.EXPRESSION_PRECEDENCE); | 1441 use(node.receiver, JSPrecedence.EXPRESSION_PRECEDENCE); |
1600 } | 1442 } |
1601 buffer.add(' = '); | 1443 buffer.add(' = '); |
1602 use(node.value, JSPrecedence.ASSIGNMENT_PRECEDENCE); | 1444 use(node.value, JSPrecedence.ASSIGNMENT_PRECEDENCE); |
1603 if (node.receiver !== null) { | 1445 if (node.receiver !== null) { |
1604 endExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE); | 1446 endExpression(JSPrecedence.ASSIGNMENT_PRECEDENCE); |
1605 } | 1447 } |
1606 } | 1448 } |
1607 | 1449 |
1608 visitForeign(HForeign node) { | 1450 visitForeign(HForeign node) { |
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1737 } else { | 1579 } else { |
1738 beginExpression(JSPrecedence.PREFIX_PRECEDENCE); | 1580 beginExpression(JSPrecedence.PREFIX_PRECEDENCE); |
1739 buffer.add('!'); | 1581 buffer.add('!'); |
1740 use(input, JSPrecedence.PREFIX_PRECEDENCE); | 1582 use(input, JSPrecedence.PREFIX_PRECEDENCE); |
1741 endExpression(JSPrecedence.PREFIX_PRECEDENCE); | 1583 endExpression(JSPrecedence.PREFIX_PRECEDENCE); |
1742 } | 1584 } |
1743 } | 1585 } |
1744 | 1586 |
1745 visitParameterValue(HParameterValue node) { | 1587 visitParameterValue(HParameterValue node) { |
1746 assert(isGenerateAtUseSite(node)); | 1588 assert(isGenerateAtUseSite(node)); |
1747 if (parameterNames[node.element] == null) { | 1589 buffer.add(variableNames.getName(node)); |
1748 buffer.add(temporary(node)); | |
1749 } else { | |
1750 buffer.add(parameterNames[node.element]); | |
1751 } | |
1752 } | 1590 } |
1753 | 1591 |
1754 visitPhi(HPhi node) { | 1592 visitPhi(HPhi node) { |
1755 String operation = logicalOperations[node]; | 1593 String operation = logicalOperations[node]; |
1756 if (operation !== null) { | 1594 if (operation !== null) { |
1757 emitLogicalOperation(node, operation); | 1595 emitLogicalOperation(node, operation); |
1758 } else { | 1596 } else { |
1759 HPhi canonicalPhi = phiEquivalence.getRepresentative(node); | 1597 buffer.add('${variableNames.getName(node)}'); |
1760 buffer.add('${temporary(canonicalPhi)}'); | |
1761 } | 1598 } |
1762 } | 1599 } |
1763 | 1600 |
1764 visitReturn(HReturn node) { | 1601 visitReturn(HReturn node) { |
1765 addIndentation(); | 1602 addIndentation(); |
1766 assert(node.inputs.length == 1); | 1603 assert(node.inputs.length == 1); |
1767 HInstruction input = node.inputs[0]; | 1604 HInstruction input = node.inputs[0]; |
1768 if (input.isConstantNull()) { | 1605 if (input.isConstantNull()) { |
1769 buffer.add('return;\n'); | 1606 buffer.add('return;\n'); |
1770 } else { | 1607 } else { |
(...skipping 702 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2473 void visitTypeGuard(HTypeGuard node) { | 2310 void visitTypeGuard(HTypeGuard node) { |
2474 indent--; | 2311 indent--; |
2475 addIndented('case ${node.state}:\n'); | 2312 addIndented('case ${node.state}:\n'); |
2476 indent++; | 2313 indent++; |
2477 addIndented('state = 0;\n'); | 2314 addIndented('state = 0;\n'); |
2478 | 2315 |
2479 setup.add(' case ${node.state}:\n'); | 2316 setup.add(' case ${node.state}:\n'); |
2480 int i = 0; | 2317 int i = 0; |
2481 for (HInstruction input in node.inputs) { | 2318 for (HInstruction input in node.inputs) { |
2482 HInstruction instruction = unwrap(input); | 2319 HInstruction instruction = unwrap(input); |
2483 setup.add(' ${temporary(instruction)} = env$i;\n'); | 2320 setup.add(' ${variableNames.getName(instruction)} = env$i;\n'); |
2484 i++; | 2321 i++; |
2485 } | 2322 } |
2486 if (i > maxBailoutParameters) maxBailoutParameters = i; | 2323 if (i > maxBailoutParameters) maxBailoutParameters = i; |
2487 setup.add(' break;\n'); | 2324 setup.add(' break;\n'); |
2488 } | 2325 } |
2489 | 2326 |
2490 void startBailoutCase(List<HTypeGuard> bailouts1, | 2327 void startBailoutCase(List<HTypeGuard> bailouts1, |
2491 List<HTypeGuard> bailouts2) { | 2328 List<HTypeGuard> bailouts2) { |
2492 indent--; | 2329 indent--; |
2493 handleBailoutCase(bailouts1); | 2330 handleBailoutCase(bailouts1); |
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2632 startBailoutSwitch(); | 2469 startBailoutSwitch(); |
2633 } | 2470 } |
2634 } | 2471 } |
2635 | 2472 |
2636 void endLabeledBlock(HLabeledBlockInformation labeledBlockInfo) { | 2473 void endLabeledBlock(HLabeledBlockInformation labeledBlockInfo) { |
2637 if (labeledBlockInfo.body.start.hasGuards()) { | 2474 if (labeledBlockInfo.body.start.hasGuards()) { |
2638 endBailoutSwitch(); | 2475 endBailoutSwitch(); |
2639 } | 2476 } |
2640 } | 2477 } |
2641 } | 2478 } |
OLD | NEW |