Avoid computing parameter states when already unknown

Change-Id: I42ad7d6c7b5b498fda257e5a551318be8489d18a
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 b80f03b..de55357 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
@@ -29,8 +29,10 @@
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteArrayTypeParameterState;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteClassTypeParameterState;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMonomorphicMethodState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMonomorphicMethodStateOrBottom;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMonomorphicMethodStateOrUnknown;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcretePolymorphicMethodState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcretePolymorphicMethodStateOrBottom;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcretePrimitiveTypeParameterState;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteReceiverParameterState;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodParameter;
@@ -193,13 +195,20 @@
     methodStates.addTemporaryMethodState(
         appView,
         representativeMethodReference,
-        () -> computeMethodState(invoke, finalResolvedMethod, context, timing),
+        existingMethodState ->
+            computeMethodState(invoke, finalResolvedMethod, context, existingMethodState, timing),
         timing);
     timing.end();
   }
 
   private MethodState computeMethodState(
-      InvokeMethod invoke, ProgramMethod resolvedMethod, ProgramMethod context, Timing timing) {
+      InvokeMethod invoke,
+      ProgramMethod resolvedMethod,
+      ProgramMethod context,
+      MethodState existingMethodState,
+      Timing timing) {
+    assert !existingMethodState.isUnknown();
+
     // If this invoke may target at most one method, then we compute a state that maps each
     // parameter to the abstract value and dynamic type provided by this call site. Otherwise, we
     // compute a polymorphic method state, which includes information about the receiver's dynamic
@@ -207,11 +216,21 @@
     timing.begin("Compute method state for invoke");
     boolean isPolymorphicInvoke =
         getRepresentativeForPolymorphicInvokeOrElse(invoke, resolvedMethod, null) != null;
-    MethodState result =
-        isPolymorphicInvoke
-            ? computePolymorphicMethodState(
-                invoke.asInvokeMethodWithReceiver(), resolvedMethod, context)
-            : computeMonomorphicMethodState(invoke, context);
+    MethodState result;
+    if (isPolymorphicInvoke) {
+      assert existingMethodState.isBottom() || existingMethodState.isPolymorphic();
+      result =
+          computePolymorphicMethodState(
+              invoke.asInvokeMethodWithReceiver(),
+              resolvedMethod,
+              context,
+              existingMethodState.asPolymorphicOrBottom());
+    } else {
+      assert existingMethodState.isBottom() || existingMethodState.isMonomorphic();
+      result =
+          computeMonomorphicMethodState(
+              invoke, context, existingMethodState.asMonomorphicOrBottom());
+    }
     timing.end();
     return result;
   }
