Use sparse map for upper bound type in call site optimization.

Bug: 139246447
Change-Id: I736261991b037caadb495e7c49324b3f83025c14
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableCallSiteOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableCallSiteOptimizationInfo.java
index b43b2fc..c18624e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableCallSiteOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableCallSiteOptimizationInfo.java
@@ -12,8 +12,7 @@
 import com.android.tools.r8.ir.analysis.type.Nullability;
 import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
 import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.utils.StringUtils;
-import java.util.Arrays;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
@@ -31,9 +30,8 @@
 
   private static class ArgumentCollection {
 
-    // TODO(b/139246447): sparse representation, like Int2ReferenceMap?
-    //   E.g., arguments with primitive types won't have any interesting dynamic*type.
-    TypeLatticeElement[] dynamicUpperBoundTypes;
+    private final int size;
+    private final Int2ReferenceArrayMap<TypeLatticeElement> dynamicUpperBoundTypes;
 
     private static final ArgumentCollection BOTTOM = new ArgumentCollection() {
       @Override
@@ -47,28 +45,21 @@
       }
     };
 
-    private ArgumentCollection() {}
+    // Only used to create a canonical BOTTOM.
+    private ArgumentCollection() {
+      this.size = -1;
+      this.dynamicUpperBoundTypes = null;
+    }
 
     ArgumentCollection(int size) {
-      this.dynamicUpperBoundTypes = new TypeLatticeElement[size];
+      this.size = size;
+      this.dynamicUpperBoundTypes = new Int2ReferenceArrayMap<>(size);
     }
 
     TypeLatticeElement getDynamicUpperBoundType(int index) {
       assert dynamicUpperBoundTypes != null;
-      assert 0 <= index && index < dynamicUpperBoundTypes.length;
-      return dynamicUpperBoundTypes[index];
-    }
-
-    ArgumentCollection copy() {
-      ArgumentCollection copy = new ArgumentCollection();
-      copy.dynamicUpperBoundTypes = new TypeLatticeElement[this.dynamicUpperBoundTypes.length];
-      System.arraycopy(
-          this.dynamicUpperBoundTypes,
-          0,
-          copy.dynamicUpperBoundTypes,
-          0,
-          this.dynamicUpperBoundTypes.length);
-      return copy;
+      assert 0 <= index && index < size;
+      return dynamicUpperBoundTypes.getOrDefault(index, null);
     }
 
     ArgumentCollection join(ArgumentCollection other, AppView<?> appView) {
@@ -78,18 +69,21 @@
       if (this == BOTTOM) {
         return other;
       }
-      assert this.dynamicUpperBoundTypes.length == other.dynamicUpperBoundTypes.length;
-      ArgumentCollection result = this.copy();
-      for (int i = 0; i < result.dynamicUpperBoundTypes.length; i++) {
-        if (result.dynamicUpperBoundTypes[i] == null) {
+      assert this.size == other.size;
+      ArgumentCollection result = new ArgumentCollection(this.size);
+      assert result.dynamicUpperBoundTypes != null;
+      for (int i = 0; i < result.size; i++) {
+        TypeLatticeElement thisUpperBoundType = this.getDynamicUpperBoundType(i);
+        if (thisUpperBoundType == null) {
           // This means the corresponding argument is primitive. The counterpart should be too.
-          assert other.dynamicUpperBoundTypes[i] == null;
+          assert other.getDynamicUpperBoundType(i) == null;
           continue;
         }
-        assert result.dynamicUpperBoundTypes[i].isReference();
-        assert other.dynamicUpperBoundTypes[i].isReference();
-        result.dynamicUpperBoundTypes[i] =
-            result.dynamicUpperBoundTypes[i].join(other.dynamicUpperBoundTypes[i], appView);
+        assert thisUpperBoundType.isReference();
+        TypeLatticeElement otherUpperBoundType = other.getDynamicUpperBoundType(i);
+        assert otherUpperBoundType != null && otherUpperBoundType.isReference();
+        result.dynamicUpperBoundTypes.put(
+            i, thisUpperBoundType.join(otherUpperBoundType, appView));
       }
       return result;
     }
@@ -103,17 +97,20 @@
       if (this == BOTTOM || otherCollection == BOTTOM) {
         return this == BOTTOM && otherCollection == BOTTOM;
       }
-      return Arrays.equals(this.dynamicUpperBoundTypes, otherCollection.dynamicUpperBoundTypes);
+      assert this.dynamicUpperBoundTypes != null;
+      return this.dynamicUpperBoundTypes.equals(otherCollection.dynamicUpperBoundTypes);
     }
 
     @Override
     public int hashCode() {
-      return Arrays.hashCode(dynamicUpperBoundTypes);
+      assert this.dynamicUpperBoundTypes != null;
+      return System.identityHashCode(dynamicUpperBoundTypes);
     }
 
     @Override
     public String toString() {
-      return "(" + StringUtils.join(Arrays.asList(dynamicUpperBoundTypes), ", ") + ")";
+      assert this.dynamicUpperBoundTypes != null;
+      return dynamicUpperBoundTypes.toString();
     }
   }
 
@@ -223,7 +220,7 @@
         continue;
       }
       assert arg.getTypeLattice().isReference();
-      newCallSiteInfo.dynamicUpperBoundTypes[i] = arg.getDynamicUpperBoundType(appView);
+      newCallSiteInfo.dynamicUpperBoundTypes.put(i, arg.getDynamicUpperBoundType(appView));
     }
     assert callingContext != null;
     ArgumentCollection accumulatedArgumentCollection =