Revert "Enable interface merging in second round of class merging"

This reverts commit 86a5ef0da87c5952e08c7e5e3afaa0f51a729dd0.

Reason for revert: Breaks compose sample app

Change-Id: I0fe46f1ba82c6d0c3f6cd1e33df5a1f109c7c501
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 4ef90be..bf21997 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -28,6 +28,7 @@
 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;
+import com.android.tools.r8.optimize.MemberRebindingLens;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.KeepInfoCollection;
 import com.android.tools.r8.shaking.LibraryModeledPredicate;
@@ -687,26 +688,13 @@
     }
 
     // Insert a member rebinding lens above the first unapplied lens.
-    // TODO(b/182129249): Once the member rebinding phase has been removed, the MemberRebindingLens
-    //  should be removed and all uses of FieldRebindingIdentityLens should be replaced by
-    //  MemberRebindingIdentityLens.
-    NonIdentityGraphLens appliedMemberRebindingLens =
-        firstUnappliedLens.findPrevious(
-            previous ->
-                previous.isMemberRebindingLens() || previous.isMemberRebindingIdentityLens());
-    GraphLens newMemberRebindingLens;
-    if (appliedMemberRebindingLens != null) {
-      newMemberRebindingLens =
-          appliedMemberRebindingLens.isMemberRebindingLens()
-              ? appliedMemberRebindingLens
-                  .asMemberRebindingLens()
-                  .toRewrittenFieldRebindingLens(appView, appliedLens)
-              : appliedMemberRebindingLens
-                  .asMemberRebindingIdentityLens()
-                  .toRewrittenMemberRebindingIdentityLens(appView, appliedLens);
-    } else {
-      newMemberRebindingLens = GraphLens.getIdentityLens();
-    }
+    MemberRebindingLens appliedMemberRebindingLens =
+        firstUnappliedLens.findPrevious(GraphLens::isMemberRebindingLens);
+    GraphLens newMemberRebindingLens =
+        appliedMemberRebindingLens != null
+            ? appliedMemberRebindingLens.toRewrittenFieldRebindingLens(
+                appView.dexItemFactory(), appliedLens)
+            : GraphLens.getIdentityLens();
 
     firstUnappliedLens.withAlternativeParentLens(
         newMemberRebindingLens,
diff --git a/src/main/java/com/android/tools/r8/graph/BottomUpClassHierarchyTraversal.java b/src/main/java/com/android/tools/r8/graph/BottomUpClassHierarchyTraversal.java
index 685188e..1c7bf88 100644
--- a/src/main/java/com/android/tools/r8/graph/BottomUpClassHierarchyTraversal.java
+++ b/src/main/java/com/android/tools/r8/graph/BottomUpClassHierarchyTraversal.java
@@ -4,19 +4,17 @@
 
 package com.android.tools.r8.graph;
 
-import java.util.function.Function;
-
 public class BottomUpClassHierarchyTraversal<T extends DexClass>
     extends ClassHierarchyTraversal<T, BottomUpClassHierarchyTraversal<T>> {
 
-  private final Function<DexType, Iterable<DexType>> immediateSubtypesProvider;
+  private final SubtypingInfo subtypingInfo;
 
   private BottomUpClassHierarchyTraversal(
       AppView<? extends AppInfoWithClassHierarchy> appView,
-      Function<DexType, Iterable<DexType>> immediateSubtypesProvider,
+      SubtypingInfo subtypingInfo,
       Scope scope) {
     super(appView, scope);
-    this.immediateSubtypesProvider = immediateSubtypesProvider;
+    this.subtypingInfo = subtypingInfo;
   }
 
   /**
@@ -25,8 +23,7 @@
    */
   public static BottomUpClassHierarchyTraversal<DexClass> forAllClasses(
       AppView<? extends AppInfoWithClassHierarchy> appView, SubtypingInfo subtypingInfo) {
-    return new BottomUpClassHierarchyTraversal<>(
-        appView, subtypingInfo::allImmediateSubtypes, Scope.ALL_CLASSES);
+    return new BottomUpClassHierarchyTraversal<>(appView, subtypingInfo, Scope.ALL_CLASSES);
   }
 
   /**
@@ -35,18 +32,8 @@
    */
   public static BottomUpClassHierarchyTraversal<DexProgramClass> forProgramClasses(
       AppView<? extends AppInfoWithClassHierarchy> appView, SubtypingInfo subtypingInfo) {
-    return forProgramClasses(appView, subtypingInfo::allImmediateSubtypes);
-  }
-
-  /**
-   * Returns a visitor that can be used to visit all the program classes that are reachable from a
-   * given set of sources.
-   */
-  public static BottomUpClassHierarchyTraversal<DexProgramClass> forProgramClasses(
-      AppView<? extends AppInfoWithClassHierarchy> appView,
-      Function<DexType, Iterable<DexType>> immediateSubtypesProvider) {
     return new BottomUpClassHierarchyTraversal<>(
-        appView, immediateSubtypesProvider, Scope.ONLY_PROGRAM_CLASSES);
+        appView, subtypingInfo, Scope.ONLY_PROGRAM_CLASSES);
   }
 
   @Override
@@ -70,7 +57,7 @@
     worklist.addFirst(clazzWithTypeT);
 
     // Add subtypes to worklist.
-    for (DexType subtype : immediateSubtypesProvider.apply(clazz.getType())) {
+    for (DexType subtype : subtypingInfo.allImmediateSubtypes(clazz.type)) {
       DexClass definition = appView.definitionFor(subtype);
       if (definition != null) {
         if (scope != Scope.ONLY_PROGRAM_CLASSES || definition.isProgramClass()) {
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLens.java b/src/main/java/com/android/tools/r8/graph/GraphLens.java
index 8abd2ec..ff2fd57 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -9,8 +9,6 @@
 import com.android.tools.r8.ir.code.Invoke.Type;
 import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
 import com.android.tools.r8.ir.desugar.itf.InterfaceProcessor.InterfaceProcessorNestedGraphLens;
-import com.android.tools.r8.optimize.MemberRebindingIdentityLens;
-import com.android.tools.r8.optimize.MemberRebindingLens;
 import com.android.tools.r8.shaking.KeepInfoCollection;
 import com.android.tools.r8.utils.Action;
 import com.android.tools.r8.utils.IterableUtils;
@@ -459,18 +457,6 @@
     return false;
   }
 
-  public MemberRebindingLens asMemberRebindingLens() {
-    return null;
-  }
-
-  public boolean isMemberRebindingIdentityLens() {
-    return false;
-  }
-
-  public MemberRebindingIdentityLens asMemberRebindingIdentityLens() {
-    return null;
-  }
-
   public abstract boolean isNonIdentityLens();
 
   public NonIdentityGraphLens asNonIdentityLens() {
@@ -685,8 +671,7 @@
     }
 
     @SuppressWarnings("unchecked")
-    public final <T extends NonIdentityGraphLens> T findPrevious(
-        Predicate<NonIdentityGraphLens> predicate) {
+    public final <T extends GraphLens> T findPrevious(Predicate<NonIdentityGraphLens> predicate) {
       GraphLens current = getPrevious();
       while (current.isNonIdentityLens()) {
         NonIdentityGraphLens nonIdentityGraphLens = current.asNonIdentityLens();
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
index 4b02558..2b65950 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -270,14 +270,15 @@
   }
 
   private void mergeInterfaces() {
-    Set<DexType> interfaces = Sets.newLinkedHashSet();
+    DexTypeList previousInterfaces = group.getTarget().getInterfaces();
+    Set<DexType> interfaces = Sets.newLinkedHashSet(previousInterfaces);
     if (group.isInterfaceGroup()) {
       // Add all implemented interfaces from the merge group to the target class, ignoring
       // implemented interfaces that are part of the merge group.
       Set<DexType> groupTypes =
           SetUtils.newImmutableSet(
               builder -> group.forEach(clazz -> builder.accept(clazz.getType())));
-      group.forEach(
+      group.forEachSource(
           clazz -> {
             for (DexType itf : clazz.getInterfaces()) {
               if (!groupTypes.contains(itf)) {
@@ -287,7 +288,7 @@
           });
     } else {
       // Add all implemented interfaces from the merge group to the target class.
-      group.forEach(clazz -> Iterables.addAll(interfaces, clazz.getInterfaces()));
+      group.forEachSource(clazz -> Iterables.addAll(interfaces, clazz.getInterfaces()));
     }
     group.getTarget().setInterfaces(DexTypeList.create(interfaces));
   }
@@ -354,8 +355,7 @@
           target = current;
         }
       }
-      group.setTarget(
-          appView.testing().horizontalClassMergingTarget.apply(appView, candidates, target));
+      group.setTarget(appView.testing().horizontalClassMergingTarget.apply(candidates, target));
     }
 
     private ClassInitializerSynthesizedCode createClassInitializerMerger() {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/MergeGroup.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/MergeGroup.java
index ba5d775..b226b72 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/MergeGroup.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/MergeGroup.java
@@ -140,10 +140,6 @@
     return classes.isEmpty();
   }
 
-  public boolean isClassGroup() {
-    return !isInterfaceGroup();
-  }
-
   public boolean isInterfaceGroup() {
     assert !isEmpty();
     assert IterableUtils.allIdentical(getClasses(), DexClass::isInterface);
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDefaultInterfaceMethodCollisions.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDefaultInterfaceMethodCollisions.java
index a2362bd..b091242 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDefaultInterfaceMethodCollisions.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDefaultInterfaceMethodCollisions.java
@@ -22,13 +22,11 @@
 import com.android.tools.r8.horizontalclassmerging.MergeGroup;
 import com.android.tools.r8.horizontalclassmerging.MultiClassPolicyWithPreprocessing;
 import com.android.tools.r8.horizontalclassmerging.policies.NoDefaultInterfaceMethodCollisions.InterfaceInfo;
-import com.android.tools.r8.utils.IterableUtils;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.MapUtils;
 import com.android.tools.r8.utils.SetUtils;
 import com.android.tools.r8.utils.collections.DexMethodSignatureSet;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
 import java.util.Collection;
 import java.util.Collections;
@@ -36,7 +34,6 @@
 import java.util.IdentityHashMap;
 import java.util.Map;
 import java.util.Set;
-import java.util.function.Function;
 
 /**
  * This policy prevents that interface merging changes semantics of invoke-interface/invoke-virtual
@@ -156,7 +153,7 @@
     Map<DexType, Map<DexMethodSignature, Set<DexMethod>>>
         defaultMethodsInheritedBySubclassesPerClass =
             computeDefaultMethodsInheritedBySubclassesPerProgramClass(
-                classesOfInterest, inheritedDefaultMethodsPerClass, groups, subtypingInfo);
+                classesOfInterest, inheritedDefaultMethodsPerClass, subtypingInfo);
 
     // Store the computed information for each interface that is subject to merging.
     Map<DexType, InterfaceInfo> infos = new IdentityHashMap<>();
@@ -273,16 +270,7 @@
       computeDefaultMethodsInheritedBySubclassesPerProgramClass(
           Collection<DexProgramClass> classesOfInterest,
           Map<DexType, Map<DexMethodSignature, Set<DexMethod>>> inheritedDefaultMethodsPerClass,
-          Collection<MergeGroup> groups,
           SubtypingInfo subtypingInfo) {
-    // Build a mapping from class types to their merge group.
-    Map<DexType, Iterable<DexProgramClass>> classGroupsByType =
-        MapUtils.newIdentityHashMap(
-            builder ->
-                Iterables.filter(groups, MergeGroup::isClassGroup)
-                    .forEach(
-                        group -> group.forEach(clazz -> builder.accept(clazz.getType(), group))));
-
     // Copy the map from classes to their inherited default methods.
     Map<DexType, Map<DexMethodSignature, Set<DexMethod>>>
         defaultMethodsInheritedBySubclassesPerClass =
@@ -291,28 +279,7 @@
                 new HashMap<>(),
                 outerValue ->
                     MapUtils.clone(outerValue, new HashMap<>(), SetUtils::newIdentityHashSet));
-
-    // Propagate data upwards. If classes A and B are in a merge group, we need to push the state
-    // for A to all of B's supertypes, and the state for B to all of A's supertypes.
-    //
-    // Therefore, it is important that we don't process any of A's supertypes until B has been
-    // processed, since that would lead to inadequate upwards propagation. To achieve this, we
-    // simulate that both A and B are subtypes of A's and B's supertypes.
-    Function<DexType, Iterable<DexType>> immediateSubtypesProvider =
-        type -> {
-          Set<DexType> immediateSubtypesAfterClassMerging = Sets.newIdentityHashSet();
-          for (DexType immediateSubtype : subtypingInfo.allImmediateSubtypes(type)) {
-            Iterable<DexProgramClass> group = classGroupsByType.get(immediateSubtype);
-            if (group != null) {
-              group.forEach(member -> immediateSubtypesAfterClassMerging.add(member.getType()));
-            } else {
-              immediateSubtypesAfterClassMerging.add(immediateSubtype);
-            }
-          }
-          return immediateSubtypesAfterClassMerging;
-        };
-
-    BottomUpClassHierarchyTraversal.forProgramClasses(appView, immediateSubtypesProvider)
+    BottomUpClassHierarchyTraversal.forProgramClasses(appView, subtypingInfo)
         .visit(
             classesOfInterest,
             clazz -> {
@@ -320,20 +287,16 @@
               Map<DexMethodSignature, Set<DexMethod>> defaultMethodsToPropagate =
                   defaultMethodsInheritedBySubclassesPerClass.getOrDefault(
                       clazz.getType(), emptyMap());
-              Iterable<DexProgramClass> group =
-                  classGroupsByType.getOrDefault(clazz.getType(), IterableUtils.singleton(clazz));
-              for (DexProgramClass member : group) {
-                for (DexType supertype : member.allImmediateSupertypes()) {
-                  Map<DexMethodSignature, Set<DexMethod>>
-                      defaultMethodsInheritedBySubclassesForSupertype =
-                          defaultMethodsInheritedBySubclassesPerClass.computeIfAbsent(
-                              supertype, ignore -> new HashMap<>());
-                  defaultMethodsToPropagate.forEach(
-                      (signature, methods) ->
-                          defaultMethodsInheritedBySubclassesForSupertype
-                              .computeIfAbsent(signature, ignore -> Sets.newIdentityHashSet())
-                              .addAll(methods));
-                }
+              for (DexType supertype : clazz.allImmediateSupertypes()) {
+                Map<DexMethodSignature, Set<DexMethod>>
+                    defaultMethodsInheritedBySubclassesForSupertype =
+                        defaultMethodsInheritedBySubclassesPerClass.computeIfAbsent(
+                            supertype, ignore -> new HashMap<>());
+                defaultMethodsToPropagate.forEach(
+                    (signature, methods) ->
+                        defaultMethodsInheritedBySubclassesForSupertype
+                            .computeIfAbsent(signature, ignore -> Sets.newIdentityHashSet())
+                            .addAll(methods));
               }
             });
     defaultMethodsInheritedBySubclassesPerClass
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyDirectlyConnectedOrUnrelatedInterfaces.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyDirectlyConnectedOrUnrelatedInterfaces.java
index fc457f7..d9e6cd1 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyDirectlyConnectedOrUnrelatedInterfaces.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyDirectlyConnectedOrUnrelatedInterfaces.java
@@ -8,26 +8,25 @@
 
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.SubtypingInfo;
 import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
 import com.android.tools.r8.horizontalclassmerging.MergeGroup;
-import com.android.tools.r8.horizontalclassmerging.MultiClassPolicyWithPreprocessing;
-import com.android.tools.r8.utils.SetUtils;
+import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
 import com.android.tools.r8.utils.WorkList;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
-import java.util.ArrayList;
+import com.google.common.collect.Iterators;
+import com.google.common.collect.Sets;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.IdentityHashMap;
+import java.util.Iterator;
 import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.function.Function;
+import java.util.function.Consumer;
 
 /**
  * This policy ensures that we do not create cycles in the class hierarchy as a result of interface
@@ -54,15 +53,11 @@
  *   interface J extends IK, ... {}
  * </pre>
  */
-public class OnlyDirectlyConnectedOrUnrelatedInterfaces
-    extends MultiClassPolicyWithPreprocessing<SubtypingInfo> {
+public class OnlyDirectlyConnectedOrUnrelatedInterfaces extends MultiClassPolicy {
 
   private final AppView<? extends AppInfoWithClassHierarchy> appView;
   private final Mode mode;
 
-  // The interface merge groups that this policy has committed to so far.
-  private final Map<DexProgramClass, MergeGroup> committed = new IdentityHashMap<>();
-
   public OnlyDirectlyConnectedOrUnrelatedInterfaces(
       AppView<? extends AppInfoWithClassHierarchy> appView, Mode mode) {
     this.appView = appView;
@@ -70,84 +65,116 @@
   }
 
   @Override
-  public Collection<MergeGroup> apply(MergeGroup group, SubtypingInfo subtypingInfo) {
+  public Collection<MergeGroup> apply(MergeGroup group) {
     if (!group.isInterfaceGroup()) {
       return ImmutableList.of(group);
     }
 
-    List<MergeGroupWithInfo> newGroupsWithInfo = new ArrayList<>();
-    for (DexProgramClass clazz : group) {
-      Set<DexProgramClass> superInterfaces = computeSuperInterfaces(clazz);
-      Set<DexProgramClass> subInterfaces = computeSubInterfaces(clazz, subtypingInfo);
-
-      MergeGroupWithInfo newGroup = null;
-      for (MergeGroupWithInfo candidateGroup : newGroupsWithInfo) {
-        // Check if adding `clazz` to `candidateGroup` would introduce a super interface that is
-        // also a sub interface. In that case we must abort since merging would lead to a cycle in
-        // the class hierarchy.
-        if (candidateGroup.isSafeToAddSubAndSuperInterfaces(
-            clazz, subInterfaces, superInterfaces)) {
-          newGroup = candidateGroup;
-          break;
-        }
-      }
-
-      if (newGroup != null) {
-        newGroup.add(clazz, superInterfaces, subInterfaces);
-      } else {
-        newGroupsWithInfo.add(new MergeGroupWithInfo(clazz, superInterfaces, subInterfaces));
-      }
+    Set<DexProgramClass> classes = new LinkedHashSet<>(group.getClasses());
+    Map<DexProgramClass, Set<DexProgramClass>> ineligibleForMerging =
+        computeIneligibleForMergingGraph(classes);
+    if (ineligibleForMerging.isEmpty()) {
+      return ImmutableList.of(group);
     }
 
+    // Extract sub-merge groups from the graph in such a way that all pairs of interfaces in each
+    // merge group are not connected by an edge in the graph.
     List<MergeGroup> newGroups = new LinkedList<>();
-    for (MergeGroupWithInfo newGroupWithInfo : newGroupsWithInfo) {
-      MergeGroup newGroup = newGroupWithInfo.getGroup();
+    while (!classes.isEmpty()) {
+      Iterator<DexProgramClass> iterator = classes.iterator();
+      MergeGroup newGroup = new MergeGroup(iterator.next());
+      Iterators.addAll(
+          newGroup,
+          Iterators.filter(
+              iterator,
+              candidate -> !isConnectedToGroup(candidate, newGroup, ineligibleForMerging)));
       if (!newGroup.isTrivial()) {
         newGroups.add(newGroup);
-        newGroup.forEach(clazz -> committed.put(clazz, newGroup));
       }
+      classes.removeAll(newGroup.getClasses());
     }
     return newGroups;
   }
 
-  private Set<DexProgramClass> computeSuperInterfaces(DexProgramClass clazz) {
-    return computeTransitiveSubOrSuperInterfaces(clazz, DexClass::getInterfaces);
+  /**
+   * Computes an undirected graph, where the nodes are the interfaces from the merge group, and an
+   * edge I <-> J represents that I and J are not eligible for merging.
+   *
+   * <p>We will insert an edge I <-> J, if interface I inherits from interface J, and the path from
+   * I to J in the class hierarchy includes an interface K that is outside the merge group. Note
+   * that if I extends J directly we will not insert an edge I <-> J (unless there are multiple
+   * paths in the class hierarchy from I to J, and one of the paths goes through an interface
+   * outside the merge group).
+   */
+  private Map<DexProgramClass, Set<DexProgramClass>> computeIneligibleForMergingGraph(
+      Set<DexProgramClass> classes) {
+    Map<DexProgramClass, Set<DexProgramClass>> ineligibleForMerging = new IdentityHashMap<>();
+    for (DexProgramClass clazz : classes) {
+      forEachIndirectlyReachableInterfaceInMergeGroup(
+          clazz,
+          classes,
+          other ->
+              ineligibleForMerging
+                  .computeIfAbsent(clazz, ignore -> Sets.newIdentityHashSet())
+                  .add(other));
+    }
+    return ineligibleForMerging;
   }
 
-  private Set<DexProgramClass> computeSubInterfaces(
-      DexProgramClass clazz, SubtypingInfo subtypingInfo) {
-    return computeTransitiveSubOrSuperInterfaces(
-        clazz, definition -> subtypingInfo.allImmediateExtendsSubtypes(definition.getType()));
-  }
-
-  private Set<DexProgramClass> computeTransitiveSubOrSuperInterfaces(
-      DexProgramClass clazz,
-      Function<DexProgramClass, Iterable<DexType>> immediateSubOrSuperInterfacesProvider) {
-    WorkList<DexProgramClass> workList = WorkList.newWorkList(new LinkedHashSet<>());
-    // Intentionally not marking `clazz` as seen, since we only want the strict sub/super types.
-    workList.addIgnoringSeenSet(clazz);
+  private void forEachIndirectlyReachableInterfaceInMergeGroup(
+      DexProgramClass clazz, Set<DexProgramClass> classes, Consumer<DexProgramClass> consumer) {
+    // First find the set of interfaces that can be reached via paths in the class hierarchy from
+    // the given interface, without visiting any interfaces outside the merge group.
+    WorkList<DexType> workList = WorkList.newIdentityWorkList(clazz.getInterfaces());
     while (workList.hasNext()) {
-      DexProgramClass interfaceDefinition = workList.next();
-      MergeGroup group = committed.get(interfaceDefinition);
-      if (group != null) {
-        workList.addIfNotSeen(group);
+      DexProgramClass directlyReachableInterface =
+          asProgramClassOrNull(appView.definitionFor(workList.next()));
+      if (directlyReachableInterface == null) {
+        continue;
       }
-      for (DexType immediateSubOrSuperInterfaceType :
-          immediateSubOrSuperInterfacesProvider.apply(interfaceDefinition)) {
-        DexProgramClass immediateSubOrSuperInterface =
-            asProgramClassOrNull(appView.definitionFor(immediateSubOrSuperInterfaceType));
-        if (immediateSubOrSuperInterface != null) {
-          workList.addIfNotSeen(immediateSubOrSuperInterface);
-        }
+      // If the implemented interface is a member of the merge group, then include it's interfaces.
+      if (classes.contains(directlyReachableInterface)) {
+        workList.addIfNotSeen(directlyReachableInterface.getInterfaces());
       }
     }
-    assert !workList.isSeen(clazz);
-    return workList.getMutableSeenSet();
+
+    // Initialize a new worklist with the first layer of indirectly reachable interface types.
+    Set<DexType> directlyReachableInterfaceTypes = workList.getSeenSet();
+    workList = WorkList.newIdentityWorkList();
+    for (DexType directlyReachableInterfaceType : directlyReachableInterfaceTypes) {
+      DexProgramClass directlyReachableInterface =
+          asProgramClassOrNull(appView.definitionFor(directlyReachableInterfaceType));
+      if (directlyReachableInterface != null) {
+        workList.addIfNotSeen(directlyReachableInterface.getInterfaces());
+      }
+    }
+
+    // Report all interfaces from the merge group that are reachable in the class hierarchy from the
+    // worklist.
+    while (workList.hasNext()) {
+      DexProgramClass indirectlyReachableInterface =
+          asProgramClassOrNull(appView.definitionFor(workList.next()));
+      if (indirectlyReachableInterface == null) {
+        continue;
+      }
+      if (classes.contains(indirectlyReachableInterface)) {
+        consumer.accept(indirectlyReachableInterface);
+      }
+      workList.addIfNotSeen(indirectlyReachableInterface.getInterfaces());
+    }
   }
 
-  @Override
-  public void clear() {
-    committed.clear();
+  private boolean isConnectedToGroup(
+      DexProgramClass clazz,
+      MergeGroup group,
+      Map<DexProgramClass, Set<DexProgramClass>> ineligibleForMerging) {
+    for (DexProgramClass member : group) {
+      if (ineligibleForMerging.getOrDefault(clazz, Collections.emptySet()).contains(member)
+          || ineligibleForMerging.getOrDefault(member, Collections.emptySet()).contains(clazz)) {
+        return true;
+      }
+    }
+    return false;
   }
 
   @Override
@@ -156,80 +183,7 @@
   }
 
   @Override
-  public SubtypingInfo preprocess(Collection<MergeGroup> groups) {
-    return new SubtypingInfo(appView);
-  }
-
-  @Override
   public boolean shouldSkipPolicy() {
     return !appView.options().horizontalClassMergerOptions().isInterfaceMergingEnabled(mode);
   }
-
-  static class MergeGroupWithInfo {
-
-    private final MergeGroup group;
-    private final Set<DexProgramClass> members;
-    private final Set<DexProgramClass> superInterfaces;
-    private final Set<DexProgramClass> subInterfaces;
-
-    MergeGroupWithInfo(
-        DexProgramClass clazz,
-        Set<DexProgramClass> superInterfaces,
-        Set<DexProgramClass> subInterfaces) {
-      this.group = new MergeGroup(clazz);
-      this.members = SetUtils.newIdentityHashSet(clazz);
-      this.superInterfaces = superInterfaces;
-      this.subInterfaces = subInterfaces;
-    }
-
-    void add(
-        DexProgramClass clazz,
-        Set<DexProgramClass> newSuperInterfaces,
-        Set<DexProgramClass> newSubInterfaces) {
-      group.add(clazz);
-      members.add(clazz);
-      Iterables.addAll(
-          superInterfaces,
-          Iterables.filter(
-              newSuperInterfaces, superInterface -> !members.contains(superInterface)));
-      superInterfaces.remove(clazz);
-      Iterables.addAll(
-          subInterfaces,
-          Iterables.filter(newSubInterfaces, subInterface -> !members.contains(subInterface)));
-      subInterfaces.remove(clazz);
-    }
-
-    MergeGroup getGroup() {
-      return group;
-    }
-
-    boolean isSafeToAddSubAndSuperInterfaces(
-        DexProgramClass clazz,
-        Set<DexProgramClass> newSubInterfaces,
-        Set<DexProgramClass> newSuperInterfaces) {
-      // Check that adding the new sub and super interfaces to the group is safe.
-      for (DexProgramClass newSubInterface : newSubInterfaces) {
-        if (!group.contains(newSubInterface) && superInterfaces.contains(newSubInterface)) {
-          return false;
-        }
-      }
-      for (DexProgramClass newSuperInterface : newSuperInterfaces) {
-        if (!group.contains(newSuperInterface) && subInterfaces.contains(newSuperInterface)) {
-          return false;
-        }
-      }
-      // Check that adding the sub and super interfaces of the group to the current class is safe.
-      for (DexProgramClass subInterface : subInterfaces) {
-        if (subInterface != clazz && newSuperInterfaces.contains(subInterface)) {
-          return false;
-        }
-      }
-      for (DexProgramClass superInterface : superInterfaces) {
-        if (superInterface != clazz && newSubInterfaces.contains(superInterface)) {
-          return false;
-        }
-      }
-      return true;
-    }
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
index 0fdc499..3f77dbb 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingIdentityLens.java
@@ -41,12 +41,7 @@
   }
 
   public static Builder builder(AppView<? extends AppInfoWithClassHierarchy> appView) {
-    return builder(appView, appView.graphLens());
-  }
-
-  public static Builder builder(
-      AppView<? extends AppInfoWithClassHierarchy> appView, GraphLens previousLens) {
-    return new Builder(appView, previousLens);
+    return new Builder(appView);
   }
 
   @Override
@@ -134,55 +129,16 @@
     return getPrevious().isContextFreeForMethods();
   }
 
-  @Override
-  public boolean isMemberRebindingIdentityLens() {
-    return true;
-  }
-
-  @Override
-  public MemberRebindingIdentityLens asMemberRebindingIdentityLens() {
-    return this;
-  }
-
-  public MemberRebindingIdentityLens toRewrittenMemberRebindingIdentityLens(
-      AppView<? extends AppInfoWithClassHierarchy> appView, GraphLens lens) {
-    DexItemFactory dexItemFactory = appView.dexItemFactory();
-    Builder builder = builder(appView, getIdentityLens());
-    nonReboundFieldReferenceToDefinitionMap.forEach(
-        (nonReboundFieldReference, reboundFieldReference) -> {
-          DexField rewrittenReboundFieldReference = lens.lookupField(reboundFieldReference);
-          DexField rewrittenNonReboundFieldReference =
-              rewrittenReboundFieldReference.withHolder(
-                  lens.lookupType(nonReboundFieldReference.getHolderType()), dexItemFactory);
-          builder.recordNonReboundFieldAccess(
-              rewrittenNonReboundFieldReference, rewrittenReboundFieldReference);
-        });
-    nonReboundMethodReferenceToDefinitionMap.forEach(
-        (nonReboundMethodReference, reboundMethodReference) -> {
-          DexMethod rewrittenReboundMethodReference =
-              lens.getRenamedMethodSignature(reboundMethodReference);
-          DexMethod rewrittenNonReboundMethodReference =
-              rewrittenReboundMethodReference.withHolder(
-                  lens.lookupType(nonReboundMethodReference.getHolderType()), dexItemFactory);
-          builder.recordNonReboundMethodAccess(
-              rewrittenNonReboundMethodReference, rewrittenReboundMethodReference);
-        });
-    return builder.build();
-  }
-
   public static class Builder {
 
     private final AppView<? extends AppInfoWithClassHierarchy> appView;
-    private final GraphLens previousLens;
-
     private final Map<DexField, DexField> nonReboundFieldReferenceToDefinitionMap =
         new IdentityHashMap<>();
     private final Map<DexMethod, DexMethod> nonReboundMethodReferenceToDefinitionMap =
         new IdentityHashMap<>();
 
-    private Builder(AppView<? extends AppInfoWithClassHierarchy> appView, GraphLens previousLens) {
+    private Builder(AppView<? extends AppInfoWithClassHierarchy> appView) {
       this.appView = appView;
-      this.previousLens = previousLens;
     }
 
     void recordNonReboundFieldAccesses(FieldAccessInfo fieldAccessInfo) {
@@ -196,12 +152,6 @@
       nonReboundFieldReferenceToDefinitionMap.put(nonReboundFieldReference, reboundFieldReference);
     }
 
-    private void recordNonReboundMethodAccess(
-        DexMethod nonReboundMethodReference, DexMethod reboundMethodReference) {
-      nonReboundMethodReferenceToDefinitionMap.put(
-          nonReboundMethodReference, reboundMethodReference);
-    }
-
     void recordMethodAccess(DexMethod reference) {
       if (reference.getHolderType().isArrayType()) {
         return;
@@ -211,7 +161,7 @@
         SingleResolutionResult resolutionResult =
             appView.appInfo().resolveMethodOn(holder, reference).asSingleResolution();
         if (resolutionResult != null && resolutionResult.getResolvedHolder() != holder) {
-          recordNonReboundMethodAccess(
+          nonReboundMethodReferenceToDefinitionMap.put(
               reference, resolutionResult.getResolvedMethod().getReference());
         }
       }
@@ -225,7 +175,7 @@
           nonReboundFieldReferenceToDefinitionMap,
           nonReboundMethodReferenceToDefinitionMap,
           appView.dexItemFactory(),
-          previousLens);
+          appView.graphLens());
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
index d202af8..4310236 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLens.java
@@ -6,7 +6,6 @@
 
 import static com.android.tools.r8.graph.NestedGraphLens.mapVirtualInterfaceInvocationTypes;
 
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -48,11 +47,6 @@
   }
 
   @Override
-  public MemberRebindingLens asMemberRebindingLens() {
-    return this;
-  }
-
-  @Override
   public DexType getOriginalType(DexType type) {
     return getPrevious().getOriginalType(type);
   }
@@ -137,8 +131,7 @@
   }
 
   public FieldRebindingIdentityLens toRewrittenFieldRebindingLens(
-      AppView<? extends AppInfoWithClassHierarchy> appView, GraphLens lens) {
-    DexItemFactory dexItemFactory = appView.dexItemFactory();
+      DexItemFactory dexItemFactory, GraphLens lens) {
     FieldRebindingIdentityLens.Builder builder = FieldRebindingIdentityLens.builder();
     nonReboundFieldReferenceToDefinitionMap.forEach(
         (nonReboundFieldReference, reboundFieldReference) -> {
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 836c9c4..849d641 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -85,6 +85,7 @@
 import java.util.TreeSet;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
 import java.util.function.BiPredicate;
 import java.util.function.Consumer;
 import java.util.function.Function;
@@ -1214,7 +1215,7 @@
     private boolean enable =
         !Version.isDevelopmentVersion()
             || System.getProperty("com.android.tools.r8.disableHorizontalClassMerging") == null;
-    private boolean enableInterfaceMergingInInitial = false;
+    private boolean enableInterfaceMerging = false;
     private boolean enableSyntheticMerging = true;
     private boolean ignoreRuntimeTypeChecksForTesting = false;
     private boolean restrictToSynthetics = false;
@@ -1237,6 +1238,10 @@
       this.enable = enable;
     }
 
+    public void enableInterfaceMerging() {
+      enableInterfaceMerging = true;
+    }
+
     public int getMaxGroupSize() {
       return maxGroupSize;
     }
@@ -1260,26 +1265,23 @@
       return ignoreRuntimeTypeChecksForTesting;
     }
 
+    public boolean isInterfaceMergingEnabled() {
+      assert !isInterfaceMergingEnabled(HorizontalClassMerger.Mode.INITIAL);
+      return isInterfaceMergingEnabled(HorizontalClassMerger.Mode.FINAL);
+    }
+
     public boolean isSyntheticMergingEnabled() {
       return enableSyntheticMerging;
     }
 
     public boolean isInterfaceMergingEnabled(HorizontalClassMerger.Mode mode) {
-      if (mode.isInitial()) {
-        return enableInterfaceMergingInInitial;
-      }
-      assert mode.isFinal();
-      return true;
+      return enableInterfaceMerging && mode.isFinal();
     }
 
     public boolean isRestrictedToSynthetics() {
       return restrictToSynthetics || !isOptimizing() || !isShrinking();
     }
 
-    public void setEnableInterfaceMergingInInitial() {
-      enableInterfaceMergingInInitial = true;
-    }
-
     public void setIgnoreRuntimeTypeChecksForTesting() {
       ignoreRuntimeTypeChecksForTesting = true;
     }
@@ -1349,8 +1351,8 @@
 
     public BiConsumer<DexItemFactory, HorizontallyMergedClasses> horizontallyMergedClassesConsumer =
         ConsumerUtils.emptyBiConsumer();
-    public TriFunction<AppView<?>, Iterable<DexProgramClass>, DexProgramClass, DexProgramClass>
-        horizontalClassMergingTarget = (appView, candidates, target) -> target;
+    public BiFunction<Iterable<DexProgramClass>, DexProgramClass, DexProgramClass>
+        horizontalClassMergingTarget = (candidates, target) -> target;
 
     public BiConsumer<DexItemFactory, EnumDataMap> unboxedEnumsConsumer =
         ConsumerUtils.emptyBiConsumer();
diff --git a/src/main/java/com/android/tools/r8/utils/MapUtils.java b/src/main/java/com/android/tools/r8/utils/MapUtils.java
index fc39146..63e881e 100644
--- a/src/main/java/com/android/tools/r8/utils/MapUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/MapUtils.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8.utils;
 
 import com.android.tools.r8.utils.StringUtils.BraceType;
-import java.util.IdentityHashMap;
 import java.util.Map;
 import java.util.function.BiFunction;
 import java.util.function.Function;
@@ -51,12 +50,6 @@
     return result;
   }
 
-  public static <K, V> IdentityHashMap<K, V> newIdentityHashMap(BiForEachable<K, V> forEachable) {
-    IdentityHashMap<K, V> map = new IdentityHashMap<>();
-    forEachable.forEach(map::put);
-    return map;
-  }
-
   public static <T> void removeIdentityMappings(Map<T, T> map) {
     map.entrySet().removeIf(entry -> entry.getKey() == entry.getValue());
   }
diff --git a/src/main/java/com/android/tools/r8/utils/WorkList.java b/src/main/java/com/android/tools/r8/utils/WorkList.java
index 67e4148..4279cb7 100644
--- a/src/main/java/com/android/tools/r8/utils/WorkList.java
+++ b/src/main/java/com/android/tools/r8/utils/WorkList.java
@@ -48,10 +48,6 @@
     return workList;
   }
 
-  public static <T> WorkList<T> newWorkList(Set<T> seen) {
-    return new WorkList<>(seen);
-  }
-
   private WorkList(EqualityTest equalityTest) {
     this(equalityTest == EqualityTest.HASH ? new HashSet<>() : Sets.newIdentityHashSet());
   }
@@ -60,10 +56,6 @@
     this.seen = seen;
   }
 
-  public void addIgnoringSeenSet(T item) {
-    workingList.addLast(item);
-  }
-
   public void addAllIgnoringSeenSet(Iterable<T> items) {
     items.forEach(workingList::addLast);
   }
@@ -94,10 +86,6 @@
     return !hasNext();
   }
 
-  public boolean isSeen(T item) {
-    return seen.contains(item);
-  }
-
   public void markAsSeen(T item) {
     seen.add(item);
   }
@@ -115,10 +103,6 @@
     return Collections.unmodifiableSet(seen);
   }
 
-  public Set<T> getMutableSeenSet() {
-    return seen;
-  }
-
   public enum EqualityTest {
     HASH,
     IDENTITY
diff --git a/src/test/examples/classmerging/NoHorizontalClassMerging.java b/src/test/examples/classmerging/NoHorizontalClassMerging.java
deleted file mode 100644
index 3b7a831..0000000
--- a/src/test/examples/classmerging/NoHorizontalClassMerging.java
+++ /dev/null
@@ -1,10 +0,0 @@
-// 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 classmerging;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Target;
-
-@Target({ElementType.TYPE})
-public @interface NoHorizontalClassMerging {}
diff --git a/src/test/examples/classmerging/SimpleInterfaceAccessTest.java b/src/test/examples/classmerging/SimpleInterfaceAccessTest.java
index 804b4ed..6fde90e 100644
--- a/src/test/examples/classmerging/SimpleInterfaceAccessTest.java
+++ b/src/test/examples/classmerging/SimpleInterfaceAccessTest.java
@@ -32,14 +32,12 @@
   }
 
   // Should only be merged into OtherSimpleInterfaceImpl if access modifications are allowed.
-  @NoHorizontalClassMerging
   public interface SimpleInterface {
 
     void foo();
   }
 
   // Should only be merged into OtherSimpleInterfaceImpl if access modifications are allowed.
-  @NoHorizontalClassMerging
   public interface OtherSimpleInterface {
 
     void bar();
diff --git a/src/test/examples/classmerging/keep-rules.txt b/src/test/examples/classmerging/keep-rules.txt
index 46fcb91..64ce874 100644
--- a/src/test/examples/classmerging/keep-rules.txt
+++ b/src/test/examples/classmerging/keep-rules.txt
@@ -72,4 +72,3 @@
 -neverinline class * {
   @classmerging.NeverInline <methods>;
 }
--nohorizontalclassmerging @classmerging.NoHorizontalClassMerging class *
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/InnerOuterClassesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/InnerOuterClassesTest.java
index 6fad862..8a80d6e 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/InnerOuterClassesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/InnerOuterClassesTest.java
@@ -41,7 +41,7 @@
         .addOptionsModification(
             options ->
                 options.testing.horizontalClassMergingTarget =
-                    (appView, candidates, target) -> candidates.iterator().next())
+                    (candidates, target) -> candidates.iterator().next())
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutputLines("a", "b", "c", "d")
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NestClassMergingTestRunner.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NestClassMergingTestRunner.java
index 0844722..bf50ab1 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/NestClassMergingTestRunner.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NestClassMergingTestRunner.java
@@ -111,7 +111,7 @@
                 .addOptionsModification(
                     options -> {
                       options.testing.horizontalClassMergingTarget =
-                          (appView, canditates, target) -> {
+                          (canditates, target) -> {
                             Set<ClassReference> candidateClassReferences =
                                 Streams.stream(canditates)
                                     .map(DexClass::getClassReference)
@@ -164,7 +164,7 @@
                 .addOptionsModification(
                     options -> {
                       options.testing.horizontalClassMergingTarget =
-                          (appView, canditates, target) -> {
+                          (canditates, target) -> {
                             Set<ClassReference> candidateClassReferences =
                                 Streams.stream(canditates)
                                     .map(DexClass::getClassReference)
@@ -217,7 +217,7 @@
                 .addOptionsModification(
                     options -> {
                       options.testing.horizontalClassMergingTarget =
-                          (appView, canditates, target) -> {
+                          (canditates, target) -> {
                             Set<ClassReference> candidateClassReferences =
                                 Streams.stream(canditates)
                                     .map(DexClass::getClassReference)
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideAbstractMethodWithDefaultTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideAbstractMethodWithDefaultTest.java
index 955ff04..3787201 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideAbstractMethodWithDefaultTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideAbstractMethodWithDefaultTest.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.classmerging.horizontal.dispatch;
 
-import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
 
@@ -13,6 +12,7 @@
 import com.android.tools.r8.NoVerticalClassMerging;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.classmerging.horizontal.HorizontalClassMergingTestBase;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
 import org.junit.Test;
 
 public class OverrideAbstractMethodWithDefaultTest extends HorizontalClassMergingTestBase {
@@ -31,14 +31,13 @@
         .enableNoVerticalClassMergingAnnotations()
         .setMinApi(parameters.getApiLevel())
         .addHorizontallyMergedClassesInspector(
-            inspector ->
-                inspector.assertIsCompleteMergeGroup(I.class, J.class).assertNoOtherClassesMerged())
+            HorizontallyMergedClassesInspector::assertNoClassesMerged)
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutputLines("J", "B2")
         .inspect(
             codeInspector -> {
               assertThat(codeInspector.clazz(I.class), isPresent());
-              assertThat(codeInspector.clazz(J.class), isAbsent());
+              assertThat(codeInspector.clazz(J.class), isPresent());
               assertThat(codeInspector.clazz(A.class), isPresent());
               assertThat(codeInspector.clazz(B1.class), isPresent());
               assertThat(codeInspector.clazz(B2.class), isPresent());
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultMethodTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultMethodTest.java
index 0af1e85..580598b 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultMethodTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultMethodTest.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8.classmerging.horizontal.dispatch;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.onlyIf;
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import com.android.tools.r8.NeverClassInline;
@@ -37,11 +36,9 @@
               } else {
                 inspector
                     .assertClassesNotMerged(A.class, B.class)
-                    .assertIsCompleteMergeGroup(I.class, J.class)
                     .assertIsCompleteMergeGroup(
                         SyntheticItemsTestUtils.syntheticCompanionClass(I.class),
-                        SyntheticItemsTestUtils.syntheticCompanionClass(J.class))
-                    .assertNoOtherClassesMerged();
+                        SyntheticItemsTestUtils.syntheticCompanionClass(J.class));
               }
             })
         .run(parameters.getRuntime(), Main.class)
@@ -49,9 +46,7 @@
         .inspect(
             codeInspector -> {
               assertThat(codeInspector.clazz(I.class), isPresent());
-              assertThat(
-                  codeInspector.clazz(J.class),
-                  onlyIf(parameters.canUseDefaultAndStaticInterfaceMethods(), isPresent()));
+              assertThat(codeInspector.clazz(J.class), isPresent());
               assertThat(codeInspector.clazz(A.class), isPresent());
               assertThat(codeInspector.clazz(B.class), isPresent());
               assertThat(codeInspector.clazz(C.class), isPresent());
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultOnSuperMethodTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultOnSuperMethodTest.java
index 7b5437e..65ca772 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultOnSuperMethodTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultOnSuperMethodTest.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8.classmerging.horizontal.dispatch;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.onlyIf;
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import com.android.tools.r8.NeverClassInline;
@@ -43,11 +42,9 @@
               } else {
                 inspector
                     .assertClassesNotMerged(A.class, B.class)
-                    .assertIsCompleteMergeGroup(I.class, J.class)
                     .assertIsCompleteMergeGroup(
                         SyntheticItemsTestUtils.syntheticCompanionClass(I.class),
-                        SyntheticItemsTestUtils.syntheticCompanionClass(J.class))
-                    .assertNoOtherClassesMerged();
+                        SyntheticItemsTestUtils.syntheticCompanionClass(J.class));
               }
             })
         .run(parameters.getRuntime(), Main.class)
@@ -55,9 +52,7 @@
         .inspect(
             codeInspector -> {
               assertThat(codeInspector.clazz(I.class), isPresent());
-              assertThat(
-                  codeInspector.clazz(J.class),
-                  onlyIf(parameters.canUseDefaultAndStaticInterfaceMethods(), isPresent()));
+              assertThat(codeInspector.clazz(J.class), isPresent());
               assertThat(codeInspector.clazz(Parent.class), isPresent());
               assertThat(codeInspector.clazz(A.class), isPresent());
               assertThat(codeInspector.clazz(B.class), isPresent());
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/ClassHierarchyCycleAfterMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/ClassHierarchyCycleAfterMergingTest.java
index 4d81f1a..650d804 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/ClassHierarchyCycleAfterMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/ClassHierarchyCycleAfterMergingTest.java
@@ -44,6 +44,11 @@
         // hierarchy.
         .addHorizontallyMergedClassesInspector(
             HorizontallyMergedClassesInspector::assertNoClassesMerged)
+        .addOptionsModification(
+            options -> {
+              assertFalse(options.horizontalClassMergerOptions().isInterfaceMergingEnabled());
+              options.horizontalClassMergerOptions().enableInterfaceMerging();
+            })
         .enableNoHorizontalClassMergingAnnotations()
         .enableNoUnusedInterfaceRemovalAnnotations()
         .enableNoVerticalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupAfterSubclassMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupAfterSubclassMergingTest.java
index cc19dd2..1c5d915 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupAfterSubclassMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupAfterSubclassMergingTest.java
@@ -7,6 +7,7 @@
 import static com.android.tools.r8.utils.codeinspector.Matchers.isImplementing;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertFalse;
 
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
@@ -15,9 +16,8 @@
 import com.android.tools.r8.NoVerticalClassMerging;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -25,21 +25,19 @@
 @RunWith(Parameterized.class)
 public class CollisionWithDefaultMethodOutsideMergeGroupAfterSubclassMergingTest extends TestBase {
 
-  private final boolean enableInterfaceMergingInInitial;
   private final TestParameters parameters;
 
-  @Parameterized.Parameters(name = "{1}, enableInterfaceMergingInInitial: {0}")
-  public static List<Object[]> data() {
-    return buildParameters(
-        BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
   }
 
   public CollisionWithDefaultMethodOutsideMergeGroupAfterSubclassMergingTest(
-      boolean enableInterfaceMergingInInitial, TestParameters parameters) {
-    this.enableInterfaceMergingInInitial = enableInterfaceMergingInInitial;
+      TestParameters parameters) {
     this.parameters = parameters;
   }
 
+  // TODO(b/173990042): Disallow merging of A and B in the first round of class merging.
   @Test
   public void test() throws Exception {
     testForR8(parameters.getBackend())
@@ -50,22 +48,23 @@
         // the default method J.m() to A.
         .addHorizontallyMergedClassesInspector(
             inspector -> {
-              inspector
-                  .assertIsCompleteMergeGroup(A.class, B.class)
-                  .assertMergedInto(B.class, A.class);
               if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
-                inspector.assertClassesNotMerged(I.class, J.class, K.class);
+                inspector
+                    .assertIsCompleteMergeGroup(A.class, B.class)
+                    .assertMergedInto(B.class, A.class)
+                    .assertClassesNotMerged(I.class, J.class, K.class);
               } else {
                 inspector
+                    .assertIsCompleteMergeGroup(A.class, B.class)
+                    .assertMergedInto(B.class, A.class)
                     .assertIsCompleteMergeGroup(I.class, J.class)
                     .assertClassesNotMerged(K.class);
               }
             })
         .addOptionsModification(
             options -> {
-              if (enableInterfaceMergingInInitial) {
-                options.horizontalClassMergerOptions().setEnableInterfaceMergingInInitial();
-              }
+              assertFalse(options.horizontalClassMergerOptions().isInterfaceMergingEnabled());
+              options.horizontalClassMergerOptions().enableInterfaceMerging();
             })
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
@@ -81,10 +80,10 @@
               assertThat(aClassSubject, isImplementing(inspector.clazz(I.class)));
               assertThat(aClassSubject, isImplementing(inspector.clazz(K.class)));
 
-              ClassSubject cClassSubject = inspector.clazz(C.class);
-              assertThat(cClassSubject, isPresent());
+              ClassSubject bClassSubject = inspector.clazz(C.class);
+              assertThat(bClassSubject, isPresent());
               assertThat(
-                  cClassSubject,
+                  bClassSubject,
                   isImplementing(
                       inspector.clazz(
                           parameters.canUseDefaultAndStaticInterfaceMethods()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupClassTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupClassTest.java
index dca511f..d4f14bd 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupClassTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupClassTest.java
@@ -51,6 +51,11 @@
                 inspector.assertIsCompleteMergeGroup(I.class, J.class);
               }
             })
+        .addOptionsModification(
+            options -> {
+              assertFalse(options.horizontalClassMergerOptions().isInterfaceMergingEnabled());
+              options.horizontalClassMergerOptions().enableInterfaceMerging();
+            })
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupLambdaTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupLambdaTest.java
index 62cf256..f9e85ba 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupLambdaTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupLambdaTest.java
@@ -52,6 +52,12 @@
                 inspector.assertNoClassesMerged();
               }
             })
+        .addOptionsModification(
+            options -> {
+              assertFalse(options.horizontalClassMergerOptions().isInterfaceMergingEnabled());
+              options.horizontalClassMergerOptions().enableInterfaceMerging();
+              options.horizontalClassMergerOptions().setIgnoreRuntimeTypeChecksForTesting();
+            })
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesMergingTest.java
index e8bd34f..7ca9cc5 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesMergingTest.java
@@ -36,6 +36,11 @@
         .addKeepMainRule(Main.class)
         .addHorizontallyMergedClassesInspector(
             inspector -> inspector.assertIsCompleteMergeGroup(I.class, J.class))
+        .addOptionsModification(
+            options -> {
+              assertFalse(options.horizontalClassMergerOptions().isInterfaceMergingEnabled());
+              options.horizontalClassMergerOptions().enableInterfaceMerging();
+            })
         .enableNoUnusedInterfaceRemovalAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         .noClassInliningOfSynthetics()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithIntersectionMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithIntersectionMergingTest.java
index a88f6c1..dad75a4 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithIntersectionMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithIntersectionMergingTest.java
@@ -36,6 +36,11 @@
         .addKeepMainRule(Main.class)
         .addHorizontallyMergedClassesInspector(
             inspector -> inspector.assertIsCompleteMergeGroup(I.class, J.class))
+        .addOptionsModification(
+            options -> {
+              assertFalse(options.horizontalClassMergerOptions().isInterfaceMergingEnabled());
+              options.horizontalClassMergerOptions().enableInterfaceMerging();
+            })
         .enableNoUnusedInterfaceRemovalAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         .noClassInliningOfSynthetics()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithSameNameAndDifferentParametersMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithSameNameAndDifferentParametersMergingTest.java
index dae6f36..e58ca16 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithSameNameAndDifferentParametersMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithSameNameAndDifferentParametersMergingTest.java
@@ -38,6 +38,11 @@
         .addKeepMainRule(Main.class)
         .addHorizontallyMergedClassesInspector(
             inspector -> inspector.assertIsCompleteMergeGroup(I.class, J.class))
+        .addOptionsModification(
+            options -> {
+              assertFalse(options.horizontalClassMergerOptions().isInterfaceMergingEnabled());
+              options.horizontalClassMergerOptions().enableInterfaceMerging();
+            })
         .enableNoUnusedInterfaceRemovalAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         .noClassInliningOfSynthetics()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithSameNameAndDifferentReturnTypeMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithSameNameAndDifferentReturnTypeMergingTest.java
index e002263..a877121 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithSameNameAndDifferentReturnTypeMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointFunctionalInterfacesWithSameNameAndDifferentReturnTypeMergingTest.java
@@ -38,6 +38,11 @@
         .addKeepMainRule(Main.class)
         .addHorizontallyMergedClassesInspector(
             inspector -> inspector.assertIsCompleteMergeGroup(I.class, J.class))
+        .addOptionsModification(
+            options -> {
+              assertFalse(options.horizontalClassMergerOptions().isInterfaceMergingEnabled());
+              options.horizontalClassMergerOptions().enableInterfaceMerging();
+            })
         .enableNoUnusedInterfaceRemovalAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         .noClassInliningOfSynthetics()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithDefaultMethodsMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithDefaultMethodsMergingTest.java
index ef6ac67..0807547 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithDefaultMethodsMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithDefaultMethodsMergingTest.java
@@ -42,6 +42,11 @@
         .addKeepMainRule(Main.class)
         .addHorizontallyMergedClassesInspector(
             inspector -> inspector.assertIsCompleteMergeGroup(I.class, J.class))
+        .addOptionsModification(
+            options -> {
+              assertFalse(options.horizontalClassMergerOptions().isInterfaceMergingEnabled());
+              options.horizontalClassMergerOptions().enableInterfaceMerging();
+            })
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .enableNoUnusedInterfaceRemovalAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithoutDefaultMethodsMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithoutDefaultMethodsMergingTest.java
index f33ec1c..5f30eff 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithoutDefaultMethodsMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithoutDefaultMethodsMergingTest.java
@@ -42,6 +42,11 @@
         .addKeepMainRule(Main.class)
         .addHorizontallyMergedClassesInspector(
             inspector -> inspector.assertIsCompleteMergeGroup(I.class, J.class))
+        .addOptionsModification(
+            options -> {
+              assertFalse(options.horizontalClassMergerOptions().isInterfaceMergingEnabled());
+              options.horizontalClassMergerOptions().enableInterfaceMerging();
+            })
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .enableNoUnusedInterfaceRemovalAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/EmptyInterfaceChainMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/EmptyInterfaceChainMergingTest.java
deleted file mode 100644
index 80160a4..0000000
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/EmptyInterfaceChainMergingTest.java
+++ /dev/null
@@ -1,79 +0,0 @@
-// 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.classmerging.horizontal.interfaces;
-
-import static com.android.tools.r8.utils.codeinspector.Matchers.isImplementing;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.MatcherAssert.assertThat;
-
-import com.android.tools.r8.NoUnusedInterfaceRemoval;
-import com.android.tools.r8.NoVerticalClassMerging;
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class EmptyInterfaceChainMergingTest extends TestBase {
-
-  private final TestParameters parameters;
-
-  @Parameterized.Parameters(name = "{0}")
-  public static TestParametersCollection data() {
-    return getTestParameters().withAllRuntimesAndApiLevels().build();
-  }
-
-  public EmptyInterfaceChainMergingTest(TestParameters parameters) {
-    this.parameters = parameters;
-  }
-
-  @Test
-  public void test() throws Exception {
-    testForR8(parameters.getBackend())
-        .addInnerClasses(getClass())
-        .addKeepMainRule(Main.class)
-        .addHorizontallyMergedClassesInspector(
-            inspector ->
-                inspector
-                    .assertIsCompleteMergeGroup(I.class, J.class, K.class)
-                    .assertNoOtherClassesMerged())
-        .enableNoUnusedInterfaceRemovalAnnotations()
-        .enableNoVerticalClassMergingAnnotations()
-        .setMinApi(parameters.getApiLevel())
-        .compile()
-        .inspect(
-            inspector -> {
-              ClassSubject aClassSubject = inspector.clazz(A.class);
-              assertThat(aClassSubject, isPresent());
-              assertThat(aClassSubject, isImplementing(inspector.clazz(I.class)));
-            })
-        .run(parameters.getRuntime(), Main.class)
-        .assertSuccess();
-  }
-
-  static class Main {
-
-    public static void main(String[] args) {
-      System.out.println(A.class);
-    }
-  }
-
-  @NoUnusedInterfaceRemoval
-  @NoVerticalClassMerging
-  interface I {}
-
-  @NoUnusedInterfaceRemoval
-  @NoVerticalClassMerging
-  interface J extends I {}
-
-  @NoUnusedInterfaceRemoval
-  @NoVerticalClassMerging
-  interface K extends J {}
-
-  static class A implements K {}
-}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/EmptyInterfacesMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/EmptyInterfacesMergingTest.java
index 7cbdad2..24e5de8 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/EmptyInterfacesMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/EmptyInterfacesMergingTest.java
@@ -40,6 +40,11 @@
         .addKeepMainRule(Main.class)
         .addHorizontallyMergedClassesInspector(
             inspector -> inspector.assertIsCompleteMergeGroup(I.class, J.class))
+        .addOptionsModification(
+            options -> {
+              assertFalse(options.horizontalClassMergerOptions().isInterfaceMergingEnabled());
+              options.horizontalClassMergerOptions().enableInterfaceMerging();
+            })
         .enableNoUnusedInterfaceRemovalAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IdenticalFunctionalInterfacesMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IdenticalFunctionalInterfacesMergingTest.java
index f13b7e0..2d264db 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IdenticalFunctionalInterfacesMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IdenticalFunctionalInterfacesMergingTest.java
@@ -36,6 +36,11 @@
         .addKeepMainRule(Main.class)
         .addHorizontallyMergedClassesInspector(
             inspector -> inspector.assertIsCompleteMergeGroup(I.class, J.class))
+        .addOptionsModification(
+            options -> {
+              assertFalse(options.horizontalClassMergerOptions().isInterfaceMergingEnabled());
+              options.horizontalClassMergerOptions().enableInterfaceMerging();
+            })
         .enableNoUnusedInterfaceRemovalAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         .noClassInliningOfSynthetics()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IdenticalFunctionalInterfacesWithIntersectionMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IdenticalFunctionalInterfacesWithIntersectionMergingTest.java
index d529fc9..23ed1ea 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IdenticalFunctionalInterfacesWithIntersectionMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IdenticalFunctionalInterfacesWithIntersectionMergingTest.java
@@ -36,6 +36,11 @@
         .addKeepMainRule(Main.class)
         .addHorizontallyMergedClassesInspector(
             inspector -> inspector.assertIsCompleteMergeGroup(I.class, J.class))
+        .addOptionsModification(
+            options -> {
+              assertFalse(options.horizontalClassMergerOptions().isInterfaceMergingEnabled());
+              options.horizontalClassMergerOptions().enableInterfaceMerging();
+            })
         .enableNoUnusedInterfaceRemovalAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         .noClassInliningOfSynthetics()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/NoDefaultMethodMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/NoDefaultMethodMergingTest.java
index e8630bb..6ad6ef1 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/NoDefaultMethodMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/NoDefaultMethodMergingTest.java
@@ -50,6 +50,11 @@
                 inspector.assertIsCompleteMergeGroup(I.class, J.class);
               }
             })
+        .addOptionsModification(
+            options -> {
+              assertFalse(options.horizontalClassMergerOptions().isInterfaceMergingEnabled());
+              options.horizontalClassMergerOptions().enableInterfaceMerging();
+            })
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontalstatic/StaticClassMergerInterfaceTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontalstatic/StaticClassMergerInterfaceTest.java
index 6905202..1c33206 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontalstatic/StaticClassMergerInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontalstatic/StaticClassMergerInterfaceTest.java
@@ -40,17 +40,17 @@
     testForR8(parameters.getBackend())
         .addInnerClasses(getClass())
         .addKeepMainRule(TestClass.class)
+        // TODO(b/173990042): Extend horizontal class merging to interfaces.
         .addHorizontallyMergedClassesInspector(
             inspector -> {
               if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
-                inspector.assertIsCompleteMergeGroup(I.class, J.class).assertNoOtherClassesMerged();
+                inspector.assertNoClassesMerged();
               } else {
                 inspector
-                    .assertClassesNotMerged(I.class, J.class)
                     .assertClassReferencesMerged(
                         SyntheticItemsTestUtils.syntheticCompanionClass(I.class),
                         SyntheticItemsTestUtils.syntheticCompanionClass(J.class))
-                    .assertNoOtherClassesMerged();
+                    .assertClassesNotMerged(I.class, J.class);
               }
             })
         .enableInliningAnnotations()
@@ -62,9 +62,11 @@
               // We do not allow horizontal class merging of interfaces and classes. Therefore, A
               // should remain in the output.
               assertThat(inspector.clazz(A.class), isPresent());
+
+              // TODO(b/173990042): I and J should be merged.
               if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
                 assertThat(inspector.clazz(I.class), isPresent());
-                assertThat(inspector.clazz(J.class), isAbsent());
+                assertThat(inspector.clazz(J.class), isPresent());
               } else {
                 assertThat(inspector.clazz(syntheticCompanionClass(I.class)), isPresent());
                 assertThat(inspector.clazz(syntheticCompanionClass(J.class)), isAbsent());
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
index 2f157d0..5468cae 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
@@ -1052,8 +1052,7 @@
           CF_DIR.resolve("pkg/SimpleInterfaceImplRetriever.class"),
           CF_DIR.resolve("pkg/SimpleInterfaceImplRetriever$SimpleInterfaceImpl.class"),
           CF_DIR.resolve("pkg/SimpleInterfaceImplRetriever$1.class"),
-          CF_DIR.resolve("NeverInline.class"),
-          CF_DIR.resolve("NoHorizontalClassMerging.class")
+          CF_DIR.resolve("NeverInline.class")
         };
     // SimpleInterface cannot be merged into SimpleInterfaceImpl because SimpleInterfaceImpl
     // is in a different package and is not public.
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java
index 71ddab8..1bfbaac 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java
@@ -63,7 +63,7 @@
                 .addOptionsModification(
                     options ->
                         options.testing.horizontalClassMergingTarget =
-                            (appView, candidates, target) -> candidates.iterator().next())
+                            (candidates, target) -> candidates.iterator().next())
                 .addHorizontallyMergedClassesInspector(
                     inspector ->
                         inspector.assertMergedInto(BaseWithStatic.class, AFeatureWithStatic.class))
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceRemovalTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceRemovalTest.java
index 12bc414..9d11655 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceRemovalTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceRemovalTest.java
@@ -85,7 +85,6 @@
     void foo();
   }
 
-  @NoHorizontalClassMerging
   @NoVerticalClassMerging
   interface J {
 
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceWithDefaultMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceWithDefaultMethodTest.java
index 6bc0862..768fbd6 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceWithDefaultMethodTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceWithDefaultMethodTest.java
@@ -10,7 +10,6 @@
 
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.NoHorizontalClassMerging;
 import com.android.tools.r8.NoVerticalClassMerging;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
@@ -43,7 +42,6 @@
         .addKeepMainRule(TestClass.class)
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
-        .enableNoHorizontalClassMergingAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         .setMinApi(parameters.getApiLevel())
         .compile()
@@ -88,7 +86,6 @@
     void m();
   }
 
-  @NoHorizontalClassMerging
   @NoVerticalClassMerging
   interface J extends I {
 
diff --git a/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java b/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
index de5d7f0..550356e 100644
--- a/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
@@ -12,26 +12,21 @@
 
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.R8;
-import com.android.tools.r8.R8FullTestBuilder;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
 import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import com.android.tools.r8.utils.graphinspector.GraphInspector;
-import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 import com.google.common.collect.Sets.SetView;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.Collections;
-import java.util.Comparator;
 import java.util.List;
 import java.util.Set;
 import java.util.TreeSet;
@@ -209,7 +204,6 @@
             .addKeepRules("-keepclassmembers class * { @" + PRESENT_ANNOTATION + " *** *(...); }")
             .addDontWarnGoogle()
             .addDontWarnJavaxNullableAnnotation()
-            .apply(this::configureHorizontalClassMerging)
             .compile()
             .graphInspector();
 
@@ -228,7 +222,6 @@
                     + " *** *(...); }")
             .addDontWarnGoogle()
             .addDontWarnJavaxNullableAnnotation()
-            .apply(this::configureHorizontalClassMerging)
             .compile()
             .graphInspector();
     assertRetainedClassesEqual(referenceInspector, ifThenKeepClassMembersInspector);
@@ -248,7 +241,6 @@
                     + " *** *(...); }")
             .addDontWarnGoogle()
             .addDontWarnJavaxNullableAnnotation()
-            .apply(this::configureHorizontalClassMerging)
             .compile()
             .graphInspector();
     assertRetainedClassesEqual(referenceInspector, ifThenKeepClassesWithMembersInspector);
@@ -270,27 +262,11 @@
                     + " *** <2>(...); }")
             .addDontWarnGoogle()
             .addDontWarnJavaxNullableAnnotation()
-            .apply(this::configureHorizontalClassMerging)
             .compile()
             .graphInspector();
     assertRetainedClassesEqual(referenceInspector, ifHasMemberThenKeepClassInspector);
   }
 
-  private void configureHorizontalClassMerging(R8FullTestBuilder testBuilder) {
-    // Attempt to ensure similar class merging across different builds by choosing the merge target
-    // as the class with the lexicographically smallest original name.
-    testBuilder.addOptionsModification(
-        options ->
-            options.testing.horizontalClassMergingTarget =
-                (appView, candidates, target) -> {
-                  List<DexProgramClass> classes = Lists.newArrayList(candidates);
-                  classes.sort(
-                      Comparator.comparing(
-                          clazz -> appView.graphLens().getOriginalType(clazz.getType())));
-                  return ListUtils.first(classes);
-                });
-  }
-
   private void assertRetainedClassesEqual(
       GraphInspector referenceResult, GraphInspector conditionalResult) {
     assertRetainedClassesEqual(referenceResult, conditionalResult, false, false);
diff --git a/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java b/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
index 7f6323a..69b0457 100644
--- a/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
@@ -11,11 +11,10 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 
-import com.android.tools.r8.NoHorizontalClassMerging;
-import com.android.tools.r8.SingleTestRunResult;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.jasmin.JasminBuilder;
@@ -34,17 +33,14 @@
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
 
-@NoHorizontalClassMerging
 interface B112452064SuperInterface1 {
   void foo();
 }
 
-@NoHorizontalClassMerging
 interface B112452064SuperInterface2 {
   void bar();
 }
 
-@NoHorizontalClassMerging
 interface B112452064SubInterface extends B112452064SuperInterface1, B112452064SuperInterface2 {}
 
 class B112452064TestMain {
@@ -85,8 +81,7 @@
 
   @Parameters(name = "{1}, argument removal: {0}")
   public static List<Object[]> data() {
-    return buildParameters(
-        BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
+    return buildParameters(BooleanUtils.values(), getTestParameters().withAllRuntimes().build());
   }
 
   public ParameterTypeTest(boolean enableArgumentRemoval, TestParameters parameters) {
@@ -132,8 +127,7 @@
                   options.enableUnusedInterfaceRemoval = enableUnusedInterfaceRemoval;
                   options.enableVerticalClassMerging = enableVerticalClassMerging;
                 })
-            .enableNoHorizontalClassMergingAnnotations()
-            .setMinApi(parameters.getApiLevel())
+            .setMinApi(parameters.getRuntime())
             .compile()
             .run(parameters.getRuntime(), B112452064TestMain.class)
             .assertSuccessWithOutput(javaResult.stdout)
@@ -291,6 +285,7 @@
         "return");
 
     final String mainClassName = mainClass.name;
+    String proguardConfig = keepMainProguardConfiguration(mainClassName, false, false);
 
     // Run input program on java.
     Path outputDirectory = temp.newFolder().toPath();
@@ -300,34 +295,36 @@
     assertThat(javaResult.stdout, containsString(bar.name));
     assertEquals(-1, javaResult.stderr.indexOf("ClassNotFoundException"));
 
-    testForR8(parameters.getBackend())
-        .addProgramClassFileData(jasminBuilder.buildClasses())
-        .addKeepMainRule(mainClassName)
-        .addOptionsModification(
+    AndroidApp processedApp =
+        compileWithR8(
+            jasminBuilder.build(),
+            proguardConfig,
             options -> {
               // Disable inlining to avoid the (short) tested method from being inlined and removed.
               options.enableInlining = false;
               options.enableArgumentRemoval = enableArgumentRemoval;
-            })
-        .noMinification()
-        .setMinApi(parameters.getApiLevel())
-        .compile()
-        .inspect(
-            inspector -> {
-              ClassSubject subSubject = inspector.clazz(sub.name);
-              assertNotEquals(enableArgumentRemoval, subSubject.isPresent());
-            })
-        .run(parameters.getRuntime(), mainClassName)
-        .applyIf(
-            enableArgumentRemoval || parameters.isCfRuntime(),
-            SingleTestRunResult::assertSuccess,
-            result ->
-                result.assertFailureWithErrorThatMatches(
-                    containsString(
-                        parameters.getDexRuntimeVersion().isNewerThan(Version.V4_4_4)
-                            ? "type Precise Reference: Foo[] but expected Reference: SubInterface[]"
-                            : "[LFoo; is not instance of [LSubInterface;")))
-        .assertStderrMatches(not(containsString("ClassNotFoundException")));
+            });
+
+    // Run processed (output) program on ART
+    ProcessResult artResult = runOnArtRaw(processedApp, mainClassName);
+    if (enableArgumentRemoval) {
+      assertEquals(0, artResult.exitCode);
+    } else {
+      assertNotEquals(0, artResult.exitCode);
+
+      DexVm.Version currentVersion = ToolHelper.getDexVm().getVersion();
+      String errorMessage =
+          currentVersion.isNewerThan(Version.V4_4_4)
+              ? "type Precise Reference: Foo[] but expected Reference: SubInterface[]"
+              : "[LFoo; is not instance of [LSubInterface;";
+      assertThat(artResult.stderr, containsString(errorMessage));
+    }
+
+    assertEquals(-1, artResult.stderr.indexOf("ClassNotFoundException"));
+
+    CodeInspector inspector = new CodeInspector(processedApp);
+    ClassSubject subSubject = inspector.clazz(sub.name);
+    assertNotEquals(enableArgumentRemoval, subSubject.isPresent());
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitializationTriggersIndirectInterfaceInitializationTest.java b/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitializationTriggersIndirectInterfaceInitializationTest.java
index b427736..c6e5eff 100644
--- a/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitializationTriggersIndirectInterfaceInitializationTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitializationTriggersIndirectInterfaceInitializationTest.java
@@ -12,7 +12,6 @@
 
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.NoHorizontalClassMerging;
 import com.android.tools.r8.NoUnusedInterfaceRemoval;
 import com.android.tools.r8.NoVerticalClassMerging;
 import com.android.tools.r8.TestBase;
@@ -44,7 +43,6 @@
         .addKeepMainRule(Main.class)
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
-        .enableNoHorizontalClassMergingAnnotations()
         .enableNoUnusedInterfaceRemovalAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         .setMinApi(parameters.getApiLevel())
@@ -81,7 +79,6 @@
     }
   }
 
-  @NoHorizontalClassMerging
   @NoUnusedInterfaceRemoval
   @NoVerticalClassMerging
   interface I {
@@ -97,7 +94,6 @@
     }
   }
 
-  @NoHorizontalClassMerging
   @NoUnusedInterfaceRemoval
   @NoVerticalClassMerging
   interface J extends I {}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
index b39fd97..0410bc0 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
@@ -21,7 +21,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import java.util.function.BiConsumer;
@@ -33,8 +32,6 @@
   private final DexItemFactory dexItemFactory;
   private final HorizontallyMergedClasses horizontallyMergedClasses;
 
-  private final Set<ClassReference> seen = new HashSet<>();
-
   public HorizontallyMergedClassesInspector(
       DexItemFactory dexItemFactory, HorizontallyMergedClasses horizontallyMergedClasses) {
     this.dexItemFactory = dexItemFactory;
@@ -105,7 +102,6 @@
             + StringUtils.join(", ", unmerged, DexType::getTypeName),
         0,
         unmerged.size());
-    seen.addAll(types.stream().map(DexType::asClassReference).collect(Collectors.toList()));
     return this;
   }
 
@@ -121,17 +117,6 @@
     return this;
   }
 
-  public HorizontallyMergedClassesInspector assertNoOtherClassesMerged() {
-    horizontallyMergedClasses.forEachMergeGroup(
-        (sources, target) -> {
-          for (DexType source : sources) {
-            assertTrue(source.getTypeName(), seen.contains(source.asClassReference()));
-          }
-          assertTrue(target.getTypeName(), seen.contains(target.asClassReference()));
-        });
-    return this;
-  }
-
   public HorizontallyMergedClassesInspector assertClassesNotMerged(Class<?>... classes) {
     return assertClassesNotMerged(Arrays.asList(classes));
   }
@@ -160,7 +145,6 @@
       assertTrue(type.isClassType());
       assertFalse(horizontallyMergedClasses.hasBeenMergedOrIsMergeTarget(type));
     }
-    seen.addAll(types.stream().map(DexType::asClassReference).collect(Collectors.toList()));
     return this;
   }
 
@@ -220,7 +204,6 @@
         classReferences.size() - 1,
         sources.size());
     assertTrue(types.containsAll(sources));
-    seen.addAll(classReferences);
     return this;
   }