Revert "[dart2js] Avoid premature interceptor optimizations"

This reverts commit db6f65e661a8d44f57c5c03141064f58d819ea86.

Reason for revert: b/184605569

Original change's description:
> [dart2js] Avoid premature interceptor optimizations
>
> Add a SsaFinalizeInterceptors phase that does optimizations with the
> interceptor calling convention.
>
> This puts the the caller and callee sides of the dummy explicit
> receiver optimization in the same phase.
>
> Change-Id: I07fe0483c2658c3744b76a235edb24720d7ea60f
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/193140
> Commit-Queue: Stephen Adams <sra@google.com>
> Reviewed-by: Mayank Patke <fishythefish@google.com>

# Not skipping CQ checks because original CL landed > 1 day ago.

Change-Id: I0e2959f329030f12a43a9aef16cfee9b94bbf250
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/194061
Reviewed-by: Ivan Inozemtsev <iinozemtsev@google.com>
Commit-Queue: Ivan Inozemtsev <iinozemtsev@google.com>
diff --git a/pkg/compiler/lib/src/ssa/codegen.dart b/pkg/compiler/lib/src/ssa/codegen.dart
index f6928ab..81f1761 100644
--- a/pkg/compiler/lib/src/ssa/codegen.dart
+++ b/pkg/compiler/lib/src/ssa/codegen.dart
@@ -414,7 +414,8 @@
       assert(graph.isValid(), 'Graph not valid after ${phase.name}');
     }
 
-    runPhase(new SsaInstructionSelection(_options, _closedWorld));
+    runPhase(
+        new SsaInstructionSelection(_options, _closedWorld, _interceptorData));
     runPhase(new SsaTypeKnownRemover());
     runPhase(new SsaTrustedCheckRemover(_options));
     runPhase(new SsaAssignmentChaining(_closedWorld));
diff --git a/pkg/compiler/lib/src/ssa/codegen_helpers.dart b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
index 5bf162b..d643936 100644
--- a/pkg/compiler/lib/src/ssa/codegen_helpers.dart
+++ b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
@@ -5,6 +5,7 @@
 import '../constants/values.dart';
 import '../elements/entities.dart';
 import '../inferrer/abstract_value_domain.dart';
+import '../js_backend/interceptor_data.dart';
 import '../options.dart';
 import '../universe/selector.dart' show Selector;
 import '../world.dart' show JClosedWorld;
@@ -26,14 +27,21 @@
 ///
 /// - Remove NullChecks where the next instruction would fail on the operand.
 ///
+/// - Dummy receiver optimization.
+///
+/// - One-shot interceptor optimization.
+///
 /// - Combine read/modify/write sequences into HReadModifyWrite instructions to
 ///   simplify codegen of expressions like `a.x += y`.
