Introduce a collection of optimization info for resolution results

Change-Id: I72a7a7c196e199d77f813ecc2644e48159986cee
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 30bbe20..4a8b427 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -31,6 +31,7 @@
 import com.android.tools.r8.ir.analysis.value.AbstractValueJoiner.AbstractValueParameterJoiner;
 import com.android.tools.r8.ir.desugar.TypeRewriter;
 import com.android.tools.r8.ir.optimize.enums.EnumDataMap;
+import com.android.tools.r8.ir.optimize.info.MethodResolutionOptimizationInfoCollection;
 import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoFactory;
 import com.android.tools.r8.ir.optimize.library.LibraryMemberOptimizer;
 import com.android.tools.r8.ir.optimize.library.LibraryMethodSideEffectModelCollection;
@@ -95,6 +96,8 @@
   private NamingLens namingLens = NamingLens.getIdentityLens();
   private ProguardCompatibilityActions proguardCompatibilityActions;
   private RootSet rootSet;
+  private MethodResolutionOptimizationInfoCollection methodResolutionOptimizationInfoCollection =
+      MethodResolutionOptimizationInfoCollection.empty();
   private MainDexRootSet mainDexRootSet = null;
   private StartupProfile startupProfile;
 
@@ -340,6 +343,16 @@
     return abstractValueParameterJoiner;
   }
 
+  public MethodResolutionOptimizationInfoCollection
+      getMethodResolutionOptimizationInfoCollection() {
+    return methodResolutionOptimizationInfoCollection;
+  }
+
+  public void setMethodResolutionOptimizationInfoCollection(
+      MethodResolutionOptimizationInfoCollection getMethodResolutionOptimizationInfoCollection) {
+    this.methodResolutionOptimizationInfoCollection = getMethodResolutionOptimizationInfoCollection;
+  }
+
   public InstanceFieldInitializationInfoFactory instanceFieldInitializationInfoFactory() {
     return instanceFieldInitializationInfoFactory;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java b/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
index d7362d6..428c57d 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
@@ -8,7 +8,11 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.LookupResult.LookupResultSuccess;
 import com.android.tools.r8.graph.LookupResult.LookupResultSuccess.LookupResultCollectionState;
+import com.android.tools.r8.ir.code.InvokeMethod;
 import com.android.tools.r8.ir.desugar.LambdaDescriptor;
+import com.android.tools.r8.ir.optimize.info.DefaultMethodOptimizationInfo;
+import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
+import com.android.tools.r8.ir.optimize.info.MethodResolutionOptimizationInfoCollection;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.InstantiatedObject;
 import com.android.tools.r8.utils.BooleanBox;
@@ -283,6 +287,19 @@
     public abstract SingleResolutionResult<T> withInitialResolutionHolder(
         DexClass newInitialResolutionHolder);
 
+    public MethodOptimizationInfo getOptimizationInfo(
+        AppView<?> appView, InvokeMethod invoke, DexClassAndMethod singleTarget) {
+      if (singleTarget != null) {
+        return singleTarget.getOptimizationInfo();
+      }
+      if (invoke.isInvokeMethodWithDynamicDispatch() && resolvedMethod.belongsToVirtualPool()) {
+        MethodResolutionOptimizationInfoCollection methodResolutionOptimizationInfoCollection =
+            appView.getMethodResolutionOptimizationInfoCollection();
+        return methodResolutionOptimizationInfoCollection.get(resolvedMethod, resolvedHolder);
+      }
+      return DefaultMethodOptimizationInfo.getInstance();
+    }
+
     @Override
     public DexClass getInitialResolutionHolder() {
       return initialResolutionHolder;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java b/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
index 0680915..21abec1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/AssumeInserter.java
@@ -250,12 +250,13 @@
       }
     }
 
-    if (singleTarget == null) {
+    MethodOptimizationInfo optimizationInfo =
+        resolutionResult.getOptimizationInfo(appView, invoke, singleTarget);
+    if (optimizationInfo.isDefault()) {
       return false;
     }
 
     boolean needsAssumeInstruction = false;
-    MethodOptimizationInfo optimizationInfo = singleTarget.getDefinition().getOptimizationInfo();
 
     // Case (2), invocations that are guaranteed to return a non-null value.
     if (invoke.hasUsedOutValue()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
index f596d2a..ea5f65d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
@@ -33,7 +33,6 @@
   static final int UNKNOWN_RETURNED_ARGUMENT = -1;
   static final boolean UNKNOWN_NEVER_RETURNS_NORMALLY = false;
   static final AbstractValue UNKNOWN_ABSTRACT_RETURN_VALUE = UnknownValue.getInstance();
-  static final boolean UNKNOWN_TRIGGERS_CLASS_INIT_BEFORE_ANY_SIDE_EFFECT = false;
   static final boolean UNKNOWN_INITIALIZER_ENABLING_JAVA_ASSERTIONS = false;
   static final boolean UNKNOWN_MAY_HAVE_SIDE_EFFECTS = true;
   static final boolean UNKNOWN_RETURN_VALUE_ONLY_DEPENDS_ON_ARGUMENTS = false;
@@ -47,6 +46,11 @@
   }
 
   @Override
+  public boolean isDefault() {
+    return true;
+  }
+
+  @Override
   public boolean cannotBeKept() {
     return false;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
index 31775ae..6a2bc86 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
@@ -28,6 +28,10 @@
     Default
   }
 
+  public boolean isDefault() {
+    return false;
+  }
+
   public abstract boolean cannotBeKept();
 
   public abstract boolean classInitializerMayBePostponed();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfoCollection.java
new file mode 100644
index 0000000..6c2c13b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodResolutionOptimizationInfoCollection.java
@@ -0,0 +1,43 @@
+// Copyright (c) 2023, 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.info;
+
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexReference;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * Collection of optimization info for virtual methods with dynamic dispatch.
+ *
+ * <p>When a call to a virtual method does not have a single target, we cannot use the optimization
+ * info we compute for each method. Given the resolved method, this collection returns a piece of
+ * optimization info that is true for all possible dispatch targets (i.e., the join of the
+ * optimization of all possible dispatch targets).
+ */
+public class MethodResolutionOptimizationInfoCollection {
+
+  private static final MethodResolutionOptimizationInfoCollection EMPTY =
+      new MethodResolutionOptimizationInfoCollection(Collections.emptyMap());
+
+  private final Map<DexReference, MethodOptimizationInfo> backing;
+
+  MethodResolutionOptimizationInfoCollection(Map<DexReference, MethodOptimizationInfo> backing) {
+    this.backing = backing;
+  }
+
+  public static MethodResolutionOptimizationInfoCollection empty() {
+    return EMPTY;
+  }
+
+  public MethodOptimizationInfo get(DexEncodedMethod method, DexClass holder) {
+    MethodOptimizationInfo defaultValue = DefaultMethodOptimizationInfo.getInstance();
+    if (!holder.isProgramClass()) {
+      return defaultValue;
+    }
+    return backing.getOrDefault(method.getReference(), defaultValue);
+  }
+}