Chromium Code Reviews| 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; | 113 ClassElement constantInterceptor = tryComputeConstantInterceptorFromType( |
| 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 | |
| 114 ClassWorld classWorld = compiler.world; | 134 ClassWorld classWorld = compiler.world; |
| 115 JavaScriptBackend backend = compiler.backend; | 135 JavaScriptBackend backend = compiler.backend; |
| 116 if (input.canBeNull()) { | 136 if (type.isNullable) { |
| 117 if (input.isNull()) { | 137 if (type.isEmpty) { |
| 118 constantInterceptor = backend.jsNullClass; | 138 return backend.jsNullClass; |
| 119 } | 139 } |
| 120 } else if (input.isInteger(compiler)) { | 140 } else if (type.containsOnlyInt(classWorld)) { |
| 121 constantInterceptor = backend.jsIntClass; | 141 return backend.jsIntClass; |
| 122 } else if (input.isDouble(compiler)) { | 142 } else if (type.containsOnlyDouble(classWorld)) { |
| 123 constantInterceptor = backend.jsDoubleClass; | 143 return backend.jsDoubleClass; |
| 124 } else if (input.isBoolean(compiler)) { | 144 } else if (type.containsOnlyBool(classWorld)) { |
| 125 constantInterceptor = backend.jsBoolClass; | 145 return backend.jsBoolClass; |
| 126 } else if (input.isString(compiler)) { | 146 } else if (type.containsOnlyString(classWorld)) { |
| 127 constantInterceptor = backend.jsStringClass; | 147 return backend.jsStringClass; |
| 128 } else if (input.isArray(compiler)) { | 148 } else if (type.satisfies(backend.jsArrayClass, classWorld)) { |
| 129 constantInterceptor = backend.jsArrayClass; | 149 return backend.jsArrayClass; |
| 130 } else if (input.isNumber(compiler) && | 150 } else if (type.containsOnlyNum(classWorld) && |
| 131 !interceptedClasses.contains(backend.jsIntClass) && | 151 !interceptedClasses.contains(backend.jsIntClass) && |
| 132 !interceptedClasses.contains(backend.jsDoubleClass)) { | 152 !interceptedClasses.contains(backend.jsDoubleClass)) { |
| 133 // If the method being intercepted is not defined in [int] or [double] we | 153 // If the method being intercepted is not defined in [int] or [double] we |
| 134 // can safely use the number interceptor. This is because none of the | 154 // can safely use the number interceptor. This is because none of the |
| 135 // [int] or [double] methods are called from a method defined on [num]. | 155 // [int] or [double] methods are called from a method defined on [num]. |
| 136 constantInterceptor = backend.jsNumberClass; | 156 return backend.jsNumberClass; |
| 137 } else { | 157 } else { |
| 138 // Try to find constant interceptor for a native class. If the receiver | 158 // Try to find constant interceptor for a native class. If the receiver |
| 139 // is constrained to a leaf native class, we can use the class's | 159 // is constrained to a leaf native class, we can use the class's |
| 140 // interceptor directly. | 160 // interceptor directly. |
| 141 | 161 |
| 142 // TODO(sra): Key DOM classes like Node, Element and Event are not leaf | 162 // TODO(sra): Key DOM classes like Node, Element and Event are not leaf |
| 143 // classes. When the receiver type is not a leaf class, we might still be | 163 // classes. When the receiver type is not a leaf class, we might still be |
| 144 // able to use the receiver class as a constant interceptor. It is | 164 // able to use the receiver class as a constant interceptor. It is |
| 145 // usually the case that methods defined on a non-leaf class don't test | 165 // usually the case that methods defined on a non-leaf class don't test |
| 146 // for a subclass or call methods defined on a subclass. Provided the | 166 // for a subclass or call methods defined on a subclass. Provided the |
| 147 // code is completely insensitive to the specific instance subclasses, we | 167 // code is completely insensitive to the specific instance subclasses, we |
| 148 // can use the non-leaf class directly. | 168 // can use the non-leaf class directly. |
| 149 ClassElement element = input.instructionType.singleClass(classWorld); | 169 ClassElement element = type.singleClass(classWorld); |
| 150 if (element != null && element.isNative) { | 170 if (element != null && element.isNative) { |
| 151 constantInterceptor = element; | 171 return element; |
| 152 } | 172 } |
| 153 } | 173 } |
| 154 | 174 |
| 155 if (constantInterceptor == null) return null; | 175 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); | |
| 167 } | 176 } |
| 168 | 177 |
| 169 HInstruction findDominator(Iterable<HInstruction> instructions) { | 178 HInstruction findDominator(Iterable<HInstruction> instructions) { |
| 170 HInstruction result; | 179 HInstruction result; |
| 171 L1: for (HInstruction candidate in instructions) { | 180 L1: for (HInstruction candidate in instructions) { |
| 172 for (HInstruction current in instructions) { | 181 for (HInstruction current in instructions) { |
| 173 if (current != candidate && !candidate.dominates(current)) continue L1; | 182 if (current != candidate && !candidate.dominates(current)) continue L1; |
| 174 } | 183 } |
| 175 result = candidate; | 184 result = candidate; |
| 176 break; | 185 break; |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 269 } | 278 } |
| 270 | 279 |
| 271 // Try computing a constant interceptor. | 280 // Try computing a constant interceptor. |
| 272 HInstruction constantInterceptor = | 281 HInstruction constantInterceptor = |
| 273 tryComputeConstantInterceptor(receiver, interceptedClasses); | 282 tryComputeConstantInterceptor(receiver, interceptedClasses); |
| 274 if (constantInterceptor != null) { | 283 if (constantInterceptor != null) { |
| 275 node.block.rewrite(node, constantInterceptor); | 284 node.block.rewrite(node, constantInterceptor); |
| 276 return false; | 285 return false; |
| 277 } | 286 } |
| 278 | 287 |
| 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()) { | |
| 294 if (!interceptedClasses.contains(backend.jsNullClass)) { | |
| 295 // Can only use `(receiver && C)` if receiver is null or truthy. | |
|
floitsch
2015/04/01 23:14:54
is either null or truthy.
sra1
2015/04/02 01:00:07
Done.
| |
| 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 if (node.inputs.length == 1) { | |
|
floitsch
2015/04/01 23:14:54
when can it have more than one? is it `node.isCond
sra1
2015/04/02 01:00:07
Done.
| |
| 303 ConstantValue constant = | |
| 304 new InterceptorConstantValue(interceptorClass.thisType); | |
| 305 HInstruction constantInstruction = | |
| 306 graph.addConstant(constant, compiler); | |
| 307 node.inputs.length = 2; | |
| 308 node.inputs[1] = constantInstruction; | |
| 309 constantInstruction.usedBy.add(node); | |
| 310 return false; | |
| 311 } | |
| 312 } | |
| 313 } | |
| 314 } | |
| 315 } | |
| 316 | |
| 279 // Try creating a one-shot interceptor or optimized is-check | 317 // Try creating a one-shot interceptor or optimized is-check |
| 280 if (compiler.hasIncrementalSupport) return false; | 318 if (compiler.hasIncrementalSupport) return false; |
| 281 if (node.usedBy.length != 1) return false; | 319 if (node.usedBy.length != 1) return false; |
| 282 HInstruction user = node.usedBy.single; | 320 HInstruction user = node.usedBy.single; |
| 283 | 321 |
| 284 // If the interceptor [node] was loop hoisted, we keep the interceptor. | 322 // If the interceptor [node] was loop hoisted, we keep the interceptor. |
| 285 if (!user.hasSameLoopHeaderAs(node)) return false; | 323 if (!user.hasSameLoopHeaderAs(node)) return false; |
| 286 | 324 |
| 287 bool replaceUserWith(HInstruction replacement) { | 325 bool replaceUserWith(HInstruction replacement) { |
| 288 HBasicBlock block = user.block; | 326 HBasicBlock block = user.block; |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 364 instruction = new HInvokeDynamicMethod( | 402 instruction = new HInvokeDynamicMethod( |
| 365 selector, inputs, node.instructionType, true); | 403 selector, inputs, node.instructionType, true); |
| 366 } | 404 } |
| 367 | 405 |
| 368 HBasicBlock block = node.block; | 406 HBasicBlock block = node.block; |
| 369 block.addAfter(node, instruction); | 407 block.addAfter(node, instruction); |
| 370 block.rewrite(node, instruction); | 408 block.rewrite(node, instruction); |
| 371 return true; | 409 return true; |
| 372 } | 410 } |
| 373 } | 411 } |
| OLD | NEW |