Add static class initializer merging

Change-Id: I75d26e021aa50e39a71ca056cb62a6e1d1d3625d
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index e9dbc44..f720ee9 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -414,7 +414,7 @@
     // Assert some of R8 optimizations are disabled.
     assert !internal.enableInlining;
     assert !internal.enableClassInlining;
-    assert !internal.enableHorizontalClassMerging;
+    assert internal.horizontalClassMergerOptions().isDisabled();
     assert !internal.enableStaticClassMerging;
     assert !internal.enableVerticalClassMerging;
     assert !internal.enableClassStaticizer;
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index dce2b21..fff3341 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -168,7 +168,7 @@
     // Assert some of R8 optimizations are disabled.
     assert !internal.enableInlining;
     assert !internal.enableClassInlining;
-    assert !internal.enableHorizontalClassMerging;
+    assert internal.horizontalClassMergerOptions().isDisabled();
     assert !internal.enableStaticClassMerging;
     assert !internal.enableVerticalClassMerging;
     assert !internal.enableClassStaticizer;
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 1dc0468..5521ef9 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -583,7 +583,9 @@
             timing.end();
           }
         }
-        if (options.enableHorizontalClassMerging && options.enableInlining) {
+        if (options.horizontalClassMergerOptions().isEnabled()
+            && options.enableInlining
+            && options.isShrinking()) {
           timing.begin("HorizontalClassMerger");
           HorizontalClassMerger merger = new HorizontalClassMerger(appViewWithLiveness);
           DirectMappedDexApplication.Builder appBuilder =
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 40be618..29112ed 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -869,7 +869,8 @@
             ? LineNumberOptimization.ON
             : LineNumberOptimization.OFF;
 
-    assert proguardConfiguration.isOptimizing() || !internal.enableHorizontalClassMerging;
+    assert proguardConfiguration.isOptimizing()
+        || internal.horizontalClassMergerOptions().isDisabled();
     assert internal.enableStaticClassMerging || !proguardConfiguration.isOptimizing();
     assert !internal.enableTreeShakingOfLibraryMethodOverrides;
     assert internal.enableVerticalClassMerging || !proguardConfiguration.isOptimizing();
@@ -879,7 +880,7 @@
       internal.getProguardConfiguration().getKeepAttributes().localVariableTypeTable = true;
       internal.enableInlining = false;
       internal.enableClassInlining = false;
-      internal.enableHorizontalClassMerging = false;
+      internal.horizontalClassMergerOptions().disable();
       internal.enableStaticClassMerging = false;
       internal.enableVerticalClassMerging = false;
       internal.enableClassStaticizer = false;
@@ -891,7 +892,7 @@
       // If R8 is not shrinking, there is no point in running various optimizations since the
       // optimized classes will still remain in the program (the application size could increase).
       internal.enableEnumUnboxing = false;
-      internal.enableHorizontalClassMerging = false;
+      internal.horizontalClassMergerOptions().disable();
       internal.enableLambdaMerging = false;
       internal.enableStaticClassMerging = false;
       internal.enableVerticalClassMerging = false;
@@ -900,7 +901,7 @@
     if (!internal.enableInlining) {
       // If R8 cannot perform inlining, then the synthetic constructors would not inline the called
       // constructors, producing invalid code.
-      internal.enableHorizontalClassMerging = false;
+      internal.horizontalClassMergerOptions().disable();
     }
 
     // Amend the proguard-map consumer with options from the proguard configuration.
@@ -958,7 +959,7 @@
     if (internal.isGeneratingClassFiles()) {
       internal.outline.enabled = false;
       internal.enableEnumUnboxing = false;
-      internal.enableHorizontalClassMerging = false;
+      internal.horizontalClassMergerOptions().disable();
     }
 
     // EXPERIMENTAL flags.
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 9c22c36..ce97d08 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -1927,6 +1927,10 @@
         holder);
   }
 
+  public DexMethod createClassInitializer(DexType holder) {
+    return createMethod(holder, createProto(voidType), classConstructorMethodName);
+  }
+
   public DexMethod createInstanceInitializerWithFreshProto(
       DexMethod method, List<DexType> extraTypes, Predicate<DexMethod> isFresh) {
     assert method.isInstanceInitializer(this);
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLens.java b/src/main/java/com/android/tools/r8/graph/GraphLens.java
index b2d04e8..0e3d784 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -4,9 +4,11 @@
 package com.android.tools.r8.graph;
 
 import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+import static com.android.tools.r8.horizontalclassmerging.ClassMerger.CLASS_ID_FIELD_NAME;
+import static com.android.tools.r8.ir.desugar.LambdaRewriter.LAMBDA_CLASS_NAME_PREFIX;
+import static com.android.tools.r8.ir.desugar.LambdaRewriter.LAMBDA_INSTANCE_FIELD_NAME;
 
 import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.horizontalclassmerging.ClassMerger;
 import com.android.tools.r8.ir.code.Invoke.Type;
 import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
 import com.android.tools.r8.ir.desugar.InterfaceProcessor.InterfaceProcessorNestedGraphLens;
@@ -614,21 +616,14 @@
         continue;
       }
       for (DexEncodedField field : clazz.fields()) {
-        // The field $r8$clinitField may be synthesized by R8 in order to trigger the initialization
-        // of the enclosing class. It is not present in the input, and therefore we do not require
-        // that it can be mapped back to the original program.
-        if (field.field.match(dexItemFactory.objectMembers.clinitField)) {
-          continue;
-        }
-
-        // TODO(b/167947782): Should be a general check to see if the field is D8/R8 synthesized.
-        if (field.getReference().name.toSourceString().equals(ClassMerger.CLASS_ID_FIELD_NAME)) {
-          continue;
-        }
-
-        DexField originalField = getOriginalFieldSignature(field.field);
+        // Fields synthesized by R8 are not present in the input, and therefore we do not require
+        // that they can be mapped back to the original program.
+        DexField originalField = getOriginalFieldSignature(field.getReference());
         assert originalFields.contains(originalField)
-            : "Unable to map field `" + field.field.toSourceString() + "` back to original program";
+                || isD8R8SynthesizedField(originalField, dexItemFactory)
+            : "Unable to map field `"
+                + field.getReference().toSourceString()
+                + "` back to original program";
       }
       for (DexEncodedMethod method : clazz.methods()) {
         if (method.isD8R8Synthesized()) {
@@ -643,6 +638,22 @@
     return true;
   }
 
+  private boolean isD8R8SynthesizedField(DexField field, DexItemFactory dexItemFactory) {
+    // TODO(b/167947782): Should be a general check to see if the field is D8/R8 synthesized
+    //  instead of relying on field names.
+    if (field.match(dexItemFactory.objectMembers.clinitField)) {
+      return true;
+    }
+    if (field.getName().toSourceString().equals(CLASS_ID_FIELD_NAME)) {
+      return true;
+    }
+    if (field.getHolderType().toSourceString().contains(LAMBDA_CLASS_NAME_PREFIX)
+        && field.getName().toSourceString().equals(LAMBDA_INSTANCE_FIELD_NAME)) {
+      return true;
+    }
+    return false;
+  }
+
   public abstract static class NonIdentityGraphLens extends GraphLens {
 
     private final DexItemFactory dexItemFactory;
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInitializerSynthesizedCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInitializerSynthesizedCode.java
new file mode 100644
index 0000000..c919e63
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInitializerSynthesizedCode.java
@@ -0,0 +1,100 @@
+// 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 static com.android.tools.r8.utils.ConsumerUtils.apply;
+import static java.lang.Integer.max;
+
+import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.cf.code.CfGoto;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfLabel;
+import com.android.tools.r8.cf.code.CfReturnVoid;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.CfVersionUtils;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class ClassInitializerSynthesizedCode {
+  private final List<DexEncodedMethod> staticClassInitializers;
+  private int maxStack = 0;
+  private int maxLocals = 0;
+
+  private ClassInitializerSynthesizedCode(List<DexEncodedMethod> staticClassInitializers) {
+    this.staticClassInitializers = staticClassInitializers;
+  }
+
+  public boolean isEmpty() {
+    return staticClassInitializers.isEmpty();
+  }
+
+  private void addCfCode(List<CfInstruction> newInstructions, DexEncodedMethod method) {
+    CfCode code = method.getCode().asCfCode();
+    maxStack = max(maxStack, code.getMaxStack());
+    maxLocals = max(maxLocals, code.getMaxLocals());
+
+    CfLabel endLabel = new CfLabel();
+    boolean requiresLabel = false;
+    int index = 1;
+    for (CfInstruction instruction : code.getInstructions()) {
+      if (instruction.isReturn()) {
+        if (code.getInstructions().size() != index) {
+          newInstructions.add(new CfGoto(endLabel));
+          requiresLabel = true;
+        }
+      } else {
+        newInstructions.add(instruction);
+      }
+
+      index++;
+    }
+    if (requiresLabel) {
+      newInstructions.add(endLabel);
+    }
+  }
+
+  public CfCode synthesizeCode(DexType originalHolder) {
+    return new CfCode(
+        originalHolder,
+        maxStack,
+        maxLocals,
+        buildInstructions(),
+        Collections.emptyList(),
+        Collections.emptyList());
+  }
+
+  private List<CfInstruction> buildInstructions() {
+    List<CfInstruction> newInstructions = new ArrayList<>();
+    staticClassInitializers.forEach(apply(this::addCfCode, newInstructions));
+    newInstructions.add(new CfReturnVoid());
+    return newInstructions;
+  }
+
+  public DexEncodedMethod getFirst() {
+    return staticClassInitializers.iterator().next();
+  }
+
+  public CfVersion getCfVersion() {
+    return CfVersionUtils.max(staticClassInitializers);
+  }
+
+  public static class Builder {
+    private final List<DexEncodedMethod> staticClassInitializers = new ArrayList<>();
+
+    public void add(DexEncodedMethod method) {
+      assert method.isClassInitializer();
+      assert method.hasCode();
+      assert method.getCode().isCfCode();
+      staticClassInitializers.add(method);
+    }
+
+    public ClassInitializerSynthesizedCode build() {
+      return new ClassInitializerSynthesizedCode(staticClassInitializers);
+    }
+  }
+}
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 04b4b68..950cf9a 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -6,8 +6,10 @@
 
 import static com.google.common.base.Predicates.not;
 
+import com.android.tools.r8.cf.CfVersion;
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.DexAnnotationSet;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -19,6 +21,9 @@
 import com.android.tools.r8.graph.DexTypeList;
 import com.android.tools.r8.graph.FieldAccessFlags;
 import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
+import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.ParameterAnnotationsList;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
@@ -46,8 +51,10 @@
 
   public static final String CLASS_ID_FIELD_NAME = "$r8$classId";
 
+  private final AppView<AppInfoWithLiveness> appView;
   private final MergeGroup group;
   private final DexItemFactory dexItemFactory;
+  private final ClassInitializerSynthesizedCode classInitializerSynthesizedCode;
   private final HorizontalClassMergerGraphLens.Builder lensBuilder;
   private final HorizontallyMergedClasses.Builder mergedClassesBuilder;
   private final FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder;
@@ -66,7 +73,9 @@
       FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder,
       MergeGroup group,
       Collection<VirtualMethodMerger> virtualMethodMergers,
-      Collection<ConstructorMerger> constructorMergers) {
+      Collection<ConstructorMerger> constructorMergers,
+      ClassInitializerSynthesizedCode classInitializerSynthesizedCode) {
+    this.appView = appView;
     this.lensBuilder = lensBuilder;
     this.mergedClassesBuilder = mergedClassesBuilder;
     this.fieldAccessChangesBuilder = fieldAccessChangesBuilder;
@@ -75,6 +84,7 @@
     this.constructorMergers = constructorMergers;
 
     this.dexItemFactory = appView.dexItemFactory();
+    this.classInitializerSynthesizedCode = classInitializerSynthesizedCode;
     this.classStaticFieldsMerger = new ClassStaticFieldsMerger(appView, lensBuilder, group);
     this.classInstanceFieldsMerger = new ClassInstanceFieldsMerger(lensBuilder, group);
 
@@ -91,18 +101,54 @@
   }
 
   void mergeDirectMethods(SyntheticArgumentClass syntheticArgumentClass) {
+    mergeStaticClassInitializers();
     mergeDirectMethods(group.getTarget());
     group.forEachSource(this::mergeDirectMethods);
     mergeConstructors(syntheticArgumentClass);
   }
 
+  void mergeStaticClassInitializers() {
+    if (classInitializerSynthesizedCode.isEmpty()) {
+      return;
+    }
+
+    DexMethod newClinit = dexItemFactory.createClassInitializer(group.getTarget().getType());
+
+    CfCode code = classInitializerSynthesizedCode.synthesizeCode(group.getTarget().getType());
+    if (!group.getTarget().hasClassInitializer()) {
+      classMethodsBuilder.addDirectMethod(
+          new DexEncodedMethod(
+              newClinit,
+              MethodAccessFlags.fromSharedAccessFlags(
+                  Constants.ACC_SYNTHETIC | Constants.ACC_STATIC, true),
+              MethodTypeSignature.noSignature(),
+              DexAnnotationSet.empty(),
+              ParameterAnnotationsList.empty(),
+              code,
+              true,
+              classInitializerSynthesizedCode.getCfVersion()));
+    } else {
+      DexEncodedMethod clinit = group.getTarget().getClassInitializer();
+      clinit.setCode(code, appView);
+      CfVersion cfVersion = classInitializerSynthesizedCode.getCfVersion();
+      if (cfVersion != null) {
+        clinit.upgradeClassFileVersion(cfVersion);
+      } else {
+        assert appView.options().isGeneratingDex();
+      }
+      classMethodsBuilder.addDirectMethod(clinit);
+    }
+  }
+
   void mergeDirectMethods(DexProgramClass toMerge) {
     toMerge.forEachProgramDirectMethod(
         method -> {
           DexEncodedMethod definition = method.getDefinition();
-          assert !definition.isClassInitializer();
-
-          if (!definition.isInstanceInitializer()) {
+          if (definition.isClassInitializer()) {
+            lensBuilder.moveMethod(
+                method.getReference(),
+                dexItemFactory.createClassInitializer(group.getTarget().getType()));
+          } else if (!definition.isInstanceInitializer()) {
             DexMethod newMethod =
                 method.getReference().withHolder(group.getTarget().getType(), dexItemFactory);
             if (!classMethodsBuilder.isFresh(newMethod)) {
@@ -114,7 +160,6 @@
             }
           }
         });
-
     // Clear the members of the class to be merged since they have now been moved to the target.
     toMerge.getMethodCollection().clearDirectMethods();
   }
@@ -217,6 +262,8 @@
   public static class Builder {
     private final AppView<AppInfoWithLiveness> appView;
     private final MergeGroup group;
+    private final ClassInitializerSynthesizedCode.Builder classInitializerSynthesizedCodeBuilder =
+        new ClassInitializerSynthesizedCode.Builder();
     private final Map<DexProto, ConstructorMerger.Builder> constructorMergerBuilders =
         new LinkedHashMap<>();
     private final List<ConstructorMerger.Builder> unmergedConstructorBuilders = new ArrayList<>();
@@ -242,20 +289,17 @@
     }
 
     private void setupForMethodMerging(DexProgramClass toMerge) {
-      toMerge.forEachProgramDirectMethod(
-          method -> {
-            DexEncodedMethod definition = method.getDefinition();
-            assert !definition.isClassInitializer();
-            if (definition.isInstanceInitializer()) {
-              addConstructor(method);
-            }
-          });
+      if (toMerge.hasClassInitializer()) {
+        classInitializerSynthesizedCodeBuilder.add(toMerge.getClassInitializer());
+      }
+      toMerge.forEachProgramDirectMethodMatching(
+          DexEncodedMethod::isInstanceInitializer, this::addConstructor);
       toMerge.forEachProgramVirtualMethod(this::addVirtualMethod);
     }
 
     private void addConstructor(ProgramMethod method) {
       assert method.getDefinition().isInstanceInitializer();
-      if (appView.options().enableHorizontalClassMergingConstructorMerging) {
+      if (appView.options().horizontalClassMergerOptions().isConstructorMergingEnabled()) {
         constructorMergerBuilders
             .computeIfAbsent(
                 method.getDefinition().getProto(), ignore -> new ConstructorMerger.Builder(appView))
@@ -276,7 +320,7 @@
     }
 
     private Collection<ConstructorMerger.Builder> getConstructorMergerBuilders() {
-      return appView.options().enableHorizontalClassMergingConstructorMerging
+      return appView.options().horizontalClassMergerOptions().isConstructorMergingEnabled()
           ? constructorMergerBuilders.values()
           : unmergedConstructorBuilders;
     }
@@ -313,7 +357,8 @@
           fieldAccessChangesBuilder,
           group,
           virtualMethodMergers,
-          constructorMergers);
+          constructorMergers,
+          classInitializerSynthesizedCodeBuilder.build());
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
index 297dcad..01d5771 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
@@ -30,7 +30,6 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.LinkedList;
 import java.util.List;
 
 public class ConstructorMerger {
@@ -154,7 +153,7 @@
       }
       DexMethod movedConstructor = moveConstructor(classMethodsBuilder, constructor);
       lensBuilder.mapMethod(movedConstructor, movedConstructor);
-      lensBuilder.mapMethodInverse(constructor.method, movedConstructor);
+      lensBuilder.recordNewMethodSignature(constructor.getReference(), movedConstructor);
       typeConstructorClassMap.put(
           classIdentifiers.getInt(constructor.getHolderType()), movedConstructor);
     }
@@ -167,13 +166,29 @@
             classMethodsBuilder::isFresh);
     int extraNulls = newConstructorReference.getArity() - methodReferenceTemplate.getArity();
 
