Introduce an extra round of vertical class merging

Change-Id: I0abf717d08ae9a46cef03b7219db89603f187994
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 2250d81..33b687b 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -755,7 +755,6 @@
     // Assert some of R8 optimizations are disabled.
     assert !internal.inlinerOptions().enableInlining;
     assert !internal.enableClassInlining;
-    assert internal.getVerticalClassMergerOptions().isDisabled();
     assert !internal.enableEnumValueOptimization;
     assert !internal.outline.enabled;
     assert !internal.enableTreeShakingOfLibraryMethodOverrides;
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index 726013e..339e80b 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -195,7 +195,6 @@
     // Assert some of R8 optimizations are disabled.
     assert !internal.inlinerOptions().enableInlining;
     assert !internal.enableClassInlining;
-    assert internal.getVerticalClassMergerOptions().isDisabled();
     assert !internal.enableEnumValueOptimization;
     assert !internal.outline.enabled;
     assert !internal.enableTreeShakingOfLibraryMethodOverrides;
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 12a84ab..ad074ed 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -499,7 +499,8 @@
 
       assert ArtProfileCompletenessChecker.verify(appView);
 
-      VerticalClassMerger.runIfNecessary(appViewWithLiveness, executorService, timing);
+      VerticalClassMerger.createForInitialClassMerging(appViewWithLiveness)
+          .runIfNecessary(executorService, timing);
       HorizontalClassMerger.createForInitialClassMerging(appViewWithLiveness)
           .runIfNecessary(
               executorService,
@@ -737,6 +738,11 @@
       GenericSignatureContextBuilder genericContextBuilderBeforeFinalMerging =
           GenericSignatureContextBuilder.create(appView);
 
+      if (appView.hasLiveness()) {
+        VerticalClassMerger.createForFinalClassMerging(appView.withLiveness())
+            .runIfNecessary(executorService, timing);
+      }
+
       // Run horizontal class merging. This runs even if shrinking is disabled to ensure synthetics
       // are always merged.
       HorizontalClassMerger.createForFinalClassMerging(appView)
diff --git a/src/main/java/com/android/tools/r8/classmerging/ClassMergerMode.java b/src/main/java/com/android/tools/r8/classmerging/ClassMergerMode.java
new file mode 100644
index 0000000..bb9a37b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/classmerging/ClassMergerMode.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.classmerging;
+
+public enum ClassMergerMode {
+  INITIAL,
+  FINAL;
+
+  public boolean isInitial() {
+    return this == INITIAL;
+  }
+
+  public boolean isFinal() {
+    return this == FINAL;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index dc2094f..05c6fa8 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -4,7 +4,7 @@
 package com.android.tools.r8.graph;
 
 import com.android.tools.r8.DesugarGraphConsumer;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger;
+import com.android.tools.r8.classmerging.ClassMergerMode;
 import com.android.tools.r8.origin.GlobalSyntheticOrigin;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -295,8 +295,7 @@
         : FieldResolutionResult.unknown();
   }
 
-  public void notifyHorizontalClassMergerFinished(
-      HorizontalClassMerger.Mode horizontalClassMergerMode) {
+  public void notifyHorizontalClassMergerFinished(ClassMergerMode horizontalClassMergerMode) {
     // Intentionally empty.
   }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index a9ebda5..ed20f69 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -6,6 +6,7 @@
 
 import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
 import com.android.tools.r8.androidapi.ComputedApiLevel;
+import com.android.tools.r8.classmerging.ClassMergerMode;
 import com.android.tools.r8.contexts.CompilationContext;
 import com.android.tools.r8.contexts.CompilationContext.ProcessorContext;
 import com.android.tools.r8.errors.dontwarn.DontWarnConfiguration;
@@ -19,7 +20,6 @@
 import com.android.tools.r8.graph.lens.GraphLens;
 import com.android.tools.r8.graph.lens.InitClassLens;
 import com.android.tools.r8.graph.lens.NonIdentityGraphLens;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger;
 import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses;
 import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraintFactory;
 import com.android.tools.r8.ir.analysis.proto.EnumLiteProtoShrinker;
@@ -843,7 +843,7 @@
   }
 
   public void setHorizontallyMergedClasses(
-      HorizontallyMergedClasses horizontallyMergedClasses, HorizontalClassMerger.Mode mode) {
+      HorizontallyMergedClasses horizontallyMergedClasses, ClassMergerMode mode) {
     assert !hasHorizontallyMergedClasses() || mode.isFinal();
     this.horizontallyMergedClasses = horizontallyMergedClasses().extend(horizontallyMergedClasses);
     testing()
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 2097c3a..cb4af41 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -7,6 +7,7 @@
 import static com.google.common.base.Predicates.not;
 
 import com.android.tools.r8.androidapi.ComputedApiLevel;
+import com.android.tools.r8.classmerging.ClassMergerMode;
 import com.android.tools.r8.classmerging.SyntheticArgumentClass;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
@@ -25,7 +26,6 @@
 import com.android.tools.r8.graph.ProgramMember;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.PrunedItems;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
 import com.android.tools.r8.horizontalclassmerging.code.ClassInitializerMerger;
 import com.android.tools.r8.horizontalclassmerging.code.SyntheticInitializerConverter;
 import com.android.tools.r8.ir.analysis.value.NumberFromIntervalValue;
@@ -59,7 +59,7 @@
   private static final OptimizationFeedback feedback = OptimizationFeedbackSimple.getInstance();
 
   private final AppView<?> appView;
-  private final Mode mode;
+  private final ClassMergerMode mode;
   private final HorizontalMergeGroup group;
   private final DexItemFactory dexItemFactory;
   private final HorizontalClassMergerGraphLens.Builder lensBuilder;
@@ -79,7 +79,7 @@
   private ClassMerger(
       AppView<?> appView,
       IRCodeProvider codeProvider,
-      Mode mode,
+      ClassMergerMode mode,
       HorizontalClassMergerGraphLens.Builder lensBuilder,
       HorizontalMergeGroup group,
       Collection<VirtualMethodMerger> virtualMethodMergers) {
@@ -375,11 +375,14 @@
   public static class Builder {
     private final AppView<?> appView;
     private final IRCodeProvider codeProvider;
-    private final Mode mode;
+    private final ClassMergerMode mode;
     private final HorizontalMergeGroup group;
 
     public Builder(
-        AppView<?> appView, IRCodeProvider codeProvider, HorizontalMergeGroup group, Mode mode) {
+        AppView<?> appView,
+        IRCodeProvider codeProvider,
+        HorizontalMergeGroup group,
+        ClassMergerMode mode) {
       this.appView = appView;
       this.codeProvider = codeProvider;
       this.group = group;
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 d7c99ba..03d62ea 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -6,6 +6,7 @@
 
 import static com.android.tools.r8.graph.DexClassAndMethod.asProgramMethodOrNull;
 
+import com.android.tools.r8.classmerging.ClassMergerMode;
 import com.android.tools.r8.classmerging.Policy;
 import com.android.tools.r8.classmerging.SyntheticArgumentClass;
 import com.android.tools.r8.graph.AppInfo;
@@ -40,24 +41,11 @@
 
 public class HorizontalClassMerger {
 
-  public enum Mode {
-    INITIAL,
-    FINAL;
-
-    public boolean isInitial() {
-      return this == INITIAL;
-    }
-
-    public boolean isFinal() {
-      return this == FINAL;
-    }
-  }
-
   private final AppView<?> appView;
-  private final Mode mode;
+  private final ClassMergerMode mode;
   private final HorizontalClassMergerOptions options;
 
-  private HorizontalClassMerger(AppView<?> appView, Mode mode) {
+  private HorizontalClassMerger(AppView<?> appView, ClassMergerMode mode) {
     this.appView = appView;
     this.mode = mode;
     this.options = appView.options().horizontalClassMergerOptions();
@@ -65,17 +53,17 @@
 
   public static HorizontalClassMerger createForInitialClassMerging(
       AppView<AppInfoWithLiveness> appView) {
-    return new HorizontalClassMerger(appView, Mode.INITIAL);
+    return new HorizontalClassMerger(appView, ClassMergerMode.INITIAL);
   }
 
   public static HorizontalClassMerger createForFinalClassMerging(
       AppView<? extends AppInfoWithClassHierarchy> appView) {
-    return new HorizontalClassMerger(appView, Mode.FINAL);
+    return new HorizontalClassMerger(appView, ClassMergerMode.FINAL);
   }
 
   public static HorizontalClassMerger createForD8ClassMerging(AppView<?> appView) {
     assert appView.options().horizontalClassMergerOptions().isRestrictedToSynthetics();
-    return new HorizontalClassMerger(appView, Mode.FINAL);
+    return new HorizontalClassMerger(appView, ClassMergerMode.FINAL);
   }
 
   public void runIfNecessary(ExecutorService executorService, Timing timing)
@@ -113,7 +101,7 @@
   }
 
   private MutableMethodConversionOptions getConversionOptions() {
-    return mode == Mode.INITIAL
+    return mode == ClassMergerMode.INITIAL
         ? MethodConversionOptions.forPreLirPhase(appView)
         : MethodConversionOptions.forPostLirPhase(appView);
   }
@@ -423,7 +411,7 @@
   private HorizontalClassMergerGraphLens createLens(
       HorizontallyMergedClasses mergedClasses,
       HorizontalClassMergerGraphLens.Builder lensBuilder,
-      Mode mode,
+      ClassMergerMode mode,
       ProfileCollectionAdditions profileCollectionAdditions,
       SyntheticArgumentClass syntheticArgumentClass,
       ExecutorService executorService,
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 fb27321..5bc07a2 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerTreeFixer.java
@@ -4,10 +4,10 @@
 
 package com.android.tools.r8.horizontalclassmerging;
 
+import com.android.tools.r8.classmerging.ClassMergerMode;
 import com.android.tools.r8.classmerging.ClassMergerTreeFixer;
 import com.android.tools.r8.classmerging.SyntheticArgumentClass;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
 import com.android.tools.r8.profile.rewriting.ProfileCollectionAdditions;
 import com.android.tools.r8.utils.Timing;
 import java.util.concurrent.ExecutionException;
@@ -24,13 +24,13 @@
         HorizontalClassMergerGraphLens,
         HorizontallyMergedClasses> {
 
-  private final Mode mode;
+  private final ClassMergerMode mode;
 
   public HorizontalClassMergerTreeFixer(
       AppView<?> appView,
       HorizontallyMergedClasses mergedClasses,
       HorizontalClassMergerGraphLens.Builder lensBuilder,
-      Mode mode,
+      ClassMergerMode mode,
       ProfileCollectionAdditions profileCollectionAdditions,
       SyntheticArgumentClass syntheticArgumentClass) {
     super(appView, lensBuilder, mergedClasses, profileCollectionAdditions, syntheticArgumentClass);
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 b522877..2355e0b 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
@@ -7,6 +7,7 @@
 import static com.android.tools.r8.dex.Constants.TEMPORARY_INSTANCE_INITIALIZER_PREFIX;
 
 import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.classmerging.ClassMergerMode;
 import com.android.tools.r8.classmerging.SyntheticArgumentClass;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
@@ -19,7 +20,6 @@
 import com.android.tools.r8.graph.DexTypeUtils;
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
 import com.android.tools.r8.horizontalclassmerging.code.ConstructorEntryPointSynthesizedCode;
 import com.android.tools.r8.horizontalclassmerging.code.SyntheticInitializerConverter;
 import com.android.tools.r8.ir.conversion.ExtraConstantIntParameter;
@@ -49,7 +49,7 @@
   private final List<ProgramMethod> instanceInitializers;
   private final InstanceInitializerDescription instanceInitializerDescription;
   private final HorizontalClassMergerGraphLens.Builder lensBuilder;
-  private final Mode mode;
+  private final ClassMergerMode mode;
 
   InstanceInitializerMerger(
       AppView<? extends AppInfoWithClassHierarchy> appView,
@@ -57,7 +57,7 @@
       HorizontalMergeGroup group,
       List<ProgramMethod> instanceInitializers,
       HorizontalClassMergerGraphLens.Builder lensBuilder,
-      Mode mode) {
+      ClassMergerMode mode) {
     this(appView, classIdentifiers, group, instanceInitializers, lensBuilder, mode, null);
   }
 
@@ -67,7 +67,7 @@
       HorizontalMergeGroup group,
       List<ProgramMethod> instanceInitializers,
       HorizontalClassMergerGraphLens.Builder lensBuilder,
-      Mode mode,
+      ClassMergerMode mode,
       InstanceInitializerDescription instanceInitializerDescription) {
     this.appView = appView;
     this.classIdentifiers = classIdentifiers;
@@ -175,13 +175,13 @@
     private int estimatedDexCodeSize;
     private final List<List<ProgramMethod>> instanceInitializerGroups = new ArrayList<>();
     private final HorizontalClassMergerGraphLens.Builder lensBuilder;
-    private final Mode mode;
+    private final ClassMergerMode mode;
 
     public Builder(
         AppView<? extends AppInfoWithClassHierarchy> appView,
         Reference2IntMap<DexType> classIdentifiers,
         HorizontalClassMergerGraphLens.Builder lensBuilder,
-        Mode mode) {
+        ClassMergerMode mode) {
       this.appView = appView;
       this.classIdentifiers = classIdentifiers;
       this.lensBuilder = lensBuilder;
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMergerCollection.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMergerCollection.java
index 62d7522..dcb9ee0 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMergerCollection.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMergerCollection.java
@@ -6,12 +6,12 @@
 
 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.DexEncodedMethod;
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
 import com.android.tools.r8.horizontalclassmerging.InstanceInitializerMerger.Builder;
 import com.android.tools.r8.utils.collections.ProgramMethodSet;
 import it.unimi.dsi.fastutil.objects.Reference2IntMap;
@@ -44,7 +44,7 @@
       IRCodeProvider codeProvider,
       HorizontalMergeGroup group,
       HorizontalClassMergerGraphLens.Builder lensBuilder,
-      Mode mode) {
+      ClassMergerMode mode) {
     if (!appView.hasClassHierarchy()) {
       assert appView.options().horizontalClassMergerOptions().isRestrictedToSynthetics();
       assert verifyNoInstanceInitializers(group);
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 fa18dbb..a9fa5b6 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
@@ -4,12 +4,12 @@
 
 package com.android.tools.r8.horizontalclassmerging;
 
+import com.android.tools.r8.classmerging.ClassMergerMode;
 import com.android.tools.r8.classmerging.Policy;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
 import com.android.tools.r8.horizontalclassmerging.policies.AllInstantiatedOrUninstantiated;
 import com.android.tools.r8.horizontalclassmerging.policies.CheckAbstractClasses;
 import com.android.tools.r8.horizontalclassmerging.policies.CheckSyntheticClasses;
@@ -76,7 +76,7 @@
   public static List<Policy> getPolicies(
       AppView<?> appView,
       IRCodeProvider codeProvider,
-      Mode mode,
+      ClassMergerMode mode,
       RuntimeTypeCheckInfo runtimeTypeCheckInfo) {
     if (appView.hasClassHierarchy()) {
       return getPoliciesForR8(
@@ -86,7 +86,7 @@
     }
   }
 
-  private static List<Policy> getPoliciesForD8(AppView<AppInfo> appView, Mode mode) {
+  private static List<Policy> getPoliciesForD8(AppView<AppInfo> appView, ClassMergerMode mode) {
     assert mode.isFinal();
     List<Policy> policies =
         ImmutableList.<Policy>builder()
@@ -101,7 +101,7 @@
   private static List<Policy> getPoliciesForR8(
       AppView<? extends AppInfoWithClassHierarchy> appView,
       IRCodeProvider codeProvider,
-      Mode mode,
+      ClassMergerMode mode,
       RuntimeTypeCheckInfo runtimeTypeCheckInfo) {
     List<Policy> policies =
         ImmutableList.<Policy>builder()
@@ -115,7 +115,7 @@
 
   private static List<SingleClassPolicy> getSingleClassPolicies(
       AppView<? extends AppInfoWithClassHierarchy> appView,
-      Mode mode,
+      ClassMergerMode mode,
       RuntimeTypeCheckInfo runtimeTypeCheckInfo) {
     ImmutableList.Builder<SingleClassPolicy> builder = ImmutableList.builder();
 
@@ -141,7 +141,7 @@
   }
 
   private static List<SingleClassPolicy> getSingleClassPoliciesForD8(
-      AppView<AppInfo> appView, Mode mode) {
+      AppView<AppInfo> appView, ClassMergerMode mode) {
     ImmutableList.Builder<SingleClassPolicy> builder =
         ImmutableList.<SingleClassPolicy>builder()
             .add(new CheckSyntheticClasses(appView))
@@ -162,7 +162,7 @@
 
   private static void addSingleClassPoliciesForMergingNonSyntheticClasses(
       AppView<AppInfoWithLiveness> appView,
-      Mode mode,
+      ClassMergerMode mode,
       RuntimeTypeCheckInfo runtimeTypeCheckInfo,
       ImmutableList.Builder<SingleClassPolicy> builder) {
     builder.add(
@@ -183,7 +183,7 @@
 
   private static boolean verifySingleClassPoliciesIrrelevantForMergingSynthetics(
       AppView<? extends AppInfoWithClassHierarchy> appView,
-      Mode mode,
+      ClassMergerMode mode,
       ImmutableList.Builder<SingleClassPolicy> builder) {
     List<SingleClassPolicy> policies =
         ImmutableList.of(
@@ -203,7 +203,9 @@
   }
 
   private static boolean verifySingleClassPoliciesIrrelevantForMergingSyntheticsInD8(
-      AppView<AppInfo> appView, Mode mode, ImmutableList.Builder<SingleClassPolicy> builder) {
+      AppView<AppInfo> appView,
+      ClassMergerMode mode,
+      ImmutableList.Builder<SingleClassPolicy> builder) {
     List<SingleClassPolicy> policies =
         ImmutableList.of(
             new NoResourceClasses(),
@@ -222,7 +224,7 @@
   private static List<Policy> getMultiClassPolicies(
       AppView<? extends AppInfoWithClassHierarchy> appView,
       IRCodeProvider codeProvider,
-      Mode mode,
+      ClassMergerMode mode,
       RuntimeTypeCheckInfo runtimeTypeCheckInfo) {
     ImmutableList.Builder<Policy> builder = ImmutableList.builder();
 
@@ -262,7 +264,7 @@
   }
 
   private static List<? extends Policy> getMultiClassPoliciesForD8(
-      AppView<AppInfo> appView, Mode mode) {
+      AppView<AppInfo> appView, ClassMergerMode mode) {
     ImmutableList.Builder<MultiClassPolicy> builder = ImmutableList.builder();
     builder.add(
         new CheckAbstractClasses(appView),
@@ -281,7 +283,7 @@
 
   private static void addRequiredMultiClassPolicies(
       AppView<? extends AppInfoWithClassHierarchy> appView,
-      Mode mode,
+      ClassMergerMode mode,
       RuntimeTypeCheckInfo runtimeTypeCheckInfo,
       ImmutableList.Builder<Policy> builder) {
     ImmediateProgramSubtypingInfo immediateSubtypingInfo =
@@ -313,7 +315,7 @@
 
   private static void addMultiClassPoliciesForInterfaceMerging(
       AppView<? extends AppInfoWithClassHierarchy> appView,
-      Mode mode,
+      ClassMergerMode mode,
       ImmutableList.Builder<Policy> builder) {
     builder.add(
         new NoDefaultInterfaceMethodMerging(appView, mode),
@@ -323,7 +325,9 @@
   }
 
   private static boolean verifyMultiClassPoliciesIrrelevantForMergingSyntheticsInD8(
-      AppView<AppInfo> appView, Mode mode, ImmutableList.Builder<MultiClassPolicy> builder) {
+      AppView<AppInfo> appView,
+      ClassMergerMode mode,
+      ImmutableList.Builder<MultiClassPolicy> builder) {
     List<MultiClassPolicy> policies =
         ImmutableList.of(new SyntheticItemsPolicy(appView, mode), new SameParentClass());
     policies.stream().map(VerifyMultiClassPolicyAlwaysSatisfied::new).forEach(builder::add);
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java
index d07b48e..2b7d783 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java
@@ -4,13 +4,13 @@
 
 package com.android.tools.r8.horizontalclassmerging.code;
 
+import com.android.tools.r8.classmerging.ClassMergerMode;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.Code;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.lens.GraphLens;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
 import com.android.tools.r8.horizontalclassmerging.IRCodeProvider;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.conversion.IRConverter;
@@ -33,7 +33,7 @@
 
   private final AppView<?> appView;
   private final IRCodeProvider codeProvider;
-  private final Mode mode;
+  private final ClassMergerMode mode;
 
   private final List<ProgramMethod> classInitializers;
 
@@ -43,7 +43,7 @@
   private SyntheticInitializerConverter(
       AppView<?> appView,
       IRCodeProvider codeProvider,
-      Mode mode,
+      ClassMergerMode mode,
       List<ProgramMethod> classInitializers,
       Set<DexProgramClass> instanceInitializers) {
     this.appView = appView;
@@ -53,7 +53,8 @@
     this.instanceInitializers = instanceInitializers;
   }
 
-  public static Builder builder(AppView<?> appView, IRCodeProvider codeProvider, Mode mode) {
+  public static Builder builder(
+      AppView<?> appView, IRCodeProvider codeProvider, ClassMergerMode mode) {
     return new Builder(appView, codeProvider, mode);
   }
 
@@ -135,12 +136,12 @@
 
     private final AppView<?> appView;
     private final IRCodeProvider codeProvider;
-    private final Mode mode;
+    private final ClassMergerMode mode;
 
     private final List<ProgramMethod> classInitializers = new ArrayList<>();
     private final Set<DexProgramClass> instanceInitializers = Sets.newIdentityHashSet();
 
-    private Builder(AppView<?> appView, IRCodeProvider codeProvider, Mode mode) {
+    private Builder(AppView<?> appView, IRCodeProvider codeProvider, ClassMergerMode mode) {
       this.appView = appView;
       this.codeProvider = codeProvider;
       this.mode = mode;
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 e0dc0fe..e5839b6 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,9 +4,9 @@
 
 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.HorizontalClassMerger.Mode;
 import com.android.tools.r8.horizontalclassmerging.MultiClassSameReferencePolicy;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 
@@ -14,7 +14,8 @@
 
   private final AppView<AppInfoWithLiveness> appView;
 
-  public AllInstantiatedOrUninstantiated(AppView<AppInfoWithLiveness> appView, Mode mode) {
+  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.
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 16e17f0..1fdb066 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,9 +4,9 @@
 
 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.HorizontalClassMerger.Mode;
 import com.android.tools.r8.horizontalclassmerging.HorizontalMergeGroup;
 import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
 import com.android.tools.r8.utils.ListUtils;
@@ -26,9 +26,9 @@
 public class FinalizeMergeGroup extends MultiClassPolicy {
 
   private final AppView<?> appView;
-  private final Mode mode;
+  private final ClassMergerMode mode;
 
-  public FinalizeMergeGroup(AppView<?> appView, Mode mode) {
+  public FinalizeMergeGroup(AppView<?> appView, ClassMergerMode mode) {
     this.appView = appView;
     this.mode = mode;
   }
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
index f4b929d..f65b9e8 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoConstructorCollisions.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoConstructorCollisions.java
@@ -4,6 +4,7 @@
 
 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.DexEncodedMethod;
 import com.android.tools.r8.graph.DexItemFactory;
@@ -11,7 +12,6 @@
 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.HorizontalClassMerger.Mode;
 import com.android.tools.r8.horizontalclassmerging.HorizontalMergeGroup;
 import com.android.tools.r8.horizontalclassmerging.MultiClassPolicyWithPreprocessing;
 import com.android.tools.r8.utils.ArrayUtils;
@@ -44,7 +44,7 @@
   private final AppView<?> appView;
   private final DexItemFactory dexItemFactory;
 
-  public NoConstructorCollisions(AppView<?> appView, Mode mode) {
+  public NoConstructorCollisions(AppView<?> appView, ClassMergerMode mode) {
     assert mode.isFinal();
     this.appView = appView;
     this.dexItemFactory = appView.dexItemFactory();
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDeadEnumLiteMaps.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDeadEnumLiteMaps.java
index 8d1205d..c1cf238 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDeadEnumLiteMaps.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDeadEnumLiteMaps.java
@@ -4,10 +4,10 @@
 
 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.graph.DexType;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
 import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
 import com.android.tools.r8.ir.analysis.proto.EnumLiteProtoShrinker;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -18,7 +18,7 @@
 
   private final Set<DexType> deadEnumLiteMaps;
 
-  public NoDeadEnumLiteMaps(AppView<AppInfoWithLiveness> appView, Mode mode) {
+  public NoDeadEnumLiteMaps(AppView<AppInfoWithLiveness> appView, ClassMergerMode mode) {
     // This policy is only relevant for the initial round of class merging, since the dead enum lite
     // maps have been removed from the application when the final round of class merging runs.
     assert mode.isInitial();
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDefaultInterfaceMethodCollisions.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDefaultInterfaceMethodCollisions.java
index f330807..4784ba5 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDefaultInterfaceMethodCollisions.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDefaultInterfaceMethodCollisions.java
@@ -8,6 +8,7 @@
 import static java.util.Collections.emptyMap;
 import static java.util.Collections.emptySet;
 
+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.BottomUpClassHierarchyTraversal;
@@ -18,7 +19,6 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.SubtypingInfo;
 import com.android.tools.r8.graph.TopDownClassHierarchyTraversal;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
 import com.android.tools.r8.horizontalclassmerging.HorizontalMergeGroup;
 import com.android.tools.r8.horizontalclassmerging.MultiClassPolicyWithPreprocessing;
 import com.android.tools.r8.horizontalclassmerging.policies.NoDefaultInterfaceMethodCollisions.InterfaceInfo;
@@ -73,10 +73,10 @@
     extends MultiClassPolicyWithPreprocessing<Map<DexType, InterfaceInfo>> {
 
   private final AppView<? extends AppInfoWithClassHierarchy> appView;
-  private final Mode mode;
+  private final ClassMergerMode mode;
 
   public NoDefaultInterfaceMethodCollisions(
-      AppView<? extends AppInfoWithClassHierarchy> appView, Mode mode) {
+      AppView<? extends AppInfoWithClassHierarchy> appView, ClassMergerMode mode) {
     this.appView = appView;
     this.mode = mode;
   }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDefaultInterfaceMethodMerging.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDefaultInterfaceMethodMerging.java
index 318a2af..b4d76dc 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDefaultInterfaceMethodMerging.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDefaultInterfaceMethodMerging.java
@@ -4,12 +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.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.horizontalclassmerging.HorizontalClassMerger.Mode;
 import com.android.tools.r8.horizontalclassmerging.HorizontalMergeGroup;
 import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
 import com.android.tools.r8.utils.WorkList;
@@ -35,7 +35,7 @@
   private final AppView<?> appView;
   private final DexType MULTIPLE_SENTINEL;
 
-  public NoDefaultInterfaceMethodMerging(AppView<?> appView, Mode mode) {
+  public NoDefaultInterfaceMethodMerging(AppView<?> appView, ClassMergerMode mode) {
     this.appView = appView;
     // Use the java.lang.Object type to indicate more than one interface type, as that type
     // itself is not an interface type.
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDirectRuntimeTypeChecks.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDirectRuntimeTypeChecks.java
index d330434..d8424e8 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDirectRuntimeTypeChecks.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDirectRuntimeTypeChecks.java
@@ -4,9 +4,9 @@
 
 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.HorizontalClassMerger.Mode;
 import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
 import com.android.tools.r8.shaking.RuntimeTypeCheckInfo;
 import com.android.tools.r8.synthesis.SyntheticItems;
@@ -18,12 +18,12 @@
   private final RuntimeTypeCheckInfo runtimeTypeCheckInfo;
   private final SyntheticItems syntheticItems;
 
-  public NoDirectRuntimeTypeChecks(AppView<?> appView, Mode mode) {
+  public NoDirectRuntimeTypeChecks(AppView<?> appView, ClassMergerMode mode) {
     this(appView, mode, null);
   }
 
   public NoDirectRuntimeTypeChecks(
-      AppView<?> appView, Mode mode, RuntimeTypeCheckInfo runtimeTypeCheckInfo) {
+      AppView<?> appView, ClassMergerMode mode, RuntimeTypeCheckInfo runtimeTypeCheckInfo) {
     assert runtimeTypeCheckInfo != null || mode.isFinal();
     this.options = appView.options();
     this.runtimeTypeCheckInfo = runtimeTypeCheckInfo;
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIllegalInlining.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIllegalInlining.java
index 872bf09..5126df8 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIllegalInlining.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIllegalInlining.java
@@ -4,12 +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.CfCode;
 import com.android.tools.r8.graph.Code;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
 import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -19,7 +19,7 @@
 
   private final AppView<AppInfoWithLiveness> appView;
 
-  public NoIllegalInlining(AppView<AppInfoWithLiveness> appView, Mode mode) {
+  public NoIllegalInlining(AppView<AppInfoWithLiveness> appView, ClassMergerMode mode) {
     // This policy is only relevant for the first round of horizontal class merging, since the final
     // round of horizontal class merging may not require any inlining.
     assert mode.isInitial();
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
index b792e84..1edca95 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInstanceInitializerMerging.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInstanceInitializerMerging.java
@@ -6,6 +6,7 @@
 
 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;
@@ -18,7 +19,6 @@
 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.HorizontalClassMerger.Mode;
 import com.android.tools.r8.horizontalclassmerging.HorizontalMergeGroup;
 import com.android.tools.r8.horizontalclassmerging.IRCodeProvider;
 import com.android.tools.r8.horizontalclassmerging.InstanceInitializerAnalysis;
@@ -64,7 +64,7 @@
   public NoInstanceInitializerMerging(
       AppView<? extends AppInfoWithClassHierarchy> appView,
       IRCodeProvider codeProvider,
-      Mode mode) {
+      ClassMergerMode mode) {
     assert mode.isFinal();
     this.appView = appView;
     this.codeProvider = codeProvider;
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInterfaces.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInterfaces.java
index feabe1c..8af3dd9 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInterfaces.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInterfaces.java
@@ -4,18 +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.HorizontalClassMerger.Mode;
 import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
 import com.android.tools.r8.utils.InternalOptions.HorizontalClassMergerOptions;
 
 public class NoInterfaces extends SingleClassPolicy {
 
-  private final Mode mode;
+  private final ClassMergerMode mode;
   private final HorizontalClassMergerOptions options;
 
-  public NoInterfaces(AppView<?> appView, Mode mode) {
+  public NoInterfaces(AppView<?> appView, ClassMergerMode mode) {
     this.mode = mode;
     this.options = appView.options().horizontalClassMergerOptions();
   }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoVerticallyMergedClasses.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoVerticallyMergedClasses.java
index 3259fe7..1aa69c7 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoVerticallyMergedClasses.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoVerticallyMergedClasses.java
@@ -4,16 +4,16 @@
 
 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.HorizontalClassMerger.Mode;
 import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 
 public class NoVerticallyMergedClasses extends SingleClassPolicy {
   private final AppView<AppInfoWithLiveness> appView;
 
-  public NoVerticallyMergedClasses(AppView<AppInfoWithLiveness> appView, Mode mode) {
+  public NoVerticallyMergedClasses(AppView<AppInfoWithLiveness> appView, ClassMergerMode mode) {
     // This policy is only relevant for the initial round, since all vertically merged classes have
     // been removed from the application in the final round of horizontal class merging.
     assert mode.isInitial();
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
index 21283e1..22d8773 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoVirtualMethodMerging.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoVirtualMethodMerging.java
@@ -4,6 +4,7 @@
 
 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;
@@ -13,7 +14,6 @@
 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.HorizontalClassMerger.Mode;
 import com.android.tools.r8.horizontalclassmerging.HorizontalMergeGroup;
 import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
 import com.android.tools.r8.utils.IterableUtils;
@@ -34,7 +34,8 @@
 
   private final AppView<? extends AppInfoWithClassHierarchy> appView;
 
-  public NoVirtualMethodMerging(AppView<? extends AppInfoWithClassHierarchy> appView, Mode mode) {
+  public NoVirtualMethodMerging(
+      AppView<? extends AppInfoWithClassHierarchy> appView, ClassMergerMode mode) {
     assert mode.isFinal();
     this.appView = appView;
   }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyDirectlyConnectedOrUnrelatedInterfaces.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyDirectlyConnectedOrUnrelatedInterfaces.java
index db74f25..3714024 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyDirectlyConnectedOrUnrelatedInterfaces.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyDirectlyConnectedOrUnrelatedInterfaces.java
@@ -6,13 +6,13 @@
 
 import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
 
+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.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.SubtypingInfo;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
 import com.android.tools.r8.horizontalclassmerging.HorizontalMergeGroup;
 import com.android.tools.r8.horizontalclassmerging.MultiClassPolicyWithPreprocessing;
 import com.android.tools.r8.utils.SetUtils;
@@ -59,13 +59,13 @@
     extends MultiClassPolicyWithPreprocessing<SubtypingInfo> {
 
   private final AppView<? extends AppInfoWithClassHierarchy> appView;
-  private final Mode mode;
+  private final ClassMergerMode mode;
 
   // The interface merge groups that this policy has committed to so far.
   private final Map<DexProgramClass, HorizontalMergeGroup> committed = new IdentityHashMap<>();
 
   public OnlyDirectlyConnectedOrUnrelatedInterfaces(
-      AppView<? extends AppInfoWithClassHierarchy> appView, Mode mode) {
+      AppView<? extends AppInfoWithClassHierarchy> appView, ClassMergerMode mode) {
     this.appView = appView;
     this.mode = mode;
   }
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 b891bcd..72d2468 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
@@ -4,12 +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.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethodSignature;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.MethodAccessFlags;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
 import com.android.tools.r8.horizontalclassmerging.HorizontalMergeGroup;
 import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -92,7 +92,7 @@
 
   private final AppView<AppInfoWithLiveness> appView;
 
-  public PreserveMethodCharacteristics(AppView<AppInfoWithLiveness> appView, Mode 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.isInitial();
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 bfc4cd1..a7e3ccc 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,6 +4,7 @@
 
 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;
@@ -14,7 +15,6 @@
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
 import com.android.tools.r8.horizontalclassmerging.HorizontalMergeGroup;
 import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
 import com.android.tools.r8.utils.TraversalContinuation;
@@ -28,9 +28,10 @@
 public class RespectPackageBoundaries extends MultiClassPolicy {
 
   private final AppView<? extends AppInfoWithClassHierarchy> appView;
-  private final Mode mode;
+  private final ClassMergerMode mode;
 
-  public RespectPackageBoundaries(AppView<? extends AppInfoWithClassHierarchy> appView, Mode mode) {
+  public RespectPackageBoundaries(
+      AppView<? extends AppInfoWithClassHierarchy> appView, ClassMergerMode mode) {
     this.appView = appView;
     this.mode = mode;
   }
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 da0951e..3e82768 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,6 +4,7 @@
 
 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;
@@ -11,7 +12,6 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.FieldAccessFlags;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
 import com.android.tools.r8.horizontalclassmerging.MultiClassSameReferencePolicy;
 import com.android.tools.r8.horizontalclassmerging.policies.SameInstanceFields.InstanceFieldInfo;
 import com.google.common.collect.HashMultiset;
@@ -21,9 +21,10 @@
 public class SameInstanceFields extends MultiClassSameReferencePolicy<Multiset<InstanceFieldInfo>> {
 
   private final DexItemFactory dexItemFactory;
-  private final Mode mode;
+  private final ClassMergerMode mode;
 
-  public SameInstanceFields(AppView<? extends AppInfoWithClassHierarchy> appView, Mode mode) {
+  public SameInstanceFields(
+      AppView<? extends AppInfoWithClassHierarchy> appView, ClassMergerMode mode) {
     this.dexItemFactory = appView.dexItemFactory();
     this.mode = mode;
   }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SyntheticItemsPolicy.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SyntheticItemsPolicy.java
index 95764f9..dd3b701 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SyntheticItemsPolicy.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SyntheticItemsPolicy.java
@@ -4,9 +4,9 @@
 
 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.HorizontalClassMerger.Mode;
 import com.android.tools.r8.horizontalclassmerging.MultiClassSameReferencePolicy;
 import com.android.tools.r8.horizontalclassmerging.policies.SyntheticItemsPolicy.ClassKind;
 import com.android.tools.r8.synthesis.SyntheticItems;
@@ -18,10 +18,10 @@
     NOT_SYNTHETIC
   }
 
-  private final Mode mode;
+  private final ClassMergerMode mode;
   private final SyntheticItems syntheticItems;
 
-  public SyntheticItemsPolicy(AppView<?> appView, Mode mode) {
+  public SyntheticItemsPolicy(AppView<?> appView, ClassMergerMode mode) {
     this.mode = mode;
     this.syntheticItems = appView.getSyntheticItems();
   }
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 6fb0c2f..93ac4f4 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -8,6 +8,7 @@
 import static com.android.tools.r8.utils.collections.ThrowingSet.isThrowingSet;
 
 import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.classmerging.ClassMergerMode;
 import com.android.tools.r8.features.ClassToFeatureSplitMap;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
@@ -50,7 +51,6 @@
 import com.android.tools.r8.graph.UnknownDispatchTargetLookupResult;
 import com.android.tools.r8.graph.lens.GraphLens;
 import com.android.tools.r8.graph.lens.NonIdentityGraphLens;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger;
 import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
 import com.android.tools.r8.ir.analysis.type.DynamicType;
 import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
@@ -447,8 +447,7 @@
   }
 
   @Override
-  public void notifyHorizontalClassMergerFinished(
-      HorizontalClassMerger.Mode horizontalClassMergerMode) {
+  public void notifyHorizontalClassMergerFinished(ClassMergerMode horizontalClassMergerMode) {
     if (horizontalClassMergerMode.isInitial()) {
       getMethodAccessInfoCollection().destroy();
     }
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index 8222389..616b98a 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.FeatureSplit;
 import com.android.tools.r8.SyntheticInfoConsumer;
 import com.android.tools.r8.SyntheticInfoConsumerData;
+import com.android.tools.r8.classmerging.ClassMergerMode;
 import com.android.tools.r8.contexts.CompilationContext.UniqueContext;
 import com.android.tools.r8.errors.MissingGlobalSyntheticsConsumerDiagnostic;
 import com.android.tools.r8.errors.Unreachable;
@@ -36,7 +37,6 @@
 import com.android.tools.r8.graph.ProgramOrClasspathDefinition;
 import com.android.tools.r8.graph.PrunedItems;
 import com.android.tools.r8.graph.lens.NonIdentityGraphLens;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.references.ClassReference;
@@ -403,7 +403,7 @@
     return committed.containsType(type) || pending.definitions.containsKey(type);
   }
 
-  public boolean isEligibleForClassMerging(DexProgramClass clazz, HorizontalClassMerger.Mode mode) {
+  public boolean isEligibleForClassMerging(DexProgramClass clazz, ClassMergerMode mode) {
     assert isSyntheticClass(clazz);
     return mode.isFinal() || isSyntheticLambda(clazz);
   }
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 6fe8388..26a4087 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -26,6 +26,7 @@
 import com.android.tools.r8.Version;
 import com.android.tools.r8.androidapi.ComputedApiLevel;
 import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.classmerging.ClassMergerMode;
 import com.android.tools.r8.classmerging.Policy;
 import com.android.tools.r8.debuginfo.DebugRepresentation;
 import com.android.tools.r8.dex.ApplicationReader.ProgramClassConflictResolver;
@@ -66,7 +67,6 @@
 import com.android.tools.r8.graph.analysis.ResourceAccessAnalysis;
 import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider;
 import com.android.tools.r8.graph.lens.GraphLens;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger;
 import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses;
 import com.android.tools.r8.inspector.internal.InspectorImpl;
 import com.android.tools.r8.ir.analysis.proto.ProtoReferences;
@@ -878,12 +878,12 @@
     WholeProgramOptimizations wholeProgramOptimizations = WholeProgramOptimizations.ON;
     if (mode.isInitialTreeShaking()) {
       return horizontalClassMergerOptions.isEnabled(
-              HorizontalClassMerger.Mode.INITIAL, wholeProgramOptimizations)
+              ClassMergerMode.INITIAL, wholeProgramOptimizations)
           && !horizontalClassMergerOptions.isRestrictedToSynthetics();
     }
     if (mode.isFinalTreeShaking()) {
       return horizontalClassMergerOptions.isEnabled(
-              HorizontalClassMerger.Mode.FINAL, wholeProgramOptimizations)
+              ClassMergerMode.FINAL, wholeProgramOptimizations)
           && !horizontalClassMergerOptions.isRestrictedToSynthetics();
     }
     assert false;
@@ -1873,7 +1873,7 @@
     }
 
     public boolean isEnabled(
-        HorizontalClassMerger.Mode mode, WholeProgramOptimizations wholeProgramOptimizations) {
+        ClassMergerMode mode, WholeProgramOptimizations wholeProgramOptimizations) {
       if (!enable || debug || intermediate) {
         return false;
       }
@@ -1901,7 +1901,7 @@
       return enableSyntheticMerging;
     }
 
-    public boolean isInterfaceMergingEnabled(HorizontalClassMerger.Mode mode) {
+    public boolean isInterfaceMergingEnabled(ClassMergerMode mode) {
       if (!enableInterfaceMerging) {
         return false;
       }
@@ -2359,7 +2359,7 @@
     public Function<AppView<AppInfoWithLiveness>, RepackagingConfiguration>
         repackagingConfigurationFactory = DefaultRepackagingConfiguration::new;
 
-    public TriConsumer<DexItemFactory, HorizontallyMergedClasses, HorizontalClassMerger.Mode>
+    public TriConsumer<DexItemFactory, HorizontallyMergedClasses, ClassMergerMode>
         horizontallyMergedClassesConsumer = ConsumerUtils.emptyTriConsumer();
     public Function<List<Policy>, List<Policy>> horizontalClassMergingPolicyRewriter =
         Function.identity();
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 9b0626b..080daa4 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java
@@ -6,6 +6,7 @@
 import static com.android.tools.r8.graph.DexClassAndMethod.asProgramMethodOrNull;
 import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
 
+import com.android.tools.r8.classmerging.ClassMergerMode;
 import com.android.tools.r8.classmerging.SyntheticArgumentClass;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
@@ -54,14 +55,26 @@
 
   private final AppView<AppInfoWithLiveness> appView;
   private final DexItemFactory dexItemFactory;
+  private final ClassMergerMode mode;
   private final InternalOptions options;
 
-  public VerticalClassMerger(AppView<AppInfoWithLiveness> appView) {
+  public VerticalClassMerger(AppView<AppInfoWithLiveness> appView, ClassMergerMode mode) {
     this.appView = appView;
     this.dexItemFactory = appView.dexItemFactory();
+    this.mode = mode;
     this.options = appView.options();
   }
 
+  public static VerticalClassMerger createForInitialClassMerging(
+      AppView<AppInfoWithLiveness> appView) {
+    return new VerticalClassMerger(appView, ClassMergerMode.INITIAL);
+  }
+
+  public static VerticalClassMerger createForFinalClassMerging(
+      AppView<AppInfoWithLiveness> appView) {
+    return new VerticalClassMerger(appView, ClassMergerMode.FINAL);
+  }
+
   // Returns a set of types that must not be merged into other types.
   private Set<DexProgramClass> getPinnedClasses() {
     Set<DexProgramClass> pinnedClasses = Sets.newIdentityHashSet();
@@ -162,12 +175,11 @@
     pinnedClasses.add(clazz);
   }
 
-  public static void runIfNecessary(
-      AppView<AppInfoWithLiveness> appView, ExecutorService executorService, Timing timing)
+  public void runIfNecessary(ExecutorService executorService, Timing timing)
       throws ExecutionException {
     timing.begin("VerticalClassMerger");
-    if (shouldRun(appView)) {
-      new VerticalClassMerger(appView).run(executorService, timing);
+    if (shouldRun()) {
+      run(executorService, timing);
     } else {
       appView.setVerticallyMergedClasses(VerticallyMergedClasses.empty());
     }
@@ -176,8 +188,8 @@
     timing.end();
   }
 
-  private static boolean shouldRun(AppView<AppInfoWithLiveness> appView) {
-    return appView.options().getVerticalClassMergerOptions().isEnabled()
+  private boolean shouldRun() {
+    return options.getVerticalClassMergerOptions().isEnabled(mode)
         && !appView.hasCfByteCodePassThroughMethods();
   }
 
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerOptions.java b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerOptions.java
index 5205d49..b7f435c 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerOptions.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerOptions.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.verticalclassmerging;
 
+import com.android.tools.r8.classmerging.ClassMergerMode;
 import com.android.tools.r8.utils.InternalOptions;
 
 public class VerticalClassMergerOptions {
@@ -19,12 +20,8 @@
     setEnabled(false);
   }
 
-  public boolean isDisabled() {
-    return !isEnabled();
-  }
-
-  public boolean isEnabled() {
-    return enabled && options.isOptimizing() && options.isShrinking();
+  public boolean isEnabled(ClassMergerMode mode) {
+    return enabled && options.isOptimizing() && options.isShrinking() && mode.isInitial();
   }
 
   public void setEnabled(boolean enabled) {
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
index 5349d73..054fb04 100644
--- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -10,8 +10,8 @@
 
 import com.android.tools.r8.TestBase.Backend;
 import com.android.tools.r8.benchmarks.BenchmarkResults;
+import com.android.tools.r8.classmerging.ClassMergerMode;
 import com.android.tools.r8.debug.DebugTestConfig;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger;
 import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorEventConsumer;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference;
 import com.android.tools.r8.testing.AndroidBuildVersion;
@@ -199,12 +199,12 @@
 
   public T addHorizontallyMergedClassesInspector(
       ThrowableConsumer<HorizontallyMergedClassesInspector> inspector) {
-    return addHorizontallyMergedClassesInspector(inspector, HorizontalClassMerger.Mode::isFinal);
+    return addHorizontallyMergedClassesInspector(inspector, ClassMergerMode::isFinal);
   }
 
   public T addHorizontallyMergedClassesInspector(
       ThrowableConsumer<HorizontallyMergedClassesInspector> inspector,
-      Predicate<HorizontalClassMerger.Mode> predicate) {
+      Predicate<ClassMergerMode> predicate) {
     return addOptionsModification(
         options ->
             options.testing.horizontallyMergedClassesConsumer =
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
index f983650..a92c140 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
@@ -15,12 +15,12 @@
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.classmerging.ClassMergerMode;
 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.DexType;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger;
 import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses;
 import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
 import com.android.tools.r8.utils.BooleanUtils;
@@ -105,7 +105,7 @@
   private void fixInliningNullabilityClass(
       DexItemFactory dexItemFactory,
       HorizontallyMergedClasses horizontallyMergedClasses,
-      HorizontalClassMerger.Mode mode) {
+      ClassMergerMode mode) {
     DexType originalType =
         dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor("inlining.Nullability"));
     nullabilityClass =