| 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/codegen.dart' show CodegenWorkItem; | 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/elements.dart'; |
| 10 import '../js_backend/backend_helpers.dart' show BackendHelpers; | 10 import '../js_backend/backend.dart'; |
| 11 import '../js_backend/js_backend.dart'; | |
| 12 import '../types/types.dart'; | 11 import '../types/types.dart'; |
| 13 import '../universe/selector.dart' show Selector; | 12 import '../universe/selector.dart' show Selector; |
| 14 import '../world.dart' show ClosedWorld; | 13 import '../world.dart' show ClosedWorld; |
| 15 import 'nodes.dart'; | 14 import 'nodes.dart'; |
| 16 import 'optimize.dart'; | 15 import 'optimize.dart'; |
| 17 | 16 |
| 18 /** | 17 /** |
| 19 * This phase simplifies interceptors in multiple ways: | 18 * This phase simplifies interceptors in multiple ways: |
| 20 * | 19 * |
| 21 * 1) If the interceptor is for an object whose type is known, it | 20 * 1) If the interceptor is for an object whose type is known, it |
| (...skipping 18 matching lines...) Expand all Loading... |
| 40 final String name = "SsaSimplifyInterceptors"; | 39 final String name = "SsaSimplifyInterceptors"; |
| 41 final ConstantSystem constantSystem; | 40 final ConstantSystem constantSystem; |
| 42 final Compiler compiler; | 41 final Compiler compiler; |
| 43 final Element element; | 42 final Element element; |
| 44 HGraph graph; | 43 HGraph graph; |
| 45 | 44 |
| 46 SsaSimplifyInterceptors(this.compiler, this.constantSystem, this.element); | 45 SsaSimplifyInterceptors(this.compiler, this.constantSystem, this.element); |
| 47 | 46 |
| 48 JavaScriptBackend get backend => compiler.backend; | 47 JavaScriptBackend get backend => compiler.backend; |
| 49 | 48 |
| 50 BackendHelpers get helpers => backend.helpers; | 49 ClosedWorld get closedWorld => compiler.closedWorld; |
| 51 | 50 |
| 52 ClosedWorld get closedWorld => compiler.closedWorld; | 51 BackendClasses get backendClasses => closedWorld.backendClasses; |
| 53 | 52 |
| 54 void visitGraph(HGraph graph) { | 53 void visitGraph(HGraph graph) { |
| 55 this.graph = graph; | 54 this.graph = graph; |
| 56 visitDominatorTree(graph); | 55 visitDominatorTree(graph); |
| 57 } | 56 } |
| 58 | 57 |
| 59 void visitBasicBlock(HBasicBlock node) { | 58 void visitBasicBlock(HBasicBlock node) { |
| 60 currentBlock = node; | 59 currentBlock = node; |
| 61 | 60 |
| 62 HInstruction instruction = node.first; | 61 HInstruction instruction = node.first; |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 95 return false; | 94 return false; |
| 96 } | 95 } |
| 97 | 96 |
| 98 bool canUseSelfForInterceptor( | 97 bool canUseSelfForInterceptor( |
| 99 HInstruction receiver, Set<ClassElement> interceptedClasses) { | 98 HInstruction receiver, Set<ClassElement> interceptedClasses) { |
| 100 if (receiver.canBePrimitive(closedWorld)) { | 99 if (receiver.canBePrimitive(closedWorld)) { |
| 101 // Primitives always need interceptors. | 100 // Primitives always need interceptors. |
| 102 return false; | 101 return false; |
| 103 } | 102 } |
| 104 if (receiver.canBeNull() && | 103 if (receiver.canBeNull() && |
| 105 interceptedClasses.contains(helpers.jsNullClass)) { | 104 interceptedClasses.contains(backendClasses.nullImplementation)) { |
| 106 // Need the JSNull interceptor. | 105 // Need the JSNull interceptor. |
| 107 return false; | 106 return false; |
| 108 } | 107 } |
| 109 | 108 |
| 110 // 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 |
| 111 // a class extending `Interceptor` then it can be called directly. | 110 // a class extending `Interceptor` then it can be called directly. |
| 112 return new TypeMask.nonNullSubclass(helpers.jsInterceptorClass, closedWorld) | 111 return new TypeMask.nonNullSubclass( |
| 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<ClassElement> 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; |
| (...skipping 13 matching lines...) Expand all Loading... |
| 136 | 136 |
| 137 ConstantValue constant = | 137 ConstantValue constant = |
| 138 new InterceptorConstantValue(constantInterceptor.thisType); | 138 new InterceptorConstantValue(constantInterceptor.thisType); |
| 139 return graph.addConstant(constant, compiler); | 139 return graph.addConstant(constant, compiler); |
| 140 } | 140 } |
| 141 | 141 |
| 142 ClassElement tryComputeConstantInterceptorFromType( | 142 ClassElement tryComputeConstantInterceptorFromType( |
| 143 TypeMask type, Set<ClassElement> interceptedClasses) { | 143 TypeMask type, Set<ClassElement> interceptedClasses) { |
| 144 if (type.isNullable) { | 144 if (type.isNullable) { |
| 145 if (type.isNull) { | 145 if (type.isNull) { |
| 146 return helpers.jsNullClass; | 146 return backendClasses.nullImplementation; |
| 147 } | 147 } |
| 148 } else if (type.containsOnlyInt(closedWorld)) { | 148 } else if (type.containsOnlyInt(closedWorld)) { |
| 149 return helpers.jsIntClass; | 149 return backendClasses.intImplementation; |
| 150 } else if (type.containsOnlyDouble(closedWorld)) { | 150 } else if (type.containsOnlyDouble(closedWorld)) { |
| 151 return helpers.jsDoubleClass; | 151 return backendClasses.doubleImplementation; |
| 152 } else if (type.containsOnlyBool(closedWorld)) { | 152 } else if (type.containsOnlyBool(closedWorld)) { |
| 153 return helpers.jsBoolClass; | 153 return backendClasses.boolImplementation; |
| 154 } else if (type.containsOnlyString(closedWorld)) { | 154 } else if (type.containsOnlyString(closedWorld)) { |
| 155 return helpers.jsStringClass; | 155 return backendClasses.stringImplementation; |
| 156 } else if (type.satisfies(helpers.jsArrayClass, closedWorld)) { | 156 } else if (type.satisfies(backendClasses.listImplementation, closedWorld)) { |
| 157 return helpers.jsArrayClass; | 157 return backendClasses.listImplementation; |
| 158 } else if (type.containsOnlyNum(closedWorld) && | 158 } else if (type.containsOnlyNum(closedWorld) && |
| 159 !interceptedClasses.contains(helpers.jsIntClass) && | 159 !interceptedClasses.contains(backendClasses.intImplementation) && |
| 160 !interceptedClasses.contains(helpers.jsDoubleClass)) { | 160 !interceptedClasses.contains(backendClasses.doubleImplementation)) { |
| 161 // If the method being intercepted is not defined in [int] or [double] we | 161 // If the method being intercepted is not defined in [int] or [double] we |
| 162 // can safely use the number interceptor. This is because none of the | 162 // can safely use the number interceptor. This is because none of the |
| 163 // [int] or [double] methods are called from a method defined on [num]. | 163 // [int] or [double] methods are called from a method defined on [num]. |
| 164 return helpers.jsNumberClass; | 164 return backendClasses.numImplementation; |
| 165 } else { | 165 } else { |
| 166 // Try to find constant interceptor for a native class. If the receiver | 166 // Try to find constant interceptor for a native class. If the receiver |
| 167 // is constrained to a leaf native class, we can use the class's | 167 // is constrained to a leaf native class, we can use the class's |
| 168 // interceptor directly. | 168 // interceptor directly. |
| 169 | 169 |
| 170 // TODO(sra): Key DOM classes like Node, Element and Event are not leaf | 170 // TODO(sra): Key DOM classes like Node, Element and Event are not leaf |
| 171 // classes. When the receiver type is not a leaf class, we might still be | 171 // classes. When the receiver type is not a leaf class, we might still be |
| 172 // able to use the receiver class as a constant interceptor. It is | 172 // able to use the receiver class as a constant interceptor. It is |
| 173 // usually the case that methods defined on a non-leaf class don't test | 173 // usually the case that methods defined on a non-leaf class don't test |
| 174 // for a subclass or call methods defined on a subclass. Provided the | 174 // for a subclass or call methods defined on a subclass. Provided the |
| 175 // code is completely insensitive to the specific instance subclasses, we | 175 // code is completely insensitive to the specific instance subclasses, we |
| 176 // can use the non-leaf class directly. | 176 // can use the non-leaf class directly. |
| 177 ClassElement element = type.singleClass(closedWorld); | 177 ClassElement element = type.singleClass(closedWorld); |
| 178 if (element != null && backend.isNative(element)) { | 178 if (element != null && backendClasses.isNative(element)) { |
| 179 return element; | 179 return element; |
| 180 } | 180 } |
| 181 } | 181 } |
| 182 | 182 |
| 183 return null; | 183 return null; |
| 184 } | 184 } |
| 185 | 185 |
| 186 HInstruction findDominator(Iterable<HInstruction> instructions) { | 186 HInstruction findDominator(Iterable<HInstruction> instructions) { |
| 187 HInstruction result; | 187 HInstruction result; |
| 188 L1: | 188 L1: |
| (...skipping 26 matching lines...) Expand all Loading... |
| 215 // a HTypeKnown instruction. | 215 // a HTypeKnown instruction. |
| 216 | 216 |
| 217 int useCount(HInstruction user, HInstruction used) => | 217 int useCount(HInstruction user, HInstruction used) => |
| 218 user.inputs.where((input) => input == used).length; | 218 user.inputs.where((input) => input == used).length; |
| 219 | 219 |
| 220 Set<ClassElement> interceptedClasses; | 220 Set<ClassElement> interceptedClasses; |
| 221 HInstruction dominator = findDominator(node.usedBy); | 221 HInstruction dominator = findDominator(node.usedBy); |
| 222 // If there is a call that dominates all other uses, we can use just the | 222 // If there is a call that dominates all other uses, we can use just the |
| 223 // selector of that instruction. | 223 // selector of that instruction. |
| 224 if (dominator is HInvokeDynamic && | 224 if (dominator is HInvokeDynamic && |
| 225 dominator.isCallOnInterceptor(compiler) && | 225 dominator.isCallOnInterceptor(closedWorld) && |
| 226 node == dominator.receiver && | 226 node == dominator.receiver && |
| 227 useCount(dominator, node) == 1) { | 227 useCount(dominator, node) == 1) { |
| 228 interceptedClasses = | 228 interceptedClasses = |
| 229 backend.getInterceptedClassesOn(dominator.selector.name); | 229 backend.getInterceptedClassesOn(dominator.selector.name); |
| 230 | 230 |
| 231 // If we found that we need number, we must still go through all | 231 // If we found that we need number, we must still go through all |
| 232 // uses to check if they require int, or double. | 232 // uses to check if they require int, or double. |
| 233 if (interceptedClasses.contains(helpers.jsNumberClass) && | 233 if (interceptedClasses.contains(backendClasses.numImplementation) && |
| 234 !(interceptedClasses.contains(helpers.jsDoubleClass) || | 234 !(interceptedClasses.contains(backendClasses.doubleImplementation) || |
| 235 interceptedClasses.contains(helpers.jsIntClass))) { | 235 interceptedClasses.contains(backendClasses.intImplementation))) { |
| 236 Set<ClassElement> required; | 236 Set<ClassElement> required; |
| 237 for (HInstruction user in node.usedBy) { | 237 for (HInstruction user in node.usedBy) { |
| 238 if (user is! HInvoke) continue; | 238 if (user is! HInvoke) continue; |
| 239 Set<ClassElement> intercepted = | 239 Set<ClassElement> intercepted = |
| 240 backend.getInterceptedClassesOn(user.selector.name); | 240 backend.getInterceptedClassesOn(user.selector.name); |
| 241 if (intercepted.contains(helpers.jsIntClass)) { | 241 if (intercepted.contains(backendClasses.intImplementation)) { |
| 242 required ??= new Set<ClassElement>(); | 242 required ??= new Set<ClassElement>(); |
| 243 required.add(helpers.jsIntClass); | 243 required.add(backendClasses.intImplementation); |
| 244 } | 244 } |
| 245 if (intercepted.contains(helpers.jsDoubleClass)) { | 245 if (intercepted.contains(backendClasses.doubleImplementation)) { |
| 246 required ??= new Set<ClassElement>(); | 246 required ??= new Set<ClassElement>(); |
| 247 required.add(helpers.jsDoubleClass); | 247 required.add(backendClasses.doubleImplementation); |
| 248 } | 248 } |
| 249 } | 249 } |
| 250 // Don't modify the result of [backend.getInterceptedClassesOn]. | 250 // Don't modify the result of [backend.getInterceptedClassesOn]. |
| 251 if (required != null) { | 251 if (required != null) { |
| 252 interceptedClasses = interceptedClasses.union(required); | 252 interceptedClasses = interceptedClasses.union(required); |
| 253 } | 253 } |
| 254 } | 254 } |
| 255 } else { | 255 } else { |
| 256 interceptedClasses = new Set<ClassElement>(); | 256 interceptedClasses = new Set<ClassElement>(); |
| 257 for (HInstruction user in node.usedBy) { | 257 for (HInstruction user in node.usedBy) { |
| 258 if (user is HInvokeDynamic && | 258 if (user is HInvokeDynamic && |
| 259 user.isCallOnInterceptor(compiler) && | 259 user.isCallOnInterceptor(closedWorld) && |
| 260 node == user.receiver && | 260 node == user.receiver && |
| 261 useCount(user, node) == 1) { | 261 useCount(user, node) == 1) { |
| 262 interceptedClasses | 262 interceptedClasses |
| 263 .addAll(backend.getInterceptedClassesOn(user.selector.name)); | 263 .addAll(backend.getInterceptedClassesOn(user.selector.name)); |
| 264 } else if (user is HInvokeSuper && | 264 } else if (user is HInvokeSuper && |
| 265 user.isCallOnInterceptor(compiler) && | 265 user.isCallOnInterceptor(closedWorld) && |
| 266 node == user.receiver && | 266 node == user.receiver && |
| 267 useCount(user, node) == 1) { | 267 useCount(user, node) == 1) { |
| 268 interceptedClasses | 268 interceptedClasses |
| 269 .addAll(backend.getInterceptedClassesOn(user.selector.name)); | 269 .addAll(backend.getInterceptedClassesOn(user.selector.name)); |
| 270 } else { | 270 } else { |
| 271 // Use a most general interceptor for other instructions, example, | 271 // Use a most general interceptor for other instructions, example, |
| 272 // is-checks and escaping interceptors. | 272 // is-checks and escaping interceptors. |
| 273 interceptedClasses.addAll(backend.interceptedClasses); | 273 interceptedClasses.addAll(backend.interceptedClasses); |
| 274 break; | 274 break; |
| 275 } | 275 } |
| (...skipping 27 matching lines...) Expand all Loading... |
| 303 // If it is a conditional constant interceptor and was not strengthened to a | 303 // If it is a conditional constant interceptor and was not strengthened to a |
| 304 // constant interceptor then there is nothing more we can do. | 304 // constant interceptor then there is nothing more we can do. |
| 305 if (node.isConditionalConstantInterceptor) return false; | 305 if (node.isConditionalConstantInterceptor) return false; |
| 306 | 306 |
| 307 // Do we have an 'almost constant' interceptor? The receiver could be | 307 // Do we have an 'almost constant' interceptor? The receiver could be |
| 308 // `null` but not any other JavaScript falsy value, `null` values cause | 308 // `null` but not any other JavaScript falsy value, `null` values cause |
| 309 // `NoSuchMethodError`s, and if the receiver was not null we would have a | 309 // `NoSuchMethodError`s, and if the receiver was not null we would have a |
| 310 // constant interceptor `C`. Then we can use `(receiver && C)` for the | 310 // constant interceptor `C`. Then we can use `(receiver && C)` for the |
| 311 // interceptor. | 311 // interceptor. |
| 312 if (receiver.canBeNull()) { | 312 if (receiver.canBeNull()) { |
| 313 if (!interceptedClasses.contains(helpers.jsNullClass)) { | 313 if (!interceptedClasses.contains(backendClasses.nullImplementation)) { |
| 314 // Can use `(receiver && C)` only if receiver is either null or truthy. | 314 // Can use `(receiver && C)` only if receiver is either null or truthy. |
| 315 if (!(receiver.canBePrimitiveNumber(closedWorld) || | 315 if (!(receiver.canBePrimitiveNumber(closedWorld) || |
| 316 receiver.canBePrimitiveBoolean(closedWorld) || | 316 receiver.canBePrimitiveBoolean(closedWorld) || |
| 317 receiver.canBePrimitiveString(closedWorld))) { | 317 receiver.canBePrimitiveString(closedWorld))) { |
| 318 ClassElement interceptorClass = tryComputeConstantInterceptorFromType( | 318 ClassElement interceptorClass = tryComputeConstantInterceptorFromType( |
| 319 receiver.instructionType.nonNullable(), interceptedClasses); | 319 receiver.instructionType.nonNullable(), interceptedClasses); |
| 320 if (interceptorClass != null) { | 320 if (interceptorClass != null) { |
| 321 HInstruction constantInstruction = graph.addConstant( | 321 HInstruction constantInstruction = graph.addConstant( |
| 322 new InterceptorConstantValue(interceptorClass.thisType), | 322 new InterceptorConstantValue(interceptorClass.thisType), |
| 323 compiler); | 323 compiler); |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 419 instruction = new HInvokeDynamicMethod( | 419 instruction = new HInvokeDynamicMethod( |
| 420 selector, mask, inputs, node.instructionType, true); | 420 selector, mask, inputs, node.instructionType, true); |
| 421 } | 421 } |
| 422 | 422 |
| 423 HBasicBlock block = node.block; | 423 HBasicBlock block = node.block; |
| 424 block.addAfter(node, instruction); | 424 block.addAfter(node, instruction); |
| 425 block.rewrite(node, instruction); | 425 block.rewrite(node, instruction); |
| 426 return true; | 426 return true; |
| 427 } | 427 } |
| 428 } | 428 } |
| OLD | NEW |