+
 class SsaInstructionSelection extends HBaseVisitor with CodegenPhase {
   final JClosedWorld _closedWorld;
+  final InterceptorData _interceptorData;
   final CompilerOptions _options;
   HGraph graph;
 
-  SsaInstructionSelection(this._options, this._closedWorld);
+  SsaInstructionSelection(
+      this._options, this._closedWorld, this._interceptorData);
 
   AbstractValueDomain get _abstractValueDomain =>
       _closedWorld.abstractValueDomain;
@@ -221,6 +229,128 @@
       .isPotentiallyTrue;
 
   @override
+  HInstruction visitInvokeDynamic(HInvokeDynamic node) {
+    if (!node.isInterceptedCall) return node;
+
+    tryReplaceExplicitReceiverWithDummy(
+        node, node.selector, node.element, node.receiverType);
+
+    // Try to replace
+    //
+    //     getInterceptor(o).method(o, ...)
+    //
+    // with a 'one shot interceptor' which is a call to a synthesized static
+    // helper function that combines the two operations.
+    //
+    //     oneShotMethod(o, 1, 2)
+    //
+    // This saves code size and makes the receiver of an intercepted call a
+    // candidate for being generated at use site.
+    //
+    // Avoid combining a hoisted interceptor back into a loop, and the faster
+    // almost-constant kind of interceptor.
+
+    HInstruction interceptor = node.inputs[0];
+    if (interceptor is HInterceptor &&
+        interceptor.usedBy.length == 1 &&
+        !interceptor.isConditionalConstantInterceptor &&
+        interceptor.hasSameLoopHeaderAs(node)) {
+      // Copy inputs and replace interceptor with `null`.
+      List<HInstruction> inputs = List.of(node.inputs);
+      inputs[0] = graph.addConstantNull(_closedWorld);
+
+      HOneShotInterceptor oneShot = HOneShotInterceptor(
+          node.selector,
+          node.receiverType,
+          inputs,
+          node.instructionType,
+          node.typeArguments,
+          interceptor.interceptedClasses);
+      oneShot.sourceInformation = node.sourceInformation;
+      oneShot.sourceElement = node.sourceElement;
+      oneShot.sideEffects.setTo(node.sideEffects);
+
+      HBasicBlock block = node.block;
+      block.addAfter(node, oneShot);
+      block.rewrite(node, oneShot);
+      block.remove(node);
+      interceptor.block.remove(interceptor);
+      return null;
+    }
+
+    return node;
+  }
+
+  @override
+  HInstruction visitInvokeSuper(HInvokeSuper node) {
+    tryReplaceExplicitReceiverWithDummy(
+        node, node.selector, node.element, null);
+    return node;
+  }
+
+  @override
+  HInstruction visitOneShotInterceptor(HOneShotInterceptor node) {
+    throw StateError('Should not see HOneShotInterceptor: $node');
+  }
+
+  void tryReplaceExplicitReceiverWithDummy(HInvoke node, Selector selector,
+      MemberEntity target, AbstractValue mask) {
+    // Calls of the form
+    //
+    //     a.foo$1(a, x)
+    //
+    // where the interceptor calling convention is used come from recognizing
+    // that 'a' is a 'self-interceptor'.  If the selector matches only methods
+    // that ignore the explicit receiver parameter, replace occurrences of the
+    // receiver argument with a dummy receiver '0':
+    //
+    //     a.foo$1(a, x)   --->   a.foo$1(0, x)
+    //
+    // This often reduces the number of references to 'a' to one, allowing 'a'
+    // to be generated at use to avoid a temporary, e.g.
+    //
+    //     t1 = b.get$thing();
+    //     t1.foo$1(t1, x)
+    // --->
+    //     b.get$thing().foo$1(0, x)
+    //
+    assert(target != null || mask != null);
+
+    if (!node.isInterceptedCall) return;
+
+    // TODO(15933): Make automatically generated property extraction closures
+    // work with the dummy receiver optimization.
+    if (selector.isGetter) return;
+
+    // This assignment of inputs is uniform for HInvokeDynamic and HInvokeSuper.
+    HInstruction interceptor = node.inputs[0];
+    HInstruction receiverArgument = node.inputs[1];
+
+    // A 'self-interceptor'?
+    if (interceptor.nonCheck() != receiverArgument.nonCheck()) return;
+
+    // TODO(sra): Should this be an assert?
+    if (!_interceptorData.isInterceptedSelector(selector)) return;
+
+    if (target != null) {
+      // A call that resolves to a single instance method (element) requires the
+      // calling convention consistent with the method.
+      ClassEntity cls = target.enclosingClass;
+      assert(_interceptorData.isInterceptedMethod(target));
+      if (_interceptorData.isInterceptedClass(cls)) return;
+    } else if (_interceptorData.isInterceptedMixinSelector(
+        selector, mask, _closedWorld)) {
+      return;
+    }
+
+    ConstantValue constant = DummyInterceptorConstantValue();
+    HConstant dummy = graph.addConstant(constant, _closedWorld);
+    receiverArgument.usedBy.remove(node);
+    node.inputs[1] = dummy;
+    dummy.usedBy.add(node);
+  }
+
+  @override
   HInstruction visitFieldSet(HFieldSet setter) {
     // Pattern match
     //     t1 = x.f; t2 = t1 + 1; x.f = t2; use(t2)   -->  ++x.f
diff --git a/pkg/compiler/lib/src/ssa/interceptor_finalizer.dart b/pkg/compiler/lib/src/ssa/interceptor_finalizer.dart
deleted file mode 100644
index 9ec829d..0000000
--- a/pkg/compiler/lib/src/ssa/interceptor_finalizer.dart
+++ /dev/null
@@ -1,240 +0,0 @@
-// Copyright (c) 2021, 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.
-
-import '../constants/values.dart';
-import '../elements/entities.dart';
-import '../inferrer/abstract_value_domain.dart';
-import '../js_backend/interceptor_data.dart';
-import '../universe/selector.dart' show Selector;
-import '../world.dart' show JClosedWorld;
-import 'nodes.dart';
-import 'optimize.dart';
-
-/// SsaFinalizeInterceptors makes adjustments for the interceptor calling
-/// convention.
-///
-/// 1. If the method cannot be invoked with an intercepted receiver, the
-///    receiver and interceptor are the same. In this case ignore the explicit
-///    receiver argument and use the interceptor (this) as the receiver.
-///
-/// 2. The call-site dual of the above is if a method ignores the explicit
-///    receiver, it can be replaced with a dummy value, i.e. a dummy receiver
-///    optimization.
-///
-/// 3. If an interceptor is used once for a call, replace the
-///    getInterceptor-call pair with a call to a 'one-shot interceptor' outlined
-///    method.
-///
-class SsaFinalizeInterceptors extends HBaseVisitor
-    implements OptimizationPhase {
-  @override
-  String get name => "SsaFinalizeInterceptors";
-  final JClosedWorld _closedWorld;
-  HGraph _graph;
-
-  SsaFinalizeInterceptors(this._closedWorld);
-
-  InterceptorData get _interceptorData => _closedWorld.interceptorData;
-
-  @override
-  void visitGraph(HGraph graph) {
-    _graph = graph;
-    MemberEntity element = graph.element;
-
-    if (usesSelfInterceptor(element)) {
-      _redirectReceiver();
-    }
-    visitDominatorTree(graph);
-  }
-
-  @override
-  visitBasicBlock(HBasicBlock node) {
-    HInstruction instruction = node.first;
-    while (instruction != null) {
-      final next = instruction.next;
-      instruction.accept(this);
-      instruction = next;
-    }
-  }
-
-  /// Returns `true` if [element] is an instance method that uses the
-  /// interceptor calling convention but the instance and interceptor arguments
-  /// will always be the same value.
-  bool usesSelfInterceptor(MemberEntity element) {
-    if (!_interceptorData.isInterceptedMethod(element)) return false;
-    ClassEntity cls = element.enclosingClass;
-    return !_interceptorData.isInterceptedClass(cls);
-  }
-
-  void _redirectReceiver() {
-    // The entry block contains the parameters in order, starting with `this`,
-    // and then the explicit receiver. There are other instructions in the
-    // block, like constants, which we ignore.
-    HThis thisParameter;
-    HParameterValue receiverParameter;
-    for (HInstruction node = _graph.entry.first;
-        node != null;
-        node = node.next) {
-      if (node is HParameterValue) {
-        if (node is HThis) {
-          thisParameter = node;
-        } else {
-          receiverParameter = node;
-          break;
-        }
-      }
-    }
-    assert(thisParameter != null,
-        '`this` parameter should be before other parameters');
-    assert(receiverParameter != null,
-        'Intercepted convention requires explicit receiver');
-    thisParameter.instructionType = receiverParameter.instructionType;
-    receiverParameter.block.rewrite(receiverParameter, thisParameter);
-    receiverParameter.sourceElement = const _RenameToUnderscore();
-  }
-
-  @override
-  void visitInvokeDynamic(HInvokeDynamic node) {
-    if (!node.isInterceptedCall) return;
-
-    if (node.element != null) {
-      tryReplaceExplicitReceiverForTargetWithDummy(
-          node, node.selector, node.element);
-    } else {
-      tryReplaceExplicitReceiverForSelectorWithDummy(
-          node, node.selector, node.receiverType);
-    }
-
-    // Try to replace
-    //
-    //     getInterceptor(o).method(o, ...)
-    //
-    // with a 'one shot interceptor' which is a call to a synthesized static
-    // helper function that combines the two operations.
-    //
-    //     oneShotMethod(o, 1, 2)
-    //
-    // This saves code size and makes the receiver of an intercepted call a
-    // candidate for being generated at use site.
-    //
-    // Avoid combining a hoisted interceptor back into a loop, and the faster
-    // almost-constant kind of interceptor.
-
-    HInstruction interceptor = node.inputs[0];
-    if (interceptor is HInterceptor &&
-        interceptor.usedBy.length == 1 &&
-        !interceptor.isConditionalConstantInterceptor &&
-        interceptor.hasSameLoopHeaderAs(node)) {
-      // Copy inputs and replace interceptor with `null`.
-      List<HInstruction> inputs = List.of(node.inputs);
-      inputs[0] = _graph.addConstantNull(_closedWorld);
-
-      HOneShotInterceptor oneShot = HOneShotInterceptor(
-          node.selector,
-          node.receiverType,
-          inputs,
-          node.instructionType,
-          node.typeArguments,
-          interceptor.interceptedClasses);
-      oneShot.sourceInformation = node.sourceInformation;
-      oneShot.sourceElement = node.sourceElement;
-      oneShot.sideEffects.setTo(node.sideEffects);
-
-      HBasicBlock block = node.block;
-      block.addAfter(node, oneShot);
-      block.rewrite(node, oneShot);
-      block.remove(node);
-      interceptor.block.remove(interceptor);
-    }
-  }
-
-  @override
-  void visitInvokeSuper(HInvokeSuper node) {
-    if (!node.isInterceptedCall) return;
-    tryReplaceExplicitReceiverForTargetWithDummy(
-        node, node.selector, node.element);
-  }
-
-  @override
-  void visitInvokeGeneratorBody(HInvokeGeneratorBody node) {
-    tryReplaceExplicitReceiverForTargetWithDummy(node, null, node.element);
-  }
-
-  @override
-  void visitOneShotInterceptor(HOneShotInterceptor node) {
-    throw StateError('Should not see HOneShotInterceptor: $node');
-  }
-
-  void tryReplaceExplicitReceiverForTargetWithDummy(
-      HInvoke node, Selector selector, MemberEntity target) {
-    assert(target != null);
-
-    // TODO(15933): Make automatically generated property extraction closures
-    // work with the dummy receiver optimization.
-    if (selector != null && selector.isGetter) return;
-
-    if (usesSelfInterceptor(target)) {
-      _replaceReceiverArgumentWithDummy(node, 1);
-    }
-  }
-
-  void tryReplaceExplicitReceiverForSelectorWithDummy(
-      HInvoke node, Selector selector, AbstractValue mask) {
-    assert(mask != null);
-    // Calls of the form
-    //
-    //     a.foo$1(a, x)
-    //
-    // where the interceptor calling convention is used come from recognizing
-    // that 'a' is a 'self-interceptor'.  If the selector matches only methods
-    // that ignore the explicit receiver parameter, replace occurrences of the
-    // receiver argument with a dummy receiver '0':
-    //
-    //     a.foo$1(a, x)   --->   a.foo$1(0, x)
-    //
-    // This often reduces the number of references to 'a' to one, allowing 'a'
-    // to be generated at use to avoid a temporary, e.g.
-    //
-    //     t1 = b.get$thing();
-    //     t1.foo$1(t1, x)
-    // --->
-    //     b.get$thing().foo$1(0, x)
-    //
-
-    // TODO(15933): Make automatically generated property extraction closures
-    // work with the dummy receiver optimization.
-    if (selector.isGetter) return;
-
-    // This assignment of inputs is uniform for HInvokeDynamic and HInvokeSuper.
-    HInstruction interceptor = node.inputs[0];
-    HInstruction receiverArgument = node.inputs[1];
-
-    // A 'self-interceptor'?
-    if (interceptor.nonCheck() != receiverArgument.nonCheck()) return;
-
-    // TODO(sra): Should this be an assert?
-    if (!_interceptorData.isInterceptedSelector(selector)) return;
-
-    if (!_interceptorData.isInterceptedMixinSelector(
-        selector, mask, _closedWorld)) {
-      _replaceReceiverArgumentWithDummy(node, 1);
-    }
-  }
-
-  void _replaceReceiverArgumentWithDummy(HInvoke node, int receiverIndex) {
-    HInstruction receiverArgument = node.inputs[receiverIndex];
-    ConstantValue constant = DummyInterceptorConstantValue();
-    HConstant dummy = _graph.addConstant(constant, _closedWorld);
-    receiverArgument.usedBy.remove(node);
-    node.inputs[receiverIndex] = dummy;
-    dummy.usedBy.add(node);
-  }
-}
-
-/// A simple Entity to rename the unused receiver to `_` in non-minified code.
-class _RenameToUnderscore implements Entity {
-  const _RenameToUnderscore();
-  @override
-  String get name => '_';
-}
diff --git a/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart b/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart
index c0b6254..968cf4f 100644
--- a/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart
+++ b/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart
@@ -12,16 +12,20 @@
 import 'nodes.dart';
 import 'optimize.dart';
 
-/// This phase computes the set of classes dispatched by an interceptor, and
-/// simplifies interceptors in multiple ways:
+/// 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.
+/// tries to use a constant interceptor instead.
 ///
 /// 2) Interceptors are specialized based on the selector it is used with.
 ///
 /// 3) If we know the object is not intercepted, we just use the object
-///    instead.
+/// instead.
+///
+/// 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.
 ///
 class SsaSimplifyInterceptors extends HBaseVisitor
     implements OptimizationPhase {
diff --git a/pkg/compiler/lib/src/ssa/locals_handler.dart b/pkg/compiler/lib/src/ssa/locals_handler.dart
index 502d335..ea01915 100644
--- a/pkg/compiler/lib/src/ssa/locals_handler.dart
+++ b/pkg/compiler/lib/src/ssa/locals_handler.dart
@@ -288,7 +288,9 @@
         element.isGenerativeConstructor &&
         _nativeData.isNativeOrExtendsNative(cls);
     if (_interceptorData.isInterceptedMethod(element)) {
-      SyntheticLocal parameter = createLocal('receiver');
+      bool isInterceptedClass = _interceptorData.isInterceptedClass(cls);
+      String name = isInterceptedClass ? 'receiver' : '_';
+      SyntheticLocal parameter = createLocal(name);
       HParameterValue value = new HParameterValue(parameter, getTypeOfThis());
       builder.graph.explicitReceiverParameter = value;
       builder.graph.entry.addAfter(directLocals[scopeInfo.thisLocal], value);
@@ -296,7 +298,10 @@
         // If this is the first parameter inserted, make sure it stays first.
         builder.lastAddedParameter = value;
       }
-      directLocals[scopeInfo.thisLocal] = value;
+      if (isInterceptedClass) {
+        // Only use the extra parameter in intercepted classes.
+        directLocals[scopeInfo.thisLocal] = value;
+      }
     } else if (isNativeUpgradeFactory) {
       SyntheticLocal parameter = createLocal('receiver');
       // Unlike `this`, receiver is nullable since direct calls to generative
diff --git a/pkg/compiler/lib/src/ssa/optimize.dart b/pkg/compiler/lib/src/ssa/optimize.dart
index 8a819a1..e0f7369 100644
--- a/pkg/compiler/lib/src/ssa/optimize.dart
+++ b/pkg/compiler/lib/src/ssa/optimize.dart
@@ -34,7 +34,6 @@
 import '../util/util.dart';
 import '../world.dart' show JClosedWorld;
 import 'interceptor_simplifier.dart';
-import 'interceptor_finalizer.dart';
 import 'logging.dart';
 import 'nodes.dart';
 import 'types.dart';
@@ -130,9 +129,9 @@
       ];
       phases.forEach(runPhase);
 
-      // Simplifying interceptors is just an optimization, it is required for
-      // implementation correctness because the code generator assumes it is
-      // always performed to compute the intercepted classes sets.
+      // Simplifying interceptors is not strictly just an optimization, it is
+      // required for implementation correctness because the code generator
+      // assumes it is always performed.
       runPhase(new SsaSimplifyInterceptors(closedWorld, member.enclosingClass));
 
       SsaDeadCodeEliminator dce = new SsaDeadCodeEliminator(closedWorld, this);
@@ -164,13 +163,6 @@
       }
       phases.forEach(runPhase);
     });
-
-    // SsaFinalizeInterceptors must always be run to ensure consistent calling
-    // conventions between SSA-generated code and other code fragments generated
-    // by the emitter.
-    // TODO(sra): Generate these other fragments via SSA, then this phase
-    // becomes an opt-in optimization.
-    runPhase(SsaFinalizeInterceptors(closedWorld));
   }
 }