| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, 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 library dart2js.cps_ir.share_interceptors; | 5 library dart2js.cps_ir.optimize_interceptors; |
| 6 | 6 |
| 7 import 'optimizers.dart'; | 7 import 'optimizers.dart'; |
| 8 import 'cps_ir_nodes.dart'; | 8 import 'cps_ir_nodes.dart'; |
| 9 import 'loop_hierarchy.dart'; | 9 import 'loop_hierarchy.dart'; |
| 10 import 'cps_fragment.dart'; | 10 import 'cps_fragment.dart'; |
| 11 import '../constants/values.dart'; | 11 import '../constants/values.dart'; |
| 12 import '../elements/elements.dart'; | 12 import '../elements/elements.dart'; |
| 13 import '../js_backend/backend_helpers.dart' show BackendHelpers; | 13 import '../js_backend/backend_helpers.dart' show BackendHelpers; |
| 14 import '../js_backend/js_backend.dart' show JavaScriptBackend; | 14 import '../js_backend/js_backend.dart' show JavaScriptBackend; |
| 15 import '../types/types.dart' show TypeMask; | 15 import '../types/types.dart' show TypeMask; |
| 16 import '../io/source_information.dart' show SourceInformation; | 16 import '../io/source_information.dart' show SourceInformation; |
| 17 | 17 |
| 18 /// Removes redundant `getInterceptor` calls. | 18 /// Replaces `getInterceptor` calls with interceptor constants when possible, |
| 19 /// | 19 /// or with "almost constant" expressions like "x && CONST" when the input |
| 20 /// The pass performs three optimizations for interceptors: | 20 /// is either null or has a known interceptor. |
| 21 ///- pull interceptors out of loops | 21 // |
| 22 ///- replace interceptors with constants | 22 // TODO(asgerf): Compute intercepted classes in this pass. |
| 23 ///- share interceptors when one is in scope of the other | 23 class OptimizeInterceptors extends TrampolineRecursiveVisitor implements Pass { |
| 24 class ShareInterceptors extends TrampolineRecursiveVisitor implements Pass { | 24 String get passName => 'Optimize interceptors'; |
| 25 String get passName => 'Share interceptors'; | |
| 26 | |
| 27 /// The innermost loop containing a given primitive. | |
| 28 final Map<Primitive, Continuation> loopHeaderFor = | |
| 29 <Primitive, Continuation>{}; | |
| 30 | |
| 31 /// An interceptor currently in scope for a given primitive. | |
| 32 final Map<Primitive, Interceptor> interceptorFor = <Primitive, Interceptor>{}; | |
| 33 | |
| 34 /// Interceptors that have been hoisted out of a given loop. | |
| 35 final Map<Continuation, List<Interceptor>> loopHoistedInterceptors = | |
| 36 <Continuation, List<Interceptor>>{}; | |
| 37 | 25 |
| 38 JavaScriptBackend backend; | 26 JavaScriptBackend backend; |
| 39 LoopHierarchy loopHierarchy; | 27 LoopHierarchy loopHierarchy; |
| 40 Continuation currentLoopHeader; | 28 Continuation currentLoopHeader; |
| 41 | 29 |
| 42 ShareInterceptors(this.backend); | 30 OptimizeInterceptors(this.backend); |
| 43 | 31 |
| 44 BackendHelpers get helpers => backend.helpers; | 32 BackendHelpers get helpers => backend.helpers; |
| 45 | 33 |
| 46 void rewrite(FunctionDefinition node) { | 34 void rewrite(FunctionDefinition node) { |
| 35 // TODO(asgerf): Computing the LoopHierarchy here may be overkill when all |
| 36 // we want is to hoist constants out of loops. |
| 47 loopHierarchy = new LoopHierarchy(node); | 37 loopHierarchy = new LoopHierarchy(node); |
| 48 visit(node.body); | 38 visit(node.body); |
| 49 new ShareConstants().visit(node); | 39 new ShareConstants().visit(node); |
| 50 } | 40 } |
| 51 | 41 |
| 52 @override | 42 @override |
| 53 Expression traverseContinuation(Continuation cont) { | 43 Expression traverseContinuation(Continuation cont) { |
| 54 Continuation oldLoopHeader = currentLoopHeader; | 44 Continuation oldLoopHeader = currentLoopHeader; |
| 55 currentLoopHeader = loopHierarchy.getLoopHeader(cont); | 45 currentLoopHeader = loopHierarchy.getLoopHeader(cont); |
| 56 for (Parameter param in cont.parameters) { | |
| 57 loopHeaderFor[param] = currentLoopHeader; | |
| 58 } | |
| 59 if (cont.isRecursive) { | |
| 60 pushAction(() { | |
| 61 // After the loop body has been processed, all interceptors hoisted | |
| 62 // to this loop fall out of scope and should be removed from the | |
| 63 // environment. | |
| 64 List<Interceptor> hoisted = loopHoistedInterceptors[cont]; | |
| 65 if (hoisted != null) { | |
| 66 for (Interceptor interceptor in hoisted) { | |
| 67 Primitive input = interceptor.input.definition; | |
| 68 assert(interceptorFor[input] == interceptor); | |
| 69 interceptorFor.remove(input); | |
| 70 constifyInterceptor(interceptor); | |
| 71 } | |
| 72 } | |
| 73 }); | |
| 74 } | |
| 75 pushAction(() { | 46 pushAction(() { |
| 76 currentLoopHeader = oldLoopHeader; | 47 currentLoopHeader = oldLoopHeader; |
| 77 }); | 48 }); |
| 78 return cont.body; | 49 return cont.body; |
| 79 } | 50 } |
| 80 | 51 |
| 81 /// If only one method table can be returned by the given interceptor, | 52 /// If only one method table can be returned by the given interceptor, |
| 82 /// returns a constant for that method table. | 53 /// returns a constant for that method table. |
| 83 InterceptorConstantValue getInterceptorConstant(Interceptor node) { | 54 InterceptorConstantValue getInterceptorConstant(Interceptor node) { |
| 84 if (node.interceptedClasses.length == 1 && | 55 if (node.interceptedClasses.length == 1 && |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 166 cps.invokeContinuation(cont, [constantPrim]); | 137 cps.invokeContinuation(cont, [constantPrim]); |
| 167 cps.context = cont; | 138 cps.context = cont; |
| 168 cps.insertAbove(let); | 139 cps.insertAbove(let); |
| 169 interceptor..replaceUsesWith(param)..destroy(); | 140 interceptor..replaceUsesWith(param)..destroy(); |
| 170 let.remove(); | 141 let.remove(); |
| 171 } | 142 } |
| 172 } | 143 } |
| 173 | 144 |
| 174 @override | 145 @override |
| 175 Expression traverseLetPrim(LetPrim node) { | 146 Expression traverseLetPrim(LetPrim node) { |
| 176 loopHeaderFor[node.primitive] = currentLoopHeader; | |
| 177 Expression next = node.body; | 147 Expression next = node.body; |
| 178 if (node.primitive is! Interceptor) { | 148 if (node.primitive is Interceptor) { |
| 179 return next; | 149 constifyInterceptor(node.primitive); |
| 180 } | 150 } |
| 181 Interceptor interceptor = node.primitive; | |
| 182 Primitive input = interceptor.input.definition; | |
| 183 | |
| 184 // Try to reuse an existing interceptor for the same input. | |
| 185 Interceptor existing = interceptorFor[input]; | |
| 186 if (existing != null) { | |
| 187 existing.interceptedClasses.addAll(interceptor.interceptedClasses); | |
| 188 existing.flags |= interceptor.flags; | |
| 189 interceptor..replaceUsesWith(existing)..destroy(); | |
| 190 node.remove(); | |
| 191 return next; | |
| 192 } | |
| 193 | |
| 194 // Put this interceptor in the environment. | |
| 195 interceptorFor[input] = interceptor; | |
| 196 | |
| 197 // Determine how far the interceptor can be lifted. The outermost loop | |
| 198 // that contains the input binding should also contain the interceptor | |
| 199 // binding. | |
| 200 Continuation referencedLoop = | |
| 201 lowestCommonAncestor(loopHeaderFor[input], currentLoopHeader); | |
| 202 if (referencedLoop != currentLoopHeader) { | |
| 203 Continuation hoistTarget = getCurrentOuterLoop(scope: referencedLoop); | |
| 204 LetCont loopBinding = hoistTarget.parent; | |
| 205 node.remove(); | |
| 206 node.insertAbove(loopBinding); | |
| 207 // Remove the interceptor from the environment after processing the loop. | |
| 208 loopHoistedInterceptors | |
| 209 .putIfAbsent(hoistTarget, () => <Interceptor>[]) | |
| 210 .add(interceptor); | |
| 211 } else { | |
| 212 // Remove the interceptor from the environment when it falls out of scope. | |
| 213 pushAction(() { | |
| 214 assert(interceptorFor[input] == interceptor); | |
| 215 interceptorFor.remove(input); | |
| 216 | |
| 217 // Now that the final set of intercepted classes has been seen, try to | |
| 218 // replace it with a constant. | |
| 219 constifyInterceptor(interceptor); | |
| 220 }); | |
| 221 } | |
| 222 | |
| 223 return next; | 151 return next; |
| 224 } | 152 } |
| 225 | |
| 226 /// Returns the the innermost loop that effectively encloses both | |
| 227 /// c1 and c2 (or `null` if there is no such loop). | |
| 228 Continuation lowestCommonAncestor(Continuation c1, Continuation c2) { | |
| 229 int d1 = getDepth(c1), d2 = getDepth(c2); | |
| 230 while (c1 != c2) { | |
| 231 if (d1 <= d2) { | |
| 232 c2 = loopHierarchy.getEnclosingLoop(c2); | |
| 233 d2 = getDepth(c2); | |
| 234 } else { | |
| 235 c1 = loopHierarchy.getEnclosingLoop(c1); | |
| 236 d1 = getDepth(c1); | |
| 237 } | |
| 238 } | |
| 239 return c1; | |
| 240 } | |
| 241 | |
| 242 int getDepth(Continuation loop) { | |
| 243 if (loop == null) return -1; | |
| 244 return loopHierarchy.loopDepth[loop]; | |
| 245 } | |
| 246 } | 153 } |
| 247 | 154 |
| 155 /// Shares interceptor constants when one is in scope of another. |
| 156 /// |
| 157 /// Interceptor optimization runs after GVN, hence this clean-up step is needed. |
| 158 /// |
| 159 /// TODO(asgerf): Handle in separate constant optimization pass? With some other |
| 160 /// constant-related optimizations, like cloning small constants at use-site. |
| 248 class ShareConstants extends TrampolineRecursiveVisitor { | 161 class ShareConstants extends TrampolineRecursiveVisitor { |
| 249 Map<ConstantValue, Constant> sharedConstantFor = <ConstantValue, Constant>{}; | 162 Map<ConstantValue, Constant> sharedConstantFor = <ConstantValue, Constant>{}; |
| 250 | 163 |
| 251 Expression traverseLetPrim(LetPrim node) { | 164 Expression traverseLetPrim(LetPrim node) { |
| 252 Expression next = node.body; | 165 Expression next = node.body; |
| 253 if (node.primitive is Constant && shouldShareConstant(node.primitive)) { | 166 if (node.primitive is Constant && shouldShareConstant(node.primitive)) { |
| 254 Constant prim = node.primitive; | 167 Constant prim = node.primitive; |
| 255 Constant existing = sharedConstantFor[prim.value]; | 168 Constant existing = sharedConstantFor[prim.value]; |
| 256 if (existing != null) { | 169 if (existing != null) { |
| 257 existing.useElementAsHint(prim.hint); | 170 existing.useElementAsHint(prim.hint); |
| 258 prim..replaceUsesWith(existing)..destroy(); | 171 prim..replaceUsesWith(existing)..destroy(); |
| 259 node.remove(); | 172 node.remove(); |
| 260 return next; | 173 return next; |
| 261 } | 174 } |
| 262 sharedConstantFor[prim.value] = prim; | 175 sharedConstantFor[prim.value] = prim; |
| 263 pushAction(() { | 176 pushAction(() { |
| 264 assert(sharedConstantFor[prim.value] == prim); | 177 assert(sharedConstantFor[prim.value] == prim); |
| 265 sharedConstantFor.remove(prim.value); | 178 sharedConstantFor.remove(prim.value); |
| 266 }); | 179 }); |
| 267 } | 180 } |
| 268 return next; | 181 return next; |
| 269 } | 182 } |
| 270 | 183 |
| 271 bool shouldShareConstant(Constant constant) { | 184 bool shouldShareConstant(Constant constant) { |
| 272 return constant.value.isInterceptor; | 185 return constant.value.isInterceptor; |
| 273 } | 186 } |
| 274 } | 187 } |
| OLD | NEW |