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..3a0f46787611b4ba445ddf7d5a93aea16d08c351 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 an interceptor are replaced with a HIs version that |
+ * uses 'instanceof' rather than testing a type flag. |
* |
*/ |
class SsaSimplifyInterceptors extends HBaseVisitor |
@@ -56,6 +59,17 @@ class SsaSimplifyInterceptors extends HBaseVisitor |
if (!invoke.isInterceptedCall) return false; |
var interceptor = invoke.inputs[0]; |
if (interceptor is! HInterceptor) return false; |
+ |
+ // TODO(sra): Move this per-call code to visitInterceptor. |
+ // |
+ // The interceptor is visited first, so we get here only when the |
+ // interceptor was not rewritten to a single shared replacement. I'm not |
+ // sure we should substitute a constant interceptor on a per-call basis if |
+ // the interceptor is already available in a local variable, but it is |
+ // possible that all uses can be rewritten to use different constants. |
+ |
+ // TODO(sra): Also do self-interceptor rewrites on a per-use basis. |
+ |
HInstruction constant = tryComputeConstantInterceptor( |
invoke.inputs[1], interceptor.interceptedClasses); |
if (constant != null) { |
@@ -238,8 +252,18 @@ class SsaSimplifyInterceptors extends HBaseVisitor |
} |
} |
+ node.interceptedClasses = interceptedClasses; |
+ |
HInstruction receiver = node.receiver; |
+ // TODO(sra): We should consider each use individually and then all uses |
+ // together. Each use might permit a different rewrite due to a refined |
+ // receiver type. Self-interceptor rewrites are always beneficial since the |
+ // receiver is live at a invocation. Constant-interceptor rewrites are only |
+ // guaranteed to be beneficial if they can eliminate the need for the |
+ // interceptor or reduce the uses to one that can be simplified with a |
+ // one-shot interceptor or optimized is-check. |
+ |
if (canUseSelfForInterceptor(receiver, interceptedClasses)) { |
return rewriteToUseSelfAsInterceptor(node, receiver); |
} |
@@ -252,32 +276,50 @@ class SsaSimplifyInterceptors extends HBaseVisitor |
return false; |
} |
- 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) { |
+ // See if we can rewrite the is-check to use 'instanceof', i.e. rewrite |
+ // "getInterceptor(x).$isT" to "x instanceof T". |
+ 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) { |