-    DexMethod representativeConstructorReference = constructors.iterator().next().method;
+    DexEncodedMethod representative = constructors.iterator().next();
+    DexMethod originalConstructorReference =
+        appView.graphLens().getOriginalMethodSignature(representative.getReference());
+
+    // Create a special original method signature for the synthesized constructor that did not exist
+    // prior to horizontal class merging. Otherwise we might accidentally think that the synthesized
+    // constructor corresponds to the previous <init>() method on the target class, which could have
+    // unintended side-effects such as leading to unused argument removal being applied to the
+    // synthesized constructor all-though it by construction doesn't have any unused arguments.
+    DexMethod bridgeConstructorReference =
+        dexItemFactory.createFreshMethodName(
+            "$r8$init$bridge",
+            null,
+            originalConstructorReference.getProto(),
+            originalConstructorReference.getHolderType(),
+            classMethodsBuilder::isFresh);
+
     ConstructorEntryPointSynthesizedCode synthesizedCode =
         new ConstructorEntryPointSynthesizedCode(
             typeConstructorClassMap,
             newConstructorReference,
             group.getClassIdField(),
-            appView.graphLens().getOriginalMethodSignature(representativeConstructorReference));
+            bridgeConstructorReference);
     DexEncodedMethod newConstructor =
         new DexEncodedMethod(
             newConstructorReference,
@@ -185,34 +200,20 @@
             true,
             classFileVersion);
 