@@ -220,40 +239,65 @@
   //  experimenting with the performance/size trade-off between precise/imprecise handling of
   //  dynamic dispatch.
   private MethodState computePolymorphicMethodState(
-      InvokeMethodWithReceiver invoke, ProgramMethod resolvedMethod, ProgramMethod context) {
+      InvokeMethodWithReceiver invoke,
+      ProgramMethod resolvedMethod,
+      ProgramMethod context,
+      ConcretePolymorphicMethodStateOrBottom existingMethodState) {
     DynamicType dynamicReceiverType = invoke.getReceiver().getDynamicType(appView);
     assert !dynamicReceiverType.getDynamicUpperBoundType().nullability().isDefinitelyNull();
 
-    if (invoke.isInvokeSuper()) {
-      ClassTypeElement exactClassType =
-          resolvedMethod.getHolderType().toTypeElement(appView).asClassType();
-      DynamicType exactType = DynamicType.create(appView, exactClassType, exactClassType);
-      return new ConcretePolymorphicMethodState(
-          exactType, computeMonomorphicMethodState(invoke, context));
+    DynamicType bounds =
+        invoke.isInvokeSuper()
+            ? DynamicType.createExact(
+                resolvedMethod.getHolderType().toTypeElement(appView).asClassType())
+            : dynamicReceiverType;
+
+    MethodState existingMethodStateForBounds =
+        existingMethodState.isPolymorphic()
+            ? existingMethodState.asPolymorphic().getMethodStateForBounds(bounds)
+            : MethodState.bottom();
+
+    if (existingMethodStateForBounds.isPolymorphic()) {
+      assert false;
+      return MethodState.unknown();
     }
 
-    ConcretePolymorphicMethodState methodState =
-        new ConcretePolymorphicMethodState(
-            dynamicReceiverType,
-            computeMonomorphicMethodState(invoke, context, dynamicReceiverType));
+    // If we already don't know anything about the parameters for the given type bounds, then don't
+    // compute a method state.
+    if (existingMethodStateForBounds.isUnknown()) {
+      return MethodState.unknown();
+    }
+
     // TODO(b/190154391): If the receiver type is effectively unknown, and the computed monomorphic
     //  method state is also unknown (i.e., we have "unknown receiver type" -> "unknown method
     //  state"), then return the canonicalized UnknownMethodState instance instead.
-    return methodState;
+    return new ConcretePolymorphicMethodState(
+        bounds,
+        computeMonomorphicMethodState(
+            invoke,
+            context,
+            existingMethodStateForBounds.asMonomorphicOrBottom(),
+            dynamicReceiverType));
   }
 
   private ConcreteMonomorphicMethodStateOrUnknown computeMonomorphicMethodState(
-      InvokeMethod invoke, ProgramMethod context) {
+      InvokeMethod invoke,
+      ProgramMethod context,
+      ConcreteMonomorphicMethodStateOrBottom existingMethodState) {
     return computeMonomorphicMethodState(
         invoke,
         context,
+        existingMethodState,
         invoke.isInvokeMethodWithReceiver()
             ? invoke.getFirstArgument().getDynamicType(appView)
             : null);
   }
 
   private ConcreteMonomorphicMethodStateOrUnknown computeMonomorphicMethodState(
-      InvokeMethod invoke, ProgramMethod context, DynamicType dynamicReceiverType) {
+      InvokeMethod invoke,
+      ProgramMethod context,
+      ConcreteMonomorphicMethodStateOrBottom existingMethodState,
+      DynamicType dynamicReceiverType) {
     List<ParameterState> parameterStates = new ArrayList<>(invoke.arguments().size());
 
     int argumentIndex = 0;
@@ -261,14 +305,18 @@
       assert dynamicReceiverType != null;
       parameterStates.add(
           computeParameterStateForReceiver(
-              invoke.asInvokeMethodWithReceiver(), dynamicReceiverType));
+              invoke.asInvokeMethodWithReceiver(), dynamicReceiverType, existingMethodState));
       argumentIndex++;
     }
 
     for (; argumentIndex < invoke.arguments().size(); argumentIndex++) {
       parameterStates.add(
           computeParameterStateForNonReceiver(
-              invoke, argumentIndex, invoke.getArgument(argumentIndex), context));
+              invoke,
+              argumentIndex,
+              invoke.getArgument(argumentIndex),
+              context,
+              existingMethodState));
     }
 
     // If all parameter states are unknown, then return a canonicalized unknown method state that
