Index: sdk/lib/_internal/compiler/implementation/ssa/interceptor_simplifier.dart |
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/interceptor_simplifier.dart b/sdk/lib/_internal/compiler/implementation/ssa/interceptor_simplifier.dart |
deleted file mode 100644 |
index 3f177585739f3ec76c88b6ccd710f2b72619b2a9..0000000000000000000000000000000000000000 |
--- a/sdk/lib/_internal/compiler/implementation/ssa/interceptor_simplifier.dart |
+++ /dev/null |
@@ -1,330 +0,0 @@ |
-// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
-// for details. All rights reserved. Use of this source code is governed by a |
-// BSD-style license that can be found in the LICENSE file. |
- |
-part of ssa; |
- |
-/** |
- * This phase simplifies interceptors in multiple ways: |
- * |
- * 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. |
- * |
- * 3) If we know the object is not intercepted, we just use it |
- * 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. |
- * |
- */ |
-class SsaSimplifyInterceptors extends HBaseVisitor |
- implements OptimizationPhase { |
- final String name = "SsaSimplifyInterceptors"; |
- final ConstantSystem constantSystem; |
- final Compiler compiler; |
- final CodegenWorkItem work; |
- HGraph graph; |
- |
- SsaSimplifyInterceptors(this.compiler, this.constantSystem, this.work); |
- |
- void visitGraph(HGraph graph) { |
- this.graph = graph; |
- visitDominatorTree(graph); |
- } |
- |
- void visitBasicBlock(HBasicBlock node) { |
- currentBlock = node; |
- |
- HInstruction instruction = node.first; |
- while (instruction != null) { |
- bool shouldRemove = instruction.accept(this); |
- HInstruction next = instruction.next; |
- if (shouldRemove) { |
- instruction.block.remove(instruction); |
- } |
- instruction = next; |
- } |
- } |
- |
- bool visitInstruction(HInstruction instruction) => false; |
- |
- bool visitInvoke(HInvoke invoke) { |
- if (!invoke.isInterceptedCall) return false; |
- var interceptor = invoke.inputs[0]; |
- if (interceptor is! HInterceptor) return false; |
- HInstruction constant = tryComputeConstantInterceptor( |
- invoke.inputs[1], interceptor.interceptedClasses); |
- if (constant != null) { |
- invoke.changeUse(interceptor, constant); |
- } |
- return false; |
- } |
- |
- bool canUseSelfForInterceptor(HInstruction receiver, |
- Set<ClassElement> interceptedClasses) { |
- JavaScriptBackend backend = compiler.backend; |
- ClassWorld classWorld = compiler.world; |
- |
- if (receiver.canBePrimitive(compiler)) { |
- // Primitives always need interceptors. |
- return false; |
- } |
- if (receiver.canBeNull() && |
- interceptedClasses.contains(backend.jsNullClass)) { |
- // Need the JSNull interceptor. |
- return false; |
- } |
- |
- // All intercepted classes extend `Interceptor`, so if the receiver can't be |
- // a class extending `Interceptor` then it can be called directly. |
- return new TypeMask.nonNullSubclass(backend.jsInterceptorClass, classWorld) |
- .intersection(receiver.instructionType, classWorld) |
- .isEmpty; |
- } |
- |
- HInstruction tryComputeConstantInterceptor( |
- HInstruction input, |
- Set<ClassElement> interceptedClasses) { |
- if (input == graph.explicitReceiverParameter) { |
- // If `explicitReceiverParameter` is set it means the current method is an |
- // interceptor method, and `this` is the interceptor. The caller just did |
- // `getInterceptor(foo).currentMethod(foo)` to enter the current method. |
- return graph.thisInstruction; |
- } |
- |
- ClassElement constantInterceptor; |
- ClassWorld classWorld = compiler.world; |
- JavaScriptBackend backend = compiler.backend; |
- if (input.canBeNull()) { |
- if (input.isNull()) { |
- constantInterceptor = backend.jsNullClass; |
- } |
- } else if (input.isInteger(compiler)) { |
- constantInterceptor = backend.jsIntClass; |
- } else if (input.isDouble(compiler)) { |
- constantInterceptor = backend.jsDoubleClass; |
- } else if (input.isBoolean(compiler)) { |
- constantInterceptor = backend.jsBoolClass; |
- } else if (input.isString(compiler)) { |
- constantInterceptor = backend.jsStringClass; |
- } else if (input.isArray(compiler)) { |
- constantInterceptor = backend.jsArrayClass; |
- } else if (input.isNumber(compiler) && |
- !interceptedClasses.contains(backend.jsIntClass) && |
- !interceptedClasses.contains(backend.jsDoubleClass)) { |
- // If the method being intercepted is not defined in [int] or [double] we |
- // can safely use the number interceptor. This is because none of the |
- // [int] or [double] methods are called from a method defined on [num]. |
- constantInterceptor = backend.jsNumberClass; |
- } else { |
- // Try to find constant interceptor for a native class. If the receiver |
- // is constrained to a leaf native class, we can use the class's |
- // interceptor directly. |
- |
- // TODO(sra): Key DOM classes like Node, Element and Event are not leaf |
- // classes. When the receiver type is not a leaf class, we might still be |
- // able to use the receiver class as a constant interceptor. It is |
- // usually the case that methods defined on a non-leaf class don't test |
- // for a subclass or call methods defined on a subclass. Provided the |
- // code is completely insensitive to the specific instance subclasses, we |
- // can use the non-leaf class directly. |
- ClassElement element = input.instructionType.singleClass(classWorld); |
- if (element != null && element.isNative) { |
- constantInterceptor = element; |
- } |
- } |
- |
- if (constantInterceptor == null) return null; |
- |
- // If we just happen to be in an instance method of the constant |
- // interceptor, `this` is a shorter alias. |
- if (constantInterceptor == work.element.enclosingClass && |
- graph.thisInstruction != null) { |
- return graph.thisInstruction; |
- } |
- |
- ConstantValue constant = |
- new InterceptorConstantValue(constantInterceptor.thisType); |
- return graph.addConstant(constant, compiler); |
- } |
- |
- HInstruction findDominator(Iterable<HInstruction> instructions) { |
- HInstruction result; |
- L1: for (HInstruction candidate in instructions) { |
- for (HInstruction current in instructions) { |
- if (current != candidate && !candidate.dominates(current)) continue L1; |
- } |
- result = candidate; |
- break; |
- } |
- return result; |
- } |
- |
- bool visitInterceptor(HInterceptor node) { |
- if (node.isConstant()) return false; |
- |
- // Specialize the interceptor with set of classes it intercepts, considering |
- // all uses. (The specialized interceptor has a shorter dispatch chain). |
- // This operation applies only where the interceptor is used to dispatch a |
- // method. Other uses, e.g. as an ordinary argument or a HIs check use the |
- // most general interceptor. |
- // |
- // TODO(sra): Take into account the receiver type at each call. e.g: |
- // |
- // (a) => a.length + a.hashCode |
- // |
- // Currently we use the most general interceptor since all intercepted types |
- // implement `hashCode`. But in this example, `a.hashCode` is only reached |
- // if `a.length` succeeds, which is indicated by the hashCode receiver being |
- // a HTypeKnown instruction. |
- |
- int useCount(HInstruction user, HInstruction used) => |
- user.inputs.where((input) => input == used).length; |
- |
- Set<ClassElement> interceptedClasses; |
- JavaScriptBackend backend = compiler.backend; |
- HInstruction dominator = findDominator(node.usedBy); |
- // If there is a call that dominates all other uses, we can use just the |
- // selector of that instruction. |
- if (dominator is HInvokeDynamic && |
- dominator.isCallOnInterceptor(compiler) && |
- node == dominator.receiver && |
- useCount(dominator, node) == 1) { |
- interceptedClasses = |
- backend.getInterceptedClassesOn(dominator.selector.name); |
- |
- // If we found that we need number, we must still go through all |
- // uses to check if they require int, or double. |
- if (interceptedClasses.contains(backend.jsNumberClass) && |
- !(interceptedClasses.contains(backend.jsDoubleClass) || |
- interceptedClasses.contains(backend.jsIntClass))) { |
- for (HInstruction user in node.usedBy) { |
- if (user is! HInvoke) continue; |
- Set<ClassElement> intercepted = |
- backend.getInterceptedClassesOn(user.selector.name); |
- if (intercepted.contains(backend.jsIntClass)) { |
- interceptedClasses.add(backend.jsIntClass); |
- } |
- if (intercepted.contains(backend.jsDoubleClass)) { |
- interceptedClasses.add(backend.jsDoubleClass); |
- } |
- } |
- } |
- } else { |
- interceptedClasses = new Set<ClassElement>(); |
- for (HInstruction user in node.usedBy) { |
- if (user is HInvokeDynamic && |
- user.isCallOnInterceptor(compiler) && |
- node == user.receiver && |
- useCount(user, node) == 1) { |
- interceptedClasses.addAll( |
- backend.getInterceptedClassesOn(user.selector.name)); |
- } else if (user is HInvokeSuper && |
- user.isCallOnInterceptor(compiler) && |
- node == user.receiver && |
- useCount(user, node) == 1) { |
- interceptedClasses.addAll( |
- backend.getInterceptedClassesOn(user.selector.name)); |
- } else { |
- // Use a most general interceptor for other instructions, example, |
- // is-checks and escaping interceptors. |
- interceptedClasses.addAll(backend.interceptedClasses); |
- break; |
- } |
- } |
- } |
- |
- HInstruction receiver = node.receiver; |
- |
- if (canUseSelfForInterceptor(receiver, interceptedClasses)) { |
- return rewriteToUseSelfAsInterceptor(node, receiver); |
- } |
- |
- // Try computing a constant interceptor. |
- HInstruction constantInterceptor = |
- tryComputeConstantInterceptor(receiver, interceptedClasses); |
- if (constantInterceptor != null) { |
- node.block.rewrite(node, constantInterceptor); |
- return false; |
- } |
- |
- node.interceptedClasses = interceptedClasses; |
- |
- // Try creating a one-shot interceptor. |
- if (node.usedBy.length != 1) return false; |
- if (node.usedBy[0] is !HInvokeDynamic) return false; |
- |
- HInvokeDynamic user = node.usedBy[0]; |
- |
- // If [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 rewriteToUseSelfAsInterceptor(HInterceptor node, HInstruction receiver) { |
- for (HInstruction user in node.usedBy.toList()) { |
- if (user is HIs) { |
- user.changeUse(node, receiver); |
- } else { |
- // Use the potentially self-argument as new receiver. Note that the |
- // self-argument could potentially have a tighter type than the |
- // receiver which was the input to the interceptor. |
- assert(user.inputs[0] == node); |
- assert(receiver.nonCheck() == user.inputs[1].nonCheck()); |
- user.changeUse(node, user.inputs[1]); |
- } |
- } |
- return false; |
- } |
- |
- bool visitOneShotInterceptor(HOneShotInterceptor node) { |
- HInstruction constant = tryComputeConstantInterceptor( |
- node.inputs[1], node.interceptedClasses); |
- |
- if (constant == null) return false; |
- |
- Selector selector = node.selector; |
- HInstruction instruction; |
- if (selector.isGetter) { |
- instruction = new HInvokeDynamicGetter( |
- selector, |
- node.element, |
- <HInstruction>[constant, node.inputs[1]], |
- node.instructionType); |
- } else if (selector.isSetter) { |
- instruction = new HInvokeDynamicSetter( |
- selector, |
- node.element, |
- <HInstruction>[constant, node.inputs[1], node.inputs[2]], |
- node.instructionType); |
- } else { |
- List<HInstruction> inputs = new List<HInstruction>.from(node.inputs); |
- inputs[0] = constant; |
- instruction = new HInvokeDynamicMethod( |
- selector, inputs, node.instructionType, true); |
- } |
- |
- HBasicBlock block = node.block; |
- block.addAfter(node, instruction); |
- block.rewrite(node, instruction); |
- return true; |
- } |
-} |