| 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 |
| 11 * tries to use a constant interceptor instead. | 11 * tries to use a constant interceptor instead. |
| 12 * | 12 * |
| 13 * 2) It specializes interceptors based on the selectors it is being | 13 * 2) Interceptors are specialized based on the selector it is used with. |
| 14 * called with. | |
| 15 * | 14 * |
| 16 * 3) If we know the object is not intercepted, we just use it | 15 * 3) If we know the object is not intercepted, we just use the object |
| 17 * instead. | 16 * instead. |
| 18 * | 17 * |
| 19 * 4) It replaces all interceptors that are used only once with | 18 * 4) Single use interceptors at dynamic invoke sites are replaced with 'one |
| 20 * one-shot interceptors. It saves code size and makes the receiver of | 19 * shot interceptors' which are synthesized static helper functions that fetch |
| 21 * an intercepted call a candidate for being generated at use site. | 20 * the interceptor and then call the method. This saves code size and makes the |
| 21 * receiver of an intercepted call a candidate for being generated at use site. |
| 22 * |
| 23 * 5) Some HIs operations on an interceptor are replaced with a HIs version that |
| 24 * uses 'instanceof' rather than testing a type flag. |
| 22 * | 25 * |
| 23 */ | 26 */ |
| 24 class SsaSimplifyInterceptors extends HBaseVisitor | 27 class SsaSimplifyInterceptors extends HBaseVisitor |
| 25 implements OptimizationPhase { | 28 implements OptimizationPhase { |
| 26 final String name = "SsaSimplifyInterceptors"; | 29 final String name = "SsaSimplifyInterceptors"; |
| 27 final ConstantSystem constantSystem; | 30 final ConstantSystem constantSystem; |
| 28 final Compiler compiler; | 31 final Compiler compiler; |
| 29 final CodegenWorkItem work; | 32 final CodegenWorkItem work; |
| 30 HGraph graph; | 33 HGraph graph; |
| 31 | 34 |
| (...skipping 17 matching lines...) Expand all Loading... |
| 49 instruction = next; | 52 instruction = next; |
| 50 } | 53 } |
| 51 } | 54 } |
| 52 | 55 |
| 53 bool visitInstruction(HInstruction instruction) => false; | 56 bool visitInstruction(HInstruction instruction) => false; |
| 54 | 57 |
| 55 bool visitInvoke(HInvoke invoke) { | 58 bool visitInvoke(HInvoke invoke) { |
| 56 if (!invoke.isInterceptedCall) return false; | 59 if (!invoke.isInterceptedCall) return false; |
| 57 var interceptor = invoke.inputs[0]; | 60 var interceptor = invoke.inputs[0]; |
| 58 if (interceptor is! HInterceptor) return false; | 61 if (interceptor is! HInterceptor) return false; |
| 62 |
| 63 // TODO(sra): Move this per-call code to visitInterceptor. |
| 64 // |
| 65 // The interceptor is visited first, so we get here only when the |
| 66 // interceptor was not rewritten to a single shared replacement. I'm not |
| 67 // sure we should substitute a constant interceptor on a per-call basis if |
| 68 // the interceptor is already available in a local variable, but it is |
| 69 // possible that all uses can be rewritten to use different constants. |
| 70 |
| 71 // TODO(sra): Also do self-interceptor rewrites on a per-use basis. |
| 72 |
| 59 HInstruction constant = tryComputeConstantInterceptor( | 73 HInstruction constant = tryComputeConstantInterceptor( |
| 60 invoke.inputs[1], interceptor.interceptedClasses); | 74 invoke.inputs[1], interceptor.interceptedClasses); |
| 61 if (constant != null) { | 75 if (constant != null) { |
| 62 invoke.changeUse(interceptor, constant); | 76 invoke.changeUse(interceptor, constant); |
| 63 } | 77 } |
| 64 return false; | 78 return false; |
| 65 } | 79 } |
| 66 | 80 |
| 67 bool canUseSelfForInterceptor(HInstruction receiver, | 81 bool canUseSelfForInterceptor(HInstruction receiver, |
| 68 Set<ClassElement> interceptedClasses) { | 82 Set<ClassElement> interceptedClasses) { |
| (...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 231 backend.getInterceptedClassesOn(user.selector.name)); | 245 backend.getInterceptedClassesOn(user.selector.name)); |
| 232 } else { | 246 } else { |
| 233 // Use a most general interceptor for other instructions, example, | 247 // Use a most general interceptor for other instructions, example, |
| 234 // is-checks and escaping interceptors. | 248 // is-checks and escaping interceptors. |
| 235 interceptedClasses.addAll(backend.interceptedClasses); | 249 interceptedClasses.addAll(backend.interceptedClasses); |
| 236 break; | 250 break; |
| 237 } | 251 } |
| 238 } | 252 } |
| 239 } | 253 } |
| 240 | 254 |
| 255 node.interceptedClasses = interceptedClasses; |
| 256 |
| 241 HInstruction receiver = node.receiver; | 257 HInstruction receiver = node.receiver; |
| 242 | 258 |
| 259 // TODO(sra): We should consider each use individually and then all uses |
| 260 // together. Each use might permit a different rewrite due to a refined |
| 261 // receiver type. Self-interceptor rewrites are always beneficial since the |
| 262 // receiver is live at a invocation. Constant-interceptor rewrites are only |
| 263 // guaranteed to be beneficial if they can eliminate the need for the |
| 264 // interceptor or reduce the uses to one that can be simplified with a |
| 265 // one-shot interceptor or optimized is-check. |
| 266 |
| 243 if (canUseSelfForInterceptor(receiver, interceptedClasses)) { | 267 if (canUseSelfForInterceptor(receiver, interceptedClasses)) { |
| 244 return rewriteToUseSelfAsInterceptor(node, receiver); | 268 return rewriteToUseSelfAsInterceptor(node, receiver); |
| 245 } | 269 } |
| 246 | 270 |
| 247 // Try computing a constant interceptor. | 271 // Try computing a constant interceptor. |
| 248 HInstruction constantInterceptor = | 272 HInstruction constantInterceptor = |
| 249 tryComputeConstantInterceptor(receiver, interceptedClasses); | 273 tryComputeConstantInterceptor(receiver, interceptedClasses); |
| 250 if (constantInterceptor != null) { | 274 if (constantInterceptor != null) { |
| 251 node.block.rewrite(node, constantInterceptor); | 275 node.block.rewrite(node, constantInterceptor); |
| 252 return false; | 276 return false; |
| 253 } | 277 } |
| 254 | 278 |
| 255 node.interceptedClasses = interceptedClasses; | 279 // Try creating a one-shot interceptor or optimized is-check |
| 256 | |
| 257 // Try creating a one-shot interceptor. | |
| 258 if (compiler.hasIncrementalSupport) return false; | 280 if (compiler.hasIncrementalSupport) return false; |
| 259 if (node.usedBy.length != 1) return false; | 281 if (node.usedBy.length != 1) return false; |
| 260 if (node.usedBy[0] is !HInvokeDynamic) return false; | 282 HInstruction user = node.usedBy.single; |
| 261 | 283 |
| 262 HInvokeDynamic user = node.usedBy[0]; | 284 // If the interceptor [node] was loop hoisted, we keep the interceptor. |
| 263 | |
| 264 // If [node] was loop hoisted, we keep the interceptor. | |
| 265 if (!user.hasSameLoopHeaderAs(node)) return false; | 285 if (!user.hasSameLoopHeaderAs(node)) return false; |
| 266 | 286 |
| 267 // Replace the user with a [HOneShotInterceptor]. | 287 bool replaceUserWith(HInstruction replacement) { |
| 268 HConstant nullConstant = graph.addConstantNull(compiler); | 288 HBasicBlock block = user.block; |
| 269 List<HInstruction> inputs = new List<HInstruction>.from(user.inputs); | 289 block.addAfter(user, replacement); |
| 270 inputs[0] = nullConstant; | 290 block.rewrite(user, replacement); |
| 271 HOneShotInterceptor interceptor = new HOneShotInterceptor( | 291 block.remove(user); |
| 272 user.selector, inputs, user.instructionType, node.interceptedClasses); | 292 return false; |
| 273 interceptor.sourcePosition = user.sourcePosition; | 293 } |
| 274 interceptor.sourceElement = user.sourceElement; | |
| 275 | 294 |
| 276 HBasicBlock block = user.block; | 295 if (user is HIs) { |
| 277 block.addAfter(user, interceptor); | 296 // See if we can rewrite the is-check to use 'instanceof', i.e. rewrite |
| 278 block.rewrite(user, interceptor); | 297 // "getInterceptor(x).$isT" to "x instanceof T". |
| 279 block.remove(user); | 298 if (node == user.interceptor) { |
| 280 return true; | 299 JavaScriptBackend backend = compiler.backend; |
| 300 if (backend.mayGenerateInstanceofCheck(user.typeExpression)) { |
| 301 HInstruction instanceofCheck = new HIs.instanceOf( |
| 302 user.typeExpression, user.expression, user.instructionType); |
| 303 instanceofCheck.sourcePosition = user.sourcePosition; |
| 304 instanceofCheck.sourceElement = user.sourceElement; |
| 305 return replaceUserWith(instanceofCheck); |
| 306 } |
| 307 } |
| 308 } else if (user is HInvokeDynamic) { |
| 309 if (node == user.inputs[0]) { |
| 310 // Replace the user with a [HOneShotInterceptor]. |
| 311 HConstant nullConstant = graph.addConstantNull(compiler); |
| 312 List<HInstruction> inputs = new List<HInstruction>.from(user.inputs); |
| 313 inputs[0] = nullConstant; |
| 314 HOneShotInterceptor oneShotInterceptor = new HOneShotInterceptor( |
| 315 user.selector, inputs, user.instructionType, interceptedClasses); |
| 316 oneShotInterceptor.sourcePosition = user.sourcePosition; |
| 317 oneShotInterceptor.sourceElement = user.sourceElement; |
| 318 return replaceUserWith(oneShotInterceptor); |
| 319 } |
| 320 } |
| 321 |
| 322 return false; |
| 281 } | 323 } |
| 282 | 324 |
| 283 bool rewriteToUseSelfAsInterceptor(HInterceptor node, HInstruction receiver) { | 325 bool rewriteToUseSelfAsInterceptor(HInterceptor node, HInstruction receiver) { |
| 284 for (HInstruction user in node.usedBy.toList()) { | 326 for (HInstruction user in node.usedBy.toList()) { |
| 285 if (user is HIs) { | 327 if (user is HIs) { |
| 286 user.changeUse(node, receiver); | 328 user.changeUse(node, receiver); |
| 287 } else { | 329 } else { |
| 288 // Use the potentially self-argument as new receiver. Note that the | 330 // Use the potentially self-argument as new receiver. Note that the |
| 289 // self-argument could potentially have a tighter type than the | 331 // self-argument could potentially have a tighter type than the |
| 290 // receiver which was the input to the interceptor. | 332 // receiver which was the input to the interceptor. |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 322 instruction = new HInvokeDynamicMethod( | 364 instruction = new HInvokeDynamicMethod( |
| 323 selector, inputs, node.instructionType, true); | 365 selector, inputs, node.instructionType, true); |
| 324 } | 366 } |
| 325 | 367 |
| 326 HBasicBlock block = node.block; | 368 HBasicBlock block = node.block; |
| 327 block.addAfter(node, instruction); | 369 block.addAfter(node, instruction); |
| 328 block.rewrite(node, instruction); | 370 block.rewrite(node, instruction); |
| 329 return true; | 371 return true; |
| 330 } | 372 } |
| 331 } | 373 } |
| OLD | NEW |