Revert "Group synthetics based on their ordering."

Bug: b/237413146

This reverts commit 937e6cae37e669cda58ead84481ead793744dbff.

Reason for revert: breaks build

Change-Id: Id628fa68647335469a72ce9fda640257a6067ec8
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
index 7020d21..527cec3 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -35,7 +35,6 @@
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.IterableUtils;
 import com.android.tools.r8.utils.ListUtils;
-import com.android.tools.r8.utils.OptionalBool;
 import com.android.tools.r8.utils.SetUtils;
 import com.android.tools.r8.utils.Timing;
 import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeHashMap;
@@ -54,7 +53,6 @@
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.IdentityHashMap;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -594,12 +592,14 @@
           List<EquivalenceGroup<T>> groups =
               groupEquivalent(appView, members, intermediate, classToFeatureSplitMap);
           for (EquivalenceGroup<T> group : groups) {
-            // If the group has a pinned representative don't construct an external type.
-            if (group.isPinned(appView)) {
+            // If the group already has a representative, then this representative is pinned.
+            // Otherwise, we select a deterministic representative.
+            if (group.hasRepresentative()) {
               EquivalenceGroup<T> previous =
                   equivalences.put(group.getRepresentative().getHolder().getType(), group);
               assert previous == null;
             } else {
+              group.selectDeterministicRepresentative();
               groupsPerPrefix
                   .computeIfAbsent(
                       group.getRepresentative().getPrefixForExternalSyntheticType(),
@@ -666,93 +666,52 @@
       List<T> potentialEquivalence,
       boolean intermediate,
       ClassToFeatureSplitMap classToFeatureSplitMap) {
-    // Fast path the singleton groups.
-    if (potentialEquivalence.size() == 1) {
-      return ImmutableList.of(EquivalenceGroup.singleton(potentialEquivalence.get(0)));
-    }
-    assert !potentialEquivalence.isEmpty();
-
-    // Sort the potential members and split them up into potential groups of members that are
-    // actually equal.
-    GraphLens graphLens = appView.graphLens();
-    boolean includeContext =
-        intermediate || appView.options().getStartupOptions().isStartupInstrumentationEnabled();
-    List<T> sortedPotentialMembers =
-        ListUtils.sort(
-            potentialEquivalence,
-            (a, b) -> a.compareTo(b, includeContext, graphLens, classToFeatureSplitMap));
-    List<List<T>> potentialGroups = new ArrayList<>();
-    {
-      List<T> currentGroup = new ArrayList<>();
-      T currentRepresentative = sortedPotentialMembers.get(0);
-      for (int i = 1; i < sortedPotentialMembers.size(); i++) {
-        T member = sortedPotentialMembers.get(i);
-        if (!currentRepresentative.isEquivalentTo(
-            member, includeContext, graphLens, classToFeatureSplitMap)) {
-          potentialGroups.add(currentGroup);
-          currentGroup = new ArrayList<>();
-          currentRepresentative = member;
-        }
-        currentGroup.add(member);
+    List<EquivalenceGroup<T>> groups = new ArrayList<>();
+    // Each other member is in a shared group if it is actually equivalent to the first member.
+    for (T synthetic : potentialEquivalence) {
+      if (groups.size() > GROUP_COUNT_THRESHOLD) {
+        return ListUtils.map(
+            potentialEquivalence, m -> new EquivalenceGroup<>(m, isPinned(appView, m)));
       }
-      potentialGroups.add(currentGroup);
-    }
-
-    // Compute the actual groups by picking the group representatives. In cases of pinned members
-    // this may need to split out representatives into their own singleton groups.
-    List<EquivalenceGroup<T>> actualGroups = new ArrayList<>();
-    for (List<T> potentialGroup : potentialGroups) {
-      assert !potentialGroup.isEmpty();
-      if (potentialGroup.size() == 1) {
-        actualGroups.add(EquivalenceGroup.singleton(potentialGroup.get(0)));
-        continue;
-      }
-      List<T> forcedRepresentatives = null;
-      if (appView.enableWholeProgramOptimizations()) {
-        Iterator<T> it = potentialGroup.iterator();
-        while (it.hasNext()) {
-          T member = it.next();
-          boolean mustBeRepresentative = isPinned(appView, member);
-          if (mustBeRepresentative) {
-            if (forcedRepresentatives == null) {
-              forcedRepresentatives = new ArrayList<>();
+      boolean mustBeRepresentative = isPinned(appView, synthetic);
+      EquivalenceGroup<T> equivalenceGroup = null;
+      for (EquivalenceGroup<T> group : groups) {
+        boolean includeContext =
+            intermediate || appView.options().getStartupOptions().isStartupInstrumentationEnabled();
+        if (synthetic.isEquivalentTo(
+            group.hasRepresentative()
+                ? group.getRepresentative()
+                : group.getFirstNonRepresentativeMember(),
+            includeContext,
+            appView.graphLens(),
+            classToFeatureSplitMap)) {
+          if (mustBeRepresentative && group.hasRepresentative()) {
+            // Check if the current synthetic is smaller than the group's representative. If so,
+            // then replace the representative, to ensure deterministic groups, and create a new
+            // singleton group containing the old representative. Otherwise, just add a singleton
+            // group containing the new synthetic.
+            T representative = group.getRepresentative();
+            if (representative
+                    .toReference()
+                    .getReference()
+                    .compareTo(synthetic.toReference().getReference())
+                > 0) {
+              group.replaceAndRemoveRepresentative(synthetic);
+              synthetic = representative;
             }
-            forcedRepresentatives.add(member);
-            it.remove();
+          } else {
+            equivalenceGroup = group;
           }
+          break;
         }
       }
-      if (forcedRepresentatives != null) {
-        assert appView.enableWholeProgramOptimizations();
-        T representative =
-            findSmallestMember(
-                forcedRepresentatives,
-                other -> actualGroups.add(EquivalenceGroup.pinnedSingleton(other)));
-        actualGroups.add(EquivalenceGroup.pinnedGroup(representative, potentialGroup));
+      if (equivalenceGroup != null) {
+        equivalenceGroup.add(synthetic, mustBeRepresentative);
       } else {
-        List<T> members = new ArrayList<>(potentialGroup.size() - 1);
-        T representative = findSmallestMember(potentialGroup, members::add);
-        actualGroups.add(EquivalenceGroup.unpinnedGroup(representative, members));
+        groups.add(new EquivalenceGroup<>(synthetic, mustBeRepresentative));
       }
     }
-    return actualGroups;
-  }
-
-  private static <T extends SyntheticDefinition<?, T, ?>> T findSmallestMember(
-      List<T> members, Consumer<T> notSmallestCallback) {
-    assert !members.isEmpty();
-    T smallest = members.get(0);
-    for (int i = 1; i < members.size(); i++) {
-      T member = members.get(i);
-      if (member.toReference().getReference().compareTo(smallest.toReference().getReference())
-          < 0) {
-        notSmallestCallback.accept(smallest);
-        smallest = member;
-      } else {
-        notSmallestCallback.accept(member);
-      }
-    }
-    return smallest;
+    return groups;
   }
 
   /**
@@ -887,47 +846,28 @@
   private static class EquivalenceGroup<T extends SyntheticDefinition<?, T, ?>> {
 
     // The members of the equivalence group, *excluding* the representative.
-    private final List<T> members;
-    private final T representative;
-    private final OptionalBool pinned;
+    private List<T> members = new ArrayList<>();
+    private T representative;
 
-    static <T extends SyntheticDefinition<?, T, ?>> EquivalenceGroup<T> singleton(T member) {
-      return new EquivalenceGroup<>(member, ImmutableList.of(), OptionalBool.UNKNOWN);
+    EquivalenceGroup(T member, boolean isRepresentative) {
+      add(member, isRepresentative);
     }
 
-    static <T extends SyntheticDefinition<?, T, ?>> EquivalenceGroup<T> unpinnedGroup(
-        T representative, List<T> members) {
-      return new EquivalenceGroup<>(
-          representative, Collections.unmodifiableList(members), OptionalBool.FALSE);
-    }
-
-    static <T extends SyntheticDefinition<?, T, ?>> EquivalenceGroup<T> pinnedSingleton(T member) {
-      return new EquivalenceGroup<>(member, ImmutableList.of(), OptionalBool.TRUE);
-    }
-
-    static <T extends SyntheticDefinition<?, T, ?>> EquivalenceGroup<T> pinnedGroup(
-        T representative, List<T> members) {
-      return new EquivalenceGroup<>(
-          representative, Collections.unmodifiableList(members), OptionalBool.TRUE);
-    }
-
-    private EquivalenceGroup(T representative, List<T> members, OptionalBool pinned) {
-      assert representative != null;
-      assert members != null;
-      assert pinned != null;
-      this.members = members;
-      this.representative = representative;
-      this.pinned = pinned;
-    }
-
-    public boolean isPinned(AppView<?> appView) {
-      if (pinned.isTrue()) {
-        return true;
+    void add(T member, boolean isRepresentative) {
+      if (isRepresentative) {
+        assert !hasRepresentative();
+        representative = member;
+      } else {
+        members.add(member);
       }
-      if (pinned.isFalse()) {
-        return false;
-      }
-      return SyntheticFinalization.isPinned(appView, representative);
+    }
+
+    int compareToIncludingContext(
+        EquivalenceGroup<T> other,
+        GraphLens graphLens,
+        ClassToFeatureSplitMap classToFeatureSplitMap) {
+      return getRepresentative()
+          .compareTo(other.getRepresentative(), true, graphLens, classToFeatureSplitMap);
     }
 
     public void forEach(Consumer<T> consumer) {
@@ -939,23 +879,64 @@
       members.forEach(consumer);
     }
 
+    T getFirstNonRepresentativeMember() {
+      assert !members.isEmpty();
+      return members.get(0);
+    }
+
     T getRepresentative() {
+      assert hasRepresentative();
       return representative;
     }
 
+    boolean hasRepresentative() {
+      return representative != null;
+    }
+
     boolean isDerivedFromMainDexList(MainDexInfo mainDexInfo) {
       return getRepresentative().getContext().isDerivedFromMainDexList(mainDexInfo)
           || Iterables.any(
               members, member -> member.getContext().isDerivedFromMainDexList(mainDexInfo));
     }
 
+    void replaceAndRemoveRepresentative(T representative) {
+      assert hasRepresentative();
+      this.representative = representative;
+    }
+
+    void selectDeterministicRepresentative() {
+      // Pick a deterministic member as representative.
+      assert !hasRepresentative();
+      int representativeIndex = 0;
+      for (int i = 1; i < members.size(); i++) {
+        T next = members.get(i);
+        T representative = members.get(representativeIndex);
+        if (next.toReference().getReference().compareTo(representative.toReference().getReference())
+            < 0) {
+          representativeIndex = i;
+        }
+      }
+      T representative = members.get(representativeIndex);
+      members.set(representativeIndex, ListUtils.last(members));
+      ListUtils.removeLast(members);
+      setRepresentative(representative);
+    }
+
+    void setRepresentative(T representative) {
+      assert !hasRepresentative();
+      this.representative = representative;
+    }
+
     @Override
     public String toString() {
-      return "EquivalenceGroup{ size = "
-          + (members.size() + 1)
-          + ", repr = "
-          + getRepresentative()
-          + " }";
+      if (hasRepresentative()) {
+        return "EquivalenceGroup{ size = "
+            + (members.size() + 1)
+            + ", repr = "
+            + getRepresentative()
+            + " }";
+      }
+      return "EquivalenceGroup{ size = " + members.size() + " }";
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/ListUtils.java b/src/main/java/com/android/tools/r8/utils/ListUtils.java
index 406d482..9c6e2a7 100644
--- a/src/main/java/com/android/tools/r8/utils/ListUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ListUtils.java
@@ -268,26 +268,12 @@
     void accept(T item, int index);
   }
 
-  public static <T> List<T> sort(List<T> items, Comparator<T> comparator) {
-    List<T> sorted = new ArrayList<>(items);
-    sorted.sort(comparator);
-    return sorted;
-  }
-
   public static <T> void destructiveSort(List<T> items, Comparator<T> comparator) {
     items.sort(comparator);
   }
 
   // Utility to add a slow verification of a comparator as part of sorting. Note that this
   // should not generally be used in asserts unless the quadratic behavior can be tolerated.
-  public static <T> List<T> sortAndVerify(List<T> items, Comparator<T> comparator) {
-    List<T> sorted = sort(items, comparator);
-    assert verifyComparatorOnSortedList(sorted, comparator);
-    return sorted;
-  }
-
-  // Utility to add a slow verification of a comparator as part of sorting. Note that this
-  // should not generally be used in asserts unless the quadratic behavior can be tolerated.
   public static <T> void destructiveSortAndVerify(List<T> items, Comparator<T> comparator) {
     destructiveSort(items, comparator);
     assert verifyComparatorOnSortedList(items, comparator);