| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | |
| 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. | |
| 4 | |
| 5 part of dart2js.optimizers; | |
| 6 | |
| 7 /// Eliminate redundant phis from the given [FunctionDefinition]. | |
| 8 /// | |
| 9 /// Phis in this case are [Continuations] together with corresponding | |
| 10 /// [InvokeContinuation]s. A [Continuation] parameter at position i is redundant | |
| 11 /// if for all [InvokeContinuation]s, the parameter at position i is identical | |
| 12 /// (except for feedback). Redundant parameters are removed from the | |
| 13 /// continuation signature, all invocations, and replaced within the | |
| 14 /// continuation body. | |
| 15 class RedundantPhiEliminator extends RecursiveVisitor implements Pass { | |
| 16 final Set<Continuation> workSet = new Set<Continuation>(); | |
| 17 | |
| 18 void rewrite(final FunctionDefinition root) { | |
| 19 if (root.isAbstract) return; | |
| 20 | |
| 21 // Set all parent pointers. | |
| 22 new _ParentVisitor().visit(root); | |
| 23 | |
| 24 // Traverse the tree once to build the work set. | |
| 25 visit(root); | |
| 26 | |
| 27 // Process each continuation one-by-one. | |
| 28 while (workSet.isNotEmpty) { | |
| 29 Continuation cont = workSet.first; | |
| 30 workSet.remove(cont); | |
| 31 | |
| 32 if (cont.isReturnContinuation) { | |
| 33 continue; // Skip function return continuations. | |
| 34 } | |
| 35 | |
| 36 _processContinuation(cont); | |
| 37 } | |
| 38 } | |
| 39 | |
| 40 /// Called for each continuation on the work set. Modifies the IR graph if | |
| 41 /// [cont] is a candidate for redundant phi elimination. | |
| 42 void _processContinuation(Continuation cont) { | |
| 43 // Generate the list of all cont invocations. If cont is used in any other | |
| 44 // context (i.e. as a continuation of InvokeMethod), it is not possible to | |
| 45 // optimize. | |
| 46 List<InvokeContinuation> invokes = <InvokeContinuation>[]; | |
| 47 for (Reference ref = cont.firstRef; ref != null; ref = ref.next) { | |
| 48 Node parent = ref.parent; | |
| 49 if (parent is InvokeContinuation && ref == parent.continuation) { | |
| 50 invokes.add(parent); | |
| 51 } else { | |
| 52 return; // Can't optimize. | |
| 53 } | |
| 54 } | |
| 55 | |
| 56 if (invokes.isEmpty) { | |
| 57 return; // Continuation is never invoked, can't optimize. | |
| 58 } | |
| 59 | |
| 60 /// Returns the unique definition of parameter i if it exists and null | |
| 61 /// otherwise. A definition is unique if it is the only value used to | |
| 62 /// invoke the continuation, excluding feedback. | |
| 63 Definition uniqueDefinitionOf(int i) { | |
| 64 Definition value = null; | |
| 65 for (InvokeContinuation invoke in invokes) { | |
| 66 Definition def = invoke.arguments[i].definition; | |
| 67 | |
| 68 if (cont.parameters[i] == def) { | |
| 69 // Invocation param == param in LetCont (i.e. a recursive call). | |
| 70 continue; | |
| 71 } else if (value == null) { | |
| 72 value = def; // Set initial comparison value. | |
| 73 } else if (value != def) { | |
| 74 return null; // Differing invocation arguments. | |
| 75 } | |
| 76 } | |
| 77 | |
| 78 return value; | |
| 79 } | |
| 80 | |
| 81 // Check if individual parameters are always called with a unique | |
| 82 // definition, and remove them if that is the case. During each iteration, | |
| 83 // we read the current parameter/argument from index `src` and copy it | |
| 84 // to index `dst`. | |
| 85 int dst = 0; | |
| 86 for (int src = 0; src < cont.parameters.length; src++) { | |
| 87 // Is the current phi redundant? | |
| 88 Definition uniqueDefinition = uniqueDefinitionOf(src); | |
| 89 if (uniqueDefinition == null) { | |
| 90 // Reorganize parameters and arguments in case of deletions. | |
| 91 cont.parameters[dst] = cont.parameters[src]; | |
| 92 for (InvokeContinuation invoke in invokes) { | |
| 93 invoke.arguments[dst] = invoke.arguments[src]; | |
| 94 } | |
| 95 | |
| 96 dst++; | |
| 97 continue; | |
| 98 } | |
| 99 | |
| 100 Definition oldDefinition = cont.parameters[src]; | |
| 101 | |
| 102 // Add continuations of about-to-be modified invokes to worklist since | |
| 103 // we might introduce new optimization opportunities. | |
| 104 for (Reference ref = oldDefinition.firstRef; ref != null; | |
| 105 ref = ref.next) { | |
| 106 Node parent = ref.parent; | |
| 107 if (parent is InvokeContinuation) { | |
| 108 Continuation thatCont = parent.continuation.definition; | |
| 109 if (thatCont != cont) { | |
| 110 workSet.add(thatCont); | |
| 111 } | |
| 112 } | |
| 113 } | |
| 114 | |
| 115 // Replace individual parameters: | |
| 116 // * In the continuation body, replace occurrence of param with value, | |
| 117 // * and implicitly remove param from continuation signature and | |
| 118 // invocations by not incrementing `dst`. References of removed | |
| 119 // arguments are unlinked to keep definition usages up to date. | |
| 120 uniqueDefinition.substituteFor(oldDefinition); | |
| 121 for (InvokeContinuation invoke in invokes) { | |
| 122 invoke.arguments[src].unlink(); | |
| 123 } | |
| 124 | |
| 125 // Finally, if the substituted definition is not in scope of the affected | |
| 126 // continuation, move the continuation binding. This is safe to do since | |
| 127 // the continuation is referenced only as the target in continuation | |
| 128 // invokes, and all such invokes must be within the scope of | |
| 129 // [uniqueDefinition]. Note that this is linear in the depth of | |
| 130 // the binding of [uniqueDefinition]. | |
| 131 LetCont letCont = cont.parent; | |
| 132 assert(letCont != null); | |
| 133 _moveIntoScopeOf(letCont, uniqueDefinition); | |
| 134 } | |
| 135 | |
| 136 // Remove trailing items from parameter and argument lists. | |
| 137 cont.parameters.length = dst; | |
| 138 for (InvokeContinuation invoke in invokes) { | |
| 139 invoke.arguments.length = dst; | |
| 140 } | |
| 141 } | |
| 142 | |
| 143 void processLetCont(LetCont node) { | |
| 144 workSet.add(node.continuation); | |
| 145 } | |
| 146 } | |
| 147 | |
| 148 /// Returns true, iff [letCont] is not scope of [definition]. | |
| 149 /// Linear in the depth of definition within the IR graph. | |
| 150 bool _isInScopeOf(LetCont letCont, Definition definition) { | |
| 151 for (Node node = definition.parent; node != null; node = node.parent) { | |
| 152 if (node == letCont) { | |
| 153 return false; | |
| 154 } | |
| 155 } | |
| 156 | |
| 157 return true; | |
| 158 } | |
| 159 | |
| 160 /// Moves [letCont] below the binding of [definition] within the IR graph. | |
| 161 /// Does nothing if [letCont] is already within the scope of [definition]. | |
| 162 /// Assumes that one argument is nested within the scope of the other | |
| 163 /// when this method is called. | |
| 164 void _moveIntoScopeOf(LetCont letCont, Definition definition) { | |
| 165 if (_isInScopeOf(letCont, definition)) return; | |
| 166 | |
| 167 // Remove the continuation binding from its current spot. | |
| 168 InteriorNode parent = letCont.parent; | |
| 169 parent.body = letCont.body; | |
| 170 letCont.body.parent = parent; | |
| 171 | |
| 172 // Insert it just below the binding of definition. | |
| 173 InteriorNode binding = definition.parent; | |
| 174 | |
| 175 letCont.body = binding.body; | |
| 176 binding.body.parent = letCont; | |
| 177 | |
| 178 binding.body = letCont; | |
| 179 letCont.parent = binding; | |
| 180 } | |
| OLD | NEW |