| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 import '../common/backend_api.dart' show BackendClasses; | 5 import '../common/backend_api.dart' show BackendClasses; |
| 6 import '../compiler.dart' show Compiler; | 6 import '../compiler.dart' show Compiler; |
| 7 import '../constants/constant_system.dart'; | 7 import '../constants/constant_system.dart'; |
| 8 import '../constants/values.dart'; | 8 import '../constants/values.dart'; |
| 9 import '../elements/elements.dart'; | 9 import '../elements/entities.dart'; |
| 10 import '../js_backend/backend.dart'; | 10 import '../js_backend/backend.dart'; |
| 11 import '../types/types.dart'; | 11 import '../types/types.dart'; |
| 12 import '../universe/selector.dart' show Selector; | 12 import '../universe/selector.dart' show Selector; |
| 13 import '../world.dart' show ClosedWorld; | 13 import '../world.dart' show ClosedWorld; |
| 14 import 'nodes.dart'; | 14 import 'nodes.dart'; |
| 15 import 'optimize.dart'; | 15 import 'optimize.dart'; |
| 16 | 16 |
| 17 /** | 17 /** |
| 18 * This phase simplifies interceptors in multiple ways: | 18 * This phase simplifies interceptors in multiple ways: |
| 19 * | 19 * |
| (...skipping 12 matching lines...) Expand all Loading... |
| 32 * | 32 * |
| 33 * 5) Some HIs operations on an interceptor are replaced with a HIs version that | 33 * 5) Some HIs operations on an interceptor are replaced with a HIs version that |
| 34 * uses 'instanceof' rather than testing a type flag. | 34 * uses 'instanceof' rather than testing a type flag. |
| 35 * | 35 * |
| 36 */ | 36 */ |
| 37 class SsaSimplifyInterceptors extends HBaseVisitor | 37 class SsaSimplifyInterceptors extends HBaseVisitor |
| 38 implements OptimizationPhase { | 38 implements OptimizationPhase { |
| 39 final String name = "SsaSimplifyInterceptors"; | 39 final String name = "SsaSimplifyInterceptors"; |
| 40 final ClosedWorld closedWorld; | 40 final ClosedWorld closedWorld; |
| 41 final Compiler compiler; | 41 final Compiler compiler; |
| 42 final Element element; | 42 final ClassEntity enclosingClass; |
| 43 HGraph graph; | 43 HGraph graph; |
| 44 | 44 |
| 45 SsaSimplifyInterceptors(this.compiler, this.closedWorld, this.element); | 45 SsaSimplifyInterceptors(this.compiler, this.closedWorld, this.enclosingClass); |
| 46 | 46 |
| 47 JavaScriptBackend get backend => compiler.backend; | 47 JavaScriptBackend get backend => compiler.backend; |
| 48 | 48 |
| 49 BackendClasses get backendClasses => closedWorld.backendClasses; | 49 BackendClasses get backendClasses => closedWorld.backendClasses; |
| 50 | 50 |
| 51 ConstantSystem get constantSystem => closedWorld.constantSystem; | 51 ConstantSystem get constantSystem => closedWorld.constantSystem; |
| 52 | 52 |
| 53 void visitGraph(HGraph graph) { | 53 void visitGraph(HGraph graph) { |
| 54 this.graph = graph; | 54 this.graph = graph; |
| 55 visitDominatorTree(graph); | 55 visitDominatorTree(graph); |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 88 | 88 |
| 89 HInstruction constant = tryComputeConstantInterceptor( | 89 HInstruction constant = tryComputeConstantInterceptor( |
| 90 invoke.inputs[1], interceptor.interceptedClasses); | 90 invoke.inputs[1], interceptor.interceptedClasses); |
| 91 if (constant != null) { | 91 if (constant != null) { |
| 92 invoke.changeUse(interceptor, constant); | 92 invoke.changeUse(interceptor, constant); |
| 93 } | 93 } |
| 94 return false; | 94 return false; |
| 95 } | 95 } |
| 96 | 96 |
| 97 bool canUseSelfForInterceptor( | 97 bool canUseSelfForInterceptor( |
| 98 HInstruction receiver, Set<ClassElement> interceptedClasses) { | 98 HInstruction receiver, Set<ClassEntity> interceptedClasses) { |
| 99 if (receiver.canBePrimitive(closedWorld)) { | 99 if (receiver.canBePrimitive(closedWorld)) { |
| 100 // Primitives always need interceptors. | 100 // Primitives always need interceptors. |
| 101 return false; | 101 return false; |
| 102 } | 102 } |
| 103 if (receiver.canBeNull() && | 103 if (receiver.canBeNull() && |
| 104 interceptedClasses.contains(backendClasses.nullImplementation)) { | 104 interceptedClasses.contains(backendClasses.nullImplementation)) { |
| 105 // Need the JSNull interceptor. | 105 // Need the JSNull interceptor. |
| 106 return false; | 106 return false; |
| 107 } | 107 } |
| 108 | 108 |
| 109 // All intercepted classes extend `Interceptor`, so if the receiver can't be | 109 // All intercepted classes extend `Interceptor`, so if the receiver can't be |
| 110 // a class extending `Interceptor` then it can be called directly. | 110 // a class extending `Interceptor` then it can be called directly. |
| 111 return new TypeMask.nonNullSubclass( | 111 return new TypeMask.nonNullSubclass( |
| 112 backend.helpers.jsInterceptorClass, closedWorld) | 112 backend.helpers.jsInterceptorClass, closedWorld) |
| 113 .isDisjoint(receiver.instructionType, closedWorld); | 113 .isDisjoint(receiver.instructionType, closedWorld); |
| 114 } | 114 } |
| 115 | 115 |
| 116 HInstruction tryComputeConstantInterceptor( | 116 HInstruction tryComputeConstantInterceptor( |
| 117 HInstruction input, Set<ClassElement> interceptedClasses) { | 117 HInstruction input, Set<ClassEntity> interceptedClasses) { |
| 118 if (input == graph.explicitReceiverParameter) { | 118 if (input == graph.explicitReceiverParameter) { |
| 119 // If `explicitReceiverParameter` is set it means the current method is an | 119 // If `explicitReceiverParameter` is set it means the current method is an |
| 120 // interceptor method, and `this` is the interceptor. The caller just did | 120 // interceptor method, and `this` is the interceptor. The caller just did |
| 121 // `getInterceptor(foo).currentMethod(foo)` to enter the current method. | 121 // `getInterceptor(foo).currentMethod(foo)` to enter the current method. |
| 122 return graph.thisInstruction; | 122 return graph.thisInstruction; |
| 123 } | 123 } |
| 124 | 124 |
| 125 ClassElement constantInterceptor = tryComputeConstantInterceptorFromType( | 125 ClassEntity constantInterceptor = tryComputeConstantInterceptorFromType( |
| 126 input.instructionType, interceptedClasses); | 126 input.instructionType, interceptedClasses); |
| 127 | 127 |
| 128 if (constantInterceptor == null) return null; | 128 if (constantInterceptor == null) return null; |
| 129 | 129 |
| 130 // If we just happen to be in an instance method of the constant | 130 // If we just happen to be in an instance method of the constant |
| 131 // interceptor, `this` is a shorter alias. | 131 // interceptor, `this` is a shorter alias. |
| 132 if (constantInterceptor == element.enclosingClass && | 132 if (constantInterceptor == enclosingClass && |
| 133 graph.thisInstruction != null) { | 133 graph.thisInstruction != null) { |
| 134 return graph.thisInstruction; | 134 return graph.thisInstruction; |
| 135 } | 135 } |
| 136 | 136 |
| 137 ConstantValue constant = new InterceptorConstantValue(constantInterceptor); | 137 ConstantValue constant = new InterceptorConstantValue(constantInterceptor); |
| 138 return graph.addConstant(constant, closedWorld); | 138 return graph.addConstant(constant, closedWorld); |
| 139 } | 139 } |
| 140 | 140 |
| 141 ClassElement tryComputeConstantInterceptorFromType( | 141 ClassEntity tryComputeConstantInterceptorFromType( |
| 142 TypeMask type, Set<ClassElement> interceptedClasses) { | 142 TypeMask type, Set<ClassEntity> interceptedClasses) { |
| 143 if (type.isNullable) { | 143 if (type.isNullable) { |
| 144 if (type.isNull) { | 144 if (type.isNull) { |
| 145 return backendClasses.nullImplementation; | 145 return backendClasses.nullImplementation; |
| 146 } | 146 } |
| 147 } else if (type.containsOnlyInt(closedWorld)) { | 147 } else if (type.containsOnlyInt(closedWorld)) { |
| 148 return backendClasses.intImplementation; | 148 return backendClasses.intImplementation; |
| 149 } else if (type.containsOnlyDouble(closedWorld)) { | 149 } else if (type.containsOnlyDouble(closedWorld)) { |
| 150 return backendClasses.doubleImplementation; | 150 return backendClasses.doubleImplementation; |
| 151 } else if (type.containsOnlyBool(closedWorld)) { | 151 } else if (type.containsOnlyBool(closedWorld)) { |
| 152 return backendClasses.boolImplementation; | 152 return backendClasses.boolImplementation; |
| (...skipping 13 matching lines...) Expand all Loading... |
| 166 // is constrained to a leaf native class, we can use the class's | 166 // is constrained to a leaf native class, we can use the class's |
| 167 // interceptor directly. | 167 // interceptor directly. |
| 168 | 168 |
| 169 // TODO(sra): Key DOM classes like Node, Element and Event are not leaf | 169 // TODO(sra): Key DOM classes like Node, Element and Event are not leaf |
| 170 // classes. When the receiver type is not a leaf class, we might still be | 170 // classes. When the receiver type is not a leaf class, we might still be |
| 171 // able to use the receiver class as a constant interceptor. It is | 171 // able to use the receiver class as a constant interceptor. It is |
| 172 // usually the case that methods defined on a non-leaf class don't test | 172 // usually the case that methods defined on a non-leaf class don't test |
| 173 // for a subclass or call methods defined on a subclass. Provided the | 173 // for a subclass or call methods defined on a subclass. Provided the |
| 174 // code is completely insensitive to the specific instance subclasses, we | 174 // code is completely insensitive to the specific instance subclasses, we |
| 175 // can use the non-leaf class directly. | 175 // can use the non-leaf class directly. |
| 176 ClassElement element = type.singleClass(closedWorld); | 176 ClassEntity element = type.singleClass(closedWorld); |
| 177 if (element != null && backendClasses.isNativeClass(element)) { | 177 if (element != null && backendClasses.isNativeClass(element)) { |
| 178 return element; | 178 return element; |
| 179 } | 179 } |
| 180 } | 180 } |
| 181 | 181 |
| 182 return null; | 182 return null; |
| 183 } | 183 } |
| 184 | 184 |
| 185 HInstruction findDominator(Iterable<HInstruction> instructions) { | 185 HInstruction findDominator(Iterable<HInstruction> instructions) { |
| 186 HInstruction result; | 186 HInstruction result; |
| (...skipping 22 matching lines...) Expand all Loading... |
| 209 // (a) => a.length + a.hashCode | 209 // (a) => a.length + a.hashCode |
| 210 // | 210 // |
| 211 // Currently we use the most general interceptor since all intercepted types | 211 // Currently we use the most general interceptor since all intercepted types |
| 212 // implement `hashCode`. But in this example, `a.hashCode` is only reached | 212 // implement `hashCode`. But in this example, `a.hashCode` is only reached |
| 213 // if `a.length` succeeds, which is indicated by the hashCode receiver being | 213 // if `a.length` succeeds, which is indicated by the hashCode receiver being |
| 214 // a HTypeKnown instruction. | 214 // a HTypeKnown instruction. |
| 215 | 215 |
| 216 int useCount(HInstruction user, HInstruction used) => | 216 int useCount(HInstruction user, HInstruction used) => |
| 217 user.inputs.where((input) => input == used).length; | 217 user.inputs.where((input) => input == used).length; |
| 218 | 218 |
| 219 Set<ClassElement> interceptedClasses; | 219 Set<ClassEntity> interceptedClasses; |
| 220 HInstruction dominator = findDominator(node.usedBy); | 220 HInstruction dominator = findDominator(node.usedBy); |
| 221 // If there is a call that dominates all other uses, we can use just the | 221 // If there is a call that dominates all other uses, we can use just the |
| 222 // selector of that instruction. | 222 // selector of that instruction. |
| 223 if (dominator is HInvokeDynamic && | 223 if (dominator is HInvokeDynamic && |
| 224 dominator.isCallOnInterceptor(closedWorld) && | 224 dominator.isCallOnInterceptor(closedWorld) && |
| 225 node == dominator.receiver && | 225 node == dominator.receiver && |
| 226 useCount(dominator, node) == 1) { | 226 useCount(dominator, node) == 1) { |
| 227 interceptedClasses = | 227 interceptedClasses = |
| 228 backend.getInterceptedClassesOn(dominator.selector.name); | 228 backend.getInterceptedClassesOn(dominator.selector.name); |
| 229 | 229 |
| 230 // If we found that we need number, we must still go through all | 230 // If we found that we need number, we must still go through all |
| 231 // uses to check if they require int, or double. | 231 // uses to check if they require int, or double. |
| 232 if (interceptedClasses.contains(backendClasses.numImplementation) && | 232 if (interceptedClasses.contains(backendClasses.numImplementation) && |
| 233 !(interceptedClasses.contains(backendClasses.doubleImplementation) || | 233 !(interceptedClasses.contains(backendClasses.doubleImplementation) || |
| 234 interceptedClasses.contains(backendClasses.intImplementation))) { | 234 interceptedClasses.contains(backendClasses.intImplementation))) { |
| 235 Set<ClassElement> required; | 235 Set<ClassEntity> required; |
| 236 for (HInstruction user in node.usedBy) { | 236 for (HInstruction user in node.usedBy) { |
| 237 if (user is! HInvoke) continue; | 237 if (user is! HInvoke) continue; |
| 238 Set<ClassElement> intercepted = | 238 Set<ClassEntity> intercepted = |
| 239 backend.getInterceptedClassesOn(user.selector.name); | 239 backend.getInterceptedClassesOn(user.selector.name); |
| 240 if (intercepted.contains(backendClasses.intImplementation)) { | 240 if (intercepted.contains(backendClasses.intImplementation)) { |
| 241 required ??= new Set<ClassElement>(); | 241 // TODO(johnniwinther): Use type argument when all uses of |
| 242 // intercepted classes expect entities instead of elements. |
| 243 required ??= new Set/*<ClassEntity>*/(); |
| 242 required.add(backendClasses.intImplementation); | 244 required.add(backendClasses.intImplementation); |
| 243 } | 245 } |
| 244 if (intercepted.contains(backendClasses.doubleImplementation)) { | 246 if (intercepted.contains(backendClasses.doubleImplementation)) { |
| 245 required ??= new Set<ClassElement>(); | 247 // TODO(johnniwinther): Use type argument when all uses of |
| 248 // intercepted classes expect entities instead of elements. |
| 249 required ??= new Set/*<ClassEntity>*/(); |
| 246 required.add(backendClasses.doubleImplementation); | 250 required.add(backendClasses.doubleImplementation); |
| 247 } | 251 } |
| 248 } | 252 } |
| 249 // Don't modify the result of [backend.getInterceptedClassesOn]. | 253 // Don't modify the result of [backend.getInterceptedClassesOn]. |
| 250 if (required != null) { | 254 if (required != null) { |
| 251 interceptedClasses = interceptedClasses.union(required); | 255 interceptedClasses = interceptedClasses.union(required); |
| 252 } | 256 } |
| 253 } | 257 } |
| 254 } else { | 258 } else { |
| 255 interceptedClasses = new Set<ClassElement>(); | 259 // TODO(johnniwinther): Use type argument when all uses of intercepted |
| 260 // classes expect entities instead of elements. |
| 261 interceptedClasses = new Set/*<ClassEntity>*/(); |
| 256 for (HInstruction user in node.usedBy) { | 262 for (HInstruction user in node.usedBy) { |
| 257 if (user is HInvokeDynamic && | 263 if (user is HInvokeDynamic && |
| 258 user.isCallOnInterceptor(closedWorld) && | 264 user.isCallOnInterceptor(closedWorld) && |
| 259 node == user.receiver && | 265 node == user.receiver && |
| 260 useCount(user, node) == 1) { | 266 useCount(user, node) == 1) { |
| 261 interceptedClasses | 267 interceptedClasses |
| 262 .addAll(backend.getInterceptedClassesOn(user.selector.name)); | 268 .addAll(backend.getInterceptedClassesOn(user.selector.name)); |
| 263 } else if (user is HInvokeSuper && | 269 } else if (user is HInvokeSuper && |
| 264 user.isCallOnInterceptor(closedWorld) && | 270 user.isCallOnInterceptor(closedWorld) && |
| 265 node == user.receiver && | 271 node == user.receiver && |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 307 // `null` but not any other JavaScript falsy value, `null` values cause | 313 // `null` but not any other JavaScript falsy value, `null` values cause |
| 308 // `NoSuchMethodError`s, and if the receiver was not null we would have a | 314 // `NoSuchMethodError`s, and if the receiver was not null we would have a |
| 309 // constant interceptor `C`. Then we can use `(receiver && C)` for the | 315 // constant interceptor `C`. Then we can use `(receiver && C)` for the |
| 310 // interceptor. | 316 // interceptor. |
| 311 if (receiver.canBeNull()) { | 317 if (receiver.canBeNull()) { |
| 312 if (!interceptedClasses.contains(backendClasses.nullImplementation)) { | 318 if (!interceptedClasses.contains(backendClasses.nullImplementation)) { |
| 313 // Can use `(receiver && C)` only if receiver is either null or truthy. | 319 // Can use `(receiver && C)` only if receiver is either null or truthy. |
| 314 if (!(receiver.canBePrimitiveNumber(closedWorld) || | 320 if (!(receiver.canBePrimitiveNumber(closedWorld) || |
| 315 receiver.canBePrimitiveBoolean(closedWorld) || | 321 receiver.canBePrimitiveBoolean(closedWorld) || |
| 316 receiver.canBePrimitiveString(closedWorld))) { | 322 receiver.canBePrimitiveString(closedWorld))) { |
| 317 ClassElement interceptorClass = tryComputeConstantInterceptorFromType( | 323 ClassEntity interceptorClass = tryComputeConstantInterceptorFromType( |
| 318 receiver.instructionType.nonNullable(), interceptedClasses); | 324 receiver.instructionType.nonNullable(), interceptedClasses); |
| 319 if (interceptorClass != null) { | 325 if (interceptorClass != null) { |
| 320 HInstruction constantInstruction = graph.addConstant( | 326 HInstruction constantInstruction = graph.addConstant( |
| 321 new InterceptorConstantValue(interceptorClass), closedWorld); | 327 new InterceptorConstantValue(interceptorClass), closedWorld); |
| 322 node.conditionalConstantInterceptor = constantInstruction; | 328 node.conditionalConstantInterceptor = constantInstruction; |
| 323 constantInstruction.usedBy.add(node); | 329 constantInstruction.usedBy.add(node); |
| 324 return false; | 330 return false; |
| 325 } | 331 } |
| 326 } | 332 } |
| 327 } | 333 } |
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 417 instruction = new HInvokeDynamicMethod( | 423 instruction = new HInvokeDynamicMethod( |
| 418 selector, mask, inputs, node.instructionType, true); | 424 selector, mask, inputs, node.instructionType, true); |
| 419 } | 425 } |
| 420 | 426 |
| 421 HBasicBlock block = node.block; | 427 HBasicBlock block = node.block; |
| 422 block.addAfter(node, instruction); | 428 block.addAfter(node, instruction); |
| 423 block.rewrite(node, instruction); | 429 block.rewrite(node, instruction); |
| 424 return true; | 430 return true; |
| 425 } | 431 } |
| 426 } | 432 } |
| OLD | NEW |