-    if (isTrivialMerge()) {
-      // The constructor does not require the additional argument, just map it like a regular
-      // method.
-      DexEncodedMethod oldConstructor = constructors.iterator().next();
-      if (extraNulls > 0) {
-        List<ExtraParameter> extraParameters = new LinkedList<>();
-        extraParameters.addAll(Collections.nCopies(extraNulls, new ExtraUnusedNullParameter()));
-        lensBuilder.moveMergedConstructor(
-            oldConstructor.method, newConstructorReference, extraParameters);
-      } else {
-        lensBuilder.moveMethod(oldConstructor.method, newConstructorReference);
-      }
-    } else {
-      // Map each old constructor to the newly synthesized constructor in the graph lens.
-      for (DexEncodedMethod oldConstructor : constructors) {
+    // Map each old constructor to the newly synthesized constructor in the graph lens.
+    for (DexEncodedMethod oldConstructor : constructors) {
+      List<ExtraParameter> extraParameters = new ArrayList<>();
+      if (constructors.size() > 1) {
         int classIdentifier = classIdentifiers.getInt(oldConstructor.getHolderType());
-
-        List<ExtraParameter> extraParameters = new LinkedList<>();
         extraParameters.add(new ExtraConstantIntParameter(classIdentifier));
-        extraParameters.addAll(Collections.nCopies(extraNulls, new ExtraUnusedNullParameter()));
-
-        lensBuilder.moveMergedConstructor(
-            oldConstructor.method, newConstructorReference, extraParameters);
       }
+      extraParameters.addAll(Collections.nCopies(extraNulls, new ExtraUnusedNullParameter()));
+      lensBuilder.mapMergedConstructor(
+          oldConstructor.getReference(), newConstructorReference, extraParameters);
     }
-    // Map the first constructor to the newly synthesized constructor.
-    lensBuilder.recordExtraOriginalSignature(
-        representativeConstructorReference, newConstructorReference);
+
+    // Add a mapping from a synthetic name to the synthetic constructor.
+    lensBuilder.recordNewMethodSignature(bridgeConstructorReference, newConstructorReference);
 
     classMethodsBuilder.addDirectMethod(newConstructor);
 
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 0821a4c..5983618 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.horizontalclassmerging.policies.IgnoreSynthetics;
 import com.android.tools.r8.horizontalclassmerging.policies.LimitGroups;
 import com.android.tools.r8.horizontalclassmerging.policies.NoAnnotations;
+import com.android.tools.r8.horizontalclassmerging.policies.NoClassInitializerWithObservableSideEffects;
 import com.android.tools.r8.horizontalclassmerging.policies.NoClassesOrMembersWithAnnotations;
 import com.android.tools.r8.horizontalclassmerging.policies.NoDirectRuntimeTypeChecks;
 import com.android.tools.r8.horizontalclassmerging.policies.NoEnums;
@@ -26,7 +27,6 @@
 import com.android.tools.r8.horizontalclassmerging.policies.NoKotlinMetadata;
 import com.android.tools.r8.horizontalclassmerging.policies.NoNativeMethods;
 import com.android.tools.r8.horizontalclassmerging.policies.NoServiceLoaders;
-import com.android.tools.r8.horizontalclassmerging.policies.NoStaticClassInitializer;
 import com.android.tools.r8.horizontalclassmerging.policies.NotMatchedByNoHorizontalClassMerging;
 import com.android.tools.r8.horizontalclassmerging.policies.NotVerticallyMergedIntoSubtype;
 import com.android.tools.r8.horizontalclassmerging.policies.PreserveMethodCharacteristics;
@@ -64,11 +64,9 @@
     MergeGroup initialGroup = new MergeGroup(appView.appInfo().classesWithDeterministicOrder());
 
     // Run the policies on all program classes to produce a final grouping.
+    List<Policy> policies = getPolicies(mainDexTracingResult, runtimeTypeCheckInfo);
     Collection<MergeGroup> groups =
-        new SimplePolicyExecutor()
-            .run(
-                Collections.singletonList(initialGroup),
-                getPolicies(mainDexTracingResult, runtimeTypeCheckInfo));
+        new SimplePolicyExecutor().run(Collections.singletonList(initialGroup), policies);
 
     // If there are no groups, then end horizontal class merging.
     if (groups.isEmpty()) {
@@ -116,7 +114,7 @@
         new IgnoreSynthetics(appView),
         new NoClassesOrMembersWithAnnotations(),
         new NoInnerClasses(),
-        new NoStaticClassInitializer(),
+        new NoClassInitializerWithObservableSideEffects(),
         new NoNativeMethods(),
         new NoKeepRules(appView),
         new NoKotlinMetadata(),
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 9b472a4..3adad2e 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
@@ -8,30 +8,26 @@
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.GraphLens;
 import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription;
 import com.android.tools.r8.ir.conversion.ExtraParameter;
 import com.android.tools.r8.utils.IterableUtils;
+import com.android.tools.r8.utils.collections.BidirectionalManyToOneHashMap;
 import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeHashMap;
 import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeMap;
-import com.android.tools.r8.utils.collections.BidirectionalOneToOneMap;
+import com.android.tools.r8.utils.collections.BidirectionalOneToManyRepresentativeHashMap;
+import com.android.tools.r8.utils.collections.BidirectionalOneToManyRepresentativeMap;
 import com.android.tools.r8.utils.collections.MutableBidirectionalManyToOneRepresentativeMap;
-import com.google.common.collect.BiMap;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Streams;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.IdentityHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.function.Function;
 
 public class HorizontalClassMergerGraphLens extends NestedGraphLens {
 
   private final Map<DexMethod, List<ExtraParameter>> methodExtraParameters;
-  private final Map<DexMethod, DexMethod> extraOriginalMethodSignatures;
   private final HorizontallyMergedClasses mergedClasses;
 
   private HorizontalClassMergerGraphLens(
@@ -40,48 +36,23 @@
       Map<DexMethod, List<ExtraParameter>> methodExtraParameters,
       BidirectionalManyToOneRepresentativeMap<DexField, DexField> fieldMap,
       Map<DexMethod, DexMethod> methodMap,
-      BidirectionalOneToOneMap<DexMethod, DexMethod> originalMethodSignatures,
-      Map<DexMethod, DexMethod> extraOriginalMethodSignatures,
-      GraphLens previousLens) {
+      BidirectionalOneToManyRepresentativeMap<DexMethod, DexMethod> originalMethodSignatures) {
     super(
         mergedClasses.getForwardMap(),
         methodMap,
         fieldMap,
         originalMethodSignatures,
-        previousLens,
+        appView.graphLens(),
         appView.dexItemFactory());
     this.methodExtraParameters = methodExtraParameters;
-    this.extraOriginalMethodSignatures = extraOriginalMethodSignatures;
     this.mergedClasses = mergedClasses;
   }
 
-  private boolean isSynthesizedByHorizontalClassMerging(DexMethod method) {
-    return methodExtraParameters.containsKey(method);
-  }
-
   @Override
   protected Iterable<DexType> internalGetOriginalTypes(DexType previous) {
     return IterableUtils.prependSingleton(previous, mergedClasses.getSourcesFor(previous));
   }
 
-  @Override
-  public RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(DexMethod method) {
-    if (isSynthesizedByHorizontalClassMerging(method)) {
-      // If we are processing the call site, the arguments should be removed.
-      return RewrittenPrototypeDescription.none();
-    }
-    return super.lookupPrototypeChangesForMethodDefinition(method);
-  }
-
-  @Override
-  public DexMethod getOriginalMethodSignature(DexMethod method) {
-    DexMethod originalConstructor = extraOriginalMethodSignatures.get(method);
-    if (originalConstructor == null) {
-      return super.getOriginalMethodSignature(method);
-    }
-    return getPrevious().getOriginalMethodSignature(originalConstructor);
-  }
-
   /**
    * If an overloaded constructor is requested, add the constructor id as a parameter to the
    * constructor. Otherwise return the lookup on the underlying graph lens.
@@ -102,39 +73,55 @@
   }
 
   public static class Builder {
-    private MutableBidirectionalManyToOneRepresentativeMap<DexField, DexField> fieldMap =
+
+    private final MutableBidirectionalManyToOneRepresentativeMap<DexField, DexField> fieldMap =
         new BidirectionalManyToOneRepresentativeHashMap<>();
-    private ManyToOneMap<DexMethod, DexMethod> methodMap = new ManyToOneMap<>();
+    private final BidirectionalManyToOneHashMap<DexMethod, DexMethod> methodMap =
+        new BidirectionalManyToOneHashMap<>();
+    private final BidirectionalOneToManyRepresentativeHashMap<DexMethod, DexMethod>
+        originalMethodSignatures = new BidirectionalOneToManyRepresentativeHashMap<>();
     private final Map<DexMethod, List<ExtraParameter>> methodExtraParameters =
         new IdentityHashMap<>();
 
+    private final BidirectionalManyToOneHashMap<DexMethod, DexMethod> pendingMethodMapUpdates =
+        new BidirectionalManyToOneHashMap<>();
+    private final BidirectionalOneToManyRepresentativeHashMap<DexMethod, DexMethod>
+        pendingOriginalMethodSignatureUpdates = new BidirectionalOneToManyRepresentativeHashMap<>();
+
     Builder() {}
 
-    public HorizontalClassMergerGraphLens build(
+    HorizontalClassMergerGraphLens build(
         AppView<?> appView, HorizontallyMergedClasses mergedClasses) {
-      ManyToOneInverseMap<DexMethod, DexMethod> inverseMethodMap =
-          methodMap.inverse(
-              group -> {
-                // Every group should have a representative. Fail in debug mode.
-                assert false;
-                return group.iterator().next();
-              });
+      assert pendingMethodMapUpdates.isEmpty();
+      assert pendingOriginalMethodSignatureUpdates.isEmpty();
       return new HorizontalClassMergerGraphLens(
           appView,
           mergedClasses,
           methodExtraParameters,
           fieldMap,
           methodMap.getForwardMap(),
-          inverseMethodMap.getBiMap(),
-          inverseMethodMap.getExtraMap(),
-          appView.graphLens());
+          originalMethodSignatures);
     }
 
-    public void remapMethods(BiMap<DexMethod, DexMethod> remapMethods) {
-      methodMap = methodMap.remap(remapMethods, Function.identity(), Function.identity());
+    void recordNewFieldSignature(DexField oldFieldSignature, DexField newFieldSignature) {
+      fieldMap.put(oldFieldSignature, newFieldSignature);
     }
 
-    Builder recordNewFieldSignature(DexField oldFieldSignature, DexField newFieldSignature) {
+    void recordNewFieldSignature(
+        Iterable<DexField> oldFieldSignatures,
+        DexField newFieldSignature,
+        DexField representative) {
+      assert Streams.stream(oldFieldSignatures)
+          .anyMatch(oldFieldSignature -> oldFieldSignature != newFieldSignature);
+      assert Streams.stream(oldFieldSignatures).noneMatch(fieldMap::containsValue);
+      assert Iterables.contains(oldFieldSignatures, representative);
+      for (DexField oldFieldSignature : oldFieldSignatures) {
+        recordNewFieldSignature(oldFieldSignature, newFieldSignature);
+      }
+      fieldMap.setRepresentative(newFieldSignature, representative);
+    }
+
+    void fixupField(DexField oldFieldSignature, DexField newFieldSignature) {
       Set<DexField> originalFieldSignatures = fieldMap.removeValue(oldFieldSignature);
       if (originalFieldSignatures.isEmpty()) {
         fieldMap.put(oldFieldSignature, newFieldSignature);
@@ -148,44 +135,59 @@
         assert representative != null;
         fieldMap.setRepresentative(newFieldSignature, representative);
       }
-      return this;
     }
 
-    Builder recordNewFieldSignature(
-        Iterable<DexField> oldFieldSignatures,
-        DexField newFieldSignature,
-        DexField representative) {
-      assert Streams.stream(oldFieldSignatures).noneMatch(fieldMap::containsValue);
-      assert Iterables.contains(oldFieldSignatures, representative);
-      for (DexField oldFieldSignature : oldFieldSignatures) {
-        fieldMap.put(oldFieldSignature, newFieldSignature);
-      }
-      fieldMap.setRepresentative(newFieldSignature, representative);
-      return this;
+    void mapMethod(DexMethod oldMethodSignature, DexMethod newMethodSignature) {
+      methodMap.put(oldMethodSignature, newMethodSignature);
     }
 
-    /** Unidirectional mapping from one method to another. */
-    public Builder recordExtraOriginalSignature(DexMethod from, DexMethod to) {
-      methodMap.setRepresentative(from, to);
-      return this;
-    }
-
-    /** Unidirectional mapping from one method to another. */
-    public Builder mapMethod(DexMethod from, DexMethod to) {
-      methodMap.put(from, to);
-      return this;
-    }
-
-    /** Unidirectional mapping from one method to another. */
-    public Builder mapMethodInverse(DexMethod from, DexMethod to) {
-      methodMap.putInverse(from, to);
-      return this;
-    }
-
-    public Builder moveMethod(DexMethod from, DexMethod to) {
+    void moveMethod(DexMethod from, DexMethod to) {
       mapMethod(from, to);
-      mapMethodInverse(from, to);
-      return this;
+      recordNewMethodSignature(from, to);
+    }
+
+    void recordNewMethodSignature(DexMethod oldMethodSignature, DexMethod newMethodSignature) {
+      originalMethodSignatures.put(newMethodSignature, oldMethodSignature);
+    }
+
+    void fixupMethod(DexMethod oldMethodSignature, DexMethod newMethodSignature) {
+      fixupMethodMap(oldMethodSignature, newMethodSignature);
+      fixupOriginalMethodSignatures(oldMethodSignature, newMethodSignature);
+    }
+
+    private void fixupMethodMap(DexMethod oldMethodSignature, DexMethod newMethodSignature) {
+      Set<DexMethod> originalMethodSignatures = methodMap.getKeys(oldMethodSignature);
+      if (originalMethodSignatures.isEmpty()) {
+        pendingMethodMapUpdates.put(oldMethodSignature, newMethodSignature);
+      } else {
+        for (DexMethod originalMethodSignature : originalMethodSignatures) {
+          pendingMethodMapUpdates.put(originalMethodSignature, newMethodSignature);
+        }
+      }
+    }
+
+    private void fixupOriginalMethodSignatures(
+        DexMethod oldMethodSignature, DexMethod newMethodSignature) {
+      Set<DexMethod> oldMethodSignatures = originalMethodSignatures.getValues(oldMethodSignature);
+      if (oldMethodSignatures.isEmpty()) {
+        pendingOriginalMethodSignatureUpdates.put(newMethodSignature, oldMethodSignature);
+      } else {
+        for (DexMethod originalMethodSignature : oldMethodSignatures) {
+          pendingOriginalMethodSignatureUpdates.put(newMethodSignature, originalMethodSignature);
+        }
+      }
+    }
+
+    void commitPendingUpdates() {
+      // Commit pending method map updates.
+      methodMap.removeAll(pendingMethodMapUpdates.keySet());
+      pendingMethodMapUpdates.forEachManyToOneMapping(methodMap::put);
+      pendingMethodMapUpdates.clear();
+
+      // Commit pending original method signatures updates.
+      originalMethodSignatures.removeAll(pendingOriginalMethodSignatureUpdates.keySet());
+      pendingOriginalMethodSignatureUpdates.forEachOneToManyMapping(originalMethodSignatures::put);
+      pendingOriginalMethodSignatureUpdates.clear();
     }
 
     /**
@@ -193,24 +195,27 @@
      * where many constructors are merged into a single constructor. The synthesized constructor
      * therefore does not have a unique reverse constructor.
      */
-    public Builder moveMergedConstructor(
-        DexMethod from, DexMethod to, List<ExtraParameter> extraParameters) {
-      moveMethod(from, to);
-      methodExtraParameters.put(from, extraParameters);
-      return this;
+    void mapMergedConstructor(DexMethod from, DexMethod to, List<ExtraParameter> extraParameters) {
+      mapMethod(from, to);
+      if (extraParameters.size() > 0) {
+        methodExtraParameters.put(from, extraParameters);
+      }
     }
 
-    public Builder addExtraParameters(DexMethod to, List<ExtraParameter> extraParameters) {
-      Set<DexMethod> mapsFrom = methodMap.lookupReverse(to);
-      if (mapsFrom == null) {
-        mapsFrom = Collections.singleton(to);
+    void addExtraParameters(DexMethod methodSignature, List<ExtraParameter> extraParameters) {
+      Set<DexMethod> originalMethodSignatures = methodMap.getKeys(methodSignature);
+      if (originalMethodSignatures.isEmpty()) {
+        methodExtraParameters
+            .computeIfAbsent(methodSignature, ignore -> new ArrayList<>(extraParameters.size()))
+            .addAll(extraParameters);
+      } else {
+        for (DexMethod originalMethodSignature : originalMethodSignatures) {
+          methodExtraParameters
+              .computeIfAbsent(
+                  originalMethodSignature, ignore -> new ArrayList<>(extraParameters.size()))
+              .addAll(extraParameters);
+        }
       }
-      mapsFrom.forEach(
-          originalFrom ->
-              methodExtraParameters
-                  .computeIfAbsent(originalFrom, ignore -> new ArrayList<>(extraParameters.size()))
-                  .addAll(extraParameters));
-      return this;
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ManyToOneInverseMap.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ManyToOneInverseMap.java
deleted file mode 100644
index 8cf8b2a..0000000
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ManyToOneInverseMap.java
+++ /dev/null
@@ -1,27 +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.utils.collections.BidirectionalOneToOneMap;
-import java.util.Map;
-
-/** The inverse of a {@link ManyToOneMap} used for generating graph lens maps. */
-public class ManyToOneInverseMap<K, V> {
-  private final BidirectionalOneToOneMap<V, K> biMap;
-  private final Map<V, K> extraMap;
-
-  ManyToOneInverseMap(BidirectionalOneToOneMap<V, K> biMap, Map<V, K> extraMap) {
-    this.biMap = biMap;
-    this.extraMap = extraMap;
-  }
-
-  public BidirectionalOneToOneMap<V, K> getBiMap() {
-    return biMap;
-  }
-
-  public Map<V, K> getExtraMap() {
-    return extraMap;
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ManyToOneMap.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ManyToOneMap.java
deleted file mode 100644
index f51dfd2..0000000
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ManyToOneMap.java
+++ /dev/null
@@ -1,122 +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.utils.collections.BidirectionalOneToOneHashMap;
-import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap;
-import com.google.common.collect.BiMap;
-import com.google.common.collect.HashBiMap;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.IdentityHashMap;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.function.Function;
-
-/**
- * This mapping class is used to track method mappings for horizontal class merging. Essentially it
- * is a bidirectional many to one map, but with support for having unidirectional mappings and with
- * support for remapping the values to new values using {@link ManyToOneMap#remap(BiMap, Function,
- * Function)}. It also supports generating an inverse mapping {@link ManyToOneInverseMap} that can
- * be used by the graph lens using {@link ManyToOneMap#inverse(Function)}. The inverse map is a
- * bidirectional one to one map with additional non-bidirectional representative entries.
- */
-public class ManyToOneMap<K, V> {
-  private final Map<K, V> forwardMap = new IdentityHashMap<>();
-  private final Map<V, Set<K>> inverseMap = new IdentityHashMap<>();
-  private final Map<V, K> representativeMap = new IdentityHashMap<>();
-
-  public Map<K, V> getForwardMap() {
-    return forwardMap;
-  }
-
-  public Set<K> lookupReverse(V to) {
-    return inverseMap.get(to);
-  }
-
-  public V put(K from, V to) {
-    return forwardMap.put(from, to);
-  }
-
-  public void putInverse(K from, V to) {
-    inverseMap.computeIfAbsent(to, ignore -> new HashSet<>()).add(from);
-  }
-
-  public K setRepresentative(K from, V to) {
-    putInverse(from, to);
-    return representativeMap.put(to, from);
-  }
-
-  public ManyToOneInverseMap<K, V> inverse(Function<Set<K>, K> pickRepresentative) {
-    MutableBidirectionalOneToOneMap<V, K> biMap = new BidirectionalOneToOneHashMap<>();
-    Map<V, K> extraMap = new HashMap<>();
-    for (Entry<V, Set<K>> entry : inverseMap.entrySet()) {
-      K representative = representativeMap.get(entry.getKey());
-      if (entry.getValue().size() == 1) {
-        K singleton = entry.getValue().iterator().next();
-        assert representative == null || singleton == representative;
-        if (representative == null) {
-          biMap.put(entry.getKey(), singleton);
-        } else {
-          extraMap.put(entry.getKey(), singleton);
-        }
-      } else {
-        if (representative == null) {
-          representative = pickRepresentative.apply(entry.getValue());
-        } else {
-          assert representative == entry.getKey() || entry.getValue().contains(representative);
-        }
-        extraMap.put(entry.getKey(), representative);
-      }
-    }
-
-    return new ManyToOneInverseMap<>(biMap, extraMap);
-  }
-
-  public <NewV> ManyToOneMap<K, NewV> remap(
-      BiMap<V, NewV> biMap, Function<V, NewV> notInBiMap, Function<V, K> notInForwardMap) {
-    ManyToOneMap<K, NewV> newMap = new ManyToOneMap<>();
-
-    // All entries that should be remapped and are already in the forward and/or inverse mappings
-    // should only be remapped in the directions they are already mapped in.
-    BiMap<V, NewV> biMapCopy = HashBiMap.create(biMap);
-    for (Entry<V, Set<K>> entry : inverseMap.entrySet()) {
-      NewV to = biMapCopy.remove(entry.getKey());
-      if (to == null) {
-        to = biMap.getOrDefault(entry.getKey(), notInBiMap.apply(entry.getKey()));
-      }
-      newMap.inverseMap.put(to, entry.getValue());
-    }
-    for (Entry<K, V> entry : forwardMap.entrySet()) {
-      NewV newTo = biMapCopy.remove(entry.getValue());
-      if (newTo == null) {
-        newTo = biMap.getOrDefault(entry.getValue(), notInBiMap.apply(entry.getValue()));
-      }
-      newMap.forwardMap.put(entry.getKey(), newTo);
-    }
-
-    // All new entries should be mapped in both directions.
-    for (Entry<V, NewV> entry : biMapCopy.entrySet()) {
-      newMap.forwardMap.put(notInForwardMap.apply(entry.getKey()), entry.getValue());
-      newMap
-          .inverseMap
-          .computeIfAbsent(entry.getValue(), ignore -> new HashSet<>())
-          .add(notInForwardMap.apply(entry.getKey()));
-    }
-
-    // Representatives are always in the inverse mapping, so they should always be remapped as new
-    // representatives.
-    for (Entry<V, K> entry : representativeMap.entrySet()) {
-      NewV newTo = biMap.get(entry.getKey());
-      if (newTo == null) {
-        newTo = notInBiMap.apply(entry.getKey());
-      }
-      newMap.representativeMap.put(newTo, entry.getValue());
-    }
-
-    return newMap;
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/Policy.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/Policy.java
index f6b86a3..4642229 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/Policy.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/Policy.java
@@ -12,6 +12,8 @@
   /** Counter keeping track of how many classes this policy has removed. For debugging only. */
   public int numberOfRemovedClasses;
 
+  public void clear() {}
+
   public boolean shouldSkipPolicy() {
     return false;
   }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/SimplePolicyExecutor.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/SimplePolicyExecutor.java
index b2fdb3d..14b249a 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/SimplePolicyExecutor.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/SimplePolicyExecutor.java
@@ -70,6 +70,8 @@
         linkedGroups = applyMultiClassPolicy((MultiClassPolicy) policy, linkedGroups);
       }
 
+      policy.clear();
+
       // Any policy should not return any trivial groups.
       assert linkedGroups.stream().allMatch(group -> group.size() >= 2);
     }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java
index b6e1f53..c3aa3c0 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java
@@ -107,7 +107,9 @@
       boolean requiresMainDex = appView.appInfo().getMainDexClasses().containsAnyOf(mergeClasses);
 
       List<DexType> syntheticArgumentTypes = new ArrayList<>();
-      for (int i = 0; i < appView.options().horizontalClassMergingSyntheticArgumentCount; i++) {
+      for (int i = 0;
+          i < appView.options().horizontalClassMergerOptions().getSyntheticArgumentCount();
+          i++) {
         syntheticArgumentTypes.add(
             synthesizeClass(appView, appBuilder, context, requiresMainDex, i));
       }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
index 045a8b1..0f85054 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
@@ -45,7 +45,6 @@
   private final FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder;
   private final AppView<AppInfoWithLiveness> appView;
   private final DexItemFactory dexItemFactory;
-  private final BiMap<DexMethod, DexMethod> movedMethods = HashBiMap.create();
   private final SyntheticArgumentClass syntheticArgumentClass;
   private final BiMap<DexMethodSignature, DexMethodSignature> reservedInterfaceSignatures =
       HashBiMap.create();
@@ -127,9 +126,6 @@
     for (DexProgramClass root : subtypingForrest.getProgramRoots()) {
       subtypingForrest.traverseNodeDepthFirst(root, HashBiMap.create(), this::fixupProgramClass);
     }
-
-    lensBuilder.remapMethods(movedMethods);
-
     HorizontalClassMergerGraphLens lens = lensBuilder.build(appView, mergedClasses);
     fieldAccessChangesBuilder.build(this::fixupMethodReference).modify(appView);
     new AnnotationFixer(lens).run(appView.appInfo().classes());
@@ -162,6 +158,8 @@
     fixupFields(clazz.staticFields(), clazz::setStaticField);
     fixupFields(clazz.instanceFields(), clazz::setInstanceField);
 
+    lensBuilder.commitPendingUpdates();
+
     return remappedClassVirtualMethods;
   }
 
@@ -171,7 +169,7 @@
     // Don't process this method if it does not refer to a merge class type.
     boolean referencesMergeClass =
         Iterables.any(
-            originalMethodReference.proto.getBaseTypes(dexItemFactory),
+            originalMethodReference.getProto().getBaseTypes(dexItemFactory),
             mergedClasses::hasBeenMergedOrIsMergeTarget);
     if (!referencesMergeClass) {
       return method;
@@ -198,8 +196,7 @@
 
     DexMethod newMethodReference =
         newMethodSignature.withHolder(originalMethodReference, dexItemFactory);
-    movedMethods.put(originalMethodReference, newMethodReference);
-
+    lensBuilder.fixupMethod(originalMethodReference, newMethodReference);
     return method.toTypeSubstitutedMethod(newMethodReference);
   }
 
@@ -215,17 +212,17 @@
     iface.getMethodCollection().replaceVirtualMethods(this::fixupVirtualInterfaceMethod);
     fixupFields(iface.staticFields(), iface::setStaticField);
     fixupFields(iface.instanceFields(), iface::setInstanceField);
+    lensBuilder.commitPendingUpdates();
   }
 
   private DexEncodedMethod fixupProgramMethod(
       DexMethod newMethodReference, DexEncodedMethod method) {
     DexMethod originalMethodReference = method.getReference();
-
     if (newMethodReference == originalMethodReference) {
       return method;
     }
 
-    movedMethods.put(originalMethodReference, newMethodReference);
+    lensBuilder.fixupMethod(originalMethodReference, newMethodReference);
 
     DexEncodedMethod newMethod = method.toTypeSubstitutedMethod(newMethodReference);
     if (newMethod.isNonPrivateVirtualMethod()) {
@@ -381,7 +378,7 @@
       }
 
       if (newFieldReference != oldFieldReference) {
-        lensBuilder.recordNewFieldSignature(oldFieldReference, newFieldReference);
+        lensBuilder.fixupField(oldFieldReference, newFieldReference);
         setter.setField(i, oldField.toTypeSubstitutedField(newFieldReference));
       }
     }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
index fe3a898..9a44de7 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
@@ -229,8 +229,7 @@
         classFileVersion = Ordered.maxIgnoreNull(classFileVersion, methodVersion);
       }
       DexMethod newMethod = moveMethod(classMethodsBuilder, method);
-      lensBuilder.mapMethod(newMethod, newMethod);
-      lensBuilder.mapMethodInverse(method.getReference(), newMethod);
+      lensBuilder.recordNewMethodSignature(method.getReference(), newMethod);
       classIdToMethodMap.put(classIdentifiers.getInt(method.getHolderType()), newMethod);
       if (representative == null) {
         representative = method;
@@ -274,16 +273,14 @@
 
     // Map each old non-abstract method to the newly synthesized method in the graph lens.
     for (ProgramMethod oldMethod : methods) {
-      if (oldMethod.getDefinition().isAbstract()) {
-        lensBuilder.mapMethod(oldMethod.getReference(), newMethodReference);
-      } else {
-        lensBuilder.moveMethod(oldMethod.getReference(), newMethodReference);
-      }
+      lensBuilder.mapMethod(oldMethod.getReference(), newMethodReference);
     }
-    lensBuilder.recordExtraOriginalSignature(bridgeMethodReference, newMethodReference);
+
+    // Add a mapping from a synthetic name to the synthetic merged method.
+    lensBuilder.recordNewMethodSignature(bridgeMethodReference, newMethodReference);
 
     classMethodsBuilder.addVirtualMethod(newMethod);
 
-    fieldAccessChangesBuilder.fieldReadByMethod(group.getClassIdField(), newMethod.method);
+    fieldAccessChangesBuilder.fieldReadByMethod(group.getClassIdField(), newMethodReference);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/IgnoreSynthetics.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/IgnoreSynthetics.java
index 78875fd..e45e2d3 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/IgnoreSynthetics.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/IgnoreSynthetics.java
@@ -19,6 +19,10 @@
 
   @Override
   public boolean canMerge(DexProgramClass program) {
-    return !appView.getSyntheticItems().isSyntheticClass(program);
+    if (appView.getSyntheticItems().isSyntheticClass(program)) {
+      return appView.options().horizontalClassMergerOptions().isJavaLambdaMergingEnabled()
+          && appView.getSyntheticItems().isLegacySyntheticClass(program);
+    }
+    return true;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/LimitGroups.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/LimitGroups.java
index f2800aa..33de8f3 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/LimitGroups.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/LimitGroups.java
@@ -18,7 +18,7 @@
   private final int maxGroupSize;
 
   public LimitGroups(AppView<AppInfoWithLiveness> appView) {
-    maxGroupSize = appView.options().horizontalClassMergingMaxGroupSize;
+    maxGroupSize = appView.options().horizontalClassMergerOptions().getMaxGroupSize();
     assert maxGroupSize >= 2;
   }
 
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassInitializerWithObservableSideEffects.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassInitializerWithObservableSideEffects.java
new file mode 100644
index 0000000..f10d78c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassInitializerWithObservableSideEffects.java
@@ -0,0 +1,31 @@
+// 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.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
+
+/**
+ * Prevent merging of classes with static initializers, as merging these causes side effects. It is
+ * okay for superclasses to have static initializers as all classes are expected to have the same
+ * super class.
+ */
+public class NoClassInitializerWithObservableSideEffects extends SingleClassPolicy {
+
+  @Override
+  public boolean canMerge(DexProgramClass program) {
+    if (!program.hasClassInitializer()) {
+      return true;
+    }
+    DexEncodedMethod clinit = program.getClassInitializer();
+    return clinit.getOptimizationInfo().classInitializerMayBePostponed() || isKotlinLambda(program);
+  }
+
+  private boolean isKotlinLambda(DexProgramClass program) {
+    return program.getKotlinInfo().isSyntheticClass()
+        && program.getKotlinInfo().asSyntheticClass().isLambda();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoEnums.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoEnums.java
index 2da0177..c8a0aab 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoEnums.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoEnums.java
@@ -22,8 +22,19 @@
   }
 
   @Override
+  public void clear() {
+    cache.clear();
+  }
+
+  @Override
   public boolean canMerge(DexProgramClass program) {
-    return !program.isEnum() && !isEnumSubtype(program);
+    if (program.isEnum()) {
+      return false;
+    }
+    if (isEnumSubtype(program)) {
+      return false;
+    }
+    return true;
   }
 
   private boolean isEnumSubtype(DexClass clazz) {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKotlinLambdas.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKotlinLambdas.java
index a137e88..dee7a4a 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKotlinLambdas.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKotlinLambdas.java
@@ -18,7 +18,7 @@
 
   @Override
   public boolean shouldSkipPolicy() {
-    return appView.options().enableHorizontalClassMergingOfKotlinLambdas;
+    return appView.options().horizontalClassMergerOptions().isKotlinLambdaMergingEnabled();
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoStaticClassInitializer.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoStaticClassInitializer.java
deleted file mode 100644
index aeca6c0..0000000
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoStaticClassInitializer.java
+++ /dev/null
@@ -1,20 +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.graph.DexProgramClass;
-import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
-
-/**
- * Prevent merging of classes with static initializers, as merging these causes side effects. It is
- * okay for superclasses to have static initializers as all classes are expected to have the same
- * super class.
- */
-public class NoStaticClassInitializer extends SingleClassPolicy {
-  @Override
-  public boolean canMerge(DexProgramClass program) {
-    return !program.hasClassInitializer();
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index c9e47c2..a50581e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -33,6 +33,8 @@
 import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
 import com.android.tools.r8.ir.code.Invoke;
 import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
 import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode;
 import com.android.tools.r8.ir.synthetic.SynthesizedCode;
 import com.android.tools.r8.origin.SynthesizedOrigin;
@@ -65,6 +67,8 @@
  */
 public final class LambdaClass {
 
+  private static final OptimizationFeedback feedback = OptimizationFeedbackSimple.getInstance();
+
   final AppView<?> appView;
   final LambdaRewriter rewriter;
   public final DexType type;
@@ -293,6 +297,7 @@
               ParameterAnnotationsList.empty(),
               LambdaClassConstructorSourceCode.build(this),
               true);
+      feedback.classInitializerMayBePostponed(methods[1]);
     }
     return methods;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
index d27fb16..f464fdd 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -72,7 +72,7 @@
   public static final String LAMBDA_CLASS_NAME_PREFIX = "-$$Lambda$";
   public static final String LAMBDA_GROUP_CLASS_NAME_PREFIX = "-$$LambdaGroup$";
   static final String EXPECTED_LAMBDA_METHOD_PREFIX = "lambda$";
-  private static final String LAMBDA_INSTANCE_FIELD_NAME = "INSTANCE";
+  public static final String LAMBDA_INSTANCE_FIELD_NAME = "INSTANCE";
 
   private final AppView<?> appView;
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
index 09c4a5d..4e3fb01 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -1076,6 +1076,7 @@
         assert !context.getHolderType().isD8R8SynthesizedLambdaClassType()
                 || options.debug
                 || appView.appInfo().hasPinnedInstanceInitializer(context.getHolderType())
+                || appView.options().horizontalClassMergerOptions().isJavaLambdaMergingEnabled()
             : "Unexpected observable side effects from lambda `" + context.toSourceString() + "`";
       }
       return;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
index 5840ea6..f0d7c28 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
@@ -195,6 +195,6 @@
 
   @Override
   public void classInitializerMayBePostponed(DexEncodedMethod method) {
-    // Ignored.
+    method.getMutableOptimizationInfo().markClassInitializerMayBePostponed();
   }
 }
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 764fc9c..bc7d42e 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -224,10 +224,18 @@
     return nonLecacySyntheticItems.containsKey(type) || legacySyntheticTypes.contains(type);
   }
 
+  private boolean isLegacyCommittedSynthetic(DexType type) {
+    return legacySyntheticTypes.contains(type);
+  }
+
   public boolean isPendingSynthetic(DexType type) {
     return pendingDefinitions.containsKey(type) || legacyPendingClasses.containsKey(type);
   }
 
+  public boolean isLegacyPendingSynthetic(DexType type) {
+    return legacyPendingClasses.containsKey(type);
+  }
+
   public boolean isSyntheticClass(DexType type) {
     return isCommittedSynthetic(type)
         || isPendingSynthetic(type)
@@ -239,6 +247,14 @@
     return isSyntheticClass(clazz.type);
   }
 
+  public boolean isLegacySyntheticClass(DexType type) {
+    return isLegacyCommittedSynthetic(type) || isLegacyPendingSynthetic(type);
+  }
+
+  public boolean isLegacySyntheticClass(DexProgramClass clazz) {
+    return isLegacySyntheticClass(clazz.getType());
+  }
+
   public Collection<DexProgramClass> getLegacyPendingClasses() {
     return Collections.unmodifiableCollection(legacyPendingClasses.values());
   }
diff --git a/src/main/java/com/android/tools/r8/utils/CfVersionUtils.java b/src/main/java/com/android/tools/r8/utils/CfVersionUtils.java
new file mode 100644
index 0000000..dadf460
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/CfVersionUtils.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2016, 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.utils;
+
+import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.utils.structural.Ordered;
+import java.util.List;
+
+public class CfVersionUtils {
+
+  public static CfVersion max(List<DexEncodedMethod> methods) {
+    CfVersion result = null;
+    for (DexEncodedMethod method : methods) {
+      if (method.hasClassFileVersion()) {
+        result = Ordered.maxIgnoreNull(result, method.getClassFileVersion());
+      }
+    }
+    return result;
+  }
+}
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 cd3a2db..fb8655a 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -202,7 +202,7 @@
     enableClassStaticizer = false;
     enableDevirtualization = false;
     enableLambdaMerging = false;
-    enableHorizontalClassMerging = false;
+    horizontalClassMergerOptions.disable();
     enableStaticClassMerging = false;
     enableVerticalClassMerging = false;
     enableEnumUnboxing = false;
@@ -238,11 +238,6 @@
   public boolean enableFieldBitAccessAnalysis =
       System.getProperty("com.android.tools.r8.fieldBitAccessAnalysis") != null;
   public boolean enableStaticClassMerging = true;
-  public boolean enableHorizontalClassMerging = true;
-  public boolean enableHorizontalClassMergingConstructorMerging = true;
-  public int horizontalClassMergingMaxGroupSize = 30;
-  public int horizontalClassMergingSyntheticArgumentCount = 3;
-  public boolean enableHorizontalClassMergingOfKotlinLambdas = true;
   public boolean enableVerticalClassMerging = true;
   public boolean enableArgumentRemoval = true;
   public boolean enableUnusedInterfaceRemoval = true;
@@ -582,7 +577,7 @@
    * and check cast instructions needs to be collected.
    */
   public boolean isClassMergingExtensionRequired() {
-    return enableHorizontalClassMerging || enableVerticalClassMerging;
+    return horizontalClassMergerOptions.isEnabled() || enableVerticalClassMerging;
   }
 
   @Override
@@ -615,6 +610,8 @@
 
   private final CallSiteOptimizationOptions callSiteOptimizationOptions =
       new CallSiteOptimizationOptions();
+  private final HorizontalClassMergerOptions horizontalClassMergerOptions =
+      new HorizontalClassMergerOptions();
   private final ProtoShrinkingOptions protoShrinking = new ProtoShrinkingOptions();
   private final KotlinOptimizationOptions kotlinOptimizationOptions =
       new KotlinOptimizationOptions();
@@ -638,6 +635,10 @@
     return callSiteOptimizationOptions;
   }
 
+  public HorizontalClassMergerOptions horizontalClassMergerOptions() {
+    return horizontalClassMergerOptions;
+  }
+
   public ProtoShrinkingOptions protoShrinking() {
     return protoShrinking;
   }
@@ -1243,6 +1244,70 @@
     }
   }
 
+  public static class HorizontalClassMergerOptions {
+
+    public boolean enable = true;
+    public boolean enableConstructorMerging = true;
+    public boolean enableJavaLambdaMerging = false;
+    public boolean enableKotlinLambdaMerging = true;
+
+    public int syntheticArgumentCount = 3;
+    public int maxGroupSize = 30;
+
+    public void disable() {
+      enable = false;
+    }
+
+    @Deprecated
+    public void disableKotlinLambdaMerging() {
+      enableKotlinLambdaMerging = false;
+    }
+
+    public void enable() {
+      enable = true;
+    }
+
+    public void enableIf(boolean enable) {
+      this.enable = enable;
+    }
+
+    public void enableJavaLambdaMerging() {
+      enableJavaLambdaMerging = true;
+    }
+
+    public void enableKotlinLambdaMergingIf(boolean enableKotlinLambdaMerging) {
+      this.enableKotlinLambdaMerging = enableKotlinLambdaMerging;
+    }
+
+    public int getMaxGroupSize() {
+      return maxGroupSize;
+    }
+
+    public int getSyntheticArgumentCount() {
+      return syntheticArgumentCount;
+    }
+
+    public boolean isConstructorMergingEnabled() {
+      return enableConstructorMerging;
+    }
+
+    public boolean isDisabled() {
+      return !isEnabled();
+    }
+
+    public boolean isEnabled() {
+      return enable;
+    }
+
+    public boolean isJavaLambdaMergingEnabled() {
+      return enableJavaLambdaMerging;
+    }
+
+    public boolean isKotlinLambdaMergingEnabled() {
+      return enableKotlinLambdaMerging;
+    }
+  }
+
   public static class ProtoShrinkingOptions {
 
     public boolean enableGeneratedExtensionRegistryShrinking = false;
diff --git a/src/test/examplesAndroidO/lambdadesugaring/LambdaDesugaring.java b/src/test/examplesAndroidO/lambdadesugaring/LambdaDesugaring.java
index b54be6b..627d255 100644
--- a/src/test/examplesAndroidO/lambdadesugaring/LambdaDesugaring.java
+++ b/src/test/examplesAndroidO/lambdadesugaring/LambdaDesugaring.java
@@ -398,7 +398,15 @@
     try {
       testEnforcedSignatureHelper();
     } catch (Exception e) {
-      System.out.println(e.getMessage());
+      if (e.getMessage().contains("cannot be cast to lambdadesugaring.LambdaDesugaring$B")
+          || e.getMessage()
+              .contains("cannot be cast to class lambdadesugaring.LambdaDesugaring$B")) {
+        System.out.println(
+            "lambdadesugaring.LambdaDesugaring$A cannot be cast to"
+                + " lambdadesugaring.LambdaDesugaring$B");
+      } else {
+        System.out.println(e.getMessage());
+      }
     }
 
     atA(t -> new LambdaDesugaring().reorder(t));
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
index abdfb83..f196151 100644
--- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -47,7 +47,7 @@
       options -> {
         options.testing.allowClassInlinerGracefulExit = false;
         options.testing.reportUnusedProguardConfigurationRules = true;
-        options.enableHorizontalClassMerging = true;
+        options.horizontalClassMergerOptions().enable();
       };
 
   final Backend backend;
@@ -130,7 +130,6 @@
 
   public T addHorizontallyMergedClassesInspectorIf(
       boolean condition, Consumer<HorizontallyMergedClassesInspector> inspector) {
-
     if (condition) {
       return addHorizontallyMergedClassesInspector(inspector);
     }
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/AbstractMethodMergingNonTrivialTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/AbstractMethodMergingNonTrivialTest.java
index 4d7d7fe..42df5c0 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/AbstractMethodMergingNonTrivialTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/AbstractMethodMergingNonTrivialTest.java
@@ -23,7 +23,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .addHorizontallyMergedClassesInspectorIf(
             enableHorizontalClassMerging, inspector -> inspector.assertMergedInto(B.class, A.class))
         .enableInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/AbstractMethodMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/AbstractMethodMergingTest.java
index cc092e9..3b0af4f 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/AbstractMethodMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/AbstractMethodMergingTest.java
@@ -23,7 +23,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .addHorizontallyMergedClassesInspectorIf(
             enableHorizontalClassMerging, inspector -> inspector.assertMergedInto(B.class, A.class))
         .enableInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/AdaptResourceFileContentsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/AdaptResourceFileContentsTest.java
index 2c4ac9d..db8d4c7 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/AdaptResourceFileContentsTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/AdaptResourceFileContentsTest.java
@@ -36,7 +36,8 @@
             .addInnerClasses(getClass())
             .addKeepMainRule(Main.class)
             .addOptionsModification(
-                options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+                options ->
+                    options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
             .addOptionsModification(options -> options.dataResourceConsumer = dataResourceConsumer)
             .enableNeverClassInliningAnnotations()
             .addDataEntryResources(
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/AdaptVerticallyMergedResourceFileContentsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/AdaptVerticallyMergedResourceFileContentsTest.java
index ea50082..0ebbb9d 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/AdaptVerticallyMergedResourceFileContentsTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/AdaptVerticallyMergedResourceFileContentsTest.java
@@ -35,7 +35,8 @@
             .addInnerClasses(getClass())
             .addKeepMainRule(Main.class)
             .addOptionsModification(
-                options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+                options ->
+                    options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
             .addOptionsModification(options -> options.dataResourceConsumer = dataResourceConsumer)
             .enableNeverClassInliningAnnotations()
             .addDataEntryResources(
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassWithInstanceFieldsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassWithInstanceFieldsTest.java
index 25bb4ec..31a49cc 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassWithInstanceFieldsTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassWithInstanceFieldsTest.java
@@ -25,7 +25,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableNeverClassInliningAnnotations()
         .enableInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistinguishedByDirectCheckCastTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistinguishedByDirectCheckCastTest.java
index 745fc26..bc5380e 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistinguishedByDirectCheckCastTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistinguishedByDirectCheckCastTest.java
@@ -24,7 +24,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistinguishedByDirectInstanceOfTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistinguishedByDirectInstanceOfTest.java
index 6a61d2f..6551ad2 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistinguishedByDirectInstanceOfTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistinguishedByDirectInstanceOfTest.java
@@ -24,7 +24,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistinguishedByIndirectCheckCastToInterfaceTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistinguishedByIndirectCheckCastToInterfaceTest.java
index 5eb49b1..6017cbe 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistinguishedByIndirectCheckCastToInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistinguishedByIndirectCheckCastToInterfaceTest.java
@@ -26,7 +26,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableNoVerticalClassMergingAnnotations()
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistuingishedByIndirectInstanceOfInterfaceCheckCast.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistuingishedByIndirectInstanceOfInterfaceCheckCast.java
index 70e7c0d..cf05da6 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistuingishedByIndirectInstanceOfInterfaceCheckCast.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistuingishedByIndirectInstanceOfInterfaceCheckCast.java
@@ -26,7 +26,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         .enableNeverClassInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithDifferentInterfacesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithDifferentInterfacesTest.java
index 3c9d066..5abdc09 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithDifferentInterfacesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithDifferentInterfacesTest.java
@@ -26,7 +26,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithDifferentVisibilityFieldsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithDifferentVisibilityFieldsTest.java
index 90e407e..68a81b1 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithDifferentVisibilityFieldsTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithDifferentVisibilityFieldsTest.java
@@ -32,7 +32,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithFeatureSplitTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithFeatureSplitTest.java
index 5907c150..9ac569b 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithFeatureSplitTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithFeatureSplitTest.java
@@ -42,7 +42,8 @@
             .addKeepFeatureMainRule(Feature1Main.class)
             .addKeepFeatureMainRule(Feature2Main.class)
             .addOptionsModification(
-                options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+                options ->
+                    options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
             .enableNeverClassInliningAnnotations()
             .setMinApi(parameters.getApiLevel())
             .compile()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithIdenticalInterfacesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithIdenticalInterfacesTest.java
index 2bab975..6714388 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithIdenticalInterfacesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithIdenticalInterfacesTest.java
@@ -25,7 +25,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithNativeMethodsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithNativeMethodsTest.java
index 5ee01b3..1f0e3e6 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithNativeMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithNativeMethodsTest.java
@@ -26,7 +26,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithOverlappingVisibilitiesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithOverlappingVisibilitiesTest.java
index 565c1bd..608f6fe 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithOverlappingVisibilitiesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithOverlappingVisibilitiesTest.java
@@ -29,7 +29,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithStaticFields.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithStaticFields.java
index f00e07e..2810ce8 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithStaticFields.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithStaticFields.java
@@ -23,7 +23,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
         .addHorizontallyMergedClassesInspectorIf(
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/CompanionClassMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/CompanionClassMergingTest.java
index b14763f..9aca9fe 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/CompanionClassMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/CompanionClassMergingTest.java
@@ -27,7 +27,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .addOptionsModification(options -> options.enableClassInlining = false)
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/CompatKeepConstructorLiveTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/CompatKeepConstructorLiveTest.java
index 9ccc2d4..c03361f 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/CompatKeepConstructorLiveTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/CompatKeepConstructorLiveTest.java
@@ -25,7 +25,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorCantInlineTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorCantInlineTest.java
index 2383a03..935e75e 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorCantInlineTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorCantInlineTest.java
@@ -27,7 +27,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingAfterUnusedArgumentRemovalTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingAfterUnusedArgumentRemovalTest.java
index 32f7454..d38efb3 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingAfterUnusedArgumentRemovalTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingAfterUnusedArgumentRemovalTest.java
@@ -22,7 +22,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
         .addHorizontallyMergedClassesInspectorIf(
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingOverlapTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingOverlapTest.java
index 185e090..8936dc1 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingOverlapTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingOverlapTest.java
@@ -32,7 +32,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
index f658909..f878fd5 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
@@ -33,7 +33,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .addHorizontallyMergedClassesInspectorIf(
             enableHorizontalClassMerging,
             inspector ->
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTest.java
index b1d77c2..21c3486 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTest.java
@@ -23,7 +23,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTrivialOverlapTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTrivialOverlapTest.java
index f6def29..50da81e 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTrivialOverlapTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTrivialOverlapTest.java
@@ -32,7 +32,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingWithArgumentsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingWithArgumentsTest.java
index a4a0fd9..7891de2 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingWithArgumentsTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingWithArgumentsTest.java
@@ -26,7 +26,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/DistinguishExceptionClassesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/DistinguishExceptionClassesTest.java
index c751735..749ae58 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/DistinguishExceptionClassesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/DistinguishExceptionClassesTest.java
@@ -22,7 +22,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutputLines("test success")
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/EmptyClassTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/EmptyClassTest.java
index 4cdca16..5d63188 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/EmptyClassTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/EmptyClassTest.java
@@ -25,7 +25,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
         .addHorizontallyMergedClassesInspectorIf(
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/FieldTypeMergedTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/FieldTypeMergedTest.java
index 616c88a..58434a9 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/FieldTypeMergedTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/FieldTypeMergedTest.java
@@ -29,7 +29,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/GenericStaticFieldTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/GenericStaticFieldTest.java
index 9b86b43..af72afe 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/GenericStaticFieldTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/GenericStaticFieldTest.java
@@ -20,7 +20,8 @@
         .addKeepMainRule(Main.class)
         .addKeepRules("-keepattributes Signatures")
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
         // .addHorizontallyMergedClassesInspectorIf(
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/IdenticalFieldMembersTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/IdenticalFieldMembersTest.java
index 6968e1a..9dae7a2 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/IdenticalFieldMembersTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/IdenticalFieldMembersTest.java
@@ -23,7 +23,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritInterfaceWithDefaultTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritInterfaceWithDefaultTest.java
index 9bdaca6..b069bb1 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritInterfaceWithDefaultTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritInterfaceWithDefaultTest.java
@@ -30,7 +30,8 @@
         .addKeepMainRule(Main.class)
         .allowStdoutMessages()
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritOverrideInterfaceTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritOverrideInterfaceTest.java
index cedee87..8c7644d 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritOverrideInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritOverrideInterfaceTest.java
@@ -24,7 +24,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritsFromLibraryClassTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritsFromLibraryClassTest.java
index cfafb12..575c2cd 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritsFromLibraryClassTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritsFromLibraryClassTest.java
@@ -30,7 +30,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/InnerOuterClassesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/InnerOuterClassesTest.java
index 075448a..98e74e4 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/InnerOuterClassesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/InnerOuterClassesTest.java
@@ -22,7 +22,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableNeverClassInliningAnnotations()
         .addKeepAttributes("InnerClasses", "EnclosingMethod")
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/InstantiatedAndUninstantiatedClassMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/InstantiatedAndUninstantiatedClassMergingTest.java
index a27fdbd..59e77dd 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/InstantiatedAndUninstantiatedClassMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/InstantiatedAndUninstantiatedClassMergingTest.java
@@ -36,7 +36,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(TestClass.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .addHorizontallyMergedClassesInspector(
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/JavaLambdaMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/JavaLambdaMergingTest.java
new file mode 100644
index 0000000..adabb3e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/JavaLambdaMergingTest.java
@@ -0,0 +1,91 @@
+// 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.classmerging.horizontal;
+
+import static com.android.tools.r8.ir.desugar.LambdaRewriter.LAMBDA_CLASS_NAME_PREFIX;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.codeinspector.VerticallyMergedClassesInspector;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.junit.Test;
+
+public class JavaLambdaMergingTest extends HorizontalClassMergingTestBase {
+
+  public JavaLambdaMergingTest(TestParameters parameters, boolean enableHorizontalClassMerging) {
+    super(parameters, enableHorizontalClassMerging);
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addOptionsModification(
+            options -> {
+              options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging);
+              assertFalse(options.horizontalClassMergerOptions().isJavaLambdaMergingEnabled());
+              options.horizontalClassMergerOptions().enableJavaLambdaMerging();
+            })
+        .addHorizontallyMergedClassesInspectorIf(
+            enableHorizontalClassMerging && parameters.isDexRuntime(),
+            inspector -> {
+              Set<DexType> lambdaSources =
+                  inspector.getSources().stream()
+                      .filter(x -> x.toSourceString().contains(LAMBDA_CLASS_NAME_PREFIX))
+                      .collect(Collectors.toSet());
+              assertEquals(3, lambdaSources.size());
+              DexType firstTarget = inspector.getTarget(lambdaSources.iterator().next());
+              for (DexType lambdaSource : lambdaSources) {
+                assertTrue(
+                    inspector
+                        .getTarget(lambdaSource)
+                        .toSourceString()
+                        .contains(LAMBDA_CLASS_NAME_PREFIX));
+                assertEquals(firstTarget, inspector.getTarget(lambdaSource));
+              }
+            })
+        .addVerticallyMergedClassesInspector(
+            VerticallyMergedClassesInspector::assertNoClassesMerged)
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("Hello world!");
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      HelloGreeter helloGreeter =
+          System.currentTimeMillis() > 0
+              ? () -> System.out.print("Hello")
+              : () -> {
+                throw new RuntimeException();
+              };
+      WorldGreeter worldGreeter =
+          System.currentTimeMillis() > 0
+              ? () -> System.out.println(" world!")
+              : () -> {
+                throw new RuntimeException();
+              };
+      helloGreeter.hello();
+      worldGreeter.world();
+    }
+  }
+
+  interface HelloGreeter {
+
+    void hello();
+  }
+
+  interface WorldGreeter {
+
+    void world();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/LargeConstructorsMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/LargeConstructorsMergingTest.java
index 847db55..a553abe 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/LargeConstructorsMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/LargeConstructorsMergingTest.java
@@ -26,7 +26,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .addOptionsModification(options -> options.testing.verificationSizeLimitInBytesOverride = 4)
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergeNonFinalAndFinalClassTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergeNonFinalAndFinalClassTest.java
index a5eb885..c8a36b6 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergeNonFinalAndFinalClassTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergeNonFinalAndFinalClassTest.java
@@ -25,7 +25,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .addHorizontallyMergedClassesInspectorIf(
             enableHorizontalClassMerging, inspector -> inspector.assertMergedInto(B.class, A.class))
         .enableNeverClassInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergePackagePrivateWithPublicClassTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergePackagePrivateWithPublicClassTest.java
index a1c8faa..04d69b1d 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergePackagePrivateWithPublicClassTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergePackagePrivateWithPublicClassTest.java
@@ -27,7 +27,8 @@
             PackagePrivateClassRunner.class, PackagePrivateClassRunner.getPrivateClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorForwardingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorForwardingTest.java
index 2256e5d..f913560 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorForwardingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorForwardingTest.java
@@ -32,7 +32,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorStackTraceTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorStackTraceTest.java
index fc4f076..a11a7d6 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorStackTraceTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorStackTraceTest.java
@@ -46,7 +46,8 @@
         .addKeepAttributeLineNumberTable()
         .addKeepAttributeSourceFile()
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableNoVerticalClassMergingAnnotations()
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
@@ -63,7 +64,9 @@
                             2,
                             StackTraceLine.builder()
                                 .setClassName(A.class.getTypeName())
-                                .setMethodName("<init>")
+                                // TODO(b/124483578): The synthetic method should not be part of the
+                                //  retraced stack trace.
+                                .setMethodName("$r8$init$bridge")
                                 .setFileName(getClass().getSimpleName() + ".java")
                                 .setLineNumber(0)
                                 .build())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedVirtualMethodStackTraceTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedVirtualMethodStackTraceTest.java
index 00d6e59..8c9d11d 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedVirtualMethodStackTraceTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedVirtualMethodStackTraceTest.java
@@ -46,7 +46,8 @@
         .addKeepAttributeSourceFile()
         .addDontWarn(C.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
@@ -64,7 +65,6 @@
                 StackTrace expectedStackTraceWithMergedMethod =
                     StackTrace.builder()
                         .add(expectedStackTrace)
-
                         .add(
                             1,
                             StackTraceLine.builder()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergingProducesFieldCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergingProducesFieldCollisionTest.java
index d25d516..34972f6 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergingProducesFieldCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergingProducesFieldCollisionTest.java
@@ -31,7 +31,8 @@
         .addProgramClassFileData(transformedC)
         .addProgramClasses(Parent.class, A.class, B.class, Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NestClassTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NestClassTest.java
index 5edc29f..b416826 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/NestClassTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NestClassTest.java
@@ -58,7 +58,8 @@
         .addKeepMainRule(examplesTypeName(BasicNestHostHorizontalClassMerging.class))
         .addExamplesProgramFiles(R.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .compile()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NoAbstractClassesWithNonAbstractClassesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NoAbstractClassesWithNonAbstractClassesTest.java
index 2bc2c97..5566f45 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/NoAbstractClassesWithNonAbstractClassesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NoAbstractClassesWithNonAbstractClassesTest.java
@@ -26,7 +26,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NoClassesOrMembersWithAnnotationsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NoClassesOrMembersWithAnnotationsTest.java
index d17fa75..74d0ad6 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/NoClassesOrMembersWithAnnotationsTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NoClassesOrMembersWithAnnotationsTest.java
@@ -31,7 +31,8 @@
         .addKeepMainRule(Main.class)
         .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableNeverClassInliningAnnotations()
         .enableInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NoHorizontalClassMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NoHorizontalClassMergingTest.java
index dc513c3..5c0e207 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/NoHorizontalClassMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NoHorizontalClassMergingTest.java
@@ -24,7 +24,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableNoHorizontalClassMergingAnnotations()
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessOnMergedClassTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessOnMergedClassTest.java
index e357250..671f740 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessOnMergedClassTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessOnMergedClassTest.java
@@ -26,7 +26,8 @@
         .addInnerClasses(NonReboundFieldAccessOnMergedClassTestClasses.class)
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .addHorizontallyMergedClassesInspector(
             inspector -> {
               if (enableHorizontalClassMerging) {
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessWithMergedTypeTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessWithMergedTypeTest.java
index 3afcfb2..b6183a3 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessWithMergedTypeTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessWithMergedTypeTest.java
@@ -27,7 +27,8 @@
         .addInnerClasses(NonReboundFieldAccessWithMergedTypeTestClasses.class)
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .addHorizontallyMergedClassesInspector(
             inspector -> {
               if (enableHorizontalClassMerging) {
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/OverlappingConstructorsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/OverlappingConstructorsTest.java
index fbd587d..c97e384 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/OverlappingConstructorsTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/OverlappingConstructorsTest.java
@@ -25,7 +25,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMemberAccessTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMemberAccessTest.java
index a1b0951..2732240 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMemberAccessTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMemberAccessTest.java
@@ -28,7 +28,8 @@
         .addProgramClasses(B.class)
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .allowAccessModification(false)
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMembersAccessedTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMembersAccessedTest.java
index cdaeee7..4d7c0c4 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMembersAccessedTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMembersAccessedTest.java
@@ -27,7 +27,8 @@
         .addProgramClasses(D.class)
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .allowAccessModification(false)
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/PinnedClassMemberReferenceTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/PinnedClassMemberReferenceTest.java
index 4a22fe3..2f01f90 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/PinnedClassMemberReferenceTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/PinnedClassMemberReferenceTest.java
@@ -28,7 +28,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .noMinification()
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/PinnedClassMemberTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/PinnedClassMemberTest.java
index 444d573..1e09a5a 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/PinnedClassMemberTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/PinnedClassMemberTest.java
@@ -23,7 +23,8 @@
         .addKeepMainRule(Main.class)
         .addKeepRules("-keepclassmembers class " + B.class.getTypeName() + " { void foo(); }")
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/PinnedClassTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/PinnedClassTest.java
index a2b36f3..0ba06e6 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/PinnedClassTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/PinnedClassTest.java
@@ -23,7 +23,8 @@
         .addKeepMainRule(Main.class)
         .addKeepClassRules(B.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/PreventMergeMainDexListTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/PreventMergeMainDexListTest.java
index 4a50200..e3e1b85 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/PreventMergeMainDexListTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/PreventMergeMainDexListTest.java
@@ -48,7 +48,7 @@
         .addMainDexListClasses(A.class, Main.class)
         .addOptionsModification(
             options -> {
-              options.enableHorizontalClassMerging = enableHorizontalClassMerging;
+              options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging);
               options.minimalMainDex = true;
             })
         .enableNeverClassInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/PreventMergeMainDexTracingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/PreventMergeMainDexTracingTest.java
index 9e81bc6..6a31a5e 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/PreventMergeMainDexTracingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/PreventMergeMainDexTracingTest.java
@@ -48,7 +48,7 @@
         .addMainDexClassRules(Main.class)
         .addOptionsModification(
             options -> {
-              options.enableHorizontalClassMerging = enableHorizontalClassMerging;
+              options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging);
               options.minimalMainDex = true;
             })
         .enableNeverClassInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/PrivateAndInterfaceMethodCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/PrivateAndInterfaceMethodCollisionTest.java
index af37199..4553469 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/PrivateAndInterfaceMethodCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/PrivateAndInterfaceMethodCollisionTest.java
@@ -25,7 +25,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .addHorizontallyMergedClassesInspector(
             HorizontallyMergedClassesInspector::assertNoClassesMerged)
         .enableInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/PrivateAndStaticMethodCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/PrivateAndStaticMethodCollisionTest.java
index df03d8a..3c7b799 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/PrivateAndStaticMethodCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/PrivateAndStaticMethodCollisionTest.java
@@ -22,7 +22,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .addHorizontallyMergedClassesInspectorIf(
             enableHorizontalClassMerging, inspector -> inspector.assertMergedInto(B.class, A.class))
         .enableInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ReferencedInAnnotationTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ReferencedInAnnotationTest.java
index 620f948..385d782 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ReferencedInAnnotationTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ReferencedInAnnotationTest.java
@@ -38,7 +38,8 @@
         .addKeepMainRule(TestClass.class)
         .addKeepClassAndMembersRules(Annotation.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .addKeepRuntimeVisibleAnnotations()
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/RemapFieldTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/RemapFieldTest.java
index 33c8a85..168d6ee 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/RemapFieldTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/RemapFieldTest.java
@@ -24,7 +24,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/RemapMethodTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/RemapMethodTest.java
index a531860..c640ffc 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/RemapMethodTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/RemapMethodTest.java
@@ -24,7 +24,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ServiceLoaderParentTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ServiceLoaderParentTest.java
index 4e89fb8..5eab9c9 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ServiceLoaderParentTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ServiceLoaderParentTest.java
@@ -38,7 +38,8 @@
                 Origin.unknown()))
         .enableNoVerticalClassMergingAnnotations()
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), Main.class)
         .assertSuccess()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ServiceLoaderTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ServiceLoaderTest.java
index ee6dc18..f9ae4bd 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ServiceLoaderTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ServiceLoaderTest.java
@@ -36,7 +36,8 @@
                 "META-INF/services/" + A.class.getTypeName(),
                 Origin.unknown()))
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), Main.class)
         .assertSuccess()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/StaticAndInterfaceMethodCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/StaticAndInterfaceMethodCollisionTest.java
index ebbfd86..e76dd447 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/StaticAndInterfaceMethodCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/StaticAndInterfaceMethodCollisionTest.java
@@ -25,7 +25,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .addHorizontallyMergedClassesInspector(
             HorizontallyMergedClassesInspector::assertNoClassesMerged)
         .enableInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/StaticAndVirtualMethodCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/StaticAndVirtualMethodCollisionTest.java
index edca180..35a15e5 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/StaticAndVirtualMethodCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/StaticAndVirtualMethodCollisionTest.java
@@ -23,7 +23,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .addHorizontallyMergedClassesInspectorIf(
             enableHorizontalClassMerging, inspector -> inspector.assertMergedInto(B.class, A.class))
         .enableInliningAnnotations()
@@ -36,10 +37,10 @@
   static class Main {
 
     public static void main(String[] args) {
-      new A().foo();
+      A.foo();
       new A().bar();
       new B().foo();
-      new B().bar();
+      B.bar();
     }
   }
 
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/SuperConstructorCallsVirtualMethodTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/SuperConstructorCallsVirtualMethodTest.java
index 119913b..ecbd84b 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/SuperConstructorCallsVirtualMethodTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/SuperConstructorCallsVirtualMethodTest.java
@@ -25,7 +25,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/SynchronizedClassesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/SynchronizedClassesTest.java
index e5c77a3..6f458b7 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/SynchronizedClassesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/SynchronizedClassesTest.java
@@ -26,7 +26,7 @@
         .addKeepMainRule(Main.class)
         .addOptionsModification(
             options -> {
-              options.enableHorizontalClassMerging = enableHorizontalClassMerging;
+              options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging);
             })
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/SyntheticConstructorArgumentsMerged.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/SyntheticConstructorArgumentsMerged.java
index 59ea0b2..6567764 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/SyntheticConstructorArgumentsMerged.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/SyntheticConstructorArgumentsMerged.java
@@ -26,7 +26,7 @@
         .addKeepMainRule(Main.class)
         .addOptionsModification(
             options -> {
-              options.enableHorizontalClassMerging = enableHorizontalClassMerging;
+              options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging);
             })
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerCollisionTest.java
index b603f66..97e41db 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerCollisionTest.java
@@ -26,7 +26,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
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 a5c5555..c7a7d9c 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
@@ -30,7 +30,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceCollisionTest.java
index f1301a0..946e113 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceCollisionTest.java
@@ -36,7 +36,8 @@
         .addKeepMainRule(Main.class)
         .noMinification()
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceFixedCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceFixedCollisionTest.java
index 2f6895a..1d3290a 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceFixedCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceFixedCollisionTest.java
@@ -38,7 +38,8 @@
         .addKeepMainRule(Main.class)
         .noMinification()
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceImplementedByParentTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceImplementedByParentTest.java
index b6a320c..afee32c 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceImplementedByParentTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceImplementedByParentTest.java
@@ -34,7 +34,8 @@
         .addKeepMainRule(Main.class)
         .noMinification()
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerSubClassCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerSubClassCollisionTest.java
index aad0efa..b68f291 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerSubClassCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerSubClassCollisionTest.java
@@ -29,7 +29,8 @@
         .addKeepMainRule(Main.class)
         .noMinification()
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticalMergingPreoptimizedTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticalMergingPreoptimizedTest.java
index 7731d5e..b1c0b4a 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticalMergingPreoptimizedTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticalMergingPreoptimizedTest.java
@@ -28,7 +28,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassDistinguishedByCheckCastTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassDistinguishedByCheckCastTest.java
index 9ecbf5a..366800a 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassDistinguishedByCheckCastTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassDistinguishedByCheckCastTest.java
@@ -23,7 +23,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassDistinguishedByInstanceOfTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassDistinguishedByInstanceOfTest.java
index bba42bb..c8ad21d 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassDistinguishedByInstanceOfTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassDistinguishedByInstanceOfTest.java
@@ -23,7 +23,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassTest.java
index e40ff51..a40b387 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassTest.java
@@ -27,7 +27,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableNoHorizontalClassMergingAnnotations()
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/VirtualMethodMergingOfFinalAndNonFinalMethodTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/VirtualMethodMergingOfFinalAndNonFinalMethodTest.java
index f9eff69..6399f3e 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/VirtualMethodMergingOfFinalAndNonFinalMethodTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/VirtualMethodMergingOfFinalAndNonFinalMethodTest.java
@@ -28,7 +28,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(TestClass.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/VirtualMethodMergingOfPublicizedMethodsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/VirtualMethodMergingOfPublicizedMethodsTest.java
index 8178138..5e23840 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/VirtualMethodMergingOfPublicizedMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/VirtualMethodMergingOfPublicizedMethodsTest.java
@@ -25,7 +25,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(TestClass.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .allowAccessModification()
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/NotOverlappingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/NotOverlappingTest.java
index 8742e67..e405aa9 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/NotOverlappingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/NotOverlappingTest.java
@@ -23,7 +23,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
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 87f4dc3..e456600 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
@@ -25,7 +25,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultMethodTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultMethodTest.java
index dd8183d..0e3a098 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultMethodTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultMethodTest.java
@@ -26,7 +26,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultOnSuperMethodTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultOnSuperMethodTest.java
index bd41f8f..fa2dcb2 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultOnSuperMethodTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultOnSuperMethodTest.java
@@ -27,7 +27,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNoUnusedInterfaceRemovalAnnotations()
         .enableNoVerticalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideMergeAbsentTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideMergeAbsentTest.java
index fe0bc23..526aa00 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideMergeAbsentTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideMergeAbsentTest.java
@@ -25,7 +25,8 @@
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideParentCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideParentCollisionTest.java
index 94b643a..639cf19 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideParentCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideParentCollisionTest.java
@@ -27,7 +27,8 @@
         .addInnerClasses(this.getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/SuperMethodMergedTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/SuperMethodMergedTest.java
index 228223f..44973bc 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/SuperMethodMergedTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/SuperMethodMergedTest.java
@@ -26,7 +26,8 @@
         .addInnerClasses(this.getClass())
         .addKeepMainRule(Main.class)
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineSynthesizedLambdaClass.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineSynthesizedLambdaClass.java
index 117a18c..1bd8db6 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineSynthesizedLambdaClass.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineSynthesizedLambdaClass.java
@@ -23,7 +23,7 @@
 
   @Parameterized.Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withDexRuntimes().build();
+    return getTestParameters().withDexRuntimes().withAllApiLevels().build();
   }
 
   public InlineSynthesizedLambdaClass(TestParameters parameters) {
@@ -42,7 +42,7 @@
             .addKeepMainRule(Lambda.class)
             .allowAccessModification()
             .noMinification()
-            .setMinApi(parameters.getRuntime())
+            .setMinApi(parameters.getApiLevel())
             .run(parameters.getRuntime(), Lambda.class)
             .assertSuccessWithOutput(javaOutput)
             .inspector();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/lambda/LambdaMethodInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/lambda/LambdaMethodInliningTest.java
index 8bdea50..a63addb 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/lambda/LambdaMethodInliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/lambda/LambdaMethodInliningTest.java
@@ -43,6 +43,8 @@
         .enableInliningAnnotations()
         .enableNoVerticalClassMergingAnnotations()
         .addOptionsModification(options -> options.enableClassInlining = false)
+        // TODO(b/173398086): Horizontal class merging breaks uniqueMethodWithName().
+        .noMinification()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(this::inspect)
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
index 0f5b195..a949213 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
@@ -335,7 +335,7 @@
                       // condition.
                       options.testing.addCallEdgesForLibraryInvokes = true;
 
-                      options.enableHorizontalClassMergingOfKotlinLambdas = false;
+                      options.horizontalClassMergerOptions().disableKotlinLambdaMerging();
                     })
                 .apply(configuration));
   }
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTest.java
index 08f4398..b9e0546 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTest.java
@@ -42,7 +42,7 @@
     // Ensure that enclosing method and inner class attributes are kept even on classes that are
     // not explicitly mentioned by a keep rule.
     options.forceProguardCompatibility = true;
-    options.enableHorizontalClassMergingOfKotlinLambdas = false;
+    options.horizontalClassMergerOptions().disableKotlinLambdaMerging();
   }
 
   private final boolean enableUnusedInterfaceRemoval;
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/b148525512/B148525512.java b/src/test/java/com/android/tools/r8/kotlin/lambda/b148525512/B148525512.java
index 1f73d0e..0dd8a62 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/b148525512/B148525512.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/b148525512/B148525512.java
@@ -126,7 +126,7 @@
             .addKeepClassAndMembersRules(featureKtClassNamet)
             .addKeepClassAndMembersRules(FeatureAPI.class)
             .addOptionsModification(
-                options -> options.enableHorizontalClassMergingOfKotlinLambdas = false)
+                options -> options.horizontalClassMergerOptions().disableKotlinLambdaMerging())
             .setMinApi(parameters.getApiLevel())
             .noMinification() // The check cast inspection above relies on original names.
             .addFeatureSplit(
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 323b533..57cdae7 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
@@ -8,15 +8,17 @@
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.R8FullTestBuilder;
 import com.android.tools.r8.R8TestCompileResult;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.InternalOptions.HorizontalClassMergerOptions;
 import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
 import java.io.IOException;
 import java.nio.file.Path;
@@ -32,16 +34,20 @@
 @RunWith(Parameterized.class)
 public class LambdaGroupGCLimitTest extends TestBase {
 
+  private final boolean enableHorizontalClassMergingOfKotlinLambdas;
   private final TestParameters parameters;
   private final int LAMBDA_HOLDER_LIMIT = 50;
   private final int LAMBDAS_PER_CLASS_LIMIT = 100;
 
-  @Parameters(name = "{0}")
-  public static TestParametersCollection data() {
-    return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+  @Parameters(name = "{1}, horizontal class merging: {0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
   }
 
-  public LambdaGroupGCLimitTest(TestParameters parameters) {
+  public LambdaGroupGCLimitTest(
+      boolean enableHorizontalClassMergingOfKotlinLambdas, TestParameters parameters) {
+    this.enableHorizontalClassMergingOfKotlinLambdas = enableHorizontalClassMergingOfKotlinLambdas;
     this.parameters = parameters;
   }
 
@@ -51,6 +57,11 @@
     R8FullTestBuilder testBuilder =
         testForR8(parameters.getBackend())
             .addProgramFiles(ToolHelper.getKotlinStdlibJar(ToolHelper.getKotlinC_1_3_72()))
+            .addOptionsModification(
+                options ->
+                    options
+                        .horizontalClassMergerOptions()
+                        .enableKotlinLambdaMergingIf(enableHorizontalClassMergingOfKotlinLambdas))
             .setMinApi(parameters.getApiLevel())
             .noMinification();
     Path classFiles = temp.newFile("classes.jar").toPath();
@@ -63,7 +74,27 @@
       testBuilder.addKeepClassAndMembersRules(PKG_NAME + ".MainKt" + mainId);
     }
     writeClassFileDataToJar(classFiles, classFileData);
-    R8TestCompileResult compileResult = testBuilder.addProgramFiles(classFiles).compile();
+    R8TestCompileResult compileResult =
+        testBuilder
+            .addProgramFiles(classFiles)
+            .addHorizontallyMergedClassesInspector(
+                inspector -> {
+                  if (enableHorizontalClassMergingOfKotlinLambdas) {
+                    HorizontalClassMergerOptions defaultHorizontalClassMergerOptions =
+                        new HorizontalClassMergerOptions();
+                    assertEquals(4833, inspector.getSources().size());
+                    assertEquals(167, inspector.getTargets().size());
+                    assertTrue(
+                        inspector.getMergeGroups().stream()
+                            .allMatch(
+                                mergeGroup ->
+                                    mergeGroup.size()
+                                        <= defaultHorizontalClassMergerOptions.getMaxGroupSize()));
+                  } else {
+                    inspector.assertNoClassesMerged();
+                  }
+                })
+            .compile();
     Path path = compileResult.writeToZip();
     compileResult
         .run(parameters.getRuntime(), PKG_NAME + ".MainKt0")
@@ -74,7 +105,9 @@
                   codeInspector.allClasses().stream()
                       .filter(c -> c.getFinalName().contains("LambdaGroup"))
                       .collect(Collectors.toList());
-              assertEquals(1, lambdaGroups.size());
+              assertEquals(
+                  1 - BooleanUtils.intValue(enableHorizontalClassMergingOfKotlinLambdas),
+                  lambdaGroups.size());
             });
     Path oatFile = temp.newFile("out.oat").toPath();
     ProcessResult processResult =
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java
index 4d3f823..c261c25 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java
@@ -67,6 +67,8 @@
     testForR8(parameters.getBackend())
         .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinc))
         .addProgramFiles(ktClasses)
+        .addOptionsModification(
+            options -> options.horizontalClassMergerOptions().disableKotlinLambdaMerging())
         .setMinApi(parameters.getApiLevel())
         .addKeepMainRule(PKG_NAME + ".SimpleKt")
         .applyIf(
@@ -75,8 +77,7 @@
                 b.addOptionsModification(
                     internalOptions ->
                         // Setting verificationSizeLimitInBytesOverride = 1 will force a a chain
-                        // having
-                        // only a single implementation method in each.
+                        // having only a single implementation method in each.
                         internalOptions.testing.verificationSizeLimitInBytesOverride =
                             splitGroup ? 1 : -1))
         .noMinification()
diff --git a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationTest.java b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationTest.java
index 4f65b48..1c7f6ac 100644
--- a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationTest.java
@@ -157,7 +157,8 @@
         .addKeepMainRule(MAIN)
         .addKeepRules(config.getKeepRules())
         .addOptionsModification(
-            options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+            options ->
+                options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging))
         .enableInliningAnnotations()
         .noMinification()
         .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
index 37dbe19..7cfc3fe 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
@@ -12,6 +12,8 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses;
+import com.android.tools.r8.utils.SetUtils;
+import com.google.common.collect.Sets;
 import java.util.Set;
 import java.util.function.BiConsumer;
 
@@ -30,10 +32,29 @@
     horizontallyMergedClasses.forEachMergeGroup(consumer);
   }
 
+  public Set<Set<DexType>> getMergeGroups() {
+    Set<Set<DexType>> mergeGroups = Sets.newLinkedHashSet();
+    forEachMergeGroup(
+        (sources, target) -> {
+          Set<DexType> mergeGroup = SetUtils.newIdentityHashSet(sources);
+          mergeGroup.add(target);
+          mergeGroups.add(mergeGroup);
+        });
+    return mergeGroups;
+  }
+
+  public Set<DexType> getSources() {
+    return horizontallyMergedClasses.getSources();
+  }
+
   public DexType getTarget(DexType clazz) {
     return horizontallyMergedClasses.getMergeTargetOrDefault(clazz);
   }
 
+  public Set<DexType> getTargets() {
+    return horizontallyMergedClasses.getTargets();
+  }
+
   public HorizontallyMergedClassesInspector assertMergedInto(Class<?> from, Class<?> target) {
     assertEquals(
         horizontallyMergedClasses.getMergeTargetOrDefault(toDexType(from, dexItemFactory)),