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 a interceptor are replaced with a HIs version that | |
floitsch
2015/01/16 14:14:34
on an interceptor
sra1
2015/01/20 20:07:27
Done.
| |
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 // TODO(sra): The interceptor is visited first, so this only does something | |
60 // when the interceptor was not replaced for all uses. I'm not sure we | |
61 // should substitute a constant interceptor if the interceptor is already | |
62 // available in a local variable. | |
floitsch
2015/01/16 14:14:34
If it's already in a local, let's just use it.
But
sra1
2015/01/20 20:07:27
I was able to contrive an example where this remov
| |
63 | |
56 if (!invoke.isInterceptedCall) return false; | 64 if (!invoke.isInterceptedCall) return false; |
57 var interceptor = invoke.inputs[0]; | 65 var interceptor = invoke.inputs[0]; |
58 if (interceptor is! HInterceptor) return false; | 66 if (interceptor is! HInterceptor) return false; |
59 HInstruction constant = tryComputeConstantInterceptor( | 67 HInstruction constant = tryComputeConstantInterceptor( |
60 invoke.inputs[1], interceptor.interceptedClasses); | 68 invoke.inputs[1], interceptor.interceptedClasses); |
61 if (constant != null) { | 69 if (constant != null) { |
62 invoke.changeUse(interceptor, constant); | 70 invoke.changeUse(interceptor, constant); |
63 } | 71 } |
64 return false; | 72 return false; |
65 } | 73 } |
(...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
233 // Use a most general interceptor for other instructions, example, | 241 // Use a most general interceptor for other instructions, example, |
234 // is-checks and escaping interceptors. | 242 // is-checks and escaping interceptors. |
235 interceptedClasses.addAll(backend.interceptedClasses); | 243 interceptedClasses.addAll(backend.interceptedClasses); |
236 break; | 244 break; |
237 } | 245 } |
238 } | 246 } |
239 } | 247 } |
240 | 248 |
241 HInstruction receiver = node.receiver; | 249 HInstruction receiver = node.receiver; |
242 | 250 |
251 // TODO(sra): We should consider all uses together and do edits here, rather | |
252 // than at the use node (e.g. visitInvoke). That would achieve an accurate | |
253 // usedBy count for the one-shot interceptor logic. | |
254 | |
243 if (canUseSelfForInterceptor(receiver, interceptedClasses)) { | 255 if (canUseSelfForInterceptor(receiver, interceptedClasses)) { |
244 return rewriteToUseSelfAsInterceptor(node, receiver); | 256 return rewriteToUseSelfAsInterceptor(node, receiver); |
245 } | 257 } |
246 | 258 |
247 // Try computing a constant interceptor. | 259 // Try computing a constant interceptor. |
248 HInstruction constantInterceptor = | 260 HInstruction constantInterceptor = |
249 tryComputeConstantInterceptor(receiver, interceptedClasses); | 261 tryComputeConstantInterceptor(receiver, interceptedClasses); |
250 if (constantInterceptor != null) { | 262 if (constantInterceptor != null) { |
251 node.block.rewrite(node, constantInterceptor); | 263 node.block.rewrite(node, constantInterceptor); |
252 return false; | 264 return false; |
253 } | 265 } |
254 | 266 |
255 node.interceptedClasses = interceptedClasses; | 267 node.interceptedClasses = interceptedClasses; |
256 | 268 |
257 // Try creating a one-shot interceptor. | 269 // Try creating a one-shot interceptor or optimized is-check |
258 if (compiler.hasIncrementalSupport) return false; | 270 if (compiler.hasIncrementalSupport) return false; |
259 if (node.usedBy.length != 1) return false; | 271 if (node.usedBy.length != 1) return false; |
260 if (node.usedBy[0] is !HInvokeDynamic) return false; | 272 HInstruction user = node.usedBy.single; |
261 | 273 |
262 HInvokeDynamic user = node.usedBy[0]; | 274 // 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; | 275 if (!user.hasSameLoopHeaderAs(node)) return false; |
266 | 276 |
267 // Replace the user with a [HOneShotInterceptor]. | 277 bool replaceUserWith(HInstruction replacement) { |
268 HConstant nullConstant = graph.addConstantNull(compiler); | 278 HBasicBlock block = user.block; |
269 List<HInstruction> inputs = new List<HInstruction>.from(user.inputs); | 279 block.addAfter(user, replacement); |
270 inputs[0] = nullConstant; | 280 block.rewrite(user, replacement); |
271 HOneShotInterceptor interceptor = new HOneShotInterceptor( | 281 block.remove(user); |
272 user.selector, inputs, user.instructionType, node.interceptedClasses); | 282 return false; |
273 interceptor.sourcePosition = user.sourcePosition; | 283 } |
274 interceptor.sourceElement = user.sourceElement; | |
275 | 284 |
276 HBasicBlock block = user.block; | 285 if (user is HIs) { |
277 block.addAfter(user, interceptor); | 286 if (node == user.interceptor) { |
278 block.rewrite(user, interceptor); | 287 JavaScriptBackend backend = compiler.backend; |
279 block.remove(user); | 288 if (backend.mayGenerateInstanceofCheck(user.typeExpression)) { |
280 return true; | 289 HInstruction instanceofCheck = new HIs.instanceOf( |
290 user.typeExpression, user.expression, user.instructionType); | |
291 instanceofCheck.sourcePosition = user.sourcePosition; | |
292 instanceofCheck.sourceElement = user.sourceElement; | |
293 return replaceUserWith(instanceofCheck); | |
294 } | |
295 } | |
296 } else if (user is HInvokeDynamic) { | |
297 if (node == user.inputs[0]) { | |
298 // Replace the user with a [HOneShotInterceptor]. | |
299 HConstant nullConstant = graph.addConstantNull(compiler); | |
300 List<HInstruction> inputs = new List<HInstruction>.from(user.inputs); | |
301 inputs[0] = nullConstant; | |
302 HOneShotInterceptor oneShotInterceptor = new HOneShotInterceptor( | |
303 user.selector, inputs, user.instructionType, interceptedClasses); | |
304 oneShotInterceptor.sourcePosition = user.sourcePosition; | |
305 oneShotInterceptor.sourceElement = user.sourceElement; | |
306 return replaceUserWith(oneShotInterceptor); | |
307 } | |
308 } | |
309 | |
310 return false; | |
281 } | 311 } |
282 | 312 |
283 bool rewriteToUseSelfAsInterceptor(HInterceptor node, HInstruction receiver) { | 313 bool rewriteToUseSelfAsInterceptor(HInterceptor node, HInstruction receiver) { |
284 for (HInstruction user in node.usedBy.toList()) { | 314 for (HInstruction user in node.usedBy.toList()) { |
285 if (user is HIs) { | 315 if (user is HIs) { |
286 user.changeUse(node, receiver); | 316 user.changeUse(node, receiver); |
287 } else { | 317 } else { |
288 // Use the potentially self-argument as new receiver. Note that the | 318 // Use the potentially self-argument as new receiver. Note that the |
289 // self-argument could potentially have a tighter type than the | 319 // self-argument could potentially have a tighter type than the |
290 // receiver which was the input to the interceptor. | 320 // 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( | 352 instruction = new HInvokeDynamicMethod( |
323 selector, inputs, node.instructionType, true); | 353 selector, inputs, node.instructionType, true); |
324 } | 354 } |
325 | 355 |
326 HBasicBlock block = node.block; | 356 HBasicBlock block = node.block; |
327 block.addAfter(node, instruction); | 357 block.addAfter(node, instruction); |
328 block.rewrite(node, instruction); | 358 block.rewrite(node, instruction); |
329 return true; | 359 return true; |
330 } | 360 } |
331 } | 361 } |
OLD | NEW |