@@ -285,7 +333,15 @@
   // TODO(b/190154391): Consider validating the above hypothesis by using
   //  computeParameterStateForNonReceiver() for receivers.
   private ParameterState computeParameterStateForReceiver(
-      InvokeMethodWithReceiver invoke, DynamicType dynamicReceiverType) {
+      InvokeMethodWithReceiver invoke,
+      DynamicType dynamicReceiverType,
+      ConcreteMonomorphicMethodStateOrBottom existingMethodState) {
+    // Don't compute a state for this parameter if the stored state is already unknown.
+    if (existingMethodState.isMonomorphic()
+        && existingMethodState.asMonomorphic().getParameterState(0).isUnknown()) {
+      return ParameterState.unknown();
+    }
+
     ClassTypeElement staticReceiverType =
         invoke
             .getInvokedMethod()
@@ -299,7 +355,17 @@
   }
 
   private ParameterState computeParameterStateForNonReceiver(
-      InvokeMethod invoke, int argumentIndex, Value argument, ProgramMethod context) {
+      InvokeMethod invoke,
+      int argumentIndex,
+      Value argument,
+      ProgramMethod context,
+      ConcreteMonomorphicMethodStateOrBottom existingMethodState) {
+    // Don't compute a state for this parameter if the stored state is already unknown.
+    if (existingMethodState.isMonomorphic()
+        && existingMethodState.asMonomorphic().getParameterState(argumentIndex).isUnknown()) {
+      return ParameterState.unknown();
+    }
+
     Value argumentRoot = argument.getAliasedValue(aliasedValueConfiguration);
     TypeElement parameterType =
         invoke
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/BottomMethodState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/BottomMethodState.java
index 2bd69e9..4487eea 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/BottomMethodState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/BottomMethodState.java
@@ -6,9 +6,10 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import java.util.function.Supplier;
+import java.util.function.Function;
 
-public class BottomMethodState extends MethodStateBase {
+public class BottomMethodState extends MethodStateBase
+    implements ConcreteMonomorphicMethodStateOrBottom, ConcretePolymorphicMethodStateOrBottom {
 
   private static final BottomMethodState INSTANCE = new BottomMethodState();
 
@@ -24,6 +25,16 @@
   }
 
   @Override
+  public ConcreteMonomorphicMethodStateOrBottom asMonomorphicOrBottom() {
+    return this;
+  }
+
+  @Override
+  public ConcretePolymorphicMethodStateOrBottom asPolymorphicOrBottom() {
+    return this;
+  }
+
+  @Override
   public MethodState mutableCopy() {
     return this;
   }
@@ -35,7 +46,8 @@
 
   @Override
   public MethodState mutableJoin(
-      AppView<AppInfoWithLiveness> appView, Supplier<MethodState> methodStateSupplier) {
-    return mutableJoin(appView, methodStateSupplier.get());
+      AppView<AppInfoWithLiveness> appView,
+      Function<MethodState, MethodState> methodStateSupplier) {
+    return mutableJoin(appView, methodStateSupplier.apply(this));
   }
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteMethodState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteMethodState.java
index a4d7070..bfb7a89 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteMethodState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteMethodState.java
@@ -6,7 +6,7 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import java.util.function.Supplier;
+import java.util.function.Function;
 
 public abstract class ConcreteMethodState extends MethodStateBase {
 
@@ -33,8 +33,9 @@
 
   @Override
   public MethodState mutableJoin(
-      AppView<AppInfoWithLiveness> appView, Supplier<MethodState> methodStateSupplier) {
-    return mutableJoin(appView, methodStateSupplier.get());
+      AppView<AppInfoWithLiveness> appView,
+      Function<MethodState, MethodState> methodStateSupplier) {
+    return mutableJoin(appView, methodStateSupplier.apply(this));
   }
 
   private MethodState mutableJoin(
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteMonomorphicMethodState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteMonomorphicMethodState.java
index 0998c52..908d97d 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteMonomorphicMethodState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteMonomorphicMethodState.java
@@ -12,7 +12,7 @@
 import java.util.List;
 
 public class ConcreteMonomorphicMethodState extends ConcreteMethodState
-    implements ConcreteMonomorphicMethodStateOrUnknown {
+    implements ConcreteMonomorphicMethodStateOrBottom, ConcreteMonomorphicMethodStateOrUnknown {
 
   List<ParameterState> parameterStates;
 
@@ -73,6 +73,11 @@
     return this;
   }
 
+  @Override
+  public ConcreteMonomorphicMethodStateOrBottom asMonomorphicOrBottom() {
+    return this;
+  }
+
   public void setParameterState(int index, ParameterState parameterState) {
     assert index == 0
         || !parameterState.isConcrete()
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteMonomorphicMethodStateOrBottom.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteMonomorphicMethodStateOrBottom.java
new file mode 100644
index 0000000..9528fd7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteMonomorphicMethodStateOrBottom.java
@@ -0,0 +1,7 @@
+// Copyright (c) 2021, 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.optimize.argumentpropagation.codescanner;
+
+public interface ConcreteMonomorphicMethodStateOrBottom extends MethodState {}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcretePolymorphicMethodState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcretePolymorphicMethodState.java
index 9aa8957..5b3b61e 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcretePolymorphicMethodState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcretePolymorphicMethodState.java
@@ -12,7 +12,8 @@
 import java.util.function.BiConsumer;
 import java.util.function.Function;
 
-public class ConcretePolymorphicMethodState extends ConcreteMethodState {
+public class ConcretePolymorphicMethodState extends ConcreteMethodState
+    implements ConcretePolymorphicMethodStateOrBottom {
 
   private final Map<DynamicType, ConcreteMonomorphicMethodStateOrUnknown> receiverBoundsToState =
       new HashMap<>();
@@ -54,6 +55,15 @@
     receiverBoundsToState.forEach(consumer);
   }
 
+  public MethodState getMethodStateForBounds(DynamicType dynamicType) {
+    ConcreteMonomorphicMethodStateOrUnknown methodStateForBounds =
+        receiverBoundsToState.get(dynamicType);
+    if (methodStateForBounds != null) {
+      return methodStateForBounds;
+    }
+    return MethodState.bottom();
+  }
+
   public boolean isEmpty() {
     return receiverBoundsToState.isEmpty();
   }
@@ -99,4 +109,9 @@
   public ConcretePolymorphicMethodState asPolymorphic() {
     return this;
   }
+
+  @Override
+  public ConcretePolymorphicMethodStateOrBottom asPolymorphicOrBottom() {
+    return this;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcretePolymorphicMethodStateOrBottom.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcretePolymorphicMethodStateOrBottom.java
new file mode 100644
index 0000000..6577c0e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcretePolymorphicMethodStateOrBottom.java
@@ -0,0 +1,7 @@
+// Copyright (c) 2021, 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.optimize.argumentpropagation.codescanner;
+
+public interface ConcretePolymorphicMethodStateOrBottom extends MethodState {}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodState.java
index 15bd5cd..97124a8 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodState.java
@@ -6,7 +6,7 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import java.util.function.Supplier;
+import java.util.function.Function;
 
 public interface MethodState {
 
@@ -28,10 +28,14 @@
 
   ConcreteMonomorphicMethodState asMonomorphic();
 
+  ConcreteMonomorphicMethodStateOrBottom asMonomorphicOrBottom();
+
   boolean isPolymorphic();
 
   ConcretePolymorphicMethodState asPolymorphic();
 
+  ConcretePolymorphicMethodStateOrBottom asPolymorphicOrBottom();
+
   boolean isUnknown();
 
   MethodState mutableCopy();
@@ -39,5 +43,5 @@
   MethodState mutableJoin(AppView<AppInfoWithLiveness> appView, MethodState methodState);
 
   MethodState mutableJoin(
-      AppView<AppInfoWithLiveness> appView, Supplier<MethodState> methodStateSupplier);
+      AppView<AppInfoWithLiveness> appView, Function<MethodState, MethodState> methodStateSupplier);
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodStateBase.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodStateBase.java
index 4bdf867..1b1800d 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodStateBase.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodStateBase.java
@@ -40,6 +40,11 @@
   }
 
   @Override
+  public ConcreteMonomorphicMethodStateOrBottom asMonomorphicOrBottom() {
+    return null;
+  }
+
+  @Override
   public boolean isPolymorphic() {
     return false;
   }
@@ -50,6 +55,11 @@
   }
 
   @Override
+  public ConcretePolymorphicMethodStateOrBottom asPolymorphicOrBottom() {
+    return null;
+  }
+
+  @Override
   public boolean isUnknown() {
     return false;
   }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodStateCollection.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodStateCollection.java
index ea8fcd8..cbc73da 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodStateCollection.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/MethodStateCollection.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.utils.Timing;
 import java.util.Map;
 import java.util.function.BiConsumer;
+import java.util.function.Function;
 import java.util.function.Supplier;
 
 abstract class MethodStateCollection<K> {
@@ -55,13 +56,13 @@
   public void addTemporaryMethodState(
       AppView<AppInfoWithLiveness> appView,
       K method,
-      Supplier<MethodState> methodStateSupplier,
+      Function<MethodState, MethodState> methodStateSupplier,
       Timing timing) {
     methodStates.compute(
         method,
         (ignore, existingMethodState) -> {
           if (existingMethodState == null) {
-            MethodState newMethodState = methodStateSupplier.get();
+            MethodState newMethodState = methodStateSupplier.apply(MethodState.bottom());
             assert !newMethodState.isBottom();
             return newMethodState;
           }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnknownMethodState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnknownMethodState.java
index 0241446..20ab101 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnknownMethodState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/UnknownMethodState.java
@@ -6,7 +6,7 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import java.util.function.Supplier;
+import java.util.function.Function;
 
 // Use this when the nothing is known.
 public class UnknownMethodState extends MethodStateBase
@@ -37,7 +37,8 @@
 
   @Override
   public MethodState mutableJoin(
-      AppView<AppInfoWithLiveness> appView, Supplier<MethodState> methodStateSupplier) {
+      AppView<AppInfoWithLiveness> appView,
+      Function<MethodState, MethodState> methodStateSupplier) {
     return this;
   }
 }