| 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 /** | 5 /** |
| 6 * Instead of emitting each SSA instruction with a temporary variable | 6 * Instead of emitting each SSA instruction with a temporary variable |
| 7 * mark instructions that can be emitted at their use-site. | 7 * mark instructions that can be emitted at their use-site. |
| 8 * For example, in: | 8 * For example, in: |
| 9 * t0 = 4; | 9 * t0 = 4; |
| 10 * t1 = 3; | 10 * t1 = 3; |
| 11 * t2 = add(t0, t1); | 11 * t2 = add(t0, t1); |
| 12 * t0 and t1 would be marked and the resulting code would then be: | 12 * t0 and t1 would be marked and the resulting code would then be: |
| 13 * t2 = add(4, 3); | 13 * t2 = add(4, 3); |
| 14 */ | 14 */ |
| 15 class SsaInstructionMerger extends HBaseVisitor { | 15 class SsaInstructionMerger extends HBaseVisitor { |
| 16 List<HInstruction> expectedInputs; | 16 List<HInstruction> expectedInputs; |
| 17 Set<HInstruction> generateAtUseSite; | 17 Set<HInstruction> generateAtUseSite; |
| 18 | 18 |
| 19 SsaInstructionMerger(this.generateAtUseSite); | 19 SsaInstructionMerger(this.generateAtUseSite); |
| 20 | 20 |
| 21 void visitGraph(HGraph graph) { | 21 void visitGraph(HGraph graph) { |
| 22 visitDominatorTree(graph); | 22 visitDominatorTree(graph); |
| 23 } | 23 } |
| 24 | 24 |
| 25 bool usedOnlyByPhis(instruction) { | |
| 26 for (HInstruction user in instruction.usedBy) { | |
| 27 if (user is! HPhi) return false; | |
| 28 } | |
| 29 return true; | |
| 30 } | |
| 31 | |
| 32 void visitInstruction(HInstruction instruction) { | 25 void visitInstruction(HInstruction instruction) { |
| 33 // A code motion invariant instruction is dealt before visiting it. | 26 // A code motion invariant instruction is dealt before visiting it. |
| 34 assert(!instruction.isCodeMotionInvariant()); | 27 assert(!instruction.isCodeMotionInvariant()); |
| 35 for (HInstruction input in instruction.inputs) { | 28 for (HInstruction input in instruction.inputs) { |
| 36 if (!generateAtUseSite.contains(input) | 29 if (!generateAtUseSite.contains(input) |
| 37 && !input.isCodeMotionInvariant() | 30 && !input.isCodeMotionInvariant() |
| 38 && input.usedBy.length == 1 | 31 && input.usedBy.length == 1 |
| 39 && input is! HPhi) { | 32 && input is! HPhi) { |
| 40 expectedInputs.add(input); | 33 expectedInputs.add(input); |
| 41 } | 34 } |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 95 HInstruction nextInput = expectedInputs.removeLast(); | 88 HInstruction nextInput = expectedInputs.removeLast(); |
| 96 assert(!generateAtUseSite.contains(nextInput)); | 89 assert(!generateAtUseSite.contains(nextInput)); |
| 97 assert(nextInput.usedBy.length == 1); | 90 assert(nextInput.usedBy.length == 1); |
| 98 if (nextInput === instruction) { | 91 if (nextInput === instruction) { |
| 99 return true; | 92 return true; |
| 100 } | 93 } |
| 101 } | 94 } |
| 102 return false; | 95 return false; |
| 103 } | 96 } |
| 104 | 97 |
| 105 for (HBasicBlock successor in block.successors) { | |
| 106 // Only add the input of the first phi. Making inputs of | |
| 107 // later phis generate-at-use-site would make them move | |
| 108 // accross the assignment of the first phi, and we need | |
| 109 // more analysis before we can do that. | |
| 110 HPhi phi = successor.phis.first; | |
| 111 if (phi != null) { | |
| 112 int index = successor.predecessors.indexOf(block); | |
| 113 HInstruction input = phi.inputs[index]; | |
| 114 if (!generateAtUseSite.contains(input) | |
| 115 && !input.isCodeMotionInvariant() | |
| 116 && input.usedBy.length == 1 | |
| 117 && input is! HPhi) { | |
| 118 expectedInputs.add(input); | |
| 119 } | |
| 120 break; | |
| 121 } | |
| 122 } | |
| 123 | |
| 124 block.last.accept(this); | 98 block.last.accept(this); |
| 125 for (HInstruction instruction = block.last.previous; | 99 for (HInstruction instruction = block.last.previous; |
| 126 instruction !== null; | 100 instruction !== null; |
| 127 instruction = instruction.previous) { | 101 instruction = instruction.previous) { |
| 128 if (generateAtUseSite.contains(instruction)) { | 102 if (generateAtUseSite.contains(instruction)) { |
| 129 continue; | 103 continue; |
| 130 } | 104 } |
| 131 if (instruction.isCodeMotionInvariant()) { | 105 if (instruction.isCodeMotionInvariant()) { |
| 132 generateAtUseSite.add(instruction); | 106 generateAtUseSite.add(instruction); |
| 133 continue; | 107 continue; |
| (...skipping 286 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 420 }; | 394 }; |
| 421 } | 395 } |
| 422 | 396 |
| 423 class JSBinaryOperatorPrecedence { | 397 class JSBinaryOperatorPrecedence { |
| 424 final int left; | 398 final int left; |
| 425 final int right; | 399 final int right; |
| 426 const JSBinaryOperatorPrecedence(this.left, this.right); | 400 const JSBinaryOperatorPrecedence(this.left, this.right); |
| 427 // All binary operators (excluding assignment) are left associative. | 401 // All binary operators (excluding assignment) are left associative. |
| 428 int get precedence() => left; | 402 int get precedence() => left; |
| 429 } | 403 } |
| 430 | |
| 431 class PhiEquivalator { | |
| 432 final Equivalence<HPhi> equivalence; | |
| 433 final Map<HPhi, String> logicalOperations; | |
| 434 PhiEquivalator(this.equivalence, this.logicalOperations); | |
| 435 | |
| 436 void analyzeGraph(HGraph graph) { | |
| 437 graph.blocks.forEach(analyzeBlock); | |
| 438 } | |
| 439 | |
| 440 void analyzeBlock(HBasicBlock block) { | |
| 441 for (HPhi phi = block.phis.first; phi !== null; phi = phi.next) { | |
| 442 if (!logicalOperations.containsKey(phi) && | |
| 443 phi.usedBy.length == 1 && | |
| 444 phi.usedBy[0] is HPhi && | |
| 445 phi.block.id < phi.usedBy[0].block.id) { | |
| 446 equivalence.makeEquivalent(phi, phi.usedBy[0]); | |
| 447 } | |
| 448 } | |
| 449 } | |
| 450 } | |
| 451 | |
| 452 | |
| 453 /** | |
| 454 * Try to figure out which phis can be represented by the same temporary | |
| 455 * variable, to avoid creating a new variable for each phi. | |
| 456 */ | |
| 457 class Equivalence<T extends Hashable> { | |
| 458 // Represent equivalence classes of HPhi nodes as a forest of trees, | |
| 459 // where each tree is one equivalence class, and the root is the | |
| 460 // canonical representative for the equivalence class. | |
| 461 // Implement the forest by having each phi point to its parent in the tree, | |
| 462 // transitively linking it to the root, which itself doesn't have a parent. | |
| 463 final Map<T,T> representative; | |
| 464 | |
| 465 Equivalence() : representative = new Map<T,T>(); | |
| 466 | |
| 467 T makeEquivalent(T a, T b) { | |
| 468 T root1 = getRepresentative(a); | |
| 469 T root2 = getRepresentative(b); | |
| 470 if (root1 !== root2) { | |
| 471 // Merge the trees for the two classes into one. | |
| 472 representative[root1] = root2; | |
| 473 } | |
| 474 } | |
| 475 | |
| 476 /** | |
| 477 * Get the canonical representative for an equivalence class of phis. | |
| 478 */ | |
| 479 T getRepresentative(T element) { | |
| 480 T parent = representative[element]; | |
| 481 if (parent === null) { | |
| 482 // This is the root of a tree (a previously unseen node is considered | |
| 483 // the root of its own tree). | |
| 484 return element; | |
| 485 } | |
| 486 // Shorten the path for all the elements on the way to the root, | |
| 487 // improving the performance of future lookups. | |
| 488 T root = getRepresentative(parent); | |
| 489 if (root !== parent) representative[element] = root; | |
| 490 return root; | |
| 491 } | |
| 492 | |
| 493 bool areEquivalent(T a, T b) { | |
| 494 return getRepresentative(a) === getRepresentative(b); | |
| 495 } | |
| 496 } | |
| OLD | NEW |