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.optimize_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'; |
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
130 | 130 |
131 /// True if [node] may return [JSNumber] instead of [JSInt] or [JSDouble]. | 131 /// True if [node] may return [JSNumber] instead of [JSInt] or [JSDouble]. |
132 bool jsNumberClassSuffices(Interceptor node) { | 132 bool jsNumberClassSuffices(Interceptor node) { |
133 // No methods on JSNumber call 'down' to methods on JSInt or JSDouble. If | 133 // No methods on JSNumber call 'down' to methods on JSInt or JSDouble. If |
134 // all uses of the interceptor are for methods is defined only on JSNumber | 134 // all uses of the interceptor are for methods is defined only on JSNumber |
135 // then JSNumber will suffice in place of choosing between JSInt or | 135 // then JSNumber will suffice in place of choosing between JSInt or |
136 // JSDouble. | 136 // JSDouble. |
137 for (Reference ref = node.firstRef; ref != null; ref = ref.next) { | 137 for (Reference ref = node.firstRef; ref != null; ref = ref.next) { |
138 if (ref.parent is InvokeMethod) { | 138 if (ref.parent is InvokeMethod) { |
139 InvokeMethod invoke = ref.parent; | 139 InvokeMethod invoke = ref.parent; |
140 if (invoke.receiver != ref) return false; | 140 if (invoke.receiverRef != ref) return false; |
141 var interceptedClasses = | 141 var interceptedClasses = |
142 backend.getInterceptedClassesOn(invoke.selector.name); | 142 backend.getInterceptedClassesOn(invoke.selector.name); |
143 if (interceptedClasses.contains(helpers.jsDoubleClass)) return false; | 143 if (interceptedClasses.contains(helpers.jsDoubleClass)) return false; |
144 if (interceptedClasses.contains(helpers.jsIntClass)) return false; | 144 if (interceptedClasses.contains(helpers.jsIntClass)) return false; |
145 continue; | 145 continue; |
146 } | 146 } |
147 // Other uses need full distinction. | 147 // Other uses need full distinction. |
148 return false; | 148 return false; |
149 } | 149 } |
150 return true; | 150 return true; |
(...skipping 15 matching lines...) Expand all Loading... |
166 } | 166 } |
167 return false; | 167 return false; |
168 } | 168 } |
169 | 169 |
170 /// Returns the only interceptor class that may be returned by [node], or | 170 /// Returns the only interceptor class that may be returned by [node], or |
171 /// `null` if no such class could be found. | 171 /// `null` if no such class could be found. |
172 ClassElement getSingleInterceptorClass(Interceptor node) { | 172 ClassElement getSingleInterceptorClass(Interceptor node) { |
173 // TODO(asgerf): This could be more precise if we used the use-site type, | 173 // TODO(asgerf): This could be more precise if we used the use-site type, |
174 // since the interceptor may have been hoisted out of a loop, where a less | 174 // since the interceptor may have been hoisted out of a loop, where a less |
175 // precise type is known. | 175 // precise type is known. |
176 Primitive input = node.input.definition; | 176 Primitive input = node.input; |
177 TypeMask type = input.type; | 177 TypeMask type = input.type; |
178 if (canInterceptNull(node)) return null; | 178 if (canInterceptNull(node)) return null; |
179 type = type.nonNullable(); | 179 type = type.nonNullable(); |
180 if (typeSystem.isDefinitelyArray(type)) { | 180 if (typeSystem.isDefinitelyArray(type)) { |
181 return backend.helpers.jsArrayClass; | 181 return backend.helpers.jsArrayClass; |
182 } | 182 } |
183 if (typeSystem.isDefinitelyInt(type)) { | 183 if (typeSystem.isDefinitelyInt(type)) { |
184 return backend.helpers.jsIntClass; | 184 return backend.helpers.jsIntClass; |
185 } | 185 } |
186 if (typeSystem.isDefinitelyNum(type) && jsNumberClassSuffices(node)) { | 186 if (typeSystem.isDefinitelyNum(type) && jsNumberClassSuffices(node)) { |
187 return backend.helpers.jsNumberClass; | 187 return backend.helpers.jsNumberClass; |
188 } | 188 } |
189 ClassElement singleClass = type.singleClass(classWorld); | 189 ClassElement singleClass = type.singleClass(classWorld); |
190 if (singleClass != null && | 190 if (singleClass != null && |
191 singleClass.isSubclassOf(backend.helpers.jsInterceptorClass)) { | 191 singleClass.isSubclassOf(backend.helpers.jsInterceptorClass)) { |
192 return singleClass; | 192 return singleClass; |
193 } | 193 } |
194 return null; | 194 return null; |
195 } | 195 } |
196 | 196 |
197 /// Try to replace [interceptor] with a constant, and return `true` if | 197 /// Try to replace [interceptor] with a constant, and return `true` if |
198 /// successful. | 198 /// successful. |
199 bool constifyInterceptor(Interceptor interceptor) { | 199 bool constifyInterceptor(Interceptor interceptor) { |
200 LetPrim let = interceptor.parent; | 200 LetPrim let = interceptor.parent; |
201 Primitive input = interceptor.input.definition; | 201 Primitive input = interceptor.input; |
202 ClassElement classElement = getSingleInterceptorClass(interceptor); | 202 ClassElement classElement = getSingleInterceptorClass(interceptor); |
203 | 203 |
204 if (classElement == null) return false; | 204 if (classElement == null) return false; |
205 ConstantValue constant = new InterceptorConstantValue(classElement.rawType); | 205 ConstantValue constant = new InterceptorConstantValue(classElement.rawType); |
206 | 206 |
207 if (!input.type.isNullable) { | 207 if (!input.type.isNullable) { |
208 Primitive constantPrim = makeConstantFor(constant, | 208 Primitive constantPrim = makeConstantFor(constant, |
209 useSite: let, | 209 useSite: let, |
210 type: interceptor.type, | 210 type: interceptor.type, |
211 sourceInformation: interceptor.sourceInformation); | 211 sourceInformation: interceptor.sourceInformation); |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
254 if (node.hasExactlyOneUse) { | 254 if (node.hasExactlyOneUse) { |
255 // Set the loop header on single-use interceptors so [visitInvokeMethod] | 255 // Set the loop header on single-use interceptors so [visitInvokeMethod] |
256 // can determine if it should become a one-shot interceptor. | 256 // can determine if it should become a one-shot interceptor. |
257 loopHeaderFor[node] = currentLoopHeader; | 257 loopHeaderFor[node] = currentLoopHeader; |
258 } | 258 } |
259 } | 259 } |
260 | 260 |
261 @override | 261 @override |
262 void visitInvokeMethod(InvokeMethod node) { | 262 void visitInvokeMethod(InvokeMethod node) { |
263 if (node.callingConvention != CallingConvention.Intercepted) return; | 263 if (node.callingConvention != CallingConvention.Intercepted) return; |
264 Primitive interceptor = node.receiver.definition; | 264 Primitive interceptor = node.receiver; |
265 if (interceptor is! Interceptor || | 265 if (interceptor is! Interceptor || |
266 interceptor.hasMultipleUses || | 266 interceptor.hasMultipleUses || |
267 loopHeaderFor[interceptor] != currentLoopHeader) { | 267 loopHeaderFor[interceptor] != currentLoopHeader) { |
268 return; | 268 return; |
269 } | 269 } |
270 // TODO(asgerf): Consider heuristics for when to use one-shot interceptors. | 270 // TODO(asgerf): Consider heuristics for when to use one-shot interceptors. |
271 // E.g. using only one-shot interceptors with a fast path. | 271 // E.g. using only one-shot interceptors with a fast path. |
272 node.callingConvention = CallingConvention.OneShotIntercepted; | 272 node.callingConvention = CallingConvention.OneShotIntercepted; |
273 node..receiver.unlink()..receiver = node.arguments.removeAt(0); | 273 node..receiverRef.unlink()..receiverRef = node.argumentRefs.removeAt(0); |
274 } | 274 } |
275 | 275 |
276 @override | 276 @override |
277 void visitTypeTestViaFlag(TypeTestViaFlag node) { | 277 void visitTypeTestViaFlag(TypeTestViaFlag node) { |
278 Primitive interceptor = node.interceptor.definition; | 278 Primitive interceptor = node.interceptor; |
279 if (interceptor is! Interceptor || | 279 if (interceptor is! Interceptor || |
280 interceptor.hasMultipleUses || | 280 interceptor.hasMultipleUses || |
281 loopHeaderFor[interceptor] != currentLoopHeader || | 281 loopHeaderFor[interceptor] != currentLoopHeader || |
282 !backend.mayGenerateInstanceofCheck(node.dartType)) { | 282 !backend.mayGenerateInstanceofCheck(node.dartType)) { |
283 return; | 283 return; |
284 } | 284 } |
285 Interceptor inter = interceptor; | 285 Interceptor inter = interceptor; |
286 Primitive value = inter.input.definition; | 286 Primitive value = inter.input; |
287 node.replaceWith(new TypeTest(value, node.dartType, [])..type = node.type); | 287 node.replaceWith(new TypeTest(value, node.dartType, [])..type = node.type); |
288 } | 288 } |
289 } | 289 } |
290 | 290 |
291 /// Shares interceptor constants when one is in scope of another. | 291 /// Shares interceptor constants when one is in scope of another. |
292 /// | 292 /// |
293 /// Interceptor optimization runs after GVN, hence this clean-up step is needed. | 293 /// Interceptor optimization runs after GVN, hence this clean-up step is needed. |
294 /// | 294 /// |
295 /// TODO(asgerf): Handle in separate constant optimization pass? With some other | 295 /// TODO(asgerf): Handle in separate constant optimization pass? With some other |
296 /// constant-related optimizations, like cloning small constants at use-site. | 296 /// constant-related optimizations, like cloning small constants at use-site. |
(...skipping 17 matching lines...) Expand all Loading... |
314 sharedConstantFor.remove(prim.value); | 314 sharedConstantFor.remove(prim.value); |
315 }); | 315 }); |
316 } | 316 } |
317 return next; | 317 return next; |
318 } | 318 } |
319 | 319 |
320 bool shouldShareConstant(Constant constant) { | 320 bool shouldShareConstant(Constant constant) { |
321 return constant.value.isInterceptor; | 321 return constant.value.isInterceptor; |
322 } | 322 } |
323 } | 323 } |
OLD | NEW |