Chromium Code Reviews| Index: pkg/compiler/lib/src/ssa/interceptor_simplifier.dart |
| diff --git a/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart b/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart |
| index 79a527cf92b5680af27fcdbb3175887c7be97769..e4140046db03593bc86acdc348e52429cd27741a 100644 |
| --- a/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart |
| +++ b/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart |
| @@ -10,15 +10,18 @@ part of ssa; |
| * 1) If the interceptor is for an object whose type is known, it |
| * tries to use a constant interceptor instead. |
| * |
| - * 2) It specializes interceptors based on the selectors it is being |
| - * called with. |
| + * 2) Interceptors are specialized based on the selector it is used with. |
| * |
| - * 3) If we know the object is not intercepted, we just use it |
| + * 3) If we know the object is not intercepted, we just use the object |
| * instead. |
| * |
| - * 4) It replaces all interceptors that are used only once with |
| - * one-shot interceptors. It saves code size and makes the receiver of |
| - * an intercepted call a candidate for being generated at use site. |
| + * 4) Single use interceptors at dynamic invoke sites are replaced with 'one |
| + * shot interceptors' which are synthesized static helper functions that fetch |
| + * the interceptor and then call the method. This saves code size and makes the |
| + * receiver of an intercepted call a candidate for being generated at use site. |
| + * |
| + * 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.
|
| + * uses 'instanceof' rather than testing a type flag. |
| * |
| */ |
| class SsaSimplifyInterceptors extends HBaseVisitor |
| @@ -53,6 +56,11 @@ class SsaSimplifyInterceptors extends HBaseVisitor |
| bool visitInstruction(HInstruction instruction) => false; |
| bool visitInvoke(HInvoke invoke) { |
| + // TODO(sra): The interceptor is visited first, so this only does something |
| + // when the interceptor was not replaced for all uses. I'm not sure we |
| + // should substitute a constant interceptor if the interceptor is already |
| + // 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
|
| + |
| if (!invoke.isInterceptedCall) return false; |
| var interceptor = invoke.inputs[0]; |
| if (interceptor is! HInterceptor) return false; |
| @@ -240,6 +248,10 @@ class SsaSimplifyInterceptors extends HBaseVisitor |
| HInstruction receiver = node.receiver; |
| + // TODO(sra): We should consider all uses together and do edits here, rather |
| + // than at the use node (e.g. visitInvoke). That would achieve an accurate |
| + // usedBy count for the one-shot interceptor logic. |
| + |
| if (canUseSelfForInterceptor(receiver, interceptedClasses)) { |
| return rewriteToUseSelfAsInterceptor(node, receiver); |
| } |
| @@ -254,30 +266,48 @@ class SsaSimplifyInterceptors extends HBaseVisitor |
| node.interceptedClasses = interceptedClasses; |
| - // Try creating a one-shot interceptor. |
| + // Try creating a one-shot interceptor or optimized is-check |
| if (compiler.hasIncrementalSupport) return false; |
| if (node.usedBy.length != 1) return false; |
| - if (node.usedBy[0] is !HInvokeDynamic) return false; |
| - |
| - HInvokeDynamic user = node.usedBy[0]; |
| + HInstruction user = node.usedBy.single; |
| - // If [node] was loop hoisted, we keep the interceptor. |
| + // If the interceptor [node] was loop hoisted, we keep the interceptor. |
| if (!user.hasSameLoopHeaderAs(node)) return false; |
| - // Replace the user with a [HOneShotInterceptor]. |
| - HConstant nullConstant = graph.addConstantNull(compiler); |
| - List<HInstruction> inputs = new List<HInstruction>.from(user.inputs); |
| - inputs[0] = nullConstant; |
| - HOneShotInterceptor interceptor = new HOneShotInterceptor( |
| - user.selector, inputs, user.instructionType, node.interceptedClasses); |
| - interceptor.sourcePosition = user.sourcePosition; |
| - interceptor.sourceElement = user.sourceElement; |
| - |
| - HBasicBlock block = user.block; |
| - block.addAfter(user, interceptor); |
| - block.rewrite(user, interceptor); |
| - block.remove(user); |
| - return true; |
| + bool replaceUserWith(HInstruction replacement) { |
| + HBasicBlock block = user.block; |
| + block.addAfter(user, replacement); |
| + block.rewrite(user, replacement); |
| + block.remove(user); |
| + return false; |
| + } |
| + |
| + if (user is HIs) { |
| + if (node == user.interceptor) { |
| + JavaScriptBackend backend = compiler.backend; |
| + if (backend.mayGenerateInstanceofCheck(user.typeExpression)) { |
| + HInstruction instanceofCheck = new HIs.instanceOf( |
| + user.typeExpression, user.expression, user.instructionType); |
| + instanceofCheck.sourcePosition = user.sourcePosition; |
| + instanceofCheck.sourceElement = user.sourceElement; |
| + return replaceUserWith(instanceofCheck); |
| + } |
| + } |
| + } else if (user is HInvokeDynamic) { |
| + if (node == user.inputs[0]) { |
| + // Replace the user with a [HOneShotInterceptor]. |
| + HConstant nullConstant = graph.addConstantNull(compiler); |
| + List<HInstruction> inputs = new List<HInstruction>.from(user.inputs); |
| + inputs[0] = nullConstant; |
| + HOneShotInterceptor oneShotInterceptor = new HOneShotInterceptor( |
| + user.selector, inputs, user.instructionType, interceptedClasses); |
| + oneShotInterceptor.sourcePosition = user.sourcePosition; |
| + oneShotInterceptor.sourceElement = user.sourceElement; |
| + return replaceUserWith(oneShotInterceptor); |
| + } |
| + } |
| + |
| + return false; |
| } |
| bool rewriteToUseSelfAsInterceptor(HInterceptor node, HInstruction receiver) { |