Revert "Reenable single caller inlining for virtual methods"

This reverts commit 6538572c13ef7a84f76411f5dfd6398d50fd4dd5.

Bug: b/336791970
Change-Id: I7ebe92ec2618da4e2ee15197e984a1386a523c37
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
index 7ee35d8..1f95169 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
@@ -774,10 +774,6 @@
       return resolvedMethod.getReference();
     }
 
-    if (isMonomorphicVirtualMethod(resolvedMethod)) {
-      return resolvedMethod.getReference();
-    }
-
     if (invoke.isInvokeInterface()) {
       assert !isMonomorphicVirtualMethod(resolvedMethod);
       return getVirtualRootMethod(resolvedMethod);
@@ -785,6 +781,10 @@
 
     assert invoke.isInvokeSuper() || invoke.isInvokeVirtual();
 
+    if (isMonomorphicVirtualMethod(resolvedMethod)) {
+      return resolvedMethod.getReference();
+    }
+
     DexMethod rootMethod = getVirtualRootMethod(resolvedMethod);
     assert rootMethod != null;
     assert !isMonomorphicVirtualMethod(resolvedMethod)
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/VirtualRootMethodsAnalysisBase.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/VirtualRootMethodsAnalysisBase.java
index 3343089..b5f1374 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/VirtualRootMethodsAnalysisBase.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/VirtualRootMethodsAnalysisBase.java
@@ -3,8 +3,9 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.optimize.argumentpropagation.codescanner;
 
+import static com.android.tools.r8.utils.MapUtils.ignoreKey;
+
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClassAndMethod;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
@@ -13,13 +14,8 @@
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.collections.DexMethodSignatureMap;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
-import com.google.common.collect.Sets;
-import java.util.ArrayList;
-import java.util.Collections;
 import java.util.IdentityHashMap;
-import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import java.util.function.Consumer;
 
 /**
@@ -31,89 +27,58 @@
   protected static class VirtualRootMethod {
 
     private final VirtualRootMethod parent;
-    private final ProgramMethod method;
-
-    private Set<VirtualRootMethod> overrides = Collections.emptySet();
-    private List<VirtualRootMethod> siblings = Collections.emptyList();
-    private boolean mayDispatchOutsideProgram = false;
+    private final ProgramMethod root;
+    private final ProgramMethodSet overrides = ProgramMethodSet.create();
 
     VirtualRootMethod(ProgramMethod root) {
       this(root, null);
     }
 
-    VirtualRootMethod(ProgramMethod method, VirtualRootMethod parent) {
-      assert method != null;
+    VirtualRootMethod(ProgramMethod root, VirtualRootMethod parent) {
+      assert root != null;
       this.parent = parent;
-      this.method = method;
+      this.root = root;
     }
 
-    void addOverride(VirtualRootMethod override) {
-      assert !override.getMethod().isStructurallyEqualTo(method);
-      assert override.getMethod().getReference().match(method.getReference());
-      if (overrides.isEmpty()) {
-        overrides = Sets.newIdentityHashSet();
-      }
+    void addOverride(ProgramMethod override) {
+      assert override.getDefinition() != root.getDefinition();
+      assert override.getMethodSignature().equals(root.getMethodSignature());
       overrides.add(override);
       if (hasParent()) {
         getParent().addOverride(override);
       }
-      for (VirtualRootMethod sibling : siblings) {
-        sibling.addOverride(override);
-      }
-    }
-
-    void addSibling(VirtualRootMethod sibling) {
-      if (siblings.isEmpty()) {
-        siblings = new ArrayList<>(1);
-      }
-      siblings.add(sibling);
-    }
-
-    void setMayDispatchOutsideProgram() {
-      mayDispatchOutsideProgram = true;
     }
 
     boolean hasParent() {
       return parent != null;
     }
 
-    boolean hasSiblings() {
-      return !siblings.isEmpty();
-    }
-
     VirtualRootMethod getParent() {
       return parent;
     }
 
-    VirtualRootMethod getRoot() {
-      return hasParent() ? getParent().getRoot() : this;
+    ProgramMethod getRoot() {
+      return root;
     }
 
-    ProgramMethod getMethod() {
-      return method;
-    }
-
-    VirtualRootMethod getSingleDispatchTarget() {
-      assert !hasParent();
-      if (isMayDispatchOutsideProgramSet()) {
-        return null;
-      }
-      VirtualRootMethod singleDispatchTarget = isAbstract() ? null : this;
-      for (VirtualRootMethod override : overrides) {
-        if (!override.isAbstract()) {
-          if (singleDispatchTarget != null) {
+    ProgramMethod getSingleNonAbstractMethod() {
+      ProgramMethod singleNonAbstractMethod = root.getAccessFlags().isAbstract() ? null : root;
+      for (ProgramMethod override : overrides) {
+        if (!override.getAccessFlags().isAbstract()) {
+          if (singleNonAbstractMethod != null) {
             // Not a single non-abstract method.
             return null;
           }
-          singleDispatchTarget = override;
+          singleNonAbstractMethod = override;
         }
       }
-      assert singleDispatchTarget == null || !singleDispatchTarget.isAbstract();
-      return singleDispatchTarget;
+      assert singleNonAbstractMethod == null
+          || !singleNonAbstractMethod.getAccessFlags().isAbstract();
+      return singleNonAbstractMethod;
     }
 
-    void forEach(Consumer<VirtualRootMethod> consumer) {
-      consumer.accept(this);
+    void forEach(Consumer<ProgramMethod> consumer) {
+      consumer.accept(root);
       overrides.forEach(consumer);
     }
 
@@ -121,12 +86,10 @@
       return !overrides.isEmpty();
     }
 
-    boolean isAbstract() {
-      return method.getAccessFlags().isAbstract();
-    }
-
-    boolean isMayDispatchOutsideProgramSet() {
-      return mayDispatchOutsideProgram;
+    boolean isInterfaceMethodWithSiblings() {
+      // TODO(b/190154391): Conservatively returns true for all interface methods, but should only
+      //  return true for those with siblings.
+      return root.getHolder().isInterface();
     }
   }
 
@@ -159,48 +122,18 @@
           DexMethodSignatureMap<VirtualRootMethod> virtualRootMethodsForSuperclass =
               virtualRootMethodsPerClass.get(superclass);
           virtualRootMethodsForSuperclass.forEach(
-              (signature, info) -> {
-                virtualRootMethodsForClass.compute(
-                    signature,
-                    (ignore, existing) -> {
-                      if (existing == null || existing == info) {
-                        return info;
-                      } else {
-                        // We iterate the immediate supertypes in-order using
-                        // forEachImmediateProgramSuperClass. Therefore, the current method is
-                        // guaranteed to be an interface method when existing != null.
-                        assert info.getMethod().getHolder().isInterface();
-                        if (!existing.getMethod().getHolder().isInterface()) {
-                          existing.addSibling(info);
-                          info.addOverride(existing);
-                        }
-                        return existing;
-                      }
-                    });
-                if (!clazz.isInterface() && superclass.isInterface()) {
-                  DexClassAndMethod resolvedMethod =
-                      appView.appInfo().resolveMethodOnClass(clazz, signature).getResolutionPair();
-                  if (resolvedMethod != null
-                      && !resolvedMethod.isProgramMethod()
-                      && !resolvedMethod.getAccessFlags().isAbstract()) {
-                    info.setMayDispatchOutsideProgram();
-                  }
-                }
-              });
+              (signature, info) ->
+                  virtualRootMethodsForClass.computeIfAbsent(
+                      signature, ignoreKey(() -> new VirtualRootMethod(info.getRoot(), info))));
         });
     clazz.forEachProgramVirtualMethod(
-        method ->
-            virtualRootMethodsForClass.compute(
-                method,
-                (ignore, parent) -> {
-                  if (parent == null) {
-                    return new VirtualRootMethod(method);
-                  } else {
-                    VirtualRootMethod override = new VirtualRootMethod(method, parent);
-                    parent.addOverride(override);
-                    return override;
-                  }
-                }));
+        method -> {
+          if (virtualRootMethodsForClass.containsKey(method)) {
+            virtualRootMethodsForClass.get(method).getParent().addOverride(method);
+          } else {
+            virtualRootMethodsForClass.put(method, new VirtualRootMethod(method));
+          }
+        });
     return virtualRootMethodsForClass;
   }
 
@@ -214,41 +147,37 @@
           VirtualRootMethod virtualRootMethod =
               virtualRootMethodsForClass.remove(rootCandidate.getMethodSignature());
           acceptVirtualMethod(rootCandidate, virtualRootMethod);
-          if (virtualRootMethod.hasParent()
-              || !rootCandidate.isStructurallyEqualTo(virtualRootMethod.getMethod())) {
+          if (!rootCandidate.isStructurallyEqualTo(virtualRootMethod.getRoot())) {
             return;
           }
-          if (!virtualRootMethod.hasOverrides()
-              && !virtualRootMethod.hasSiblings()
-              && !virtualRootMethod.isMayDispatchOutsideProgramSet()) {
+          boolean isMonomorphicVirtualMethod =
+              !clazz.isInterface() && !virtualRootMethod.hasOverrides();
+          if (isMonomorphicVirtualMethod) {
             monomorphicVirtualRootMethods.add(rootCandidate);
           } else {
-            VirtualRootMethod singleDispatchTarget = virtualRootMethod.getSingleDispatchTarget();
-            if (singleDispatchTarget != null) {
+            ProgramMethod singleNonAbstractMethod = virtualRootMethod.getSingleNonAbstractMethod();
+            if (singleNonAbstractMethod != null
+                && !virtualRootMethod.isInterfaceMethodWithSiblings()) {
               virtualRootMethod.forEach(
-                  method -> setRootMethod(method, virtualRootMethod, singleDispatchTarget));
-              monomorphicVirtualNonRootMethods.add(singleDispatchTarget.getMethod());
+                  method -> {
+                    // Interface methods can have siblings and can therefore not be mapped to their
+                    // unique non-abstract implementation, unless the interface method does not have
+                    // any siblings.
+                    virtualRootMethods.put(
+                        method.getReference(), singleNonAbstractMethod.getReference());
+                  });
+              if (!singleNonAbstractMethod.getHolder().isInterface()) {
+                monomorphicVirtualNonRootMethods.add(singleNonAbstractMethod);
+              }
             } else {
               virtualRootMethod.forEach(
-                  method -> setRootMethod(method, virtualRootMethod, virtualRootMethod));
+                  method ->
+                      virtualRootMethods.put(method.getReference(), rootCandidate.getReference()));
             }
           }
         });
   }
 
-  private void setRootMethod(
-      VirtualRootMethod method, VirtualRootMethod currentRoot, VirtualRootMethod root) {
-    // Since the same method can have multiple roots due to interface methods, we only allow
-    // controlling the virtual root of methods that are rooted at the current root. Otherwise, we
-    // would be setting the virtual root of the same method multiple times, which could lead to
-    // non-determinism in the result (i.e., the `virtualRootMethods` map).
-    if (method.getRoot() == currentRoot) {
-      DexMethod rootReference = root.getMethod().getReference();
-      DexMethod previous = virtualRootMethods.put(method.getMethod().getReference(), rootReference);
-      assert previous == null || previous.isIdenticalTo(rootReference);
-    }
-  }
-
   protected void acceptVirtualMethod(ProgramMethod method, VirtualRootMethod virtualRootMethod) {
     // Intentionally empty.
   }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InterfaceMethodArgumentPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InterfaceMethodArgumentPropagator.java
index 5fcd8db..675bff8 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InterfaceMethodArgumentPropagator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InterfaceMethodArgumentPropagator.java
@@ -108,12 +108,10 @@
             return;
           }
 
-          // If the method state is monomorphic, then this is an interface method with no overrides.
-          // In this case, there is no need to add methodState to interfaceState.
-          if (methodState.isMonomorphic()) {
-            return;
-          }
-
+          // TODO(b/190154391): We should always have an unknown or polymorphic state, but it would
+          //  be better to use a monomorphic state when the interface method is a default method
+          //  with no overrides (CF backend only). In this case, there is no need to add methodState
+          //  to interfaceState.
           assert methodState.isUnknown() || methodState.asConcrete().isPolymorphic();
           interfaceState.addMethodState(appView, method, methodState);
         });
diff --git a/src/main/java/com/android/tools/r8/optimize/singlecaller/SingleCallerInliner.java b/src/main/java/com/android/tools/r8/optimize/singlecaller/SingleCallerInliner.java
index 70b50c9..a6e1da7 100644
--- a/src/main/java/com/android/tools/r8/optimize/singlecaller/SingleCallerInliner.java
+++ b/src/main/java/com/android/tools/r8/optimize/singlecaller/SingleCallerInliner.java
@@ -68,8 +68,8 @@
   }
 
   public void run(ExecutorService executorService) throws ExecutionException {
-    ProgramMethodSet monomorphicVirtualMethods =
-        computeMonomorphicVirtualRootMethods(executorService);
+    // TODO(b/335584013): Re-enable monomorphic method analysis.
+    ProgramMethodSet monomorphicVirtualMethods = ProgramMethodSet.empty();
     ProgramMethodMap<ProgramMethod> singleCallerMethods =
         new SingleCallerScanner(appView, monomorphicVirtualMethods)
             .getSingleCallerMethods(executorService);
diff --git a/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureMap.java b/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureMap.java
index e4872c7..377be48 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureMap.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/DexMethodSignatureMap.java
@@ -8,7 +8,6 @@
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexMethodSignature;
-import com.android.tools.r8.graph.ProgramMethod;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
@@ -144,12 +143,6 @@
     return backing.compute(key, remappingFunction);
   }
 
-  public T compute(
-      ProgramMethod method,
-      BiFunction<? super DexMethodSignature, ? super T, ? extends T> remappingFunction) {
-    return compute(method.getMethodSignature(), remappingFunction);
-  }
-
   @Override
   public T merge(
       DexMethodSignature key,
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/IllegalSingleCallerInliningOfNonAbstractSiblingTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/IllegalSingleCallerInliningOfNonAbstractSiblingTest.java
deleted file mode 100644
index 117b090..0000000
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/IllegalSingleCallerInliningOfNonAbstractSiblingTest.java
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright (c) 2024, the R8 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.
-package com.android.tools.r8.ir.optimize.inliner;
-
-import com.android.tools.r8.NeverClassInline;
-import com.android.tools.r8.NoHorizontalClassMerging;
-import com.android.tools.r8.NoVerticalClassMerging;
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ir.optimize.Inliner.Reason;
-import com.google.common.collect.ImmutableSet;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameter;
-import org.junit.runners.Parameterized.Parameters;
-
-@RunWith(Parameterized.class)
-public class IllegalSingleCallerInliningOfNonAbstractSiblingTest extends TestBase {
-
-  @Parameter(0)
-  public TestParameters parameters;
-
-  @Parameters(name = "{0}")
-  public static TestParametersCollection data() {
-    return getTestParameters().withAllRuntimesAndApiLevels().build();
-  }
-
-  @Test
-  public void test() throws Exception {
-    testForR8(parameters.getBackend())
-        .addInnerClasses(getClass())
-        .addKeepMainRule(Main.class)
-        .addOptionsModification(
-            options -> options.testing.validInliningReasons = ImmutableSet.of(Reason.SINGLE_CALLER))
-        .enableNoHorizontalClassMergingAnnotations()
-        .enableNoVerticalClassMergingAnnotations()
-        .enableNeverClassInliningAnnotations()
-        .setMinApi(parameters)
-        .compile()
-        .run(parameters.getRuntime(), Main.class)
-        .assertSuccessWithOutputLines("A", "A");
-  }
-
-  static class Main {
-
-    public static void main(String[] args) {
-      I i = System.currentTimeMillis() > 0 ? new B() : new C();
-      i.m();
-
-      new A().m();
-    }
-  }
-
-  interface I {
-
-    default void m() {
-      System.out.println("C");
-    }
-  }
-
-  @NeverClassInline
-  @NoHorizontalClassMerging
-  @NoVerticalClassMerging
-  static class A {
-
-    public void m() {
-      System.out.println("A");
-    }
-  }
-
-  static class B extends A implements I {}
-
-  @NoHorizontalClassMerging
-  static class C implements I {}
-}