Widen dynamic types to unknown when they are not useful

Change-Id: I3cc64c92febb26a60f75d09de07b7f13ea1f95b8
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index a165209..77c7af7 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -94,6 +94,7 @@
 import com.android.tools.r8.ir.regalloc.RegisterAllocator;
 import com.android.tools.r8.logging.Log;
 import com.android.tools.r8.naming.IdentifierNameStringMarker;
+import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagator;
 import com.android.tools.r8.position.MethodPosition;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.LibraryMethodOverrideAnalysis;
@@ -835,6 +836,7 @@
     if (options.enableFieldAssignmentTracker) {
       fieldAccessAnalysis.fieldAssignmentTracker().waveDone(wave, delayedOptimizationFeedback);
     }
+    appView.withArgumentPropagator(ArgumentPropagator::publishDelayedReprocessingCriteria);
     if (appView.options().protoShrinking().enableRemoveProtoEnumSwitchMap()) {
       appView.protoShrinker().protoEnumSwitchMapRemover.updateVisibleStaticFieldValues();
     }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
index cabc28f..645bac0 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
@@ -67,8 +67,8 @@
     timing.begin("Argument propagator");
     timing.begin("Initialize code scanner");
 
-    codeScanner = new ArgumentPropagatorCodeScanner(appView);
     reprocessingCriteriaCollection = new ArgumentPropagatorReprocessingCriteriaCollection(appView);
+    codeScanner = new ArgumentPropagatorCodeScanner(appView, reprocessingCriteriaCollection);
 
     ImmediateProgramSubtypingInfo immediateSubtypingInfo =
         ImmediateProgramSubtypingInfo.create(appView);
@@ -110,6 +110,11 @@
     }
   }
 
