| 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 part of ssa; | 5 part of ssa; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * This phase simplifies interceptors in multiple ways: | 8 * This phase simplifies interceptors in multiple ways: |
| 9 * | 9 * |
| 10 * 1) If the interceptor is for an object whose type is known, it | 10 * 1) If the interceptor is for an object whose type is known, it |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 103 HInstruction tryComputeConstantInterceptor( | 103 HInstruction tryComputeConstantInterceptor( |
| 104 HInstruction input, | 104 HInstruction input, |
| 105 Set<ClassElement> interceptedClasses) { | 105 Set<ClassElement> interceptedClasses) { |
| 106 if (input == graph.explicitReceiverParameter) { | 106 if (input == graph.explicitReceiverParameter) { |
| 107 // If `explicitReceiverParameter` is set it means the current method is an | 107 // If `explicitReceiverParameter` is set it means the current method is an |
| 108 // interceptor method, and `this` is the interceptor. The caller just did | 108 // interceptor method, and `this` is the interceptor. The caller just did |
| 109 // `getInterceptor(foo).currentMethod(foo)` to enter the current method. | 109 // `getInterceptor(foo).currentMethod(foo)` to enter the current method. |
| 110 return graph.thisInstruction; | 110 return graph.thisInstruction; |
| 111 } | 111 } |
| 112 | 112 |
| 113 ClassElement constantInterceptor = tryComputeConstantInterceptorFromType( | 113 ClassElement constantInterceptor; |
| 114 input.instructionType, interceptedClasses); | |
| 115 | |
| 116 if (constantInterceptor == null) return null; | |
| 117 | |
| 118 // If we just happen to be in an instance method of the constant | |
| 119 // interceptor, `this` is a shorter alias. | |
| 120 if (constantInterceptor == work.element.enclosingClass && | |
| 121 graph.thisInstruction != null) { | |
| 122 return graph.thisInstruction; | |
| 123 } | |
| 124 | |
| 125 ConstantValue constant = | |
| 126 new InterceptorConstantValue(constantInterceptor.thisType); | |
| 127 return graph.addConstant(constant, compiler); | |
| 128 } | |
| 129 | |
| 130 ClassElement tryComputeConstantInterceptorFromType( | |
| 131 TypeMask type, | |
| 132 Set<ClassElement> interceptedClasses) { | |
| 133 | |
| 134 ClassWorld classWorld = compiler.world; | 114 ClassWorld classWorld = compiler.world; |
| 135 JavaScriptBackend backend = compiler.backend; | 115 JavaScriptBackend backend = compiler.backend; |
| 136 if (type.isNullable) { | 116 if (input.canBeNull()) { |
| 137 if (type.isEmpty) { | 117 if (input.isNull()) { |
| 138 return backend.jsNullClass; | 118 constantInterceptor = backend.jsNullClass; |
| 139 } | 119 } |
| 140 } else if (type.containsOnlyInt(classWorld)) { | 120 } else if (input.isInteger(compiler)) { |
| 141 return backend.jsIntClass; | 121 constantInterceptor = backend.jsIntClass; |
| 142 } else if (type.containsOnlyDouble(classWorld)) { | 122 } else if (input.isDouble(compiler)) { |
| 143 return backend.jsDoubleClass; | 123 constantInterceptor = backend.jsDoubleClass; |
| 144 } else if (type.containsOnlyBool(classWorld)) { | 124 } else if (input.isBoolean(compiler)) { |
| 145 return backend.jsBoolClass; | 125 constantInterceptor = backend.jsBoolClass; |
| 146 } else if (type.containsOnlyString(classWorld)) { | 126 } else if (input.isString(compiler)) { |
| 147 return backend.jsStringClass; | 127 constantInterceptor = backend.jsStringClass; |
| 148 } else if (type.satisfies(backend.jsArrayClass, classWorld)) { | 128 } else if (input.isArray(compiler)) { |
| 149 return backend.jsArrayClass; | 129 constantInterceptor = backend.jsArrayClass; |
| 150 } else if (type.containsOnlyNum(classWorld) && | 130 } else if (input.isNumber(compiler) && |
| 151 !interceptedClasses.contains(backend.jsIntClass) && | 131 !interceptedClasses.contains(backend.jsIntClass) && |
| 152 !interceptedClasses.contains(backend.jsDoubleClass)) { | 132 !interceptedClasses.contains(backend.jsDoubleClass)) { |
| 153 // If the method being intercepted is not defined in [int] or [double] we | 133 // If the method being intercepted is not defined in [int] or [double] we |
| 154 // can safely use the number interceptor. This is because none of the | 134 // can safely use the number interceptor. This is because none of the |
| 155 // [int] or [double] methods are called from a method defined on [num]. | 135 // [int] or [double] methods are called from a method defined on [num]. |
| 156 return backend.jsNumberClass; | 136 constantInterceptor = backend.jsNumberClass; |
| 157 } else { | 137 } else { |
| 158 // Try to find constant interceptor for a native class. If the receiver | 138 // Try to find constant interceptor for a native class. If the receiver |
| 159 // is constrained to a leaf native class, we can use the class's | 139 // is constrained to a leaf native class, we can use the class's |
| 160 // interceptor directly. | 140 // interceptor directly. |
| 161 | 141 |
| 162 // TODO(sra): Key DOM classes like Node, Element and Event are not leaf | 142 // TODO(sra): Key DOM classes like Node, Element and Event are not leaf |
| 163 // classes. When the receiver type is not a leaf class, we might still be | 143 // classes. When the receiver type is not a leaf class, we might still be |
| 164 // able to use the receiver class as a constant interceptor. It is | 144 // able to use the receiver class as a constant interceptor. It is |
| 165 // usually the case that methods defined on a non-leaf class don't test | 145 // usually the case that methods defined on a non-leaf class don't test |
| 166 // for a subclass or call methods defined on a subclass. Provided the | 146 // for a subclass or call methods defined on a subclass. Provided the |
| 167 // code is completely insensitive to the specific instance subclasses, we | 147 // code is completely insensitive to the specific instance subclasses, we |
| 168 // can use the non-leaf class directly. | 148 // can use the non-leaf class directly. |
| 169 ClassElement element = type.singleClass(classWorld); | 149 ClassElement element = input.instructionType.singleClass(classWorld); |
| 170 if (element != null && element.isNative) { | 150 if (element != null && element.isNative) { |
| 171 return element; | 151 constantInterceptor = element; |
| 172 } | 152 } |
| 173 } | 153 } |
| 174 | 154 |
| 175 return null; | 155 if (constantInterceptor == null) return null; |
| 156 |
| 157 // If we just happen to be in an instance method of the constant |
| 158 // interceptor, `this` is a shorter alias. |
| 159 if (constantInterceptor == work.element.enclosingClass && |
| 160 graph.thisInstruction != null) { |
| 161 return graph.thisInstruction; |
| 162 } |
| 163 |
| 164 ConstantValue constant = |
| 165 new InterceptorConstantValue(constantInterceptor.thisType); |
| 166 return graph.addConstant(constant, compiler); |
| 176 } | 167 } |
| 177 | 168 |
| 178 HInstruction findDominator(Iterable<HInstruction> instructions) { | 169 HInstruction findDominator(Iterable<HInstruction> instructions) { |
| 179 HInstruction result; | 170 HInstruction result; |
| 180 L1: for (HInstruction candidate in instructions) { | 171 L1: for (HInstruction candidate in instructions) { |
| 181 for (HInstruction current in instructions) { | 172 for (HInstruction current in instructions) { |
| 182 if (current != candidate && !candidate.dominates(current)) continue L1; | 173 if (current != candidate && !candidate.dominates(current)) continue L1; |
| 183 } | 174 } |
| 184 result = candidate; | 175 result = candidate; |
| 185 break; | 176 break; |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 278 } | 269 } |
| 279 | 270 |
| 280 // Try computing a constant interceptor. | 271 // Try computing a constant interceptor. |
| 281 HInstruction constantInterceptor = | 272 HInstruction constantInterceptor = |
| 282 tryComputeConstantInterceptor(receiver, interceptedClasses); | 273 tryComputeConstantInterceptor(receiver, interceptedClasses); |
| 283 if (constantInterceptor != null) { | 274 if (constantInterceptor != null) { |
| 284 node.block.rewrite(node, constantInterceptor); | 275 node.block.rewrite(node, constantInterceptor); |
| 285 return false; | 276 return false; |
| 286 } | 277 } |
| 287 | 278 |
| 288 // Do we have an 'almost constant' interceptor? The receiver could be | |
| 289 // `null` but not any other JavaScript falsy value, `null` values cause | |
| 290 // `NoSuchMethodError`s, and if the receiver was not null we would have a | |
| 291 // constant interceptor `C`. Then we can use `(receiver && C)` for the | |
| 292 // interceptor. | |
| 293 if (receiver.canBeNull() && !node.isConditionalConstantInterceptor) { | |
| 294 if (!interceptedClasses.contains(backend.jsNullClass)) { | |
| 295 // Can use `(receiver && C)` only if receiver is either null or truthy. | |
| 296 if (!(receiver.canBePrimitiveNumber(compiler) || | |
| 297 receiver.canBePrimitiveBoolean(compiler) || | |
| 298 receiver.canBePrimitiveString(compiler))) { | |
| 299 ClassElement interceptorClass = tryComputeConstantInterceptorFromType( | |
| 300 receiver.instructionType.nonNullable(), interceptedClasses); | |
| 301 if (interceptorClass != null) { | |
| 302 HInstruction constantInstruction = | |
| 303 graph.addConstant( | |
| 304 new InterceptorConstantValue(interceptorClass.thisType), | |
| 305 compiler); | |
| 306 node.conditionalConstantInterceptor = constantInstruction; | |
| 307 constantInstruction.usedBy.add(node); | |
| 308 return false; | |
| 309 } | |
| 310 } | |
| 311 } | |
| 312 } | |
| 313 | |
| 314 // Try creating a one-shot interceptor or optimized is-check | 279 // Try creating a one-shot interceptor or optimized is-check |
| 315 if (compiler.hasIncrementalSupport) return false; | 280 if (compiler.hasIncrementalSupport) return false; |
| 316 if (node.usedBy.length != 1) return false; | 281 if (node.usedBy.length != 1) return false; |
| 317 HInstruction user = node.usedBy.single; | 282 HInstruction user = node.usedBy.single; |
| 318 | 283 |
| 319 // If the interceptor [node] was loop hoisted, we keep the interceptor. | 284 // If the interceptor [node] was loop hoisted, we keep the interceptor. |
| 320 if (!user.hasSameLoopHeaderAs(node)) return false; | 285 if (!user.hasSameLoopHeaderAs(node)) return false; |
| 321 | 286 |
| 322 bool replaceUserWith(HInstruction replacement) { | 287 bool replaceUserWith(HInstruction replacement) { |
| 323 HBasicBlock block = user.block; | 288 HBasicBlock block = user.block; |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 399 instruction = new HInvokeDynamicMethod( | 364 instruction = new HInvokeDynamicMethod( |
| 400 selector, inputs, node.instructionType, true); | 365 selector, inputs, node.instructionType, true); |
| 401 } | 366 } |
| 402 | 367 |
| 403 HBasicBlock block = node.block; | 368 HBasicBlock block = node.block; |
| 404 block.addAfter(node, instruction); | 369 block.addAfter(node, instruction); |
| 405 block.rewrite(node, instruction); | 370 block.rewrite(node, instruction); |
| 406 return true; | 371 return true; |
| 407 } | 372 } |
| 408 } | 373 } |
| OLD | NEW |