Full horizontal class merging before writing

Bug: b/324527514
Fixes: b/296852026
Change-Id: I63ce734ee2da1258364764f88ed44b9a5bbeeba4
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 2717f8a..eb4825c 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -78,6 +78,7 @@
 import com.android.tools.r8.optimize.interfaces.analysis.CfOpenClosedInterfacesAnalysis;
 import com.android.tools.r8.optimize.proto.ProtoNormalizer;
 import com.android.tools.r8.optimize.redundantbridgeremoval.RedundantBridgeRemover;
+import com.android.tools.r8.optimize.singlecaller.SingleCallerInliner;
 import com.android.tools.r8.origin.CommandLineOrigin;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.profile.art.ArtProfileCompletenessChecker;
@@ -769,6 +770,7 @@
               .runIfNecessary(executorService, timing);
           assert appView.dexItemFactory().verifyNoCachedTypeElements();
 
+          new SingleCallerInliner(appViewWithLiveness).runIfNecessary(executorService, timing);
           new ProtoNormalizer(appViewWithLiveness).run(executorService, timing);
         }
       }
diff --git a/src/main/java/com/android/tools/r8/classmerging/ClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/classmerging/ClassMergerGraphLens.java
index 300d399..7408427 100644
--- a/src/main/java/com/android/tools/r8/classmerging/ClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/classmerging/ClassMergerGraphLens.java
@@ -9,10 +9,8 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.classmerging.MergedClasses;
 import com.android.tools.r8.graph.lens.NestedGraphLens;
