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