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 |