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