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 14 matching lines...) Expand all Loading... | |
46 if (shouldRemove) { | 49 if (shouldRemove) { |
47 instruction.block.remove(instruction); | 50 instruction.block.remove(instruction); |
48 } | 51 } |
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) { |
59 //return false; | |
floitsch
2015/01/20 20:15:42
commented code.
| |
56 if (!invoke.isInterceptedCall) return false; | 60 if (!invoke.isInterceptedCall) return false; |
57 var interceptor = invoke.inputs[0]; | 61 var interceptor = invoke.inputs[0]; |
58 if (interceptor is! HInterceptor) return false; | 62 if (interceptor is! HInterceptor) return false; |
63 | |
64 // TODO(sra): Move this per-call code to visitInterceptor. | |
65 // | |
66 // The interceptor is visited first, so we get here only when the | |
67 // interceptor was not rewritten to a single shared replacement. I'm not | |
68 // sure we should substitute a constant interceptor on a per-call basis if | |
69 // the interceptor is already available in a local variable, but it is | |
70 // possible that all uses can be rewritten to use different constants. | |
71 | |
72 // TODO(sra): Also do self-interceptor rewrites on a per-use basis. | |
73 | |
59 HInstruction constant = tryComputeConstantInterceptor( | 74 HInstruction constant = tryComputeConstantInterceptor( |
60 invoke.inputs[1], interceptor.interceptedClasses); | 75 invoke.inputs[1], interceptor.interceptedClasses); |
61 if (constant != null) { | 76 if (constant != null) { |
62 invoke.changeUse(interceptor, constant); | 77 invoke.changeUse(interceptor, constant); |
63 } | 78 } |
64 return false; | 79 return false; |
65 } | 80 } |
66 | 81 |
67 bool canUseSelfForInterceptor(HInstruction receiver, | 82 bool canUseSelfForInterceptor(HInstruction receiver, |
68 Set<ClassElement> interceptedClasses) { | 83 Set<ClassElement> interceptedClasses) { |
(...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
231 backend.getInterceptedClassesOn(user.selector.name)); | 246 backend.getInterceptedClassesOn(user.selector.name)); |
232 } else { | 247 } else { |
233 // Use a most general interceptor for other instructions, example, | 248 // Use a most general interceptor for other instructions, example, |
234 // is-checks and escaping interceptors. | 249 // is-checks and escaping interceptors. |
235 interceptedClasses.addAll(backend.interceptedClasses); | 250 interceptedClasses.addAll(backend.interceptedClasses); |
236 break; | 251 break; |
237 } | 252 } |
238 } | 253 } |
239 } | 254 } |
240 | 255 |
256 node.interceptedClasses = interceptedClasses; | |
257 | |
241 HInstruction receiver = node.receiver; | 258 HInstruction receiver = node.receiver; |
242 | 259 |
260 // TODO(sra): We should consider each use individually and then all uses | |
261 // together. Each use might permit a different rewrite due to a refined | |
262 // receiver type. Self-interceptor rewrites are always beneficial since the | |
263 // receiver is live at a invocation. Constant-interceptor rewrites are only | |
264 // guaranteed to be beneficial if they can eliminate the need for the | |
265 // interceptor or reduce the uses to one that can be simplified with a | |
266 // one-shot interceptor or optimized is-check. | |
267 | |
243 if (canUseSelfForInterceptor(receiver, interceptedClasses)) { | 268 if (canUseSelfForInterceptor(receiver, interceptedClasses)) { |
244 return rewriteToUseSelfAsInterceptor(node, receiver); | 269 return rewriteToUseSelfAsInterceptor(node, receiver); |
245 } | 270 } |
246 | 271 |
247 // Try computing a constant interceptor. | 272 // Try computing a constant interceptor. |
248 HInstruction constantInterceptor = | 273 HInstruction constantInterceptor = |
249 tryComputeConstantInterceptor(receiver, interceptedClasses); | 274 tryComputeConstantInterceptor(receiver, interceptedClasses); |
250 if (constantInterceptor != null) { | 275 if (constantInterceptor != null) { |
251 node.block.rewrite(node, constantInterceptor); | 276 node.block.rewrite(node, constantInterceptor); |
252 return false; | 277 return false; |
253 } | 278 } |
254 | 279 |
255 node.interceptedClasses = interceptedClasses; | 280 // Try creating a one-shot interceptor or optimized is-check |
256 | |
257 // Try creating a one-shot interceptor. | |
258 if (compiler.hasIncrementalSupport) return false; | 281 if (compiler.hasIncrementalSupport) return false; |
259 if (node.usedBy.length != 1) return false; | 282 if (node.usedBy.length != 1) return false; |
260 if (node.usedBy[0] is !HInvokeDynamic) return false; | 283 HInstruction user = node.usedBy.single; |
261 | 284 |
262 HInvokeDynamic user = node.usedBy[0]; | 285 // 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; | 286 if (!user.hasSameLoopHeaderAs(node)) return false; |
266 | 287 |
267 // Replace the user with a [HOneShotInterceptor]. | 288 bool replaceUserWith(HInstruction replacement) { |
268 HConstant nullConstant = graph.addConstantNull(compiler); | 289 HBasicBlock block = user.block; |
269 List<HInstruction> inputs = new List<HInstruction>.from(user.inputs); | 290 block.addAfter(user, replacement); |
270 inputs[0] = nullConstant; | 291 block.rewrite(user, replacement); |
271 HOneShotInterceptor interceptor = new HOneShotInterceptor( | 292 block.remove(user); |
272 user.selector, inputs, user.instructionType, node.interceptedClasses); | 293 return false; |
273 interceptor.sourcePosition = user.sourcePosition; | 294 } |
274 interceptor.sourceElement = user.sourceElement; | |
275 | 295 |
276 HBasicBlock block = user.block; | 296 if (user is HIs) { |
277 block.addAfter(user, interceptor); | 297 // See if we can rewrite the is-check to use 'instanceof', i.e. rewrite |
278 block.rewrite(user, interceptor); | 298 // "getInterceptor(x).$isT" to "x instanceof T". |
279 block.remove(user); | 299 if (node == user.interceptor) { |
280 return true; | 300 JavaScriptBackend backend = compiler.backend; |
301 if (backend.mayGenerateInstanceofCheck(user.typeExpression)) { | |
302 HInstruction instanceofCheck = new HIs.instanceOf( | |
303 user.typeExpression, user.expression, user.instructionType); | |
304 instanceofCheck.sourcePosition = user.sourcePosition; | |
305 instanceofCheck.sourceElement = user.sourceElement; | |
306 return replaceUserWith(instanceofCheck); | |
307 } | |
308 } | |
309 } else if (user is HInvokeDynamic) { | |
310 if (node == user.inputs[0]) { | |
311 // Replace the user with a [HOneShotInterceptor]. | |
312 HConstant nullConstant = graph.addConstantNull(compiler); | |
313 List<HInstruction> inputs = new List<HInstruction>.from(user.inputs); | |
314 inputs[0] = nullConstant; | |
315 HOneShotInterceptor oneShotInterceptor = new HOneShotInterceptor( | |
316 user.selector, inputs, user.instructionType, interceptedClasses); | |
317 oneShotInterceptor.sourcePosition = user.sourcePosition; | |
318 oneShotInterceptor.sourceElement = user.sourceElement; | |
319 return replaceUserWith(oneShotInterceptor); | |
320 } | |
321 } | |
322 | |
323 return false; | |
281 } | 324 } |
282 | 325 |
283 bool rewriteToUseSelfAsInterceptor(HInterceptor node, HInstruction receiver) { | 326 bool rewriteToUseSelfAsInterceptor(HInterceptor node, HInstruction receiver) { |
284 for (HInstruction user in node.usedBy.toList()) { | 327 for (HInstruction user in node.usedBy.toList()) { |
285 if (user is HIs) { | 328 if (user is HIs) { |
286 user.changeUse(node, receiver); | 329 user.changeUse(node, receiver); |
287 } else { | 330 } else { |
288 // Use the potentially self-argument as new receiver. Note that the | 331 // Use the potentially self-argument as new receiver. Note that the |
289 // self-argument could potentially have a tighter type than the | 332 // self-argument could potentially have a tighter type than the |
290 // receiver which was the input to the interceptor. | 333 // 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( | 365 instruction = new HInvokeDynamicMethod( |
323 selector, inputs, node.instructionType, true); | 366 selector, inputs, node.instructionType, true); |
324 } | 367 } |
325 | 368 |
326 HBasicBlock block = node.block; | 369 HBasicBlock block = node.block; |
327 block.addAfter(node, instruction); | 370 block.addAfter(node, instruction); |
328 block.rewrite(node, instruction); | 371 block.rewrite(node, instruction); |
329 return true; | 372 return true; |
330 } | 373 } |
331 } | 374 } |
OLD | NEW |