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 |