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 |