-import com.android.tools.r8.ir.conversion.ExtraParameter;
 import com.android.tools.r8.utils.collections.BidirectionalManyToManyRepresentativeMap;
 import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeMap;
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -30,9 +28,6 @@
   public abstract static class BuilderBase<
       GL extends ClassMergerGraphLens, MC extends MergedClasses> {
 
-    public abstract void addExtraParameters(
-        DexMethod from, DexMethod to, List<? extends ExtraParameter> extraParameters);
-
     public abstract void commitPendingUpdates();
 
     public abstract void fixupField(DexField from, DexField to);
diff --git a/src/main/java/com/android/tools/r8/classmerging/ClassMergerMode.java b/src/main/java/com/android/tools/r8/classmerging/ClassMergerMode.java
index 9c459af..bb9a37b 100644
--- a/src/main/java/com/android/tools/r8/classmerging/ClassMergerMode.java
+++ b/src/main/java/com/android/tools/r8/classmerging/ClassMergerMode.java
@@ -14,17 +14,4 @@
   public boolean isFinal() {
     return this == FINAL;
   }
-
-  public boolean isRestrictedToAlphaRenaming() {
-    return isFinal();
-  }
-
-  // Similar to isRestrictedToAlphaRenaming(), but used on paths that are R8 specific. All usages of
-  // this method should be removed as a result of running a full final round of horizontal class
-  // merging.
-  @Deprecated
-  @SuppressWarnings("InlineMeSuggester")
-  public boolean isRestrictedToAlphaRenamingInR8() {
-    return isRestrictedToAlphaRenaming();
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/classmerging/ClassMergerTreeFixer.java b/src/main/java/com/android/tools/r8/classmerging/ClassMergerTreeFixer.java
index 6f58680..c1e27ad 100644
--- a/src/main/java/com/android/tools/r8/classmerging/ClassMergerTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/classmerging/ClassMergerTreeFixer.java
@@ -3,7 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.classmerging;
 
-import static com.android.tools.r8.ir.conversion.ExtraUnusedParameter.computeExtraUnusedParameters;
+import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
 
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
@@ -18,9 +18,9 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DexTypeList;
 import com.android.tools.r8.graph.EnclosingMethodAttribute;
+import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
 import com.android.tools.r8.graph.classmerging.MergedClasses;
 import com.android.tools.r8.graph.fixup.TreeFixerBase;
-import com.android.tools.r8.horizontalclassmerging.SubtypingForrestForClasses;
 import com.android.tools.r8.shaking.AnnotationFixer;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.ArrayUtils;
@@ -32,8 +32,10 @@
 import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.IdentityHashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
@@ -46,9 +48,10 @@
         MC extends MergedClasses>
     extends TreeFixerBase {
 
+  private final ClassMergerSharedData classMergerSharedData;
+  private final ImmediateProgramSubtypingInfo immediateSubtypingInfo;
   protected final LB lensBuilder;
   protected final MC mergedClasses;
-  private final ClassMergerSharedData classMergerSharedData;
 
   private final Map<DexProgramClass, DexType> originalSuperTypes = new IdentityHashMap<>();
 
@@ -59,10 +62,12 @@
   public ClassMergerTreeFixer(
       AppView<?> appView,
       ClassMergerSharedData classMergerSharedData,
+      ImmediateProgramSubtypingInfo immediateSubtypingInfo,
       LB lensBuilder,
       MC mergedClasses) {
     super(appView);
     this.classMergerSharedData = classMergerSharedData;
+    this.immediateSubtypingInfo = immediateSubtypingInfo;
     this.lensBuilder = lensBuilder;
     this.mergedClasses = mergedClasses;
   }
@@ -78,12 +83,10 @@
     Iterables.filter(classes, DexProgramClass::isInterface).forEach(this::fixupInterfaceClass);
     classes.forEach(this::fixupAttributes);
     classes.forEach(this::fixupProgramClassSuperTypes);
-    SubtypingForrestForClasses subtypingForrest =
-        new SubtypingForrestForClasses(appView.withClassHierarchy());
+
     // TODO(b/170078037): parallelize this code segment.
-    for (DexProgramClass root : subtypingForrest.getProgramRoots()) {
-      subtypingForrest.traverseNodeDepthFirst(
-          root, new DexMethodSignatureBiMap<>(), this::fixupProgramClass);
+    for (DexProgramClass root : getRoots()) {
+      traverseProgramClassesDepthFirst(root, new DexMethodSignatureBiMap<>());
     }
     postprocess();
     GL lens = lensBuilder.build(appViewWithLiveness, mergedClasses);
@@ -92,6 +95,38 @@
     return lens;
   }
 
+  private List<DexProgramClass> getRoots() {
+    List<DexProgramClass> roots = new ArrayList<>();
+    for (DexProgramClass clazz : appView.appInfo().classes()) {
+      if (!clazz.isInterface() && !mergedClasses.isMergeSource(clazz.getType())) {
+        DexProgramClass superClass =
+            asProgramClassOrNull(appView.definitionFor(clazz.getSuperType(), clazz));
+        if (superClass == null) {
+          roots.add(clazz);
+        }
+      }
+    }
+    return roots;
+  }
+
+  private void traverseProgramClassesDepthFirst(
+      DexProgramClass clazz, DexMethodSignatureBiMap<DexMethodSignature> state) {
+    DexMethodSignatureBiMap<DexMethodSignature> newState = fixupProgramClass(clazz, state);
+    for (DexProgramClass subclass : immediateSubtypingInfo.getSubclasses(clazz)) {
+      traverseProgramClassesDepthFirst(subclass, newState);
+    }
+    if (mergedClasses.isMergeTarget(clazz.getType())) {
+      for (DexType sourceType : mergedClasses.getSourcesFor(clazz.getType())) {
+        DexProgramClass sourceClass = appView.definitionFor(sourceType).asProgramClass();
+        for (DexProgramClass subclass : immediateSubtypingInfo.getSubclasses(sourceClass)) {
+          if (subclass != clazz) {
+            traverseProgramClassesDepthFirst(subclass, newState);
+          }
+        }
+      }
+    }
+  }
+
   public void preprocess() {
     // Intentionally empty.
   }
@@ -289,10 +324,6 @@
                     newMethodReference,
                     classMergerSharedData.getExtraUnusedArgumentTypes(),
                     tryMethod -> !newMethodSignatures.containsValue(tryMethod.getSignature()));
-            lensBuilder.addExtraParameters(
-                originalMethodReference,
-                newMethodReference,
-                computeExtraUnusedParameters(originalMethodReference, newMethodReference));
           } else {
             newMethodReference =
                 dexItemFactory.createFreshMethodNameWithoutHolder(
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 5d0b33c..9618cd6 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -242,7 +242,6 @@
 
   void appendClassIdField() {
     assert appView.hasLiveness();
-    assert !mode.isRestrictedToAlphaRenamingInR8();
 
     DexEncodedField classIdField =
         DexEncodedField.syntheticBuilder()
@@ -373,11 +372,14 @@
   }
 
   public static class Builder {
+
     private final AppView<?> appView;
     private final IRCodeProvider codeProvider;
     private final ClassMergerMode mode;
     private final HorizontalMergeGroup group;
 
+    private List<VirtualMethodMerger> virtualMethodMergers;
+
     public Builder(
         AppView<?> appView,
         IRCodeProvider codeProvider,
@@ -389,19 +391,19 @@
       this.mode = mode;
     }
 
-    @SuppressWarnings("MixedMutabilityReturnType")
-    private List<VirtualMethodMerger> createVirtualMethodMergers() {
+    public Builder initializeVirtualMethodMergers() {
       if (!appView.hasClassHierarchy()) {
         assert getVirtualMethodMergerBuilders().isEmpty();
-        return Collections.emptyList();
+        virtualMethodMergers = Collections.emptyList();
+        return this;
       }
       Map<DexMethodSignature, VirtualMethodMerger.Builder> virtualMethodMergerBuilders =
           getVirtualMethodMergerBuilders();
       if (virtualMethodMergerBuilders.isEmpty()) {
-        return Collections.emptyList();
+        virtualMethodMergers = Collections.emptyList();
+        return this;
       }
-      List<VirtualMethodMerger> virtualMethodMergers =
-          new ArrayList<>(virtualMethodMergerBuilders.size());
+      virtualMethodMergers = new ArrayList<>(virtualMethodMergerBuilders.size());
       List<VirtualMethodMerger> nonNopOrTrivialVirtualMethodMergers =
           new ArrayList<>(virtualMethodMergerBuilders.size());
       for (VirtualMethodMerger.Builder builder : virtualMethodMergerBuilders.values()) {
@@ -418,7 +420,7 @@
       // first, then the non trivial mergers are processed and correctly deal with conflicting
       // signatures.
       assert verifyNopOrTrivialAreFirst(virtualMethodMergers);
-      return virtualMethodMergers;
+      return this;
     }
 
     private boolean verifyNopOrTrivialAreFirst(List<VirtualMethodMerger> virtualMethodMergers) {
@@ -450,6 +452,16 @@
       return virtualMethodMergerBuilders;
     }
 
+    public void initializeClassIdField() {
+      boolean requiresClassIdField =
+          virtualMethodMergers.stream()
+              .anyMatch(virtualMethodMerger -> !virtualMethodMerger.isNopOrTrivial());
+      if (requiresClassIdField) {
+        assert appView.enableWholeProgramOptimizations();
+        createClassIdField();
+      }
+    }
+
     private void createClassIdField() {
       DexField field =
           appView
@@ -467,16 +479,6 @@
 
     public ClassMerger build(
         HorizontalClassMergerGraphLens.Builder lensBuilder) {
-      List<VirtualMethodMerger> virtualMethodMergers = createVirtualMethodMergers();
-
-      boolean requiresClassIdField =
-          virtualMethodMergers.stream()
-              .anyMatch(virtualMethodMerger -> !virtualMethodMerger.isNopOrTrivial());
-      if (requiresClassIdField) {
-        assert !mode.isRestrictedToAlphaRenaming();
-        createClassIdField();
-      }
-
       return new ClassMerger(appView, codeProvider, mode, lensBuilder, group, virtualMethodMergers);
     }
   }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
index 18f1d1a..849e67a 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -16,6 +16,7 @@
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.PrunedItems;
 import com.android.tools.r8.horizontalclassmerging.code.SyntheticInitializerConverter;
@@ -116,8 +117,7 @@
       Timing timing)
       throws ExecutionException {
     // Run the policies on all program classes to produce a final grouping.
-    List<Policy> policies =
-        PolicyScheduler.getPolicies(appView, codeProvider, mode, runtimeTypeCheckInfo);
+    List<Policy> policies = PolicyScheduler.getPolicies(appView, mode, runtimeTypeCheckInfo);
     Collection<HorizontalMergeGroup> groups =
         new HorizontalClassMergerPolicyExecutor()
             .run(getInitialGroups(), policies, executorService, timing);
@@ -128,12 +128,26 @@
       return;
     }
 
+    ClassMergerSharedData classMergerSharedData = new ClassMergerSharedData(appView);
     HorizontalClassMergerGraphLens.Builder lensBuilder =
         new HorizontalClassMergerGraphLens.Builder();
 
-    // Merge the classes.
-    List<ClassMerger> classMergers = initializeClassMergers(codeProvider, lensBuilder, groups);
-    ClassMergerSharedData classMergerSharedData = new ClassMergerSharedData(appView);
+    // Determine which classes need a class id field.
+    List<ClassMerger.Builder> classMergerBuilders = createClassMergerBuilders(codeProvider, groups);
+    initializeClassIdFields(classMergerBuilders);
+
+    // Ensure that all allocations of classes that end up needing a class id use a constructor on
+    // the class itself.
+    ImmediateProgramSubtypingInfo immediateSubtypingInfo =
+        appView.enableWholeProgramOptimizations()
+            ? ImmediateProgramSubtypingInfo.createWithDeterministicOrder(
+                appView.withClassHierarchy())
+            : null;
+    new UndoConstructorInlining(appView, classMergerSharedData, immediateSubtypingInfo)
+        .runIfNecessary(groups, mode, executorService, timing);
+
+    // Merge classes.
+    List<ClassMerger> classMergers = createClassMergers(classMergerBuilders, lensBuilder);
     ProfileCollectionAdditions profileCollectionAdditions =
         ProfileCollectionAdditions.create(appView);
     SyntheticInitializerConverter.Builder syntheticInitializerConverterBuilder =
@@ -158,7 +172,13 @@
     appView.setHorizontallyMergedClasses(mergedClasses, mode);
 
     HorizontalClassMergerGraphLens horizontalClassMergerGraphLens =
-        createLens(classMergerSharedData, mergedClasses, lensBuilder, executorService, timing);
+        createLens(
+            classMergerSharedData,
+            immediateSubtypingInfo,
+            mergedClasses,
+            lensBuilder,
+            executorService,
+            timing);
     profileCollectionAdditions =
         profileCollectionAdditions.rewriteMethodReferences(
             horizontalClassMergerGraphLens::getNextMethodToInvoke);
@@ -166,10 +186,10 @@
     assert verifyNoCyclesInInterfaceHierarchies(appView, groups);
 
     FieldAccessInfoCollectionModifier fieldAccessInfoCollectionModifier = null;
-    if (mode.isRestrictedToAlphaRenaming()) {
-      assert groups.stream().noneMatch(HorizontalMergeGroup::hasClassIdField);
-    } else {
+    if (appView.enableWholeProgramOptimizations()) {
       fieldAccessInfoCollectionModifier = createFieldAccessInfoCollectionModifier(groups);
+    } else {
+      assert groups.stream().noneMatch(HorizontalMergeGroup::hasClassIdField);
     }
 
     // Set the new graph lens before finalizing any synthetic code.
@@ -381,21 +401,34 @@
     return initialGroups;
   }
 
-  /**
-   * Prepare horizontal class merging by determining which virtual methods and constructors need to
-   * be merged and how the merging should be performed.
-   */
-  private List<ClassMerger> initializeClassMergers(
-      IRCodeProvider codeProvider,
-      HorizontalClassMergerGraphLens.Builder lensBuilder,
-      Collection<HorizontalMergeGroup> groups) {
-    List<ClassMerger> classMergers = new ArrayList<>(groups.size());
+  private List<ClassMerger.Builder> createClassMergerBuilders(
+      IRCodeProvider codeProvider, Collection<HorizontalMergeGroup> groups) {
+    List<ClassMerger.Builder> classMergerBuilders = new ArrayList<>(groups.size());
     for (HorizontalMergeGroup group : groups) {
       assert group.isNonTrivial();
       assert group.hasInstanceFieldMap();
       assert group.hasTarget();
-      classMergers.add(
-          new ClassMerger.Builder(appView, codeProvider, group, mode).build(lensBuilder));
+      classMergerBuilders.add(new ClassMerger.Builder(appView, codeProvider, group, mode));
+    }
+    return classMergerBuilders;
+  }
+
+  private void initializeClassIdFields(List<ClassMerger.Builder> classMergerBuilders) {
+    for (ClassMerger.Builder classMergerBuilder : classMergerBuilders) {
+      classMergerBuilder.initializeVirtualMethodMergers().initializeClassIdField();
+    }
+  }
+
+  /**
+   * Prepare horizontal class merging by determining which virtual methods and constructors need to
+   * be merged and how the merging should be performed.
+   */
+  private List<ClassMerger> createClassMergers(
+      List<ClassMerger.Builder> classMergerBuilders,
+      HorizontalClassMergerGraphLens.Builder lensBuilder) {
+    List<ClassMerger> classMergers = new ArrayList<>(classMergerBuilders.size());
+    for (ClassMerger.Builder classMergerBuilder : classMergerBuilders) {
+      classMergers.add(classMergerBuilder.build(lensBuilder));
     }
     appView.dexItemFactory().clearTypeElementsCache();
     return classMergers;
@@ -427,13 +460,14 @@
    */
   private HorizontalClassMergerGraphLens createLens(
       ClassMergerSharedData classMergerSharedData,
+      ImmediateProgramSubtypingInfo immediateSubtypingInfo,
       HorizontallyMergedClasses mergedClasses,
       HorizontalClassMergerGraphLens.Builder lensBuilder,
       ExecutorService executorService,
       Timing timing)
       throws ExecutionException {
     return new HorizontalClassMergerTreeFixer(
-            appView, classMergerSharedData, mergedClasses, lensBuilder)
+            appView, classMergerSharedData, immediateSubtypingInfo, mergedClasses, lensBuilder)
         .run(executorService, timing);
   }
 
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
index 81d95ee..17bfc2b 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.horizontalclassmerging;
 
+import static com.android.tools.r8.ir.conversion.ExtraUnusedParameter.computeExtraUnusedParameters;
+
 import com.android.tools.r8.classmerging.ClassMergerGraphLens;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexEncodedMember;
@@ -15,7 +17,9 @@
 import com.android.tools.r8.graph.lens.FieldLookupResult;
 import com.android.tools.r8.graph.lens.GraphLens;
 import com.android.tools.r8.graph.lens.MethodLookupResult;
+import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import com.android.tools.r8.ir.code.InvokeType;
+import com.android.tools.r8.ir.conversion.ExtraConstantIntParameter;
 import com.android.tools.r8.ir.conversion.ExtraParameter;
 import com.android.tools.r8.utils.IterableUtils;
 import com.android.tools.r8.utils.collections.BidirectionalManyToOneHashMap;
@@ -26,7 +30,6 @@
 import com.android.tools.r8.utils.collections.MutableBidirectionalManyToOneRepresentativeMap;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Streams;
-import java.util.ArrayList;
 import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
@@ -34,13 +37,13 @@
 
 public class HorizontalClassMergerGraphLens extends ClassMergerGraphLens {
 
-  private final Map<DexMethod, List<ExtraParameter>> methodExtraParameters;
+  private final Map<DexMethod, ExtraConstantIntParameter> methodExtraParameters;
   private final HorizontallyMergedClasses mergedClasses;
 
   private HorizontalClassMergerGraphLens(
       AppView<?> appView,
       HorizontallyMergedClasses mergedClasses,
-      Map<DexMethod, List<ExtraParameter>> methodExtraParameters,
+      Map<DexMethod, ExtraConstantIntParameter> methodExtraParameters,
       BidirectionalManyToOneRepresentativeMap<DexField, DexField> fieldMap,
       BidirectionalManyToOneMap<DexMethod, DexMethod> methodMap,
       BidirectionalManyToOneRepresentativeMap<DexMethod, DexMethod> newMethodSignatures) {
@@ -116,18 +119,7 @@
       return super.internalDescribeLookupMethod(previous, context, codeLens);
     }
     assert previous.hasReboundReference();
-    List<ExtraParameter> extraParameters =
-        methodExtraParameters.get(previous.getReboundReference());
-    MethodLookupResult lookup = super.internalDescribeLookupMethod(previous, context, codeLens);
-    if (extraParameters == null) {
-      return lookup;
-    }
-    return MethodLookupResult.builder(this, codeLens)
-        .setReboundReference(lookup.getReboundReference())
-        .setReference(lookup.getReference())
-        .setPrototypeChanges(lookup.getPrototypeChanges().withExtraParameters(extraParameters))
-        .setType(lookup.getType())
-        .build();
+    return super.internalDescribeLookupMethod(previous, context, codeLens);
   }
 
   @Override
@@ -147,6 +139,47 @@
         .build();
   }
 
+  @Override
+  protected RewrittenPrototypeDescription internalDescribePrototypeChanges(
+      RewrittenPrototypeDescription prototypeChanges,
+      DexMethod previousMethod,
+      DexMethod newMethod) {
+    if (newMethod.getArity() > previousMethod.getArity()) {
+      assert dexItemFactory().isConstructor(previousMethod);
+      RewrittenPrototypeDescription collisionResolution =
+          RewrittenPrototypeDescription.createForExtraParameters(
+              computeExtraUnusedParameters(previousMethod, newMethod));
+      ExtraConstantIntParameter extraParameter = methodExtraParameters.get(previousMethod);
+      if (extraParameter != null) {
+        List<ExtraParameter> extraParameters =
+            (List<ExtraParameter>) collisionResolution.getExtraParameters();
+        extraParameters.set(0, extraParameter);
+      }
+      return prototypeChanges.combine(collisionResolution);
+    }
+    assert newMethod.getArity() == previousMethod.getArity();
+    return prototypeChanges;
+  }
+
+  @Override
+  public RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(
+      DexMethod newMethod, GraphLens codeLens) {
+    if (this == codeLens) {
+      return getIdentityLens().lookupPrototypeChangesForMethodDefinition(newMethod, codeLens);
+    }
+    DexMethod previousMethod = getPreviousMethodSignature(newMethod);
+    RewrittenPrototypeDescription lookup =
+        getPrevious().lookupPrototypeChangesForMethodDefinition(previousMethod, codeLens);
+    if (newMethod.getArity() > previousMethod.getArity()) {
+      assert dexItemFactory().isConstructor(previousMethod);
+      RewrittenPrototypeDescription collisionResolution =
+          RewrittenPrototypeDescription.createForExtraParameters(
+              computeExtraUnusedParameters(previousMethod, newMethod));
+      return lookup.combine(collisionResolution);
+    }
+    return lookup;
+  }
+
   public static class Builder
       extends BuilderBase<HorizontalClassMergerGraphLens, HorizontallyMergedClasses> {
 
@@ -156,7 +189,7 @@
         BidirectionalManyToOneHashMap.newIdentityHashMap();
     private final MutableBidirectionalManyToOneRepresentativeMap<DexMethod, DexMethod>
         newMethodSignatures = BidirectionalManyToOneRepresentativeHashMap.newIdentityHashMap();
-    private final Map<DexMethod, List<ExtraParameter>> methodExtraParameters =
+    private final Map<DexMethod, ExtraConstantIntParameter> methodExtraParameters =
         new IdentityHashMap<>();
 
     private final MutableBidirectionalManyToOneMap<DexMethod, DexMethod> pendingMethodMapUpdates =
@@ -329,28 +362,11 @@
      * where many constructors are merged into a single constructor. The synthesized constructor
      * therefore does not have a unique reverse constructor.
      */
-    void mapMergedConstructor(DexMethod from, DexMethod to, List<ExtraParameter> extraParameters) {
+    void mapMergedConstructor(
+        DexMethod from, DexMethod to, ExtraConstantIntParameter extraParameter) {
       mapMethod(from, to);
-      if (!extraParameters.isEmpty()) {
-        methodExtraParameters.put(from, extraParameters);
-      }
-    }
-
-    @Override
-    public void addExtraParameters(
-        DexMethod from, DexMethod to, List<? extends ExtraParameter> extraParameters) {
-      Set<DexMethod> originalMethodSignatures = methodMap.getKeys(from);
-      if (originalMethodSignatures.isEmpty()) {
-        methodExtraParameters
-            .computeIfAbsent(from, ignore -> new ArrayList<>(extraParameters.size()))
-            .addAll(extraParameters);
-      } else {
-        for (DexMethod originalMethodSignature : originalMethodSignatures) {
-          methodExtraParameters
-              .computeIfAbsent(
-                  originalMethodSignature, ignore -> new ArrayList<>(extraParameters.size()))
-              .addAll(extraParameters);
-        }
+      if (extraParameter != null) {
+        methodExtraParameters.put(from, extraParameter);
       }
     }
   }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerTreeFixer.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerTreeFixer.java
index b1859ae..794ca05 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerTreeFixer.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.classmerging.ClassMergerSharedData;
 import com.android.tools.r8.classmerging.ClassMergerTreeFixer;
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
 import com.android.tools.r8.utils.Timing;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
@@ -25,9 +26,10 @@
   public HorizontalClassMergerTreeFixer(
       AppView<?> appView,
       ClassMergerSharedData classMergerSharedData,
+      ImmediateProgramSubtypingInfo immediateSubtypingInfo,
       HorizontallyMergedClasses mergedClasses,
       HorizontalClassMergerGraphLens.Builder lensBuilder) {
-    super(appView, classMergerSharedData, lensBuilder, mergedClasses);
+    super(appView, classMergerSharedData, immediateSubtypingInfo, lensBuilder, mergedClasses);
   }
 
   /**
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteVirtuallyMergedMethodCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteVirtuallyMergedMethodCode.java
index 05088a9..75deda3 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteVirtuallyMergedMethodCode.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteVirtuallyMergedMethodCode.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.horizontalclassmerging;
 
+import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
+
 import com.android.tools.r8.cf.code.CfFrame;
 import com.android.tools.r8.cf.code.CfInstanceFieldRead;
 import com.android.tools.r8.cf.code.CfInstruction;
@@ -15,7 +17,6 @@
 import com.android.tools.r8.cf.code.CfSwitch;
 import com.android.tools.r8.cf.code.CfSwitch.Kind;
 import com.android.tools.r8.cf.code.frame.FrameType;
-import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.CfCode;
@@ -24,9 +25,16 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.lens.GraphLens;
 import com.android.tools.r8.horizontalclassmerging.VirtualMethodMerger.SuperMethodReference;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.code.IRMetadata;
+import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.lightir.LirBuilder;
 import com.android.tools.r8.lightir.LirCode;
+import com.android.tools.r8.lightir.LirEncodingStrategy;
+import com.android.tools.r8.lightir.LirStrategy;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.IterableUtils;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
@@ -164,7 +172,117 @@
       AppView<? extends AppInfoWithClassHierarchy> appView,
       ProgramMethod method,
       HorizontalClassMergerGraphLens lens) {
-    throw new Unreachable();
+    LirEncodingStrategy<Value, Integer> strategy =
+        LirStrategy.getDefaultStrategy().getEncodingStrategy();
+    LirBuilder<Value, Integer> lirBuilder =
+        LirCode.builder(
+                method.getReference(),
+                method.getDefinition().isD8R8Synthesized(),
+                strategy,
+                appView.options())
+            .setMetadata(IRMetadata.unknown());
+
+    int instructionIndex = 0;
+    List<Value> argumentValues = new ArrayList<>();
+    TypeElement returnType =
+        method.getReturnType().isVoidType() ? null : method.getReturnType().toTypeElement(appView);
+
+    // Add receiver argument.
+    DexType receiverType = method.getHolderType();
+    TypeElement receiverTypeElement = receiverType.toTypeElement(appView, definitelyNotNull());
+    Value receiverValue = Value.createNoDebugLocal(instructionIndex, receiverTypeElement);
+    argumentValues.add(receiverValue);
+    strategy.defineValue(receiverValue, receiverValue.getNumber());
+    lirBuilder.addArgument(receiverValue.getNumber(), false);
+    instructionIndex++;
+
+    // Add non-receiver arguments.
+    for (; instructionIndex < method.getDefinition().getNumberOfArguments(); instructionIndex++) {
+      DexType argumentType = method.getArgumentType(instructionIndex);
+      TypeElement argumentTypeElement = argumentType.toTypeElement(appView);
+      Value argumentValue = Value.createNoDebugLocal(instructionIndex, argumentTypeElement);
+      argumentValues.add(argumentValue);
+      strategy.defineValue(argumentValue, argumentValue.getNumber());
+      lirBuilder.addArgument(argumentValue.getNumber(), argumentType.isBooleanType());
+    }
+
+    // Read class id field from receiver.
+    TypeElement classIdValueType = TypeElement.getInt();
+    Value classIdValue = Value.createNoDebugLocal(instructionIndex, classIdValueType);
+    strategy.defineValue(classIdValue, classIdValue.getNumber());
+    lirBuilder.addInstanceGet(classIdField, receiverValue);
+    instructionIndex++;
+
+    // Emit switch.
+    IntBidirectionalIterator classIdIterator = mappedMethods.keySet().iterator();
+    int[] keys = new int[mappedMethods.size() - BooleanUtils.intValue(superMethod == null)];
+    int[] targets = new int[keys.length];
+    int nextTarget = instructionIndex - argumentValues.size() + 3;
+    for (int i = 0; i < keys.length; i++) {
+      keys[i] = classIdIterator.nextInt();
+      targets[i] = nextTarget;
+      nextTarget += 2;
+    }
+    lirBuilder.addIntSwitch(classIdValue, keys, targets);
+    instructionIndex++;
+
+    // Emit switch fallthrough.
+    if (superMethod == null) {
+      DexMethod fallthroughTarget =
+          lens.getNextMethodSignature(mappedMethods.get(mappedMethods.lastIntKey()));
+      if (method.getHolder().isInterface()) {
+        lirBuilder.addInvokeInterface(fallthroughTarget, argumentValues);
+      } else {
+        lirBuilder.addInvokeVirtual(fallthroughTarget, argumentValues);
+      }
+    } else {
+      DexMethod reboundFallthroughTarget =
+          lens.lookupInvokeSuper(superMethod.getReboundReference(), method).getReference();
+      DexMethod fallthroughTarget =
+          reboundFallthroughTarget.withHolder(
+              lens.getNextClassType(superMethod.getReference().getHolderType()),
+              appView.dexItemFactory());
+      lirBuilder.addInvokeSuper(fallthroughTarget, argumentValues, false);
+    }
+    if (method.getReturnType().isVoidType()) {
+      lirBuilder.addReturnVoid();
+    } else {
+      Value returnValue = Value.createNoDebugLocal(instructionIndex, returnType);
+      strategy.defineValue(returnValue, returnValue.getNumber());
+      lirBuilder.addReturn(returnValue);
+    }
+    instructionIndex += 2;
+
+    // Emit switch cases.
+    for (int classId : keys) {
+      DexMethod target = lens.getNextMethodSignature(mappedMethods.get(classId));
+      if (method.getHolder().isInterface()) {
+        lirBuilder.addInvokeInterface(target, argumentValues);
+      } else {
+        lirBuilder.addInvokeVirtual(target, argumentValues);
+      }
+      if (method.getReturnType().isVoidType()) {
+        lirBuilder.addReturnVoid();
+      } else {
+        Value returnValue = Value.createNoDebugLocal(instructionIndex, returnType);
+        strategy.defineValue(returnValue, returnValue.getNumber());
+        lirBuilder.addReturn(returnValue);
+      }
+      instructionIndex += 2;
+    }
+
+    return new LirCode<>(lirBuilder.build()) {
+
+      @Override
+      public boolean hasExplicitCodeLens() {
+        return true;
+      }
+
+      @Override
+      public GraphLens getCodeLens(AppView<?> appView) {
+        return lens;
+      }
+    };
   }
 
   private static CfFrame createCfFrameForSwitchCase(ProgramMethod representative, int localsSize) {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerAnalysis.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerAnalysis.java
index f49af82..96aeb10 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerAnalysis.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerAnalysis.java
@@ -150,7 +150,10 @@
 
               // TODO(b/189296638): Consider allowing constructor forwarding.
               if (!lensRewrittenInvokedMethod.isInstanceInitializer(appView.dexItemFactory())
-                  || lensRewrittenInvokedMethod.getHolderType() != group.getSuperType()) {
+                  || !appView
+                      .appInfo()
+                      .isSubtype(
+                          group.getSuperType(), lensRewrittenInvokedMethod.getHolderType())) {
                 return invalid();
               }
 
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java
index dc5f15e..a543580 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java
@@ -72,7 +72,7 @@
    *
    * @param originalMethodReference the original reference of the representative method
    */
-  public IncompleteMergedInstanceInitializerCode createCfCode(
+  public IncompleteMergedInstanceInitializerCode createCode(
       DexMethod originalMethodReference,
       HorizontalMergeGroup group,
       boolean hasClassId,
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
index 2a87bb4..7f25de0 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
@@ -22,7 +22,6 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.horizontalclassmerging.code.ConstructorEntryPointSynthesizedCode;
 import com.android.tools.r8.ir.conversion.ExtraConstantIntParameter;
-import com.android.tools.r8.ir.conversion.ExtraParameter;
 import com.android.tools.r8.ir.conversion.ExtraUnusedParameter;
 import com.android.tools.r8.profile.rewriting.ProfileCollectionAdditions;
 import com.android.tools.r8.utils.ArrayUtils;
@@ -30,7 +29,6 @@
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.SetUtils;
 import com.android.tools.r8.utils.structural.Ordered;
-import com.google.common.collect.ImmutableList;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
 import it.unimi.dsi.fastutil.objects.Reference2IntMap;
@@ -275,11 +273,8 @@
       boolean needsClassId,
       int extraNulls) {
     if (hasInstanceInitializerDescription()) {
-      return instanceInitializerDescription.createCfCode(
-          getOriginalMethodReference(),
-          group,
-          needsClassId,
-          extraNulls);
+      return instanceInitializerDescription.createCode(
+          getOriginalMethodReference(), group, needsClassId, extraNulls);
     }
     assert useSyntheticMethod();
     return new ConstructorEntryPointSynthesizedCode(
@@ -305,28 +300,19 @@
     boolean needsClassId =
         instanceInitializers.size() > 1
             && (!hasInstanceInitializerDescription() || group.hasClassIdField());
-    assert !mode.isRestrictedToAlphaRenamingInR8() || !needsClassId;
 
     DexMethod newMethodReferenceTemplate = getNewMethodReference(representative, needsClassId);
-    assert !mode.isRestrictedToAlphaRenamingInR8()
-        || classMethodsBuilder.isFresh(newMethodReferenceTemplate);
 
     DexMethod newMethodReference =
         dexItemFactory.createInstanceInitializerWithFreshProto(
             newMethodReferenceTemplate,
-            mode.isRestrictedToAlphaRenamingInR8()
-                ? ImmutableList.of()
-                : classMergerSharedData.getExtraUnusedArgumentTypes(),
+            classMergerSharedData.getExtraUnusedArgumentTypes(),
             classMethodsBuilder::isFresh);
 
     // Compute the extra unused null parameters.
     List<ExtraUnusedParameter> extraUnusedParameters =
         computeExtraUnusedParameters(newMethodReferenceTemplate, newMethodReference);
 
-    // Verify that the merge is a simple renaming in the final round of merging.
-    assert !mode.isRestrictedToAlphaRenamingInR8()
-        || newMethodReference == newMethodReferenceTemplate;
-
     // Move instance initializers to target class.
     if (hasInstanceInitializerDescription()) {
       lensBuilder.moveMethods(instanceInitializers, newMethodReference);
@@ -360,14 +346,13 @@
 
     // Map each of the instance initializers to the new instance initializer in the graph lens.
     for (ProgramMethod instanceInitializer : instanceInitializers) {
-      List<ExtraParameter> extraParameters = new ArrayList<>();
-      if (needsClassId) {
-        int classIdentifier = classIdentifiers.getInt(instanceInitializer.getHolderType());
-        extraParameters.add(new ExtraConstantIntParameter(classIdentifier));
-      }
-      extraParameters.addAll(extraUnusedParameters);
+      ExtraConstantIntParameter extraParameter =
+          needsClassId
+              ? new ExtraConstantIntParameter(
+                  classIdentifiers.getInt(instanceInitializer.getHolderType()))
+              : null;
       lensBuilder.mapMergedConstructor(
-          instanceInitializer.getReference(), newMethodReference, extraParameters);
+          instanceInitializer.getReference(), newMethodReference, extraParameter);
     }
 
     DexEncodedMethod representativeMethod = representative.getDefinition();
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
index 5ea7df7..75cc55c 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
@@ -23,7 +23,6 @@
 import com.android.tools.r8.horizontalclassmerging.policies.NoClassAnnotationCollisions;
 import com.android.tools.r8.horizontalclassmerging.policies.NoClassInitializerCycles;
 import com.android.tools.r8.horizontalclassmerging.policies.NoClassInitializerWithObservableSideEffects;
-import com.android.tools.r8.horizontalclassmerging.policies.NoConstructorCollisions;
 import com.android.tools.r8.horizontalclassmerging.policies.NoDeadEnumLiteMaps;
 import com.android.tools.r8.horizontalclassmerging.policies.NoDeadLocks;
 import com.android.tools.r8.horizontalclassmerging.policies.NoDefaultInterfaceMethodCollisions;
@@ -35,7 +34,6 @@
 import com.android.tools.r8.horizontalclassmerging.policies.NoIndirectRuntimeTypeChecks;
 import com.android.tools.r8.horizontalclassmerging.policies.NoInnerClasses;
 import com.android.tools.r8.horizontalclassmerging.policies.NoInstanceFieldAnnotations;
-import com.android.tools.r8.horizontalclassmerging.policies.NoInstanceInitializerMerging;
 import com.android.tools.r8.horizontalclassmerging.policies.NoInterfaces;
 import com.android.tools.r8.horizontalclassmerging.policies.NoKeepRules;
 import com.android.tools.r8.horizontalclassmerging.policies.NoKotlinMetadata;
@@ -44,7 +42,6 @@
 import com.android.tools.r8.horizontalclassmerging.policies.NoResourceClasses;
 import com.android.tools.r8.horizontalclassmerging.policies.NoServiceLoaders;
 import com.android.tools.r8.horizontalclassmerging.policies.NoVerticallyMergedClasses;
-import com.android.tools.r8.horizontalclassmerging.policies.NoVirtualMethodMerging;
 import com.android.tools.r8.horizontalclassmerging.policies.NoWeakerAccessPrivileges;
 import com.android.tools.r8.horizontalclassmerging.policies.NotMatchedByNoHorizontalClassMerging;
 import com.android.tools.r8.horizontalclassmerging.policies.NotTwoInitsWithMonitors;
@@ -74,12 +71,10 @@
 
   public static List<Policy> getPolicies(
       AppView<?> appView,
-      IRCodeProvider codeProvider,
       ClassMergerMode mode,
       RuntimeTypeCheckInfo runtimeTypeCheckInfo) {
     if (appView.hasClassHierarchy()) {
-      return getPoliciesForR8(
-          appView.withClassHierarchy(), codeProvider, mode, runtimeTypeCheckInfo);
+      return getPoliciesForR8(appView.withClassHierarchy(), mode, runtimeTypeCheckInfo);
     } else {
       return getPoliciesForD8(appView.withoutClassHierarchy(), mode);
     }
@@ -99,13 +94,12 @@
 
   private static List<Policy> getPoliciesForR8(
       AppView<? extends AppInfoWithClassHierarchy> appView,
-      IRCodeProvider codeProvider,
       ClassMergerMode mode,
       RuntimeTypeCheckInfo runtimeTypeCheckInfo) {
     List<Policy> policies =
         ImmutableList.<Policy>builder()
             .addAll(getSingleClassPolicies(appView, mode, runtimeTypeCheckInfo))
-            .addAll(getMultiClassPolicies(appView, codeProvider, mode, runtimeTypeCheckInfo))
+            .addAll(getMultiClassPolicies(appView, mode, runtimeTypeCheckInfo))
             .build();
     policies = appView.options().testing.horizontalClassMergingPolicyRewriter.apply(policies);
     assert verifyPolicyOrderingConstraints(policies);
@@ -221,44 +215,24 @@
 
   private static List<Policy> getMultiClassPolicies(
       AppView<? extends AppInfoWithClassHierarchy> appView,
-      IRCodeProvider codeProvider,
       ClassMergerMode mode,
       RuntimeTypeCheckInfo runtimeTypeCheckInfo) {
     ImmutableList.Builder<Policy> builder = ImmutableList.builder();
-
     addRequiredMultiClassPolicies(appView, mode, runtimeTypeCheckInfo, builder);
-
     if (!appView.options().horizontalClassMergerOptions().isRestrictedToSynthetics()) {
       AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
       addMultiClassPoliciesForMergingNonSyntheticClasses(appViewWithLiveness, builder);
     }
-
-    if (mode.isRestrictedToAlphaRenamingInR8()) {
-      builder.add(
-          new NoVirtualMethodMerging(appView, mode), new NoConstructorCollisions(appView, mode));
-    } else {
-      AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
-      if (mode.isInitial()) {
-        builder.add(new AllInstantiatedOrUninstantiated(appViewWithLiveness, mode));
-      }
-      builder.add(
-          new PreserveMethodCharacteristics(appViewWithLiveness, mode),
-          new MinimizeInstanceFieldCasts());
+    if (mode.isInitial()) {
+      assert appView.hasLiveness();
+      builder.add(new AllInstantiatedOrUninstantiated(appView.withLiveness()));
     }
-
+    if (appView.hasLiveness()) {
+      builder.add(new PreserveMethodCharacteristics(appView.withLiveness(), mode));
+    }
+    builder.add(new MinimizeInstanceFieldCasts());
     addMultiClassPoliciesForInterfaceMerging(appView, mode, builder);
-
-    builder.add(new LimitClassGroups(appView));
-
-    if (mode.isRestrictedToAlphaRenamingInR8()) {
-      // This needs to reason about equivalence of instance initializers, which relies on the
-      // mapping from instance fields on source classes to the instance fields on target classes.
-      // This policy therefore selects a target for each merge group and creates the mapping for
-      // instance fields. For this reason we run this policy in the very end.
-      builder.add(new NoInstanceInitializerMerging(appView, codeProvider, mode));
-    }
-
-    return builder.add(new FinalizeMergeGroup(appView, mode)).build();
+    return builder.add(new LimitClassGroups(appView), new FinalizeMergeGroup(appView)).build();
   }
 
   private static List<? extends Policy> getMultiClassPoliciesForD8(
@@ -275,7 +249,7 @@
         new NoDifferentApiReferenceLevel(appView),
         new LimitClassGroups(appView));
     assert verifyMultiClassPoliciesIrrelevantForMergingSyntheticsInD8(appView, mode, builder);
-    builder.add(new FinalizeMergeGroup(appView, mode));
+    builder.add(new FinalizeMergeGroup(appView));
     return builder.build();
   }
 
@@ -292,12 +266,12 @@
         new NoClassAnnotationCollisions(),
         new SameFeatureSplit(appView),
         new SameStartupPartition(appView),
-        new SameInstanceFields(appView, mode),
+        new SameInstanceFields(appView),
         new SameMainDexGroup(appView),
         new SameNestHost(appView),
         new SameParentClass(),
         new SyntheticItemsPolicy(appView, mode),
-        new RespectPackageBoundaries(appView, mode),
+        new RespectPackageBoundaries(appView),
         new NoDifferentApiReferenceLevel(appView),
         new NoIndirectRuntimeTypeChecks(appView, runtimeTypeCheckInfo),
         new NoWeakerAccessPrivileges(appView, immediateSubtypingInfo),
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/SubtypingForrestForClasses.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/SubtypingForrestForClasses.java
deleted file mode 100644
index afb02cd..0000000
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/SubtypingForrestForClasses.java
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright (c) 2020, 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.horizontalclassmerging;
-
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexProgramClass;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.IdentityHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.function.BiFunction;
-
-/**
- * Calculates the subtyping forrest for all classes. Unlike {@link
- * com.android.tools.r8.graph.SubtypingInfo}, interfaces are not included in this subtyping
- * information and only the immediate parents are stored (i.e. the transitive parents are not
- * calculated). In the following example graph, the roots are A, E and G, and each edge indicates an
- * entry in {@link SubtypingForrestForClasses#subtypeMap} going from the parent to an entry in the
- * collection of children. <code>
- *     A      E     G
- *    / \     |
- *   B  C     F
- *   |
- *   D
- * </code>
- */
-public class SubtypingForrestForClasses {
-
-  private final AppView<? extends AppInfoWithClassHierarchy> appView;
-  private final Collection<DexProgramClass> roots = new ArrayList<>();
-  private final Map<DexProgramClass, List<DexProgramClass>> subtypeMap = new IdentityHashMap<>();
-
-  public SubtypingForrestForClasses(AppView<? extends AppInfoWithClassHierarchy> appView) {
-    this.appView = appView;
-    calculateSubtyping(appView.appInfo().classes());
-  }
-
-  private DexProgramClass superClass(DexProgramClass clazz) {
-    return appView.programDefinitionFor(clazz.superType, clazz);
-  }
-
-  private void calculateSubtyping(Iterable<DexProgramClass> classes) {
-    classes.forEach(this::calculateSubtyping);
-  }
-
-  private void calculateSubtyping(DexProgramClass clazz) {
-    if (clazz.isInterface()) {
-      return;
-    }
-    DexProgramClass superClass = superClass(clazz);
-    if (superClass == null) {
-      roots.add(clazz);
-    } else {
-      subtypeMap.computeIfAbsent(superClass, ignore -> new ArrayList<>()).add(clazz);
-    }
-  }
-
-  public Collection<DexProgramClass> getProgramRoots() {
-    return roots;
-  }
-
-  public Collection<DexProgramClass> getSubtypesFor(DexProgramClass clazz) {
-    return subtypeMap.getOrDefault(clazz, Collections.emptyList());
-  }
-
-  public <T> T traverseNodeDepthFirst(
-      DexProgramClass clazz, T state, BiFunction<DexProgramClass, T, T> consumer) {
-    T newState = consumer.apply(clazz, state);
-    getSubtypesFor(clazz).forEach(subClazz -> traverseNodeDepthFirst(subClazz, newState, consumer));
-    return newState;
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ConstructorEntryPointSynthesizedCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ConstructorEntryPointSynthesizedCode.java
index 6b87383..d872901 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ConstructorEntryPointSynthesizedCode.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ConstructorEntryPointSynthesizedCode.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider;
 import com.android.tools.r8.graph.lens.GraphLens;
 import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import com.android.tools.r8.horizontalclassmerging.ConstructorEntryPoint;
@@ -24,11 +25,13 @@
 import com.android.tools.r8.ir.code.Position;
 import com.android.tools.r8.ir.code.Position.SyntheticPosition;
 import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.ir.conversion.IRToLirFinalizer;
 import com.android.tools.r8.ir.conversion.MethodConversionOptions;
 import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
 import com.android.tools.r8.ir.conversion.SourceCode;
 import com.android.tools.r8.lightir.LirCode;
 import com.android.tools.r8.utils.RetracerForCodePrinting;
+import com.android.tools.r8.utils.Timing;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
 
@@ -94,7 +97,25 @@
       AppView<? extends AppInfoWithClassHierarchy> appView,
       ProgramMethod method,
       HorizontalClassMergerGraphLens lens) {
-    throw new Unreachable();
+    for (Int2ReferenceMap.Entry<DexMethod> entry : typeConstructors.int2ReferenceEntrySet()) {
+      entry.setValue(lens.getNextMethodSignature(entry.getValue()));
+    }
+    IRCode irCode = buildIR(method, appView);
+    LirCode<Integer> lirCode =
+        new IRToLirFinalizer(appView)
+            .finalizeCode(irCode, BytecodeMetadataProvider.empty(), Timing.empty());
+    return new LirCode<>(lirCode) {
+
+      @Override
+      public boolean hasExplicitCodeLens() {
+        return true;
+      }
+
+      @Override
+      public GraphLens getCodeLens(AppView<?> appView) {
+        return lens;
+      }
+    };
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/AllInstantiatedOrUninstantiated.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/AllInstantiatedOrUninstantiated.java
index e5839b6..d118b1d 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/AllInstantiatedOrUninstantiated.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/AllInstantiatedOrUninstantiated.java
@@ -4,22 +4,18 @@
 
 package com.android.tools.r8.horizontalclassmerging.policies;
 
-import com.android.tools.r8.classmerging.ClassMergerMode;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.horizontalclassmerging.MultiClassSameReferencePolicy;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 
+// This policy is only used to prevent that horizontal class merging regresses the
+// uninstantiated type optimization.
 public class AllInstantiatedOrUninstantiated extends MultiClassSameReferencePolicy<Boolean> {
 
   private final AppView<AppInfoWithLiveness> appView;
 
-  public AllInstantiatedOrUninstantiated(
-      AppView<AppInfoWithLiveness> appView, ClassMergerMode mode) {
-    // This policy is only used to prevent that horizontal class merging regresses the
-    // uninstantiated type optimization. Since there won't be any IR processing after the final
-    // round of horizontal class merging, there is no need to use the policy.
-    assert mode.isInitial();
+  public AllInstantiatedOrUninstantiated(AppView<AppInfoWithLiveness> appView) {
     this.appView = appView;
   }
 
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/FinalizeMergeGroup.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/FinalizeMergeGroup.java
index c70a156..0d32b98 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/FinalizeMergeGroup.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/FinalizeMergeGroup.java
@@ -4,16 +4,12 @@
 
 package com.android.tools.r8.horizontalclassmerging.policies;
 
-import com.android.tools.r8.classmerging.ClassMergerMode;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.horizontalclassmerging.HorizontalMergeGroup;
 import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
 import com.android.tools.r8.utils.ListUtils;
-import com.android.tools.r8.utils.SetUtils;
 import com.android.tools.r8.utils.collections.EmptyBidirectionalOneToOneMap;
 import java.util.Collection;
-import java.util.Set;
 
 /**
  * Identifies when instance initializer merging is required and bails out. This is needed to ensure
@@ -26,24 +22,16 @@
 public class FinalizeMergeGroup extends MultiClassPolicy {
 
   private final AppView<?> appView;
-  private final ClassMergerMode mode;
 
-  public FinalizeMergeGroup(AppView<?> appView, ClassMergerMode mode) {
+  public FinalizeMergeGroup(AppView<?> appView) {
     this.appView = appView;
-    this.mode = mode;
   }
 
   @Override
   public Collection<HorizontalMergeGroup> apply(HorizontalMergeGroup group) {
     if (appView.enableWholeProgramOptimizations()) {
-      if (group.isInterfaceGroup() || !mode.isRestrictedToAlphaRenamingInR8()) {
-        group.selectTarget(appView);
-        group.selectInstanceFieldMap(appView.withClassHierarchy());
-      } else {
-        // In the final round of merging each group should be finalized by the
-        // NoInstanceInitializerMerging policy.
-        assert verifyAlreadyFinalized(group);
-      }
+      group.selectTarget(appView);
+      group.selectInstanceFieldMap(appView.withClassHierarchy());
     } else {
       assert !group.hasTarget();
       assert !group.hasInstanceFieldMap();
@@ -62,21 +50,4 @@
   public boolean isIdentityForInterfaceGroups() {
     return true;
   }
-
-  private boolean verifyAlreadyFinalized(HorizontalMergeGroup group) {
-    assert group.hasTarget();
-    assert group.getClasses().contains(group.getTarget());
-    assert group.hasInstanceFieldMap();
-    Set<DexType> types =
-        SetUtils.newIdentityHashSet(
-            builder -> group.forEach(clazz -> builder.accept(clazz.getType())));
-    group
-        .getInstanceFieldMap()
-        .forEach(
-            (sourceField, targetField) -> {
-              assert types.contains(sourceField.getHolderType());
-              assert types.contains(targetField.getHolderType());
-            });
-    return true;
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoConstructorCollisions.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoConstructorCollisions.java
deleted file mode 100644
index c1d9320..0000000
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoConstructorCollisions.java
+++ /dev/null
@@ -1,156 +0,0 @@
-// Copyright (c) 2020, 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.horizontalclassmerging.policies;
-
-import com.android.tools.r8.classmerging.ClassMergerMode;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexProto;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.horizontalclassmerging.HorizontalMergeGroup;
-import com.android.tools.r8.horizontalclassmerging.MultiClassPolicyWithPreprocessing;
-import com.android.tools.r8.utils.ArrayUtils;
-import com.android.tools.r8.utils.IterableUtils;
-import com.android.tools.r8.utils.ListUtils;
-import com.android.tools.r8.utils.WorkList;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.IdentityHashMap;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ExecutorService;
-
-/**
- * In the final round, we're not allowed to resolve constructor collisions by appending null
- * arguments to constructor calls.
- *
- * <p>As an example, if a class in the program declares the constructors {@code <init>(A)} and
- * {@code <init>(B)}, the classes A and B must not be merged.
- *
- * <p>To avoid collisions of this kind, we run over all the classes in the program, and apply the
- * current set of merge groups to the constructor signatures of each class. Then, in case of a
- * collision, we extract all the mapped types from the constructor signatures, and prevent merging
- * of these types.
- */
-public class NoConstructorCollisions extends MultiClassPolicyWithPreprocessing<Set<DexType>> {
-
-  private final AppView<? extends AppInfoWithClassHierarchy> appView;
-  private final DexItemFactory dexItemFactory;
-
-  public NoConstructorCollisions(
-      AppView<? extends AppInfoWithClassHierarchy> appView, ClassMergerMode mode) {
-    assert mode.isRestrictedToAlphaRenamingInR8();
-    this.appView = appView;
-    this.dexItemFactory = appView.dexItemFactory();
-  }
-
-  /**
-   * Removes the classes in {@param collisionResolution} from {@param group}, and returns the new
-   * filtered group.
-   */
-  @Override
-  public Collection<HorizontalMergeGroup> apply(
-      HorizontalMergeGroup group, Set<DexType> collisionResolution) {
-    HorizontalMergeGroup newGroup =
-        new HorizontalMergeGroup(
-            Iterables.filter(group, clazz -> !collisionResolution.contains(clazz.getType())));
-    return newGroup.isTrivial() ? Collections.emptyList() : ListUtils.newLinkedList(newGroup);
-  }
-
-  /**
-   * Computes the set of classes that must not be merged, because the merging of these classes could
-   * lead to constructor collisions.
-   */
-  @Override
-  public Set<DexType> preprocess(
-      Collection<HorizontalMergeGroup> groups, ExecutorService executorService) {
-    // Build a mapping from types to groups.
-    Map<DexType, HorizontalMergeGroup> groupsByType = new IdentityHashMap<>();
-    for (HorizontalMergeGroup group : groups) {
-      for (DexProgramClass clazz : group) {
-        groupsByType.put(clazz.getType(), group);
-      }
-    }
-
-    // Find the set of types that must not be merged, because they could lead to a constructor
-    // collision.
-    Set<DexType> collisionResolution = Sets.newIdentityHashSet();
-    // Iterate over all the instance initializers of the current class. If the current class is in
-    // a merge group, we must include all constructors of the entire merge group.
-    WorkList.newIdentityWorkList(appView.appInfo().classes())
-        .process(
-            (current, workList) -> {
-              Iterable<DexProgramClass> group =
-                  groupsByType.containsKey(current.getType())
-                      ? groupsByType.get(current.getType())
-                      : IterableUtils.singleton(current);
-              Set<DexMethod> seen = Sets.newIdentityHashSet();
-              for (DexProgramClass clazz : group) {
-                for (DexEncodedMethod method :
-                    clazz.directMethods(DexEncodedMethod::isInstanceInitializer)) {
-                  // Rewrite the constructor reference using the current merge groups.
-                  DexMethod newReference = rewriteReference(method.getReference(), groupsByType);
-                  if (!seen.add(newReference)) {
-                    // Found a collision. Block all referenced types from being merged.
-                    for (DexType type : method.getProto().getBaseTypes(dexItemFactory)) {
-                      if (type.isClassType() && groupsByType.containsKey(type)) {
-                        collisionResolution.add(type);
-                      }
-                    }
-                  }
-                }
-              }
-              workList.markAsSeen(group);
-            });
-    return collisionResolution;
-  }
-
-  private DexProto rewriteProto(DexProto proto, Map<DexType, HorizontalMergeGroup> groups) {
-    DexType[] parameters =
-        ArrayUtils.map(
-            proto.getParameters().values,
-            parameter -> rewriteType(parameter, groups),
-            DexType.EMPTY_ARRAY);
-    return dexItemFactory.createProto(rewriteType(proto.getReturnType(), groups), parameters);
-  }
-
-  private DexMethod rewriteReference(DexMethod method, Map<DexType, HorizontalMergeGroup> groups) {
-    return dexItemFactory.createMethod(
-        rewriteType(method.getHolderType(), groups),
-        rewriteProto(method.getProto(), groups),
-        method.getName());
-  }
-
-  @SuppressWarnings("ReferenceEquality")
-  private DexType rewriteType(DexType type, Map<DexType, HorizontalMergeGroup> groups) {
-    if (type.isArrayType()) {
-      DexType baseType = type.toBaseType(dexItemFactory);
-      DexType rewrittenBaseType = rewriteType(baseType, groups);
-      if (rewrittenBaseType == baseType) {
-        return type;
-      }
-      return type.replaceBaseType(rewrittenBaseType, dexItemFactory);
-    }
-    if (type.isClassType()) {
-      if (!groups.containsKey(type)) {
-        return type;
-      }
-      return groups.get(type).getClasses().getFirst().getType();
-    }
-    assert type.isPrimitiveType() || type.isVoidType();
-    return type;
-  }
-
-  @Override
-  public String getName() {
-    return "NoConstructorCollisions";
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInstanceInitializerMerging.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInstanceInitializerMerging.java
deleted file mode 100644
index dffdc02..0000000
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInstanceInitializerMerging.java
+++ /dev/null
@@ -1,346 +0,0 @@
-// Copyright (c) 2020, 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.horizontalclassmerging.policies;
-
-import static com.android.tools.r8.utils.MapUtils.ignoreKey;
-
-import com.android.tools.r8.classmerging.ClassMergerMode;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexMethodSignature;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexTypeList;
-import com.android.tools.r8.graph.MethodAccessInfoCollection;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.horizontalclassmerging.ClassInstanceFieldsMerger;
-import com.android.tools.r8.horizontalclassmerging.HorizontalMergeGroup;
-import com.android.tools.r8.horizontalclassmerging.IRCodeProvider;
-import com.android.tools.r8.horizontalclassmerging.InstanceInitializerAnalysis;
-import com.android.tools.r8.horizontalclassmerging.InstanceInitializerAnalysis.AbsentInstanceInitializer;
-import com.android.tools.r8.horizontalclassmerging.InstanceInitializerAnalysis.InstanceInitializer;
-import com.android.tools.r8.horizontalclassmerging.InstanceInitializerAnalysis.PresentInstanceInitializer;
-import com.android.tools.r8.horizontalclassmerging.InstanceInitializerDescription;
-import com.android.tools.r8.horizontalclassmerging.MultiClassPolicyWithPreprocessing;
-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.BidirectionalManyToOneHashMap;
-import com.android.tools.r8.utils.collections.MutableBidirectionalManyToOneMap;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.IdentityHashMap;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.ExecutorService;
-import java.util.function.Function;
-
-/**
- * Identifies when instance initializer merging is required and bails out. This is needed to ensure
- * that we don't need to append extra null arguments at constructor call sites, such that the result
- * of the final round of class merging can be described as a renaming only.
- *
- * <p>This policy requires that all instance initializers with the same signature (relaxed, by
- * converting references types to java.lang.Object) have the same behavior.
- */
-public class NoInstanceInitializerMerging
-    extends MultiClassPolicyWithPreprocessing<Map<DexProgramClass, Set<DexMethod>>> {
-
-  private final AppView<? extends AppInfoWithClassHierarchy> appView;
-  private final IRCodeProvider codeProvider;
-
-  public NoInstanceInitializerMerging(
-      AppView<? extends AppInfoWithClassHierarchy> appView,
-      IRCodeProvider codeProvider,
-      ClassMergerMode mode) {
-    assert mode.isRestrictedToAlphaRenamingInR8();
-    this.appView = appView;
-    this.codeProvider = codeProvider;
-  }
-
-  @Override
-  @SuppressWarnings("MixedMutabilityReturnType")
-  public Map<DexProgramClass, Set<DexMethod>> preprocess(
-      Collection<HorizontalMergeGroup> groups, ExecutorService executorService) {
-    if (!appView.options().canHaveNonReboundConstructorInvoke()) {
-      return Collections.emptyMap();
-    }
-
-    if (appView.hasLiveness()) {
-      DexItemFactory dexItemFactory = appView.dexItemFactory();
-      MethodAccessInfoCollection methodAccessInfoCollection =
-          appView.appInfoWithLiveness().getMethodAccessInfoCollection();
-
-      // Compute a mapping for the merge candidates to efficiently determine if a given type is a
-      // merge candidate.
-      Map<DexType, DexProgramClass> mergeCandidates =
-          MapUtils.newImmutableMap(
-              builder ->
-                  IterableUtils.flatten(groups)
-                      .forEach(
-                          mergeCandidate -> builder.put(mergeCandidate.getType(), mergeCandidate)));
-
-      // Compute a mapping from merge candidates to the constructors that have been removed by
-      // constructor shrinking.
-      return MapUtils.newIdentityHashMap(
-          builder ->
-              methodAccessInfoCollection.forEachDirectInvoke(
-                  (method, contexts) -> {
-                    DexProgramClass mergeCandidateHolder =
-                        mergeCandidates.get(method.getHolderType());
-                    if (mergeCandidateHolder != null
-                        && method.isInstanceInitializer(dexItemFactory)
-                        && mergeCandidateHolder.getMethodCollection().getMethod(method) == null) {
-                      builder
-                          .computeIfAbsent(
-                              mergeCandidateHolder, ignoreKey(Sets::newIdentityHashSet))
-                          .add(method);
-                    }
-                  }));
-    }
-
-    // Constructor shrinking is disabled when shrinking is disabled.
-    assert !appView.options().isShrinking();
-    return Collections.emptyMap();
-  }
-
-  @Override
-  @SuppressWarnings("MixedMutabilityReturnType")
-  public Collection<HorizontalMergeGroup> apply(
-      HorizontalMergeGroup group, Map<DexProgramClass, Set<DexMethod>> absentInstanceInitializers) {
-    assert !group.hasTarget();
-    assert !group.hasInstanceFieldMap();
-
-    if (group.isInterfaceGroup()) {
-      return ListUtils.newLinkedList(group);
-    }
-
-    // When we merge equivalent instance initializers with different protos, we find the least upper
-    // bound of each parameter type. As a result of this, the final instance initializer signatures
-    // are not known until all instance initializers in the group are known. Therefore, we disallow
-    // merging of classes that have multiple methods with the same relaxed method signature (where
-    // reference parameters are converted to java.lang.Object), to ensure that merging will result
-    // in a simple renaming (specifically, we must not need to append null arguments to constructor
-    // calls due to constructor collisions).
-    group.removeIf(
-        clazz ->
-            hasMultipleInstanceInitializersWithSameRelaxedSignature(
-                clazz, absentInstanceInitializers));
-
-    if (group.isEmpty()) {
-      return Collections.emptyList();
-    }
-
-    // We want to allow merging of equivalent instance initializers. Equivalence depends on the
-    // mapping of instance fields, so we must compute this mapping now.
-    group.selectTarget(appView);
-    group.selectInstanceFieldMap(appView);
-
-    Map<HorizontalMergeGroup, Map<DexMethodSignature, InstanceInitializer>> newGroups =
-        new LinkedHashMap<>();
-
-    // Caching of instance initializer descriptions, which are used to determine equivalence.
-    // TODO(b/181846319): Make this cache available to the instance initializer merger so that we
-    //  don't reanalyze instance initializers.
-    Map<DexMethod, Optional<InstanceInitializerDescription>> instanceInitializerDescriptions =
-        new IdentityHashMap<>();
-    Function<InstanceInitializer, Optional<InstanceInitializerDescription>>
-        instanceInitializerDescriptionProvider =
-            instanceInitializer ->
-                getOrComputeInstanceInitializerDescription(
-                    group, instanceInitializer, instanceInitializerDescriptions);
-
-    // Partition group into smaller groups where there are no (non-equivalent) instance initializer
-    // collisions.
-    for (DexProgramClass clazz : group) {
-      HorizontalMergeGroup newGroup = null;
-      Map<DexMethodSignature, InstanceInitializer> classInstanceInitializers =
-          getInstanceInitializersByRelaxedSignature(clazz, absentInstanceInitializers);
-      for (Entry<HorizontalMergeGroup, Map<DexMethodSignature, InstanceInitializer>> entry :
-          newGroups.entrySet()) {
-        HorizontalMergeGroup candidateGroup = entry.getKey();
-        Map<DexMethodSignature, InstanceInitializer> groupInstanceInitializers = entry.getValue();
-        if (canAddClassToGroup(
-            classInstanceInitializers,
-            groupInstanceInitializers,
-            instanceInitializerDescriptionProvider)) {
-          newGroup = candidateGroup;
-          classInstanceInitializers.forEach(groupInstanceInitializers::put);
-          break;
-        }
-      }
-      if (newGroup != null) {
-        newGroup.add(clazz);
-      } else {
-        newGroups.put(new HorizontalMergeGroup(clazz), classInstanceInitializers);
-      }
-    }
-
-    // Remove trivial groups and finalize the newly created groups.
-    Collection<HorizontalMergeGroup> newNonTrivialGroups = removeTrivialGroups(newGroups.keySet());
-    setInstanceFieldMaps(newNonTrivialGroups, group);
-    return newNonTrivialGroups;
-  }
-
-  private boolean canAddClassToGroup(
-      Map<DexMethodSignature, InstanceInitializer> classInstanceInitializers,
-      Map<DexMethodSignature, InstanceInitializer> groupInstanceInitializers,
-      Function<InstanceInitializer, Optional<InstanceInitializerDescription>>
-          instanceInitializerDescriptionProvider) {
-    for (Entry<DexMethodSignature, InstanceInitializer> entry :
-        classInstanceInitializers.entrySet()) {
-      DexMethodSignature relaxedSignature = entry.getKey();
-      InstanceInitializer classInstanceInitializer = entry.getValue();
-      InstanceInitializer groupInstanceInitializer =
-          groupInstanceInitializers.get(relaxedSignature);
-      if (groupInstanceInitializer == null) {
-        continue;
-      }
-
-      Optional<InstanceInitializerDescription> classInstanceInitializerDescription =
-          instanceInitializerDescriptionProvider.apply(classInstanceInitializer);
-      if (!classInstanceInitializerDescription.isPresent()) {
-        return false;
-      }
-
-      Optional<InstanceInitializerDescription> groupInstanceInitializerDescription =
-          instanceInitializerDescriptionProvider.apply(groupInstanceInitializer);
-      if (!groupInstanceInitializerDescription.isPresent()
-          || !classInstanceInitializerDescription.equals(groupInstanceInitializerDescription)) {
-        return false;
-      }
-    }
-    return true;
-  }
-
-  private boolean hasMultipleInstanceInitializersWithSameRelaxedSignature(
-      DexProgramClass clazz, Map<DexProgramClass, Set<DexMethod>> absentInstanceInitializers) {
-    Set<DexMethod> instanceInitializerReferences =
-        SetUtils.unionIdentityHashSet(
-            SetUtils.newIdentityHashSet(
-                IterableUtils.transform(
-                    clazz.programInstanceInitializers(), ProgramMethod::getReference)),
-            absentInstanceInitializers.getOrDefault(clazz, Collections.emptySet()));
-    if (instanceInitializerReferences.size() <= 1) {
-      return false;
-    }
-
-    Set<DexMethod> seen = SetUtils.newIdentityHashSet();
-    return Iterables.any(
-        instanceInitializerReferences,
-        instanceInitializerReference ->
-            !seen.add(getRelaxedSignature(instanceInitializerReference)));
-  }
-
-  private Map<DexMethodSignature, InstanceInitializer> getInstanceInitializersByRelaxedSignature(
-      DexProgramClass clazz, Map<DexProgramClass, Set<DexMethod>> absentInstanceInitializers) {
-    Map<DexMethodSignature, InstanceInitializer> result = new HashMap<>();
-    for (ProgramMethod presentInstanceInitializer : clazz.programInstanceInitializers()) {
-      DexMethodSignature relaxedSignature =
-          getRelaxedSignature(presentInstanceInitializer).getSignature();
-      InstanceInitializer previous =
-          result.put(relaxedSignature, new PresentInstanceInitializer(presentInstanceInitializer));
-      assert previous == null;
-    }
-    for (DexMethod absentInstanceInitializer :
-        absentInstanceInitializers.getOrDefault(clazz, Collections.emptySet())) {
-      DexMethodSignature relaxedSignature =
-          getRelaxedSignature(absentInstanceInitializer).getSignature();
-      InstanceInitializer previous =
-          result.put(relaxedSignature, new AbsentInstanceInitializer(absentInstanceInitializer));
-      assert previous == null;
-    }
-    return result;
-  }
-
-  private Optional<InstanceInitializerDescription> getOrComputeInstanceInitializerDescription(
-      HorizontalMergeGroup group,
-      InstanceInitializer instanceInitializer,
-      Map<DexMethod, Optional<InstanceInitializerDescription>> instanceInitializerDescriptions) {
-    return instanceInitializerDescriptions.computeIfAbsent(
-        instanceInitializer.getReference(),
-        key -> {
-          InstanceInitializerDescription instanceInitializerDescription =
-              InstanceInitializerAnalysis.analyze(
-                  appView, codeProvider, group, instanceInitializer);
-          return Optional.ofNullable(instanceInitializerDescription);
-        });
-  }
-
-  private DexMethod getRelaxedSignature(ProgramMethod instanceInitializer) {
-    return getRelaxedSignature(instanceInitializer.getReference());
-  }
-
-  @SuppressWarnings("ReferenceEquality")
-  private DexMethod getRelaxedSignature(DexMethod instanceInitializerReference) {
-    DexType objectType = appView.dexItemFactory().objectType;
-    DexTypeList parameters = instanceInitializerReference.getParameters();
-    DexTypeList relaxedParameters =
-        parameters.map(parameter -> parameter.isPrimitiveType() ? parameter : objectType);
-    return parameters != relaxedParameters
-        ? appView
-            .dexItemFactory()
-            .createInstanceInitializer(
-                instanceInitializerReference.getHolderType(), relaxedParameters)
-        : instanceInitializerReference;
-  }
-
-  private void setInstanceFieldMaps(
-      Iterable<HorizontalMergeGroup> newGroups, HorizontalMergeGroup group) {
-    for (HorizontalMergeGroup newGroup : newGroups) {
-      // Set target.
-      newGroup.selectTarget(appView);
-
-      // Construct mapping from instance fields on old target to instance fields on new target.
-      // Note the importance of this: If we create a fresh mapping from the instance fields of each
-      // source class to the new target class, we could invalidate the constructor equivalence.
-      Map<DexEncodedField, DexEncodedField> oldTargetToNewTargetInstanceFieldMap =
-          new IdentityHashMap<>();
-      if (newGroup.getTarget() != group.getTarget()) {
-        ClassInstanceFieldsMerger.mapFields(
-            appView,
-            group.getTarget(),
-            newGroup.getTarget(),
-            oldTargetToNewTargetInstanceFieldMap::put);
-      }
-
-      // Construct mapping from source to target fields.
-      MutableBidirectionalManyToOneMap<DexEncodedField, DexEncodedField> instanceFieldMap =
-          BidirectionalManyToOneHashMap.newLinkedHashMap();
-      newGroup.forEachSource(
-          source ->
-              source.forEachProgramInstanceField(
-                  sourceField -> {
-                    DexEncodedField oldTargetInstanceField =
-                        group.getTargetInstanceField(sourceField).getDefinition();
-                    DexEncodedField newTargetInstanceField =
-                        oldTargetToNewTargetInstanceFieldMap.getOrDefault(
-                            oldTargetInstanceField, oldTargetInstanceField);
-                    instanceFieldMap.put(sourceField.getDefinition(), newTargetInstanceField);
-                  }));
-      newGroup.setInstanceFieldMap(instanceFieldMap);
-    }
-  }
-
-  @Override
-  public String getName() {
-    return "NoInstanceInitializerMerging";
-  }
-
-  @Override
-  public boolean isIdentityForInterfaceGroups() {
-    return true;
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoVirtualMethodMerging.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoVirtualMethodMerging.java
deleted file mode 100644
index 3320310..0000000
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoVirtualMethodMerging.java
+++ /dev/null
@@ -1,138 +0,0 @@
-// Copyright (c) 2020, 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.horizontalclassmerging.policies;
-
-import com.android.tools.r8.classmerging.ClassMergerMode;
-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.DexEncodedMethod;
-import com.android.tools.r8.graph.DexMethodSignature;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.horizontalclassmerging.HorizontalMergeGroup;
-import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
-import com.android.tools.r8.utils.IterableUtils;
-import com.android.tools.r8.utils.SetUtils;
-import com.google.common.collect.Iterables;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.Map.Entry;
-
-/**
- * Identifies when virtual method merging is required and bails out. This is needed to ensure that
- * we don't need to synthesize any $r8$classId fields, such that the result of the final round of
- * class merging can be described as a renaming only.
- */
-public class NoVirtualMethodMerging extends MultiClassPolicy {
-
-  private final AppView<? extends AppInfoWithClassHierarchy> appView;
-
-  public NoVirtualMethodMerging(
-      AppView<? extends AppInfoWithClassHierarchy> appView, ClassMergerMode mode) {
-    assert mode.isRestrictedToAlphaRenamingInR8();
-    this.appView = appView;
-  }
-
-  @Override
-  public Collection<HorizontalMergeGroup> apply(HorizontalMergeGroup group) {
-    Map<HorizontalMergeGroup, Map<DexMethodSignature, ProgramMethod>> newGroups =
-        new LinkedHashMap<>();
-    for (DexProgramClass clazz : group) {
-      Map<DexMethodSignature, ProgramMethod> classMethods = new HashMap<>();
-      clazz.forEachProgramVirtualMethodMatching(
-          DexEncodedMethod::isNonAbstractVirtualMethod,
-          method -> classMethods.put(method.getMethodSignature(), method));
-
-      HorizontalMergeGroup newGroup = null;
-      for (Entry<HorizontalMergeGroup, Map<DexMethodSignature, ProgramMethod>> entry :
-          newGroups.entrySet()) {
-        HorizontalMergeGroup candidateGroup = entry.getKey();
-        Map<DexMethodSignature, ProgramMethod> groupMethods = entry.getValue();
-        if (canAddNonAbstractVirtualMethodsToGroup(
-            clazz, classMethods.values(), candidateGroup, groupMethods)) {
-          newGroup = candidateGroup;
-          groupMethods.putAll(classMethods);
-          break;
-        }
-      }
-
-      if (newGroup != null) {
-        newGroup.add(clazz);
-      } else {
-        newGroups.put(new HorizontalMergeGroup(clazz), classMethods);
-      }
-    }
-    return removeTrivialGroups(newGroups.keySet());
-  }
-
-  private boolean canAddNonAbstractVirtualMethodsToGroup(
-      DexProgramClass clazz,
-      Collection<ProgramMethod> methods,
-      HorizontalMergeGroup group,
-      Map<DexMethodSignature, ProgramMethod> groupMethods) {
-    // For each of clazz' virtual methods, check that adding these methods to the group does not
-    // require method merging.
-    for (ProgramMethod method : methods) {
-      ProgramMethod groupMethod = groupMethods.get(method.getMethodSignature());
-      if (groupMethod != null || hasNonAbstractDefinitionInHierarchy(group, method)) {
-        return false;
-      }
-    }
-    // For each of the group's virtual methods, check that adding these methods clazz does not
-    // require method merging.
-    for (ProgramMethod method : groupMethods.values()) {
-      if (hasNonAbstractDefinitionInHierarchy(clazz, method)) {
-        return false;
-      }
-    }
-    return true;
-  }
-
-  private boolean hasNonAbstractDefinitionInHierarchy(
-      HorizontalMergeGroup group, ProgramMethod method) {
-    return hasNonAbstractDefinitionInSuperClass(group.getSuperType(), method)
-        || hasNonAbstractDefinitionInSuperInterface(
-            SetUtils.newIdentityHashSet(IterableUtils.flatMap(group, DexClass::getInterfaces)),
-            method);
-  }
-
-  private boolean hasNonAbstractDefinitionInHierarchy(DexProgramClass clazz, ProgramMethod method) {
-    return hasNonAbstractDefinitionInSuperClass(clazz.getSuperType(), method)
-        || hasNonAbstractDefinitionInSuperInterface(clazz.getInterfaces(), method);
-  }
-
-  private boolean hasNonAbstractDefinitionInSuperClass(DexType superType, ProgramMethod method) {
-    SingleResolutionResult<?> resolutionResult =
-        appView
-            .appInfo()
-            .resolveMethodOnClassLegacy(superType, method.getReference())
-            .asSingleResolution();
-    return resolutionResult != null && !resolutionResult.getResolvedMethod().isAbstract();
-  }
-
-  private boolean hasNonAbstractDefinitionInSuperInterface(
-      Iterable<DexType> interfaceTypes, ProgramMethod method) {
-    return Iterables.any(
-        interfaceTypes,
-        interfaceType -> {
-          SingleResolutionResult<?> resolutionResult =
-              appView
-                  .appInfo()
-                  .resolveMethodOnInterfaceLegacy(interfaceType, method.getReference())
-                  .asSingleResolution();
-          return resolutionResult != null && !resolutionResult.getResolvedMethod().isAbstract();
-        });
-  }
-
-  @Override
-  public String getName() {
-    return "NoVirtualMethodMerging";
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreserveMethodCharacteristics.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreserveMethodCharacteristics.java
index caf75a0..1486c2f 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreserveMethodCharacteristics.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreserveMethodCharacteristics.java
@@ -39,10 +39,10 @@
     private final MethodAccessFlags accessFlags;
     private final boolean isAssumeNoSideEffectsMethod;
     private final OptionalBool isLibraryMethodOverride;
-    private final boolean isMainDexRoot;
+    private final OptionalBool isMainDexRoot;
 
     private MethodCharacteristics(
-        DexEncodedMethod method, boolean isAssumeNoSideEffectsMethod, boolean isMainDexRoot) {
+        DexEncodedMethod method, boolean isAssumeNoSideEffectsMethod, OptionalBool isMainDexRoot) {
       this.accessFlags =
           MethodAccessFlags.builder()
               .setPrivate(method.getAccessFlags().isPrivate())
@@ -57,11 +57,14 @@
     }
 
     static MethodCharacteristics create(
-        AppView<AppInfoWithLiveness> appView, DexEncodedMethod method) {
+        AppView<AppInfoWithLiveness> appView, ClassMergerMode mode, DexEncodedMethod method) {
       return new MethodCharacteristics(
           method,
           appView.getAssumeInfoCollection().isSideEffectFree(method.getReference()),
-          appView.appInfo().getMainDexInfo().isTracedMethodRoot(method.getReference()));
+          mode.isInitial()
+              ? OptionalBool.of(
+                  appView.appInfo().getMainDexInfo().isTracedMethodRoot(method.getReference()))
+              : OptionalBool.unknown());
     }
 
     @Override
@@ -91,12 +94,12 @@
   }
 
   private final AppView<AppInfoWithLiveness> appView;
+  private final ClassMergerMode mode;
 
   public PreserveMethodCharacteristics(AppView<AppInfoWithLiveness> appView, ClassMergerMode mode) {
-    // This policy checks that method merging does invalidate various properties. Thus there is no
-    // reason to run this policy if method merging is not allowed.
-    assert !mode.isRestrictedToAlphaRenamingInR8();
+    // This policy checks that method merging does invalidate various properties.
     this.appView = appView;
+    this.mode = mode;
   }
 
   public static class TargetGroup {
@@ -108,12 +111,14 @@
       return group;
     }
 
-    public boolean tryAdd(AppView<AppInfoWithLiveness> appView, DexProgramClass clazz) {
+    public boolean tryAdd(
+        AppView<AppInfoWithLiveness> appView, ClassMergerMode mode, DexProgramClass clazz) {
       Map<DexMethodSignature, MethodCharacteristics> newMethods = new HashMap<>();
       for (DexEncodedMethod method : clazz.methods(this::isSubjectToMethodMerging)) {
         DexMethodSignature signature = method.getSignature();
         MethodCharacteristics existingCharacteristics = methodMap.get(signature);
-        MethodCharacteristics methodCharacteristics = MethodCharacteristics.create(appView, method);
+        MethodCharacteristics methodCharacteristics =
+            MethodCharacteristics.create(appView, mode, method);
         if (existingCharacteristics == null) {
           newMethods.put(signature, methodCharacteristics);
           continue;
@@ -143,10 +148,11 @@
     List<TargetGroup> groups = new ArrayList<>();
 
     for (DexProgramClass clazz : group) {
-      boolean added = Iterables.any(groups, targetGroup -> targetGroup.tryAdd(appView, clazz));
+      boolean added =
+          Iterables.any(groups, targetGroup -> targetGroup.tryAdd(appView, mode, clazz));
       if (!added) {
         TargetGroup newGroup = new TargetGroup();
-        added = newGroup.tryAdd(appView, clazz);
+        added = newGroup.tryAdd(appView, mode, clazz);
         assert added;
         groups.add(newGroup);
       }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java
index 9b8306d..892c40c 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.horizontalclassmerging.policies;
 
-import com.android.tools.r8.classmerging.ClassMergerMode;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
@@ -28,12 +27,9 @@
 public class RespectPackageBoundaries extends MultiClassPolicy {
 
   private final AppView<? extends AppInfoWithClassHierarchy> appView;
-  private final ClassMergerMode mode;
 
-  public RespectPackageBoundaries(
-      AppView<? extends AppInfoWithClassHierarchy> appView, ClassMergerMode mode) {
+  public RespectPackageBoundaries(AppView<? extends AppInfoWithClassHierarchy> appView) {
     this.appView = appView;
-    this.mode = mode;
   }
 
   boolean shouldRestrictMergingAcrossPackageBoundary(DexProgramClass clazz) {
@@ -93,20 +89,15 @@
 
                         @Override
                         protected boolean checkRewrittenFieldType(DexClassAndField field) {
-                          if (mode.isRestrictedToAlphaRenamingInR8()) {
-                            // No relaxing of field types, hence no insertion of casts where we need
-                            // to guarantee visibility.
-                          } else {
-                            // If the type of the field is package private, we need to keep the
-                            // current class in its package in case we end up synthesizing a
-                            // check-cast for the field type after relaxing the type of the field
-                            // after instance field merging.
-                            DexType fieldBaseType = field.getType().toBaseType(dexItemFactory());
-                            if (fieldBaseType.isClassType()) {
-                              DexClass fieldBaseClass = appView.definitionFor(fieldBaseType);
-                              if (fieldBaseClass == null || !fieldBaseClass.isPublic()) {
-                                return setFoundPackagePrivateAccess();
-                              }
+                          // If the type of the field is package private, we need to keep the
+                          // current class in its package in case we end up synthesizing a
+                          // check-cast for the field type after relaxing the type of the field
+                          // after instance field merging.
+                          DexType fieldBaseType = field.getType().toBaseType(dexItemFactory());
+                          if (fieldBaseType.isClassType()) {
+                            DexClass fieldBaseClass = appView.definitionFor(fieldBaseType);
+                            if (fieldBaseClass == null || !fieldBaseClass.isPublic()) {
+                              return setFoundPackagePrivateAccess();
                             }
                           }
                           return continueSearchForPackagePrivateAccess();
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameInstanceFields.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameInstanceFields.java
index 73ff6fc..d3cb879 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameInstanceFields.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameInstanceFields.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.horizontalclassmerging.policies;
 
-import com.android.tools.r8.classmerging.ClassMergerMode;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexEncodedField;
@@ -21,25 +20,16 @@
 public class SameInstanceFields extends MultiClassSameReferencePolicy<Multiset<InstanceFieldInfo>> {
 
   private final DexItemFactory dexItemFactory;
-  private final ClassMergerMode mode;
 
-  public SameInstanceFields(
-      AppView<? extends AppInfoWithClassHierarchy> appView, ClassMergerMode mode) {
+  public SameInstanceFields(AppView<? extends AppInfoWithClassHierarchy> appView) {
     this.dexItemFactory = appView.dexItemFactory();
-    this.mode = mode;
   }
 
   @Override
   public Multiset<InstanceFieldInfo> getMergeKey(DexProgramClass clazz) {
     Multiset<InstanceFieldInfo> fields = HashMultiset.create();
     for (DexEncodedField field : clazz.instanceFields()) {
-      // We do not allow merging fields with different types in the final round of horizontal class
-      // merging, since that requires inserting check-cast instructions at reads.
-      InstanceFieldInfo instanceFieldInfo =
-          mode.isRestrictedToAlphaRenamingInR8()
-              ? InstanceFieldInfo.createExact(field)
-              : InstanceFieldInfo.createRelaxed(field, dexItemFactory);
-      fields.add(instanceFieldInfo);
+      fields.add(InstanceFieldInfo.createRelaxed(field, dexItemFactory));
     }
     return fields;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 048460e..31577fc 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -709,7 +709,7 @@
     }
 
     if (!isDebugMode) {
-      new StringOptimizer(appView).run(code, timing);
+      new StringOptimizer(appView).run(code, methodProcessor, methodProcessingContext, timing);
       timing.begin("Optimize library methods");
       appView
           .libraryMethodOptimizer()
@@ -821,7 +821,8 @@
       constantCanonicalizer.canonicalize();
       timing.end();
       previous = printMethod(code, "IR after constant canonicalization (SSA)", previous);
-      new DexConstantOptimizer(appView, constantCanonicalizer).run(code, timing);
+      new DexConstantOptimizer(appView, constantCanonicalizer)
+          .run(code, methodProcessor, methodProcessingContext, timing);
       previous = printMethod(code, "IR after dex constant optimization (SSA)", previous);
     }
 
@@ -847,7 +848,8 @@
 
     deadCodeRemover.run(code, timing);
 
-    new ParentConstructorHoistingCodeRewriter(appView).run(code, timing);
+    new ParentConstructorHoistingCodeRewriter(appView)
+        .run(code, methodProcessor, methodProcessingContext, timing);
 
     BytecodeMetadataProvider.Builder bytecodeMetadataProviderBuilder =
         BytecodeMetadataProvider.builder();
@@ -877,7 +879,7 @@
       assert code.isConsistentSSA(appView);
 
       // TODO(b/214496607): Remove when dynamic types are safe w.r.t. interface assignment rules.
-      new MoveResultRewriter(appView).run(code, timing);
+      new MoveResultRewriter(appView).run(code, methodProcessor, methodProcessingContext, timing);
     }
 
     // Assert that we do not have unremoved non-sense code in the output, e.g., v <- non-null NULL.
diff --git a/src/main/java/com/android/tools/r8/lightir/LirBuilder.java b/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
index 6f68845..a35a20f 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
@@ -810,6 +810,10 @@
     for (int i = 0; i < keys.length; i++) {
       targets[i] = getBlockIndex(keyToTargetMap.get(keys[i]));
     }
+    return addIntSwitch(value, keys, targets);
+  }
+
+  public LirBuilder<V, EV> addIntSwitch(V value, int[] keys, int[] targets) {
     IntSwitchPayload payload = new IntSwitchPayload(keys, targets);
     return addInstructionTemplate(
         LirOpcodes.TABLESWITCH,
diff --git a/src/main/java/com/android/tools/r8/lightir/LirCode.java b/src/main/java/com/android/tools/r8/lightir/LirCode.java
index 0158379..6f93b21 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirCode.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirCode.java
@@ -534,8 +534,11 @@
       ProgramMethod method,
       AppView<?> appView,
       MutableMethodConversionOptions conversionOptions) {
+    GraphLens codeLens = method.getDefinition().getCode().getCodeLens(appView);
     RewrittenPrototypeDescription protoChanges =
-        appView.graphLens().lookupPrototypeChangesForMethodDefinition(method.getReference());
+        appView
+            .graphLens()
+            .lookupPrototypeChangesForMethodDefinition(method.getReference(), codeLens);
     return internalBuildIR(
         method, appView, new NumberGenerator(), null, protoChanges, conversionOptions);
   }
diff --git a/src/main/java/com/android/tools/r8/lightir/LirLensCodeRewriter.java b/src/main/java/com/android/tools/r8/lightir/LirLensCodeRewriter.java
index 26a4261..a610af6 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirLensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirLensCodeRewriter.java
@@ -138,6 +138,9 @@
 
   private boolean hasPotentialNonTrivialInvokeRewriting(
       DexMethod method, InvokeType type, MethodLookupResult result) {
+    if (graphLens.isHorizontalClassMergerGraphLens()) {
+      return !result.getPrototypeChanges().isEmpty();
+    }
     if (graphLens.isProtoNormalizerLens()) {
       return result.getPrototypeChanges().getArgumentInfoCollection().hasArgumentPermutation();
     }
@@ -315,25 +318,29 @@
   }
 
   private boolean hasNonTrivialMethodChanges() {
+    if (graphLens.isClassMergerLens()) {
+      RewrittenPrototypeDescription prototypeChanges =
+          graphLens.lookupPrototypeChangesForMethodDefinition(context.getReference(), codeLens);
+      assert prototypeChanges.getArgumentInfoCollection().isEmpty();
+      assert !prototypeChanges.hasRewrittenReturnInfo();
+      if (prototypeChanges.hasExtraParameters()) {
+        return true;
+      }
+      VerticalClassMergerGraphLens verticalClassMergerLens = graphLens.asVerticalClassMergerLens();
+      if (verticalClassMergerLens != null) {
+        DexMethod previousReference =
+            verticalClassMergerLens.getPreviousMethodSignature(contextReference);
+        return verticalClassMergerLens.hasInterfaceBeenMergedIntoClass(
+            previousReference.getReturnType());
+      }
+    }
     if (graphLens.isProtoNormalizerLens()) {
       RewrittenPrototypeDescription prototypeChanges =
           graphLens.lookupPrototypeChangesForMethodDefinition(context.getReference(), codeLens);
+      assert !prototypeChanges.hasExtraParameters();
+      assert !prototypeChanges.hasRewrittenReturnInfo();
       return prototypeChanges.getArgumentInfoCollection().hasArgumentPermutation();
     }
-    VerticalClassMergerGraphLens verticalClassMergerLens = graphLens.asVerticalClassMergerLens();
-    if (verticalClassMergerLens != null) {
-      DexMethod previousReference =
-          verticalClassMergerLens.getPreviousMethodSignature(contextReference);
-      if (verticalClassMergerLens.hasInterfaceBeenMergedIntoClass(
-          previousReference.getReturnType())) {
-        return true;
-      }
-      RewrittenPrototypeDescription prototypeChanges =
-          graphLens.lookupPrototypeChangesForMethodDefinition(context.getReference(), codeLens);
-      if (!prototypeChanges.isEmpty()) {
-        return true;
-      }
-    }
     return false;
   }
 
diff --git a/src/main/java/com/android/tools/r8/naming/Minifier.java b/src/main/java/com/android/tools/r8/naming/Minifier.java
index 937d6a3..376c969 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -277,7 +277,6 @@
         InternalNamingState internalState,
         BiPredicate<DexString, DexMethod> isAvailable) {
       if (!method.isProgramMethod()) {
-        assert isAvailable.test(method.getName(), method.getReference());
         return method.getName();
       }
       assert allowMemberRenaming(method);
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java
index b19cfe0..dfdd31b 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java
@@ -97,7 +97,7 @@
   private void run(ExecutorService executorService, Timing timing) throws ExecutionException {
     timing.begin("Setup");
     ImmediateProgramSubtypingInfo immediateSubtypingInfo =
-        ImmediateProgramSubtypingInfo.create(appView);
+        ImmediateProgramSubtypingInfo.createWithDeterministicOrder(appView);
 
     // Compute the disjoint class hierarchies for parallel processing.
     List<Set<DexProgramClass>> connectedComponents =
@@ -123,7 +123,12 @@
       return;
     }
     VerticalClassMergerGraphLens lens =
-        runFixup(classMergerSharedData, verticalClassMergerResult, executorService, timing);
+        runFixup(
+            classMergerSharedData,
+            immediateSubtypingInfo,
+            verticalClassMergerResult,
+            executorService,
+            timing);
     assert verifyGraphLens(lens, verticalClassMergerResult);
 
     // Update keep info and art profiles.
@@ -230,12 +235,13 @@
 
   private VerticalClassMergerGraphLens runFixup(
       ClassMergerSharedData classMergerSharedData,
+      ImmediateProgramSubtypingInfo immediateSubtypingInfo,
       VerticalClassMergerResult verticalClassMergerResult,
       ExecutorService executorService,
       Timing timing)
       throws ExecutionException {
     return new VerticalClassMergerTreeFixer(
-            appView, classMergerSharedData, verticalClassMergerResult)
+            appView, classMergerSharedData, immediateSubtypingInfo, verticalClassMergerResult)
         .run(executorService, timing);
   }
 
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerGraphLens.java
index 783b3d2..8d0ad13 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerGraphLens.java
@@ -23,7 +23,6 @@
 import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
 import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import com.android.tools.r8.ir.code.InvokeType;
-import com.android.tools.r8.ir.conversion.ExtraParameter;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.KeepInfoCollection;
 import com.android.tools.r8.utils.InternalOptions;
@@ -40,7 +39,6 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.IdentityHashMap;
-import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
@@ -372,12 +370,6 @@
     }
 
     @Override
-    public void addExtraParameters(
-        DexMethod from, DexMethod to, List<? extends ExtraParameter> extraParameters) {
-      // Intentionally empty.
-    }
-
-    @Override
     public void commitPendingUpdates() {
       // Commit new field signatures.
       newFieldSignatures.putAll(pendingNewFieldSignatureUpdates);
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerTreeFixer.java b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerTreeFixer.java
index ff87a99..b601485 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerTreeFixer.java
@@ -6,6 +6,7 @@
 import com.android.tools.r8.classmerging.ClassMergerSharedData;
 import com.android.tools.r8.classmerging.ClassMergerTreeFixer;
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.Timing;
 import java.util.List;
@@ -23,10 +24,12 @@
   VerticalClassMergerTreeFixer(
       AppView<AppInfoWithLiveness> appView,
       ClassMergerSharedData classMergerSharedData,
+      ImmediateProgramSubtypingInfo immediateSubtypingInfo,
       VerticalClassMergerResult verticalClassMergerResult) {
     super(
         appView,
         classMergerSharedData,
+        immediateSubtypingInfo,
         VerticalClassMergerGraphLens.Builder.createBuilderForFixup(verticalClassMergerResult),
         verticalClassMergerResult.getVerticallyMergedClasses());
     this.synthesizedBridges = verticalClassMergerResult.getSynthesizedBridges();
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
index e2beb9e..93a9889 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -128,7 +128,7 @@
         .withOptionConsumer(opts -> opts.enableClassInlining = false)
         .withBuilderTransformation(
             b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
-        .withDexCheck(inspector -> checkLambdaCount(inspector, 11, "lambdadesugaring"))
+        .withDexCheck(inspector -> checkLambdaCount(inspector, 6, "lambdadesugaring"))
         .run(Paths.get(ToolHelper.THIRD_PARTY_DIR, "examplesAndroidOLegacy"));
 
     test("lambdadesugaring", "lambdadesugaring", "LambdaDesugaring")
@@ -167,7 +167,7 @@
         .withOptionConsumer(opts -> opts.enableClassInlining = false)
         .withBuilderTransformation(
             b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
-        .withDexCheck(inspector -> checkLambdaCount(inspector, 11, "lambdadesugaring"))
+        .withDexCheck(inspector -> checkLambdaCount(inspector, 6, "lambdadesugaring"))
         .run(Paths.get(ToolHelper.THIRD_PARTY_DIR, "examplesAndroidOLegacy"));
 
     test("lambdadesugaring", "lambdadesugaring", "LambdaDesugaring")
@@ -202,7 +202,7 @@
             b ->
                 b.addProguardConfiguration(
                     getProguardOptionsNPlus(enableProguardCompatibilityMode), Origin.unknown()))
-        .withDexCheck(inspector -> checkLambdaCount(inspector, 6, "lambdadesugaringnplus"))
+        .withDexCheck(inspector -> checkLambdaCount(inspector, 3, "lambdadesugaringnplus"))
         .run();
 
     test("lambdadesugaringnplus", "lambdadesugaringnplus", "LambdasWithStaticAndDefaultMethods")
@@ -214,7 +214,7 @@
             b ->
                 b.addProguardConfiguration(
                     getProguardOptionsNPlus(enableProguardCompatibilityMode), Origin.unknown()))
-        .withDexCheck(inspector -> checkLambdaCount(inspector, 3, "lambdadesugaringnplus"))
+        .withDexCheck(inspector -> checkLambdaCount(inspector, 2, "lambdadesugaringnplus"))
         .run();
   }
 
@@ -244,7 +244,7 @@
             b ->
                 b.addProguardConfiguration(
                     getProguardOptionsNPlus(enableProguardCompatibilityMode), Origin.unknown()))
-        .withDexCheck(inspector -> checkLambdaCount(inspector, 6, "lambdadesugaringnplus"))
+        .withDexCheck(inspector -> checkLambdaCount(inspector, 3, "lambdadesugaringnplus"))
         .run();
 
     test("lambdadesugaringnplus", "lambdadesugaringnplus", "LambdasWithStaticAndDefaultMethods")
@@ -256,7 +256,7 @@
             b ->
                 b.addProguardConfiguration(
                     getProguardOptionsNPlus(enableProguardCompatibilityMode), Origin.unknown()))
-        .withDexCheck(inspector -> checkLambdaCount(inspector, 3, "lambdadesugaringnplus"))
+        .withDexCheck(inspector -> checkLambdaCount(inspector, 2, "lambdadesugaringnplus"))
         .run();
   }
 
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingSubReferenceApiTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingSubReferenceApiTest.java
index dff5d14..4f6282a 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingSubReferenceApiTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingSubReferenceApiTest.java
@@ -6,7 +6,7 @@
 
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
 import static com.android.tools.r8.utils.AndroidApiLevel.L_MR1;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsentIf;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static junit.framework.TestCase.assertEquals;
 import static org.hamcrest.CoreMatchers.not;
@@ -14,7 +14,6 @@
 
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.NoInliningOfDefaultInitializer;
 import com.android.tools.r8.NoMethodStaticizing;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
@@ -26,22 +25,20 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
 public class ApiModelNoVerticalMergingSubReferenceApiTest extends TestBase {
 
-  private final TestParameters parameters;
+  @Parameter(0)
+  public TestParameters parameters;
 
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
     return getTestParameters().withAllRuntimesAndApiLevels().build();
   }
 
-  public ApiModelNoVerticalMergingSubReferenceApiTest(TestParameters parameters) {
-    this.parameters = parameters;
-  }
-
   @Test()
   public void testR8() throws Exception {
     Method apiMethod = Api.class.getDeclaredMethod("apiLevel22");
@@ -57,7 +54,6 @@
         .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
-        .enableNoInliningOfDefaultInitializerAnnotations()
         .enableNoMethodStaticizingAnnotations()
         .addVerticallyMergedClassesInspector(
             inspector -> {
@@ -78,9 +74,7 @@
                 assertThat(base, not(isPresent()));
                 ClassSubject sub = inspector.clazz(Sub.class);
                 assertThat(sub, isPresent());
-                assertThat(
-                    sub.uniqueInstanceInitializer(),
-                    isAbsentIf(parameters.canHaveNonReboundConstructorInvoke()));
+                assertThat(sub.uniqueInstanceInitializer(), isAbsent());
                 assertEquals(1, sub.virtualMethods().size());
                 FoundMethodSubject callCallApi = sub.virtualMethods().get(0);
                 assertEquals("callCallApi", callCallApi.getOriginalName());
@@ -109,7 +103,6 @@
   }
 
   @NeverClassInline
-  @NoInliningOfDefaultInitializer
   public static class Sub extends Base {
 
     @NeverInline
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingTest.java
index 2e368e9..3f93300 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingTest.java
@@ -6,7 +6,7 @@
 
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
 import static com.android.tools.r8.utils.AndroidApiLevel.L_MR1;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsentIf;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static junit.framework.TestCase.assertEquals;
 import static org.hamcrest.CoreMatchers.not;
@@ -14,7 +14,6 @@
 
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.NoInliningOfDefaultInitializer;
 import com.android.tools.r8.NoMethodStaticizing;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
@@ -26,22 +25,20 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
 public class ApiModelNoVerticalMergingTest extends TestBase {
 
-  private final TestParameters parameters;
+  @Parameter(0)
+  public TestParameters parameters;
 
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
     return getTestParameters().withAllRuntimesAndApiLevels().build();
   }
 
-  public ApiModelNoVerticalMergingTest(TestParameters parameters) {
-    this.parameters = parameters;
-  }
-
   @Test()
   public void testR8() throws Exception {
     Method apiMethod = Api.class.getDeclaredMethod("apiLevel22");
@@ -57,7 +54,6 @@
         .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
-        .enableNoInliningOfDefaultInitializerAnnotations()
         .enableNoMethodStaticizingAnnotations()
         .addVerticallyMergedClassesInspector(
             inspector -> {
@@ -77,9 +73,7 @@
                 assertThat(base, not(isPresent()));
                 ClassSubject sub = inspector.clazz(Sub.class);
                 assertThat(sub, isPresent());
-                assertThat(
-                    sub.uniqueInstanceInitializer(),
-                    isAbsentIf(parameters.canHaveNonReboundConstructorInvoke()));
+                assertThat(sub.uniqueInstanceInitializer(), isAbsent());
                 assertEquals(1, sub.virtualMethods().size());
                 FoundMethodSubject callCallApi = sub.virtualMethods().get(0);
                 assertEquals("callCallApi", callCallApi.getOriginalName());
@@ -109,7 +103,6 @@
   }
 
   @NeverClassInline
-  @NoInliningOfDefaultInitializer
   public static class Sub extends Base {
 
     @NeverInline
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/BridgeWithInvokeSuperOnInterfaceTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/BridgeWithInvokeSuperOnInterfaceTest.java
index 4c6af25..e6d8721 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/BridgeWithInvokeSuperOnInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/BridgeWithInvokeSuperOnInterfaceTest.java
@@ -4,9 +4,9 @@
 
 package com.android.tools.r8.bridgeremoval;
 
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.CoreMatchers.not;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.NoVerticalClassMerging;
@@ -16,7 +16,7 @@
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -60,9 +60,12 @@
               // Check that we are removing the bridge if we support default methods.
               if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
                 ClassSubject J = inspector.clazz(J.class);
-                assertThat(J, isPresent());
-                MethodSubject fooMethod = J.uniqueMethodWithOriginalName("foo");
-                assertThat(fooMethod, not(isPresent()));
+                assertThat(J, isAbsent());
+                assertTrue(
+                    inspector.allClasses().stream()
+                        .allMatch(
+                            classSubject ->
+                                classSubject.allMethods(FoundMethodSubject::isBridge).isEmpty()));
               }
             })
         .run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingAfterConstructorShrinkingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingAfterConstructorShrinkingTest.java
index 4c390c7..f13a1a1 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingAfterConstructorShrinkingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingAfterConstructorShrinkingTest.java
@@ -54,7 +54,8 @@
             options -> options.horizontalClassMergerOptions().disableInitialRoundOfClassMerging())
         .addHorizontallyMergedClassesInspector(
             i -> {
-              if (enableRetargetingOfConstructorBridgeCalls) {
+              if (enableRetargetingOfConstructorBridgeCalls
+                  || parameters.canInitNewInstanceUsingSuperclassConstructor()) {
                 i.assertClassesMerged(A.class, B.class);
               } else {
                 i.assertNoClassesMerged();
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingAfterConstructorShrinkingWithRepackagingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingAfterConstructorShrinkingWithRepackagingTest.java
index 1dab3a8..9ac51d6 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingAfterConstructorShrinkingWithRepackagingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingAfterConstructorShrinkingWithRepackagingTest.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.classmerging.horizontal;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsentIf;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
@@ -77,7 +78,8 @@
             options -> options.horizontalClassMergerOptions().disableInitialRoundOfClassMerging())
         .addHorizontallyMergedClassesInspector(
             i -> {
-              if (enableRetargetingOfConstructorBridgeCalls) {
+              if (enableRetargetingOfConstructorBridgeCalls
+                  || parameters.canInitNewInstanceUsingSuperclassConstructor()) {
                 // Repackaging will have moved both classes to top-level package, so rename them.
                 i.assertClassReferencesMerged(
                     moveToTopLevelPackage(A.class), moveToTopLevelPackage(B.class));
@@ -96,29 +98,45 @@
               // Verify Parent and Parent.<init>(Parent) are present and that Parent has been
               // repackaged into the default package.
               ClassSubject parentClassSubject = inspector.clazz(Parent.class);
-              assertThat(parentClassSubject, isPresent());
-              assertEquals("", parentClassSubject.getDexProgramClass().getType().getPackageName());
+              assertThat(
+                  parentClassSubject,
+                  isAbsentIf(parameters.canInitNewInstanceUsingSuperclassConstructor()));
+              if (parentClassSubject.isPresent()) {
+                assertEquals(
+                    "", parentClassSubject.getDexProgramClass().getType().getPackageName());
 
-              MethodSubject parentInstanceInitializerSubject =
-                  parentClassSubject.uniqueInstanceInitializer();
-              assertThat(parentInstanceInitializerSubject, isPresent());
-              assertEquals(
-                  parentClassSubject.asTypeSubject(),
-                  parentInstanceInitializerSubject.getParameter(0));
+                MethodSubject parentInstanceInitializerSubject =
+                    parentClassSubject.uniqueInstanceInitializer();
+                assertThat(parentInstanceInitializerSubject, isPresent());
+                assertEquals(
+                    parentClassSubject.asTypeSubject(),
+                    parentInstanceInitializerSubject.getParameter(0));
+              }
 
               // Verify that A and A.<init>(Parent) are present.
               ClassSubject aClassSubject = inspector.clazz(A.class);
               assertThat(aClassSubject, isPresent());
               assertEquals("", aClassSubject.getDexProgramClass().getType().getPackageName());
 
-              MethodSubject aInstanceInitializerSubject = aClassSubject.uniqueInstanceInitializer();
+              MethodSubject aInstanceInitializerSubject =
+                  aClassSubject.uniqueMethodThatMatches(
+                      method ->
+                          method.isInstanceInitializer()
+                              && method
+                                  .streamInstructions()
+                                  .anyMatch(instruction -> instruction.isConstString("Ouch!")));
               assertThat(aInstanceInitializerSubject, isPresent());
+
               assertEquals(
-                  parentClassSubject.asTypeSubject(), aInstanceInitializerSubject.getParameter(0));
+                  parentClassSubject.isPresent()
+                      ? parentClassSubject.asTypeSubject()
+                      : aClassSubject.asTypeSubject(),
+                  aInstanceInitializerSubject.getParameter(0));
 
               // Verify that B or B's initializer was removed.
               ClassSubject bClassSubject = inspector.clazz(B.class);
-              if (enableRetargetingOfConstructorBridgeCalls) {
+              if (enableRetargetingOfConstructorBridgeCalls
+                  || parameters.canInitNewInstanceUsingSuperclassConstructor()) {
                 assertThat(bClassSubject, isAbsent());
               } else {
                 assertThat(bClassSubject, isPresent());
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerConstructorCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerConstructorCollisionTest.java
index 79b5725..67c2d49 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerConstructorCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerConstructorCollisionTest.java
@@ -12,6 +12,10 @@
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.NoHorizontalClassMerging;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+import com.android.tools.r8.utils.Box;
 import org.junit.Test;
 
 public class TreeFixerConstructorCollisionTest extends HorizontalClassMergingTestBase {
@@ -21,14 +25,28 @@
 
   @Test
   public void testR8() throws Exception {
+    Box<ClassReference> aClassReferenceAfterRepackaging = new Box<>();
     testForR8(parameters.getBackend())
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addHorizontallyMergedClassesInspector(
-            inspector ->
-                inspector.assertIsCompleteMergeGroup(A.class, B.class).assertNoOtherClassesMerged())
+            inspector -> {
+              ClassReference aClassReference = aClassReferenceAfterRepackaging.get();
+              inspector
+                  .assertIsCompleteMergeGroup(A.class, B.class)
+                  .assertIsCompleteMergeGroup(
+                      SyntheticItemsTestUtils.syntheticInitializerArgumentType(aClassReference, 0),
+                      SyntheticItemsTestUtils.syntheticInitializerArgumentType(aClassReference, 1),
+                      SyntheticItemsTestUtils.syntheticInitializerArgumentType(aClassReference, 2),
+                      SyntheticItemsTestUtils.syntheticInitializerArgumentType(aClassReference, 3))
+                  .assertNoOtherClassesMerged();
+            })
         .addOptionsModification(
             options -> options.inlinerOptions().setEnableConstructorInlining(false))
+        .addRepackagingInspector(
+            inspector ->
+                aClassReferenceAfterRepackaging.set(
+                    inspector.getTarget(Reference.classFromClass(A.class))))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
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 abff6e4..c9b170b 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
@@ -32,10 +32,10 @@
                 inspector
                     .applyIf(
                         parameters.canUseDefaultAndStaticInterfaceMethods(),
-                        i -> i.assertIsCompleteMergeGroup(I.class, J.class))
-                    .applyIf(
-                        !parameters.canUseDefaultAndStaticInterfaceMethods(),
-                        i -> i.assertIsCompleteMergeGroup(B1.class, B2.class))
+                        i -> i.assertIsCompleteMergeGroup(I.class, J.class),
+                        i ->
+                            i.assertIsCompleteMergeGroup(B1.class, B2.class)
+                                .assertIsCompleteMergeGroup(C1.class, C2.class))
                     .assertNoOtherClassesMerged())
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
@@ -55,7 +55,9 @@
                   codeInspector.clazz(B2.class),
                   isPresentIf(parameters.canUseDefaultAndStaticInterfaceMethods()));
               assertThat(codeInspector.clazz(C1.class), isPresent());
-              assertThat(codeInspector.clazz(C2.class), isPresent());
+              assertThat(
+                  codeInspector.clazz(C2.class),
+                  isPresentIf(parameters.canUseDefaultAndStaticInterfaceMethods()));
             });
   }
 
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/whyareyounotinlining/WhyAreYouNotInliningInvokeWithUnknownTargetTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/whyareyounotinlining/WhyAreYouNotInliningInvokeWithUnknownTargetTest.java
index 5baa779..06f5aa2 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/whyareyounotinlining/WhyAreYouNotInliningInvokeWithUnknownTargetTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/whyareyounotinlining/WhyAreYouNotInliningInvokeWithUnknownTargetTest.java
@@ -8,6 +8,7 @@
 import static org.hamcrest.CoreMatchers.is;
 
 import com.android.tools.r8.DiagnosticsMatcher;
+import com.android.tools.r8.NeverReprocessMethod;
 import com.android.tools.r8.NoHorizontalClassMerging;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
@@ -39,6 +40,7 @@
         .addKeepRules("-whyareyounotinlining class " + A.class.getTypeName() + " { void m(); }")
         .enableExperimentalWhyAreYouNotInlining()
         .enableNoHorizontalClassMergingAnnotations()
+        .enableNeverReprocessMethodAnnotations()
         .setMinApi(parameters)
         .allowDiagnosticInfoMessages()
         .compile()
@@ -59,6 +61,7 @@
 
   static class TestClass {
 
+    @NeverReprocessMethod
     public static void main(String[] args) {
       (System.currentTimeMillis() >= 0 ? new A() : new B()).m();
     }
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaGroupGCLimitTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaGroupGCLimitTest.java
index c9c9e05..0a52142 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaGroupGCLimitTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaGroupGCLimitTest.java
@@ -66,8 +66,8 @@
                 inspector -> {
                   HorizontalClassMergerOptions defaultHorizontalClassMergerOptions =
                       new InternalOptions().horizontalClassMergerOptions();
-                  assertEquals(4833, inspector.getSources().size());
-                  assertEquals(167, inspector.getTargets().size());
+                  assertEquals(4994, inspector.getSources().size());
+                  assertEquals(173, inspector.getTargets().size());
                   assertTrue(
                       inspector.getMergeGroups().stream()
                           .allMatch(
diff --git a/src/test/java/com/android/tools/r8/kotlin/sealed/SealedInterfaceSubClassesHorizontalMergeTest.java b/src/test/java/com/android/tools/r8/kotlin/sealed/SealedInterfaceSubClassesHorizontalMergeTest.java
index 238099b..a206c4a 100644
--- a/src/test/java/com/android/tools/r8/kotlin/sealed/SealedInterfaceSubClassesHorizontalMergeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/sealed/SealedInterfaceSubClassesHorizontalMergeTest.java
@@ -13,8 +13,10 @@
 import com.android.tools.r8.KotlinTestBase;
 import com.android.tools.r8.KotlinTestParameters;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
+import com.android.tools.r8.utils.Box;
 import java.io.IOException;
 import java.nio.file.Path;
 import java.util.Collection;
@@ -86,6 +88,8 @@
 
   @Test
   public void testR8() throws ExecutionException, CompilationFailedException, IOException {
+    Box<ClassReference> aClassReferenceAfterRepackaging = new Box<>();
+    Box<ClassReference> bClassReferenceAfterRepackaging = new Box<>();
     testForR8(parameters.getBackend())
         .addProgramFiles(compilationResults.getForConfiguration(kotlinc, targetVersion))
         .addProgramFiles(kotlinc.getKotlinStdlibJar())
@@ -103,11 +107,21 @@
                             inspector
                                 .assertIsCompleteMergeGroup(A, B)
                                 .assertNoOtherClassesMerged()),
-            // TODO(b/296852026): Updates to horizintal class merging can cause this to start
-            // failing as the classes can actually bne merged without -assumenosideeffects rules.
             b ->
                 b.addHorizontallyMergedClassesInspector(
-                    HorizontallyMergedClassesInspector::assertNoClassesMerged))
+                        inspector ->
+                            inspector
+                                .assertIsCompleteMergeGroup(
+                                    aClassReferenceAfterRepackaging.get(),
+                                    bClassReferenceAfterRepackaging.get())
+                                .assertNoOtherClassesMerged())
+                    .addRepackagingInspector(
+                        inspector -> {
+                          aClassReferenceAfterRepackaging.set(
+                              inspector.getTarget(Reference.classFromTypeName(A)));
+                          bClassReferenceAfterRepackaging.set(
+                              inspector.getTarget(Reference.classFromTypeName(B)));
+                        }))
         .compile()
         .assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
         .run(parameters.getRuntime(), MAIN)
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveVirtualBridgeTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveVirtualBridgeTest.java
index 4013b58..7e99f86 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveVirtualBridgeTest.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveVirtualBridgeTest.java
@@ -62,12 +62,10 @@
             inspector -> {
               ClassSubject clazz = inspector.clazz(B.class);
               assertThat(clazz, isPresent());
-              if (parameters.canHaveNonReboundConstructorInvoke()) {
-                assertEquals(0, clazz.allMethods().size());
-              } else {
-                assertEquals(1, clazz.allMethods().size());
-                assertThat(clazz.allMethods().get(0), isInstanceInitializer());
-              }
+              // TODO(b/324527514): Initializer should be removed when
+              //  canHaveNonReboundConstructorInvoke().
+              assertEquals(1, clazz.allMethods().size());
+              assertThat(clazz.allMethods().get(0), isInstanceInitializer());
             });
   }
 
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/LambdaStaticLibraryMethodImplementationProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/LambdaStaticLibraryMethodImplementationProfileRewritingTest.java
index c83d81d..6eac7c2 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/LambdaStaticLibraryMethodImplementationProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/LambdaStaticLibraryMethodImplementationProfileRewritingTest.java
@@ -103,9 +103,7 @@
     assertThat(lambdaClassSubject, notIf(isPresent(), canUseLambdas));
 
     MethodSubject lambdaInitializerSubject = lambdaClassSubject.uniqueInstanceInitializer();
-    assertThat(
-        lambdaInitializerSubject,
-        notIf(isPresent(), canHaveNonReboundConstructorInvoke || canUseLambdas));
+    assertThat(lambdaInitializerSubject, notIf(isPresent(), canUseLambdas));
 
     MethodSubject lambdaMainMethodSubject =
         lambdaClassSubject.uniqueMethodThatMatches(FoundMethodSubject::isVirtual);
@@ -116,10 +114,8 @@
     } else {
       profileInspector
           .assertContainsClassRules(lambdaClassSubject)
-          .assertContainsMethodRules(mainMethodSubject, lambdaMainMethodSubject)
-          .applyIf(
-              !canHaveNonReboundConstructorInvoke,
-              i -> i.assertContainsMethodRule(lambdaInitializerSubject));
+          .assertContainsMethodRules(
+              mainMethodSubject, lambdaInitializerSubject, lambdaMainMethodSubject);
     }
 
     profileInspector.assertContainsNoOtherRules();
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java
index 577518d..f296a71 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java
@@ -133,7 +133,8 @@
         syntheticConstructorArgumentClassSubject, notIf(isPresent(), canUseNestBasedAccesses));
 
     MethodSubject instanceInitializer = nestMemberClassSubject.init();
-    assertThat(instanceInitializer, notIf(isPresent(), canHaveNonReboundConstructorInvoke));
+    // TODO(b/324527514): Should be absent when canHaveNonReboundConstructorInvoke is true.
+    assertThat(instanceInitializer, isPresent());
 
     MethodSubject instanceInitializerWithSyntheticArgumentSubject =
         syntheticConstructorArgumentClassSubject.isPresent()
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
index cd72b73..cbab790 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
@@ -199,8 +199,7 @@
     ClassSubject recordTagClassSubject = inspector.clazz(recordClassReference);
     assertThat(recordTagClassSubject, isAbsentIf(canMergeRecordTag || canUseRecords));
     if (recordTagClassSubject.isPresent()) {
-      assertEquals(
-          canHaveNonReboundConstructorInvoke ? 0 : 1, recordTagClassSubject.allMethods().size());
+      assertEquals(1, recordTagClassSubject.allMethods().size());
     }
 
     MethodSubject recordTagInstanceInitializerSubject = recordTagClassSubject.init();
@@ -216,13 +215,12 @@
                 : recordTagClassSubject.asTypeSubject(),
         personRecordClassSubject.getSuperType());
     assertEquals(
-        canUseRecords ? 6 : canHaveNonReboundConstructorInvoke || !canMergeRecordTag ? 10 : 11,
+        canUseRecords ? 6 : canMergeRecordTag ? 11 : 10,
         personRecordClassSubject.allMethods().size());
 
     MethodSubject personDefaultInstanceInitializerSubject = personRecordClassSubject.init();
     assertThat(
-        personDefaultInstanceInitializerSubject,
-        isPresentIf(!canHaveNonReboundConstructorInvoke && canMergeRecordTag && !canUseRecords));
+        personDefaultInstanceInitializerSubject, isPresentIf(canMergeRecordTag && !canUseRecords));
 
     MethodSubject personInstanceInitializerSubject =
         canMergeRecordTag
@@ -317,7 +315,7 @@
                 i.assertContainsMethodRules(
                     nameNestAccessorMethodSubject, ageNestAccessorMethodSubject))
         .applyIf(
-            !canHaveNonReboundConstructorInvoke && canMergeRecordTag && !canUseRecords,
+            canMergeRecordTag && !canUseRecords,
             i -> i.assertContainsMethodRule(personDefaultInstanceInitializerSubject))
         .applyIf(
             !canUseRecords,
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithOverridesOfPackagePrivateMethodsTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithOverridesOfPackagePrivateMethodsTest.java
index 2f7657a..d3b2371 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageWithOverridesOfPackagePrivateMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithOverridesOfPackagePrivateMethodsTest.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.repackage;
 
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import com.android.tools.r8.NeverClassInline;
@@ -30,6 +31,12 @@
     testForR8(parameters.getBackend())
         .addInnerClasses(getClass())
         .addKeepMainRule(TestClass.class)
+        .addHorizontallyMergedClassesInspector(
+            inspector ->
+                inspector
+                    .assertIsCompleteMergeGroup(HelloGreeterBase.class, WorldGreeterBase.class)
+                    .assertIsCompleteMergeGroup(HelloGreeter.class, WorldGreeter.class)
+                    .assertNoOtherClassesMerged())
         .apply(this::configureRepackaging)
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
@@ -45,7 +52,7 @@
 
   private void inspect(CodeInspector inspector) {
     assertThat(HelloGreeter.class, isNotRepackaged(inspector));
-    assertThat(WorldGreeter.class, isNotRepackaged(inspector));
+    assertThat(inspector.clazz(WorldGreeter.class), isAbsent());
   }
 
   public static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithOverridesOfProtectedMethodsTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithOverridesOfProtectedMethodsTest.java
index 356bf73..bebcdba 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageWithOverridesOfProtectedMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithOverridesOfProtectedMethodsTest.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.repackage;
 
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import com.android.tools.r8.NeverClassInline;
@@ -11,6 +12,9 @@
 import com.android.tools.r8.NoParameterTypeStrengthening;
 import com.android.tools.r8.NoVerticalClassMerging;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.Box;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -26,9 +30,25 @@
 
   @Test
   public void test() throws Exception {
+    Box<ClassReference> helloGreeterAfterRepackaging = new Box<>();
+    Box<ClassReference> worldGreeterAfterRepackaging = new Box<>();
     testForR8(parameters.getBackend())
         .addInnerClasses(getClass())
         .addKeepMainRule(TestClass.class)
+        .addHorizontallyMergedClassesInspector(
+            inspector ->
+                inspector
+                    .assertIsCompleteMergeGroup(HelloGreeterBase.class, WorldGreeterBase.class)
+                    .assertIsCompleteMergeGroup(
+                        helloGreeterAfterRepackaging.get(), worldGreeterAfterRepackaging.get())
+                    .assertNoOtherClassesMerged())
+        .addRepackagingInspector(
+            inspector -> {
+              helloGreeterAfterRepackaging.set(
+                  inspector.getTarget(Reference.classFromClass(HelloGreeter.class)));
+              worldGreeterAfterRepackaging.set(
+                  inspector.getTarget(Reference.classFromClass(WorldGreeter.class)));
+            })
         .apply(this::configureRepackaging)
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
@@ -43,7 +63,7 @@
 
   private void inspect(CodeInspector inspector) {
     assertThat(HelloGreeter.class, isRepackaged(inspector));
-    assertThat(WorldGreeter.class, isRepackaged(inspector));
+    assertThat(inspector.clazz(WorldGreeter.class), isAbsent());
   }
 
   public static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/shaking/RetainIndirectlyReferencedConstructorShakingOnDexTest.java b/src/test/java/com/android/tools/r8/shaking/RetainIndirectlyReferencedConstructorShakingOnDexTest.java
index a3d1fef..102603a 100644
--- a/src/test/java/com/android/tools/r8/shaking/RetainIndirectlyReferencedConstructorShakingOnDexTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/RetainIndirectlyReferencedConstructorShakingOnDexTest.java
@@ -76,19 +76,17 @@
 
     ClassSubject bClassSubject = inspector.clazz(B.class);
     assertThat(bClassSubject, isPresent());
-    assertEquals(
-        parameters.canHaveNonReboundConstructorInvoke() ? 0 : 1,
-        bClassSubject.allMethods(FoundMethodSubject::isInstanceInitializer).size());
+    // TODO(b/324527514): Should be `1 - intValue(parameters.canHaveNonReboundConstructorInvoke())`.
+    assertEquals(1, bClassSubject.allMethods(FoundMethodSubject::isInstanceInitializer).size());
 
     MethodSubject mainMethodSubject = mainClassSubject.mainMethod();
+    // TODO(b/324527514): Should invoke A.<init> when canHaveNonReboundConstructorInvoke()
+    //  && enableRetargetingOfConstructorBridgeCalls.
     assertThat(
         mainMethodSubject,
         invokesMethod(
             MethodReferenceUtils.instanceConstructor(
-                parameters.canHaveNonReboundConstructorInvoke()
-                        && enableRetargetingOfConstructorBridgeCalls
-                    ? Reference.classFromDescriptor(aClassSubject.getFinalDescriptor())
-                    : Reference.classFromDescriptor(bClassSubject.getFinalDescriptor()))));
+                Reference.classFromDescriptor(bClassSubject.getFinalDescriptor()))));
   }
 
   static class Main {
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/AllowHorizontalClassMergingWithIfRuleTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/AllowHorizontalClassMergingWithIfRuleTest.java
index af28109..f8ca87c 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/AllowHorizontalClassMergingWithIfRuleTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/AllowHorizontalClassMergingWithIfRuleTest.java
@@ -31,7 +31,9 @@
         .addKeepRules("-if class * { void foo(); } -keep class " + Main.class.getTypeName())
         .addHorizontallyMergedClassesInspector(
             inspector ->
-                inspector.assertIsCompleteMergeGroup(B.class, C.class).assertNoOtherClassesMerged())
+                inspector
+                    .assertIsCompleteMergeGroup(A.class, B.class, C.class)
+                    .assertNoOtherClassesMerged())
         .setMinApi(parameters)
         .compile()
         .run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
index 47d969c..a5e5f19 100644
--- a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
+++ b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
@@ -152,6 +152,11 @@
         originalMethod.getMethodDescriptor());
   }
 
+  public static ClassReference syntheticInitializerArgumentType(
+      ClassReference classReference, int id) {
+    return syntheticClass(classReference, naming.NON_FIXED_INIT_TYPE_ARGUMENT, id);
+  }
+
   public static ClassReference syntheticNestConstructorArgumentClass(
       ClassReference classReference) {
     return Reference.classFromDescriptor(