| 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 |