+  public void publishDelayedReprocessingCriteria() {
+    assert reprocessingCriteriaCollection != null;
+    reprocessingCriteriaCollection.publishDelayedReprocessingCriteria();
+  }
+
   public void transferArgumentInformation(ProgramMethod from, ProgramMethod to) {
     assert codeScanner != null;
     MethodStateCollectionByReference methodStates = codeScanner.getMethodStates();
@@ -125,6 +130,8 @@
       Timing timing)
       throws ExecutionException {
     assert !appView.getSyntheticItems().hasPendingSyntheticClasses();
+    assert reprocessingCriteriaCollection.verifyNoDelayedReprocessingCriteria();
+
     timing.begin("Argument propagator");
 
     // Compute the strongly connected program components for parallel execution.
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 a61d0b4..4d19a71 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
@@ -40,6 +40,9 @@
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ParameterState;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.UnknownMethodState;
+import com.android.tools.r8.optimize.argumentpropagation.reprocessingcriteria.ArgumentPropagatorReprocessingCriteriaCollection;
+import com.android.tools.r8.optimize.argumentpropagation.reprocessingcriteria.MethodReprocessingCriteria;
+import com.android.tools.r8.optimize.argumentpropagation.reprocessingcriteria.ParameterReprocessingCriteria;
 import com.android.tools.r8.optimize.argumentpropagation.utils.WideningUtils;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.Timing;
@@ -68,6 +71,8 @@
 
   private final Set<DexMethod> monomorphicVirtualMethods = Sets.newIdentityHashSet();
 
+  private final ArgumentPropagatorReprocessingCriteriaCollection reprocessingCriteriaCollection;
+
   /**
    * Maps each non-private virtual method to the upper most method in the class hierarchy with the
    * same method signature. Virtual methods that do not override other virtual methods are mapped to
@@ -82,8 +87,11 @@
   private final MethodStateCollectionByReference methodStates =
       MethodStateCollectionByReference.createConcurrent();
 
-  ArgumentPropagatorCodeScanner(AppView<AppInfoWithLiveness> appView) {
+  ArgumentPropagatorCodeScanner(
+      AppView<AppInfoWithLiveness> appView,
+      ArgumentPropagatorReprocessingCriteriaCollection reprocessingCriteriaCollection) {
     this.appView = appView;
+    this.reprocessingCriteriaCollection = reprocessingCriteriaCollection;
   }
 
   public synchronized void addMonomorphicVirtualMethods(Set<DexMethod> extension) {
@@ -259,7 +267,11 @@
       assert existingMethodState.isBottom() || existingMethodState.isMonomorphic();
       result =
           computeMonomorphicMethodState(
-              invoke, resolvedMethod, context, existingMethodState.asMonomorphicOrBottom());
+              invoke,
+              resolvedMethod,
+              invoke.lookupSingleProgramTarget(appView, context),
+              context,
+              existingMethodState.asMonomorphicOrBottom());
     }
     timing.end();
     return result;
@@ -276,9 +288,10 @@
     DynamicType dynamicReceiverType = invoke.getReceiver().getDynamicType(appView);
     assert !dynamicReceiverType.getDynamicUpperBoundType().nullability().isDefinitelyNull();
 
+    ProgramMethod singleTarget = invoke.lookupSingleProgramTarget(appView, context);
     DynamicType bounds =
         computeBoundsForPolymorphicMethodState(
-            invoke, resolvedMethod, context, dynamicReceiverType);
+            invoke, resolvedMethod, singleTarget, context, dynamicReceiverType);
     MethodState existingMethodStateForBounds =
         existingMethodState.isPolymorphic()
             ? existingMethodState.asPolymorphic().getMethodStateForBounds(bounds)
@@ -299,6 +312,7 @@
         computeMonomorphicMethodState(
             invoke,
             resolvedMethod,
+            singleTarget,
             context,
             existingMethodStateForBounds.asMonomorphicOrBottom(),
             dynamicReceiverType);
@@ -308,9 +322,9 @@
   private DynamicType computeBoundsForPolymorphicMethodState(
       InvokeMethodWithReceiver invoke,
       ProgramMethod resolvedMethod,
+      ProgramMethod singleTarget,
       ProgramMethod context,
       DynamicType dynamicReceiverType) {
-    ProgramMethod singleTarget = invoke.lookupSingleProgramTarget(appView, context);
     DynamicType bounds =
         singleTarget != null
             ? DynamicType.createExact(
@@ -343,11 +357,13 @@
   private ConcreteMonomorphicMethodStateOrUnknown computeMonomorphicMethodState(
       InvokeMethod invoke,
       ProgramMethod resolvedMethod,
+      ProgramMethod singleTarget,
       ProgramMethod context,
       ConcreteMonomorphicMethodStateOrBottom existingMethodState) {
     return computeMonomorphicMethodState(
         invoke,
         resolvedMethod,
+        singleTarget,
         context,
         existingMethodState,
         invoke.isInvokeMethodWithReceiver()
@@ -358,11 +374,17 @@
   private ConcreteMonomorphicMethodStateOrUnknown computeMonomorphicMethodState(
       InvokeMethod invoke,
       ProgramMethod resolvedMethod,
+      ProgramMethod singleTarget,
       ProgramMethod context,
       ConcreteMonomorphicMethodStateOrBottom existingMethodState,
       DynamicType dynamicReceiverType) {
     List<ParameterState> parameterStates = new ArrayList<>(invoke.arguments().size());
 
+    MethodReprocessingCriteria methodReprocessingCriteria =
+        singleTarget != null
+            ? reprocessingCriteriaCollection.getReprocessingCriteria(singleTarget)
+            : MethodReprocessingCriteria.alwaysReprocess();
+
     int argumentIndex = 0;
     if (invoke.isInvokeMethodWithReceiver()) {
       assert dynamicReceiverType != null;
@@ -371,7 +393,8 @@
               invoke.asInvokeMethodWithReceiver(),
               resolvedMethod,
               dynamicReceiverType,
-              existingMethodState));
+              existingMethodState,
+              methodReprocessingCriteria.getParameterReprocessingCriteria(0)));
       argumentIndex++;
     }
 
@@ -382,7 +405,8 @@
               argumentIndex,
               invoke.getArgument(argumentIndex),
               context,
-              existingMethodState));
+              existingMethodState,
+              methodReprocessingCriteria.getParameterReprocessingCriteria(argumentIndex)));
     }
 
     // If all parameter states are unknown, then return a canonicalized unknown method state that
@@ -402,13 +426,20 @@
       InvokeMethodWithReceiver invoke,
       ProgramMethod resolvedMethod,
       DynamicType dynamicReceiverType,
-      ConcreteMonomorphicMethodStateOrBottom existingMethodState) {
+      ConcreteMonomorphicMethodStateOrBottom existingMethodState,
+      ParameterReprocessingCriteria parameterReprocessingCriteria) {
     // 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();
     }
 
+    // For receivers we only track the dynamic type. Therefore, if there is no need to track the
+    // dynamic type of the receiver of the targeted method, then just return unknown.
+    if (!parameterReprocessingCriteria.shouldReprocessDueToDynamicType()) {
+      return ParameterState.unknown();
+    }
+
     DynamicType widenedDynamicReceiverType =
         WideningUtils.widenDynamicReceiverType(appView, resolvedMethod, dynamicReceiverType);
     return widenedDynamicReceiverType.isUnknown()
@@ -421,7 +452,8 @@
       int argumentIndex,
       Value argument,
       ProgramMethod context,
-      ConcreteMonomorphicMethodStateOrBottom existingMethodState) {
+      ConcreteMonomorphicMethodStateOrBottom existingMethodState,
+      ParameterReprocessingCriteria parameterReprocessingCriteria) {
     // Don't compute a state for this parameter if the stored state is already unknown.
     if (existingMethodState.isMonomorphic()
         && existingMethodState.asMonomorphic().getParameterState(argumentIndex).isUnknown()) {
@@ -469,6 +501,12 @@
     // then use UnknownParameterState.
     if (parameterTypeElement.isClassType()) {
       DynamicType dynamicType = argument.getDynamicType(appView);
+      if (!parameterReprocessingCriteria.shouldReprocessDueToDynamicType()) {
+        dynamicType =
+            parameterReprocessingCriteria.widenDynamicClassType(
+                appView, dynamicType, parameterTypeElement.asClassType());
+      }
+
       DynamicType widenedDynamicType =
           WideningUtils.widenDynamicNonReceiverType(appView, dynamicType, parameterType);
       return abstractValue.isUnknown() && widenedDynamicType.isUnknown()
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/AlwaysFalseParameterReprocessingCriteria.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/AlwaysFalseParameterReprocessingCriteria.java
index 90d7e4a..fd343f9 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/AlwaysFalseParameterReprocessingCriteria.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/AlwaysFalseParameterReprocessingCriteria.java
@@ -6,6 +6,8 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteParameterState;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 
@@ -53,4 +55,10 @@
   public boolean shouldReprocessDueToNullability() {
     return false;
   }
+
+  @Override
+  public DynamicType widenDynamicClassType(
+      AppView<AppInfoWithLiveness> appView, DynamicType dynamicType, ClassTypeElement staticType) {
+    return null;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ArgumentPropagatorReprocessingCriteriaCollection.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ArgumentPropagatorReprocessingCriteriaCollection.java
index 68191f9..90e9ead 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ArgumentPropagatorReprocessingCriteriaCollection.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ArgumentPropagatorReprocessingCriteriaCollection.java
@@ -27,6 +27,7 @@
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
+import java.util.IdentityHashMap;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -35,6 +36,9 @@
   private final AppView<AppInfoWithLiveness> appView;
 
   private final Map<DexMethod, MethodReprocessingCriteria> reproccessingCriteria =
+      new IdentityHashMap<>();
+
+  private final Map<DexMethod, MethodReprocessingCriteria> delayedReproccessingCriteria =
       new ConcurrentHashMap<>();
 
   public ArgumentPropagatorReprocessingCriteriaCollection(AppView<AppInfoWithLiveness> appView) {
@@ -43,7 +47,12 @@
 
   public MethodReprocessingCriteria getReprocessingCriteria(ProgramMethod method) {
     return reproccessingCriteria.getOrDefault(
-        method.getReference(), MethodReprocessingCriteria.empty());
+        method.getReference(), MethodReprocessingCriteria.alwaysReprocess());
+  }
+
+  public void publishDelayedReprocessingCriteria() {
+    reproccessingCriteria.putAll(delayedReproccessingCriteria);
+    delayedReproccessingCriteria.clear();
   }
 
   /**
@@ -69,7 +78,7 @@
     // optimization info, then record this information. If the map is empty, then the method should
     // always be reprocessed if we find non-trivial optimization info for some of the parameters.
     if (!methodReprocessingCriteria.isEmpty()) {
-      reproccessingCriteria.put(
+      delayedReproccessingCriteria.put(
           method.getReference(), new MethodReprocessingCriteria(methodReprocessingCriteria));
     }
   }
@@ -142,4 +151,9 @@
 
     return builder.build();
   }
+
+  public boolean verifyNoDelayedReprocessingCriteria() {
+    assert delayedReproccessingCriteria.isEmpty();
+    return true;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/MethodReprocessingCriteria.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/MethodReprocessingCriteria.java
index 527ee9e..0afc8ca 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/MethodReprocessingCriteria.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/MethodReprocessingCriteria.java
@@ -15,7 +15,8 @@
 
 public class MethodReprocessingCriteria {
 
-  public static final MethodReprocessingCriteria EMPTY = new MethodReprocessingCriteria();
+  public static final MethodReprocessingCriteria ALWAYS_REPROCESS =
+      new MethodReprocessingCriteria();
 
   private final Int2ReferenceMap<ParameterReprocessingCriteria> reproccesingCriteria;
 
@@ -29,8 +30,8 @@
     this.reproccesingCriteria = reproccesingCriteria;
   }
 
-  public static MethodReprocessingCriteria empty() {
-    return EMPTY;
+  public static MethodReprocessingCriteria alwaysReprocess() {
+    return ALWAYS_REPROCESS;
   }
 
   public ParameterReprocessingCriteria getParameterReprocessingCriteria(int parameterIndex) {
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ParameterReprocessingCriteria.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ParameterReprocessingCriteria.java
index 9022f81..d7c8890 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ParameterReprocessingCriteria.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ParameterReprocessingCriteria.java
@@ -6,6 +6,8 @@
 
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteParameterState;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 
@@ -42,6 +44,14 @@
 
   public abstract boolean shouldReprocessDueToNullability();
 
+  public final DynamicType widenDynamicClassType(
+      AppView<AppInfoWithLiveness> appView, DynamicType dynamicType, ClassTypeElement staticType) {
+    if (dynamicType.getNullability().isMaybeNull()) {
+      return DynamicType.unknown();
+    }
+    return DynamicType.create(appView, staticType.getOrCreateVariant(dynamicType.getNullability()));
+  }
+
   public static class Builder {
 
     private boolean reprocessDueToAbstractValue;