Add CompatProguard flag to enable vertical class merging

Change-Id: Ic6c410e3bbda89140160a7ffeb694e42385d357e
diff --git a/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java b/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java
index 89a33c1..1e16b73 100644
--- a/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java
+++ b/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java
@@ -23,9 +23,15 @@
   }
 
   public CompatProguardCommandBuilder(boolean forceProguardCompatibility) {
+    this(forceProguardCompatibility, false);
+  }
+
+  public CompatProguardCommandBuilder(
+      boolean forceProguardCompatibility, boolean enableVerticalClassMerging) {
     if (forceProguardCompatibility) {
       internalForceProguardCompatibility();
     }
+    setEnableVerticalClassMerging(enableVerticalClassMerging);
     setIgnoreDexInArchive(true);
   }
 
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 9f12853..475d34f 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -160,6 +160,7 @@
       options.enableMinification = false;
       options.enableInlining = false;
       options.enableClassInlining = false;
+      options.enableVerticalClassMerging = false;
       options.enableClassStaticizer = false;
       options.outline.enabled = false;
 
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 99b0a1e..1a08113 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -237,6 +237,9 @@
     internal.enableInlining = false;
     assert internal.enableClassInlining;
     internal.enableClassInlining = false;
+    // TODO(christofferqa): Remove negation when enabling vertical class merging by default.
+    assert !internal.enableVerticalClassMerging;
+    internal.enableVerticalClassMerging = false;
     assert internal.enableClassStaticizer;
     internal.enableClassStaticizer = false;
     assert internal.enableSwitchMapRemoval;
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 320180e..9738c8d 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -369,7 +369,7 @@
           timing.end();
         }
         appView.setGraphLense(new MemberRebindingAnalysis(appViewWithLiveness).run());
