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) { |