Use monomorphic state for abstract methods with single implementation

Change-Id: If284f39a90d1577f8def96f7f54c7919af64cdfd
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 8fe8d8c..a61d0b4 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
@@ -121,11 +121,15 @@
   }
 
   boolean isMonomorphicVirtualMethod(ProgramMethod method) {
-    boolean isMonomorphicVirtualMethod = monomorphicVirtualMethods.contains(method.getReference());
+    boolean isMonomorphicVirtualMethod = isMonomorphicVirtualMethod(method.getReference());
     assert method.getDefinition().belongsToVirtualPool() || !isMonomorphicVirtualMethod;
     return isMonomorphicVirtualMethod;
   }
 
+  boolean isMonomorphicVirtualMethod(DexMethod method) {
+    return monomorphicVirtualMethods.contains(method);
+  }
+
   void scan(ProgramMethod method, IRCode code, Timing timing) {
     timing.begin("Argument propagation scanner");
     for (Invoke invoke : code.<Invoke>instructions(Instruction::isInvoke)) {
@@ -218,14 +222,11 @@
     // possible dispatch targets and propagate the information to these methods (this is expensive).
     // Instead we record the information in one place and then later propagate the information to
     // all dispatch targets.
-    DexMethod representativeMethodReference =
-        getRepresentativeForPolymorphicInvokeOrElse(
-            invoke, resolvedMethod, resolvedMethod.getReference());
     ProgramMethod finalResolvedMethod = resolvedMethod;
     timing.begin("Add method state");
     methodStates.addTemporaryMethodState(
         appView,
-        representativeMethodReference,
+        getRepresentative(invoke, resolvedMethod),
         existingMethodState ->
             computeMethodState(invoke, finalResolvedMethod, context, existingMethodState, timing),
         timing);
@@ -245,10 +246,8 @@
     // compute a polymorphic method state, which includes information about the receiver's dynamic
     // type bounds.
     timing.begin("Compute method state for invoke");
-    boolean isPolymorphicInvoke =
-        getRepresentativeForPolymorphicInvokeOrElse(invoke, resolvedMethod, null) != null;
     MethodState result;
-    if (isPolymorphicInvoke) {
+    if (shouldUsePolymorphicMethodState(invoke, resolvedMethod)) {
       assert existingMethodState.isBottom() || existingMethodState.isPolymorphic();
       result =
           computePolymorphicMethodState(
@@ -485,10 +484,9 @@
         : new ConcretePrimitiveTypeParameterState(abstractValue);
   }
 
-  private DexMethod getRepresentativeForPolymorphicInvokeOrElse(
-      InvokeMethod invoke, ProgramMethod resolvedMethod, DexMethod defaultValue) {
+  private DexMethod getRepresentative(InvokeMethod invoke, ProgramMethod resolvedMethod) {
     if (resolvedMethod.getDefinition().belongsToDirectPool()) {
-      return defaultValue;
+      return resolvedMethod.getReference();
     }
 
     if (invoke.isInvokeInterface()) {
@@ -499,14 +497,22 @@
     assert invoke.isInvokeSuper() || invoke.isInvokeVirtual();
 
     if (isMonomorphicVirtualMethod(resolvedMethod)) {
-      return defaultValue;
+      return resolvedMethod.getReference();
     }
 
     DexMethod rootMethod = getVirtualRootMethod(resolvedMethod);
     assert rootMethod != null;
+    assert !isMonomorphicVirtualMethod(resolvedMethod)
+        || rootMethod == resolvedMethod.getReference();
     return rootMethod;
   }
 
+  private boolean shouldUsePolymorphicMethodState(
+      InvokeMethod invoke, ProgramMethod resolvedMethod) {
+    return !resolvedMethod.getDefinition().belongsToDirectPool()
+        && !isMonomorphicVirtualMethod(getRepresentative(invoke, resolvedMethod));
+  }
+
   private void scan(InvokeCustom invoke, ProgramMethod context) {
     // If the bootstrap method is program declared it will be called. The call is with runtime
     // provided arguments so ensure that the argument information is unknown.
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/VirtualRootMethodsAnalysis.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/VirtualRootMethodsAnalysis.java
index 5d5399d..75bcbba 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/VirtualRootMethodsAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/VirtualRootMethodsAnalysis.java
@@ -45,6 +45,22 @@
       return root;
     }
 
+    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;
+          }
+          singleNonAbstractMethod = override;
+        }
+      }
+      assert singleNonAbstractMethod == null
+          || !singleNonAbstractMethod.getAccessFlags().isAbstract();
+      return singleNonAbstractMethod;
+    }
+
     void forEach(Consumer<ProgramMethod> consumer) {
       consumer.accept(root);
       overrides.forEach(consumer);
@@ -125,9 +141,20 @@
           if (isMonomorphicVirtualMethod) {
             monomorphicVirtualMethods.add(rootCandidate.getReference());
           } else {
-            virtualRootMethod.forEach(
-                method ->
-                    virtualRootMethods.put(method.getReference(), rootCandidate.getReference()));
+            ProgramMethod singleNonAbstractMethod = virtualRootMethod.getSingleNonAbstractMethod();
+            if (singleNonAbstractMethod != null) {
+              virtualRootMethod.forEach(
+                  method ->
+                      virtualRootMethods.put(
+                          method.getReference(), singleNonAbstractMethod.getReference()));
+              if (!singleNonAbstractMethod.getHolder().isInterface()) {
+                monomorphicVirtualMethods.add(singleNonAbstractMethod.getReference());
+              }
+            } else {
+              virtualRootMethod.forEach(
+                  method ->
+                      virtualRootMethods.put(method.getReference(), rootCandidate.getReference()));
+            }
           }
         });
   }