-        if (options.enableClassMerging) {
+        if (options.enableVerticalClassMerging) {
           timing.begin("ClassMerger");
           VerticalClassMerger classMerger =
               new VerticalClassMerger(
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index e3904fb..11fd637 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -61,6 +61,7 @@
     private final List<ProguardConfigurationSource> proguardConfigs = new ArrayList<>();
     private boolean disableTreeShaking = false;
     private boolean disableMinification = false;
+    private boolean enableVerticalClassMerging = false;
     private boolean forceProguardCompatibility = false;
     private StringConsumer proguardMapConsumer = null;
 
@@ -93,6 +94,10 @@
       this.forceProguardCompatibility = true;
     }
 
+    void setEnableVerticalClassMerging(boolean enableVerticalClassMerging) {
+      this.enableVerticalClassMerging = enableVerticalClassMerging;
+    }
+
     @Override
     Builder self() {
       return this;
@@ -375,6 +380,7 @@
               desugaring,
               configuration.isShrinking(),
               configuration.isObfuscating(),
+              enableVerticalClassMerging,
               forceProguardCompatibility,
               proguardMapConsumer,
               proguardCompatibilityRulesOutput,
@@ -440,6 +446,7 @@
   private final ProguardConfiguration proguardConfiguration;
   private final boolean enableTreeShaking;
   private final boolean enableMinification;
+  private final boolean enableVerticalClassMerging;
   private final boolean forceProguardCompatibility;
   private final StringConsumer proguardMapConsumer;
   private final Path proguardCompatibilityRulesOutput;
@@ -503,6 +510,7 @@
       boolean enableDesugaring,
       boolean enableTreeShaking,
       boolean enableMinification,
+      boolean enableVerticalClassMerging,
       boolean forceProguardCompatibility,
       StringConsumer proguardMapConsumer,
       Path proguardCompatibilityRulesOutput,
@@ -516,6 +524,7 @@
     this.proguardConfiguration = proguardConfiguration;
     this.enableTreeShaking = enableTreeShaking;
     this.enableMinification = enableMinification;
+    this.enableVerticalClassMerging = enableVerticalClassMerging;
     this.forceProguardCompatibility = forceProguardCompatibility;
     this.proguardMapConsumer = proguardMapConsumer;
     this.proguardCompatibilityRulesOutput = proguardCompatibilityRulesOutput;
@@ -528,6 +537,7 @@
     proguardConfiguration = null;
     enableTreeShaking = false;
     enableMinification = false;
+    enableVerticalClassMerging = false;
     forceProguardCompatibility = false;
     proguardMapConsumer = null;
     proguardCompatibilityRulesOutput = null;
@@ -580,6 +590,7 @@
       // TODO(zerny): Should we support inlining in debug mode? b/62937285
       internal.enableInlining = false;
       internal.enableClassInlining = false;
+      internal.enableVerticalClassMerging = false;
       internal.enableClassStaticizer = false;
       // TODO(zerny): Should we support outlining in debug mode? b/62937285
       internal.outline.enabled = false;
@@ -638,6 +649,8 @@
     // EXPERIMENTAL flags.
     assert !internal.forceProguardCompatibility;
     internal.forceProguardCompatibility = forceProguardCompatibility;
+    assert !internal.enableVerticalClassMerging;
+    internal.enableVerticalClassMerging = enableVerticalClassMerging;
 
     internal.enableInheritanceClassInDexDistributor = isOptimizeMultidexForLinearAlloc();
 
diff --git a/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java b/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
index 615a3e2..dad3120 100644
--- a/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
+++ b/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
@@ -8,13 +8,11 @@
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.OutputMode;
 import com.android.tools.r8.R8;
-import com.android.tools.r8.R8Command;
 import com.android.tools.r8.Version;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.origin.CommandLineOrigin;
 import com.android.tools.r8.utils.AbortException;
 import com.google.common.collect.ImmutableList;
-import java.io.IOException;
 import java.nio.file.Paths;
 import java.util.List;
 
@@ -41,6 +39,9 @@
     public final List<String> proguardConfig;
     public boolean printHelpAndExit;
 
+    // Flags to enable experimental features.
+    public boolean enableVerticalClassMerging;
+
     CompatProguardOptions(
         List<String> proguardConfig,
         String output,
@@ -48,7 +49,8 @@
         boolean multiDex,
         boolean forceProguardCompatibility,
         String mainDexList,
-        boolean printHelpAndExit) {
+        boolean printHelpAndExit,
+        boolean verticalClassMerging) {
       this.output = output;
       this.minApi = minApi;
       this.forceProguardCompatibility = forceProguardCompatibility;
@@ -56,6 +58,7 @@
       this.mainDexList = mainDexList;
       this.proguardConfig = proguardConfig;
       this.printHelpAndExit = printHelpAndExit;
+      this.enableVerticalClassMerging = verticalClassMerging;
     }
 
     public static CompatProguardOptions parse(String[] args) {
@@ -65,6 +68,8 @@
       boolean multiDex = false;
       String mainDexList = null;
       boolean printHelpAndExit = false;
+      // Flags to enable experimental features.
+      boolean verticalClassMerging = false;
       // These flags are currently ignored.
       boolean minimalMainDex = false;
       boolean coreLibrary = false;
@@ -90,6 +95,8 @@
               mainDexList = args[++i];
             } else if (arg.startsWith("--main-dex-list=")) {
               mainDexList = arg.substring("--main-dex-list=".length());
+            } else if (arg.equals("--vertical-class-merging")) {
+              verticalClassMerging = true;
             } else if (arg.equals("--minimal-main-dex")) {
               minimalMainDex = true;
             } else if (arg.equals("--core-library")) {
@@ -123,7 +130,8 @@
           multiDex,
           forceProguardCompatibility,
           mainDexList,
-          printHelpAndExit);
+          printHelpAndExit,
+          verticalClassMerging);
     }
 
     public static void print() {
@@ -150,7 +158,7 @@
     CompatProguardOptions.print();
   }
 
-  private static void run(String[] args) throws IOException, CompilationFailedException {
+  private static void run(String[] args) throws CompilationFailedException {
     // Run R8 passing all the options from the command line as a Proguard configuration.
     CompatProguardOptions options = CompatProguardOptions.parse(args);
     if (options.printHelpAndExit || options.output == null) {
@@ -158,8 +166,9 @@
       printHelp();
       return;
     }
-    R8Command.Builder builder =
-        new CompatProguardCommandBuilder(options.forceProguardCompatibility);
+    CompatProguardCommandBuilder builder =
+        new CompatProguardCommandBuilder(
+            options.forceProguardCompatibility, options.enableVerticalClassMerging);
     builder
         .setOutput(Paths.get(options.output), OutputMode.DexIndexed)
         .addProguardConfiguration(options.proguardConfig, CommandLineOrigin.INSTANCE)
@@ -171,7 +180,7 @@
     R8.run(builder.build());
   }
 
-  public static void main(String[] args) throws IOException {
+  public static void main(String[] args) {
     try {
       run(args);
     } catch (CompilationFailedException | AbortException e) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java b/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
index 8e587b9..db9845f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
@@ -109,7 +109,7 @@
     for (BasicBlock block : code.blocks) {
       if (block.hasCatchHandlers()) {
         if (block.canThrow()) {
-          if (options.enableClassMerging) {
+          if (options.enableVerticalClassMerging) {
             // Handle the case where an exception class has been merged into its sub class.
             block.renameGuardsInCatchHandlers(graphLense);
             unlinkDeadCatchHandlers(block, graphLense);
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 d3c9e6d..67d8027 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -79,7 +79,7 @@
     itemFactory = proguardConfiguration.getDexItemFactory();
     // -dontoptimize disables optimizations by flipping related flags.
     if (!proguardConfiguration.isOptimizing()) {
-      enableClassMerging = false;
+      enableVerticalClassMerging = false;
       enableDevirtualization = false;
       enableNonNullTracking = false;
       enableInlining = false;
@@ -97,7 +97,7 @@
   public boolean passthroughDexCode = false;
 
   // Optimization-related flags. These should conform to -dontoptimize.
-  public boolean enableClassMerging = false;
+  public boolean enableVerticalClassMerging = false;
   public boolean enableDevirtualization = true;
   public boolean enableNonNullTracking = true;
   public boolean enableInlining =
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/ConstructorRelaxationTest.java b/src/test/java/com/android/tools/r8/accessrelaxation/ConstructorRelaxationTest.java
index d90ae45..48b511c 100644
--- a/src/test/java/com/android/tools/r8/accessrelaxation/ConstructorRelaxationTest.java
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/ConstructorRelaxationTest.java
@@ -179,10 +179,13 @@
         ),
         Origin.unknown());
 
-    AndroidApp app = ToolHelper.runR8(builder.build(), options -> {
-      options.enableInlining = false;
-      options.enableClassMerging = false;
-    });
+    AndroidApp app =
+        ToolHelper.runR8(
+            builder.build(),
+            options -> {
+              options.enableInlining = false;
+              options.enableVerticalClassMerging = false;
+            });
     compareJvmAndArt(app, mainClass);
 
     CodeInspector codeInspector = new CodeInspector(app);
diff --git a/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
index aad109a..b6f8c76 100644
--- a/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
@@ -77,7 +77,7 @@
       .resolve("classmerging").resolve("keep-rules-dontoptimize.txt");
 
   private void configure(InternalOptions options) {
-    options.enableClassMerging = true;
+    options.enableVerticalClassMerging = true;
     options.enableClassInlining = false;
     options.enableMinification = false;
     options.testing.nondeterministicCycleElimination = true;
@@ -407,7 +407,7 @@
         getProguardConfig(EXAMPLE_KEEP),
         options -> {
           configure(options);
-          options.enableClassMerging = false;
+          options.enableVerticalClassMerging = false;
           options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
           options.proguardMapConsumer =
               (proguardMap, handler) ->
@@ -472,7 +472,7 @@
         getProguardConfig(EXAMPLE_KEEP),
         options -> {
           configure(options);
-          options.enableClassMerging = false;
+          options.enableVerticalClassMerging = false;
           options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
           options.proguardMapConsumer =
               (proguardMap, handler) ->
@@ -543,7 +543,7 @@
             "-forceinline class classmerging.ProguardMethodMapTest$A { public void method(); }"),
         options -> {
           configure(options);
-          options.enableClassMerging = false;
+          options.enableVerticalClassMerging = false;
           options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
           options.proguardMapConsumer =
               (proguardMap, handler) ->
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
index 08fcebe..079b979 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
@@ -83,11 +83,12 @@
           .addProperty("internalLateInitProp", JAVA_LANG_STRING, Visibility.INTERNAL)
           .addProperty("publicLateInitProp", JAVA_LANG_STRING, Visibility.PUBLIC);
 
-  private Consumer<InternalOptions> disableAggressiveClassOptimizations = o -> {
-    o.enableClassInlining = false;
-    o.enableClassMerging = false;
-    o.enableClassStaticizer = false;
-  };
+  private Consumer<InternalOptions> disableAggressiveClassOptimizations =
+      o -> {
+        o.enableClassInlining = false;
+        o.enableVerticalClassMerging = false;
+        o.enableClassStaticizer = false;
+      };
 
   @Test
   public void testMutableProperty_getterAndSetterAreRemoveIfNotUsed() throws Exception {
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/CompositionalLenseTest.java b/src/test/java/com/android/tools/r8/memberrebinding/CompositionalLenseTest.java
index 517bd03..711ae60 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/CompositionalLenseTest.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/CompositionalLenseTest.java
@@ -89,10 +89,13 @@
                 "-dontobfuscate"), // to use the renamed names in test-mapping.txt
             Origin.unknown())
         .addLibraryFiles(runtimeJar(backend));
-    AndroidApp processedApp = ToolHelper.runR8(builder.build(), options -> {
-      options.enableInlining = false;
-      options.enableClassMerging = false;
-    });
+    AndroidApp processedApp =
+        ToolHelper.runR8(
+            builder.build(),
+            options -> {
+              options.enableInlining = false;
+              options.enableVerticalClassMerging = false;
+            });
     CodeInspector codeInspector = new CodeInspector(processedApp);
     ClassSubject classSubject = codeInspector.clazz(TestMain.class);
     assertThat(classSubject, isPresent());
diff --git a/src/test/java/com/android/tools/r8/naming/AdaptResourceFileContentsTest.java b/src/test/java/com/android/tools/r8/naming/AdaptResourceFileContentsTest.java
index 413a511..9ba82a9 100644
--- a/src/test/java/com/android/tools/r8/naming/AdaptResourceFileContentsTest.java
+++ b/src/test/java/com/android/tools/r8/naming/AdaptResourceFileContentsTest.java
@@ -328,7 +328,7 @@
         options -> {
           // TODO(christofferqa): Class inliner should respect -neverinline.
           options.enableClassInlining = false;
-          options.enableClassMerging = true;
+          options.enableVerticalClassMerging = true;
           options.dataResourceConsumer = dataResourceConsumer;
         });
   }
diff --git a/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java b/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java
index 9ffb4f2..8876860 100644
--- a/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java
+++ b/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java
@@ -266,7 +266,7 @@
         options -> {
           // TODO(christofferqa): Class inliner should respect -neverinline.
           options.enableClassInlining = false;
-          options.enableClassMerging = true;
+          options.enableVerticalClassMerging = true;
           options.dataResourceConsumer = dataResourceConsumer;
           options.proguardMapConsumer = proguardMapConsumer;
           options.testing.suppressExperimentalCfBackendWarning = true;
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking19Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking19Test.java
index f30bcac..0af3668 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking19Test.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking19Test.java
@@ -49,7 +49,7 @@
         null,
         ImmutableList.of("src/test/examples/shaking19/keep-rules.txt"),
         // Disable vertical class merging to prevent A from being merged into B.
-        opt -> opt.enableClassMerging = false);
+        opt -> opt.enableVerticalClassMerging = false);
   }
 
   private static void unusedRemoved(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/IfRuleWithVerticalClassMerging.java b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/IfRuleWithVerticalClassMerging.java
index 8bc82db..b003b7b 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/IfRuleWithVerticalClassMerging.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/IfRuleWithVerticalClassMerging.java
@@ -85,7 +85,7 @@
   }
 
   private void configure(InternalOptions options) {
-    options.enableClassMerging = enableClassMerging;
+    options.enableVerticalClassMerging = enableClassMerging;
   }
 
   @Override