Introduce LIR prior to vertical class merging

Fixes: b/312890994
Change-Id: I49cad56df9bbd6f5fc8b5ab360b5413db4bf6539
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 056ebd3..797a842 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -497,6 +497,9 @@
           .setMustRetargetInvokesToTargetMethod()
           .run(executorService, timing);
 
+      // TODO(b/225838009): Move higher up.
+      LirConverter.enterLirSupportedPhase(appView, executorService);
+
       BridgeHoistingToSharedSyntheticSuperClass.run(appViewWithLiveness, executorService, timing);
 
       assert ArtProfileCompletenessChecker.verify(appView);
@@ -504,9 +507,6 @@
       VerticalClassMerger.createForInitialClassMerging(appViewWithLiveness, timing)
           .runIfNecessary(executorService, timing);
 
-      // TODO(b/225838009): Horizontal merging currently assumes pre-phase CF conversion.
-      LirConverter.enterLirSupportedPhase(appView, executorService);
-
       // Clear traced methods roots to not hold on to the main dex live method set.
       appView.appInfo().getMainDexInfo().clearTracedMethodRoots();
 
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 53dc227..0db2480 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -112,9 +112,6 @@
     mergeInstanceInitializers(classMergerSharedData, profileCollectionAdditions);
     mergeStaticClassInitializers(syntheticInitializerConverterBuilder);
     group.forEach(this::mergeDirectMethods);
-    if (classInitializerMerger.size() > 1 && classInitializerMerger.isTrivialMerge()) {
-      classInitializerMerger.setObsolete();
-    }
     instanceInitializerMergers.setObsolete();
   }
 
@@ -146,7 +143,7 @@
         DexEncodedMethod.syntheticBuilder()
             .setMethod(newMethodReference)
             .setAccessFlags(MethodAccessFlags.createForClassInitializer())
-            .setCode(classInitializerMerger.getCode(newMethodReference))
+            .setCode(classInitializerMerger.getCode())
             .setClassFileVersion(classInitializerMerger.getCfVersion(appView.options()))
             .setApiLevelForDefinition(apiReferenceLevel)
             .setApiLevelForCode(apiReferenceLevel)
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ClassInitializerMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ClassInitializerMerger.java
index 21e4584..adf8f89 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ClassInitializerMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ClassInitializerMerger.java
@@ -4,18 +4,11 @@
 
 package com.android.tools.r8.horizontalclassmerging.code;
 
-import static java.lang.Integer.max;
 
 import com.android.tools.r8.androidapi.ComputedApiLevel;
 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.CfPosition;
-import com.android.tools.r8.cf.code.CfReturnVoid;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.ClasspathMethod;
 import com.android.tools.r8.graph.Code;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -38,13 +31,10 @@
 import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
 import com.android.tools.r8.utils.CfVersionUtils;
 import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.IterableUtils;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.RetracerForCodePrinting;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Sets;
-import java.util.ArrayList;
-import java.util.List;
 import java.util.ListIterator;
 import java.util.Set;
 
@@ -75,14 +65,7 @@
     return classInitializers.isEmpty();
   }
 
-  public Code getCode(DexMethod syntheticMethodReference) {
-    assert !isEmpty();
-    if (isTrivialMerge()) {
-      assert IterableUtils.allIdentical(
-          classInitializers,
-          classInitializer -> classInitializer.getDefinition().getCode().isCfCode());
-      return new CfCodeBuilder().build(syntheticMethodReference);
-    }
+  public Code getCode() {
     return new IRProvider(classInitializers);
   }
 
@@ -105,11 +88,6 @@
     return definition.toTypeSubstitutedMethodAsInlining(newReference, dexItemFactory);
   }
 
-  public boolean isTrivialMerge() {
-    ProgramMethod firstClassInitializer = ListUtils.first(classInitializers);
-    return firstClassInitializer.getDefinition().getCode().isCfCode();
-  }
-
   public ComputedApiLevel getApiReferenceLevel(AppView<?> appView) {
     assert !classInitializers.isEmpty();
     return ListUtils.fold(
@@ -141,67 +119,6 @@
     }
   }
 
-  /** Concatenates a collection of class initializers with CF code into a new piece of CF code. */
-  private class CfCodeBuilder {
-
-    private int maxStack = 0;
-    private int maxLocals = 0;
-
-    public CfCode build(DexMethod syntheticMethodReference) {
-      // Building the instructions will adjust maxStack and maxLocals. Build it here before invoking
-      // the CfCode constructor to ensure that the value passed in is the updated values.
-      Position callerPosition =
-          SyntheticPosition.builder()
-              .setLine(0)
-              .setMethod(syntheticMethodReference)
-              .setIsD8R8Synthesized(true)
-              .build();
-      List<CfInstruction> instructions = buildInstructions(callerPosition);
-      return new CfCode(
-          syntheticMethodReference.getHolderType(), maxStack, maxLocals, instructions);
-    }
-
-    private List<CfInstruction> buildInstructions(Position callerPosition) {
-      List<CfInstruction> newInstructions = new ArrayList<>();
-      classInitializers.forEach(
-          classInitializer -> addCfCode(newInstructions, classInitializer, callerPosition));
-      newInstructions.add(new CfReturnVoid());
-      return newInstructions;
-    }
-
-    private void addCfCode(
-        List<CfInstruction> newInstructions, ProgramMethod method, Position callerPosition) {
-      CfCode code = method.getDefinition().getCode().asCfCode();
-      maxStack = max(maxStack, code.getMaxStack());
-      maxLocals = max(maxLocals, code.getMaxLocals());
-      CfLabel endLabel = new CfLabel();
-      boolean requiresLabel = false;
-      int index = 1;
-      boolean calleeD8R8Synthesized = method.getDefinition().isD8R8Synthesized();
-      for (CfInstruction instruction : code.getInstructions()) {
-        if (instruction.isPosition()) {
-          CfPosition calleePosition = instruction.asPosition();
-          newInstructions.add(
-              new CfPosition(
-                  calleePosition.getLabel(),
-                  Code.newInlineePosition(
-                      callerPosition, calleePosition.getPosition(), calleeD8R8Synthesized)));
-        } else 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);
-      }
-    }
-  }
-
   /**
    * Provides a piece of {@link IRCode} that is the concatenation of a collection of class
    * initializers.
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LirConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/LirConverter.java
index 43f1e55..f168d4e 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LirConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LirConverter.java
@@ -47,16 +47,10 @@
     ThreadUtils.processItems(
         appView.appInfo().classes(),
         clazz -> {
-          // TODO(b/225838009): Also convert instance initializers to LIR, by adding support for
-          //  computing the inlining constraint for LIR and using that in the class mergers, and
-          //  class initializers, by updating the concatenation of clinits in horizontal class
-          //  merging.
           clazz.forEachProgramMethodMatching(
-              method ->
-                  method.hasCode()
-                      && !method.isInstanceInitializer()
-                      && !appView.isCfByteCodePassThrough(method),
+              method -> method.hasCode() && !appView.isCfByteCodePassThrough(method),
               method -> {
+                assert !method.getDefinition().getCode().hasExplicitCodeLens();
                 IRCode code = method.buildIR(appView, MethodConversionOptions.forLirPhase(appView));
                 constResourceNumberRewriter.run(code, Timing.empty());
                 LirCode<Integer> lirCode =
@@ -65,25 +59,6 @@
                         BytecodeMetadataProvider.empty(),
                         LirStrategy.getDefaultStrategy().getEncodingStrategy(),
                         appView.options());
-                // TODO(b/312890994): Setting a custom code lens is only needed until we convert
-                //  code objects to LIR before we create the first code object with a custom code
-                //  lens (horizontal class merging).
-                GraphLens codeLens = method.getDefinition().getCode().getCodeLens(appView);
-                if (codeLens != appView.codeLens()) {
-                  lirCode =
-                      new LirCode<>(lirCode) {
-
-                        @Override
-                        public boolean hasExplicitCodeLens() {
-                          return true;
-                        }
-
-                        @Override
-                        public GraphLens getCodeLens(AppView<?> appView) {
-                          return codeLens;
-                        }
-                      };
-                }
                 method.setCode(lirCode, appView);
               });
         },
diff --git a/src/main/java/com/android/tools/r8/optimize/BridgeHoistingToSharedSyntheticSuperClass.java b/src/main/java/com/android/tools/r8/optimize/BridgeHoistingToSharedSyntheticSuperClass.java
index b4036b7..125f719 100644
--- a/src/main/java/com/android/tools/r8/optimize/BridgeHoistingToSharedSyntheticSuperClass.java
+++ b/src/main/java/com/android/tools/r8/optimize/BridgeHoistingToSharedSyntheticSuperClass.java
@@ -84,6 +84,7 @@
       updateArtProfiles(groups);
       new BridgeHoisting(appView).run(executorService, timing);
     }
+    appView.dexItemFactory().clearTypeElementsCache();
   }
 
   /** Returns the set of (non-singleton) groups that have the same superclass. */
diff --git a/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoisting.java b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoisting.java
index 7503fa5..8e8fd00 100644
--- a/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoisting.java
+++ b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoisting.java
@@ -5,19 +5,10 @@
 
 import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
 
-import com.android.tools.r8.cf.code.CfInstruction;
-import com.android.tools.r8.cf.code.CfInvoke;
-import com.android.tools.r8.dex.code.DexInstruction;
-import com.android.tools.r8.dex.code.DexInvokeVirtual;
-import com.android.tools.r8.dex.code.DexInvokeVirtualRange;
-import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.BottomUpClassHierarchyTraversal;
-import com.android.tools.r8.graph.CfCode;
-import com.android.tools.r8.graph.Code;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexClassAndMember;
-import com.android.tools.r8.graph.DexCode;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
@@ -333,22 +324,9 @@
     return false;
   }
 
-  private Code createCodeForVirtualBridge(ProgramMethod representative, DexMethod methodToInvoke) {
-    Code code = representative.getDefinition().getCode();
-    if (code.isLirCode()) {
-      return createLirCodeForVirtualBridge(code.asLirCode(), methodToInvoke);
-    }
-    if (code.isCfCode()) {
-      return createCfCodeForVirtualBridge(code.asCfCode(), methodToInvoke);
-    }
-    if (code.isDexCode()) {
-      return createDexCodeForVirtualBridge(code.asDexCode(), methodToInvoke);
-    }
-    throw new Unreachable("Unexpected code object of type " + code.getClass().getTypeName());
-  }
-
-  private LirCode<Integer> createLirCodeForVirtualBridge(
-      LirCode<Integer> code, DexMethod methodToInvoke) {
+  private LirCode<Integer> createCodeForVirtualBridge(
+      ProgramMethod representative, DexMethod methodToInvoke) {
+    LirCode<Integer> code = representative.getDefinition().getCode().asLirCode();
     return code.newCodeWithRewrittenConstantPool(
         item -> {
           if (item instanceof DexMethod) {
@@ -358,61 +336,4 @@
           return item;
         });
   }
-
-  @SuppressWarnings("ReferenceEquality")
-  private CfCode createCfCodeForVirtualBridge(CfCode code, DexMethod methodToInvoke) {
-    List<CfInstruction> newInstructions = new ArrayList<>();
-    boolean modified = false;
-    for (CfInstruction instruction : code.getInstructions()) {
-      if (instruction.isInvoke() && instruction.asInvoke().getMethod() != methodToInvoke) {
-        CfInvoke invoke = instruction.asInvoke();
-        assert invoke.isInvokeVirtual();
-        assert !invoke.isInterface();
-        assert invoke.getMethod().match(methodToInvoke);
-        newInstructions.add(new CfInvoke(invoke.getOpcode(), methodToInvoke, false));
-        modified = true;
-      } else {
-        newInstructions.add(instruction);
-      }
-    }
-    return modified
-        ? new CfCode(
-            methodToInvoke.holder,
-            code.getMaxStack(),
-            code.getMaxLocals(),
-            newInstructions,
-            code.getTryCatchRanges(),
-            code.getLocalVariables())
-        : code;
-  }
-
-  @SuppressWarnings("ReferenceEquality")
-  private DexCode createDexCodeForVirtualBridge(DexCode code, DexMethod methodToInvoke) {
-    DexInstruction[] newInstructions = new DexInstruction[code.instructions.length];
-    boolean modified = false;
-    for (int i = 0; i < code.instructions.length; i++) {
-      DexInstruction instruction = code.instructions[i];
-      if (instruction.isInvokeVirtual()
-          && instruction.asInvokeVirtual().getMethod() != methodToInvoke) {
-        DexInvokeVirtual invoke = instruction.asInvokeVirtual();
-        DexInvokeVirtual newInvoke =
-            new DexInvokeVirtual(
-                invoke.A, methodToInvoke, invoke.C, invoke.D, invoke.E, invoke.F, invoke.G);
-        newInvoke.setOffset(invoke.getOffset());
-        newInstructions[i] = newInvoke;
-        modified = true;
-      } else if (instruction.isInvokeVirtualRange()
-          && instruction.asInvokeVirtualRange().getMethod() != methodToInvoke) {
-        DexInvokeVirtualRange invoke = instruction.asInvokeVirtualRange();
-        DexInvokeVirtualRange newInvoke =
-            new DexInvokeVirtualRange(invoke.CCCC, invoke.AA, methodToInvoke);
-        newInvoke.setOffset(invoke.getOffset());
-        modified = true;
-        newInstructions[i] = newInvoke;
-      } else {
-        newInstructions[i] = instruction;
-      }
-    }
-    return modified ? code.withNewInstructions(newInstructions) : code;
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/IncompleteVerticalClassMergerBridgeCode.java b/src/main/java/com/android/tools/r8/verticalclassmerging/IncompleteVerticalClassMergerBridgeCode.java
index e732306..d8856f9 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/IncompleteVerticalClassMergerBridgeCode.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/IncompleteVerticalClassMergerBridgeCode.java
@@ -6,9 +6,9 @@
 import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
 import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
 
+import com.android.tools.r8.classmerging.ClassMergerMode;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.ClasspathMethod;
 import com.android.tools.r8.graph.Code;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -17,12 +17,12 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.graph.lens.GraphLens;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.InvokeType;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
-import com.android.tools.r8.ir.synthetic.ForwardMethodBuilder;
 import com.android.tools.r8.lightir.LirBuilder;
 import com.android.tools.r8.lightir.LirCode;
 import com.android.tools.r8.lightir.LirEncodingStrategy;
@@ -33,8 +33,8 @@
 import java.util.List;
 
 /**
- * A short-lived piece of code that will be converted into {@link CfCode} using {@link
- * #toCfCode(DexItemFactory)} or {@link LirCode} using {@link #toLirCode(AppView)}.
+ * A short-lived piece of code that will be converted into {@link LirCode} using {@link
+ * #toLirCode(AppView)}.
  */
 public class IncompleteVerticalClassMergerBridgeCode extends Code {
 
@@ -87,18 +87,10 @@
     method = lens.getNextBridgeMethodSignature(method);
   }
 
-  public CfCode toCfCode(DexItemFactory dexItemFactory, VerticalClassMergerGraphLens lens) {
-    return ForwardMethodBuilder.builder(dexItemFactory)
-        .applyIf(
-            type.isStatic(),
-            builder -> builder.setStaticTarget(invocationTarget, isInterface),
-            builder -> builder.setVirtualTarget(invocationTarget, isInterface))
-        .setNonStaticSource(method)
-        .setCodeLens(lens)
-        .build();
-  }
-
-  public LirCode<?> toLirCode(AppView<AppInfoWithLiveness> appView) {
+  public LirCode<?> toLirCode(
+      AppView<AppInfoWithLiveness> appView,
+      VerticalClassMergerGraphLens lens,
+      ClassMergerMode mode) {
     boolean isD8R8Synthesized = true;
     LirEncodingStrategy<Value, Integer> strategy =
         LirStrategy.getDefaultStrategy().getEncodingStrategy();
@@ -136,7 +128,21 @@
       lirBuilder.addReturn(returnValue);
     }
 
-    return lirBuilder.build();
+    LirCode<Integer> lirCode = lirBuilder.build();
+    return mode.isFinal()
+        ? lirCode
+        : new LirCode<>(lirCode) {
+
+          @Override
+          public boolean hasExplicitCodeLens() {
+            return true;
+          }
+
+          @Override
+          public GraphLens getCodeLens(AppView<?> appView) {
+            return lens;
+          }
+        };
   }
 
   // Implement Code.
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/InvokeSpecialToDefaultLibraryMethodUseRegistry.java b/src/main/java/com/android/tools/r8/verticalclassmerging/InvokeSpecialToDefaultLibraryMethodUseRegistry.java
index 9dc57d5..837e3ac 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/InvokeSpecialToDefaultLibraryMethodUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/InvokeSpecialToDefaultLibraryMethodUseRegistry.java
@@ -1,6 +1,5 @@
 package com.android.tools.r8.verticalclassmerging;
 
-import com.android.tools.r8.classmerging.ClassMergerMode;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DefaultUseRegistryWithResult;
 import com.android.tools.r8.graph.DexEncodedMethod;
@@ -11,26 +10,19 @@
 public class InvokeSpecialToDefaultLibraryMethodUseRegistry
     extends DefaultUseRegistryWithResult<Boolean, ProgramMethod> {
 
-  private final ClassMergerMode mode;
-
   public InvokeSpecialToDefaultLibraryMethodUseRegistry(
-      AppView<AppInfoWithLiveness> appView, ProgramMethod context, ClassMergerMode mode) {
+      AppView<AppInfoWithLiveness> appView, ProgramMethod context) {
     super(appView, context, false);
-    this.mode = mode;
     assert context.getHolder().isInterface();
   }
 
   @Override
   public void registerInvokeSpecial(DexMethod method) {
-    assert mode.isInitial();
-    handleInvokeSpecial(method);
+    assert false;
   }
 
-  // Handle invoke-super callbacks in the final round of class merging where we have LIR instead of
-  // CF.
   @Override
   public void registerInvokeSuper(DexMethod method) {
-    assert mode.isFinal();
     handleInvokeSpecial(method);
   }
 
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/InvokeSuperExtractor.java b/src/main/java/com/android/tools/r8/verticalclassmerging/InvokeSuperExtractor.java
index b0770ee..73c7272 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/InvokeSuperExtractor.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/InvokeSuperExtractor.java
@@ -39,20 +39,17 @@
     this.source = source;
   }
 
-  // TODO(b/323022702): This should not need to be overridden, but we sometimes (correctly) map an
-  //  invoke-special to invoke-direct, but then later decides to map the same invoke-special to
-  //  invoke-super.
   @Override
   public void registerInvokeSpecial(DexMethod method) {
-    handleInvokeSpecial(method);
+    assert false;
   }
 
   @Override
   public void registerInvokeSuper(DexMethod method) {
-    handleInvokeSpecial(method);
+    handleInvokeSuper(method);
   }
 
-  private void handleInvokeSpecial(DexMethod method) {
+  private void handleInvokeSuper(DexMethod method) {
     MethodLookupResult lookupResult = graphLens.lookupInvokeSuper(method, getContext(), codeLens);
     DexMethod rewrittenMethod = lookupResult.getReference();
     if (!methodsOfInterest.contains(rewrittenMethod) || result.contains(rewrittenMethod)) {
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java
index 390443d..4b16a1c 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java
@@ -146,7 +146,7 @@
     // Remove merged classes from app now that the code is fully rewritten.
     removeMergedClasses(verticalClassMergerResult.getVerticallyMergedClasses(), timing);
 
-    // Convert the (incomplete) synthesized bridges to CF or LIR.
+    // Convert the (incomplete) synthesized bridges to LIR.
     finalizeSynthesizedBridges(verticalClassMergerResult.getSynthesizedBridges(), lens, timing);
 
     // Finally update the code lens to signal that the code is fully up to date.
@@ -180,7 +180,7 @@
     TimingMerger merger = timing.beginMerger("Compute classes to merge", executorService);
     List<ConnectedComponentVerticalClassMerger> connectedComponentMergers =
         new ArrayList<>(connectedComponents.size());
-    Collection<Policy> policies = VerticalClassMergerPolicyScheduler.getPolicies(appView, mode);
+    Collection<Policy> policies = VerticalClassMergerPolicyScheduler.getPolicies(appView);
     Collection<Timing> timings =
         ThreadUtils.processItemsWithResults(
             connectedComponents,
@@ -324,11 +324,7 @@
       assert target != null;
 
       // Finalize code.
-      assert mode.isInitial() == appView.testing().isPreLirPhase();
-      assert mode.isFinal() == appView.testing().isSupportedLirPhase();
-      bridge.setCode(
-          mode.isInitial() ? code.toCfCode(dexItemFactory, lens) : code.toLirCode(appView),
-          appView);
+      bridge.setCode(code.toLirCode(appView, lens, mode), appView);
 
       // Copy keep info to newly synthesized methods.
       keepInfo.mutate(
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerPolicyScheduler.java b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerPolicyScheduler.java
index 6d58316..e50986a 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerPolicyScheduler.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMergerPolicyScheduler.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.verticalclassmerging;
 
-import com.android.tools.r8.classmerging.ClassMergerMode;
 import com.android.tools.r8.classmerging.Policy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -34,8 +33,7 @@
 
 public class VerticalClassMergerPolicyScheduler {
 
-  public static List<Policy> getPolicies(
-      AppView<AppInfoWithLiveness> appView, ClassMergerMode mode) {
+  public static List<Policy> getPolicies(AppView<AppInfoWithLiveness> appView) {
     return List.of(
         new NoDirectlyInstantiatedClassesPolicy(appView),
         new NoInterfacesWithUnknownSubtypesPolicy(appView),
@@ -55,7 +53,7 @@
         new NoMethodResolutionChangesPolicy(appView),
         new NoIllegalAccessesPolicy(appView),
         new NoClassInitializationChangesPolicy(appView),
-        new NoInterfacesWithInvokeSpecialToDefaultMethodIntoClassPolicy(appView, mode),
+        new NoInterfacesWithInvokeSpecialToDefaultMethodIntoClassPolicy(appView),
         new NoInvokeSuperNoSuchMethodErrorsPolicy(appView),
         new SuccessfulVirtualMethodResolutionInTargetPolicy(appView),
         new NoAbstractMethodsOnAbstractClassesPolicy(appView),
diff --git a/src/main/java/com/android/tools/r8/verticalclassmerging/policies/NoInterfacesWithInvokeSpecialToDefaultMethodIntoClassPolicy.java b/src/main/java/com/android/tools/r8/verticalclassmerging/policies/NoInterfacesWithInvokeSpecialToDefaultMethodIntoClassPolicy.java
index 8f36d05..13b1342 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/policies/NoInterfacesWithInvokeSpecialToDefaultMethodIntoClassPolicy.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/policies/NoInterfacesWithInvokeSpecialToDefaultMethodIntoClassPolicy.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.verticalclassmerging.policies;
 
-import com.android.tools.r8.classmerging.ClassMergerMode;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -15,12 +14,10 @@
     extends VerticalClassMergerPolicy {
 
   private final AppView<AppInfoWithLiveness> appView;
-  private final ClassMergerMode mode;
 
   public NoInterfacesWithInvokeSpecialToDefaultMethodIntoClassPolicy(
-      AppView<AppInfoWithLiveness> appView, ClassMergerMode mode) {
+      AppView<AppInfoWithLiveness> appView) {
     this.appView = appView;
-    this.mode = mode;
   }
 
   @Override
@@ -37,7 +34,7 @@
             method -> {
               boolean foundInvokeSpecialToDefaultLibraryMethod =
                   method.registerCodeReferencesWithResult(
-                      new InvokeSpecialToDefaultLibraryMethodUseRegistry(appView, method, mode));
+                      new InvokeSpecialToDefaultLibraryMethodUseRegistry(appView, method));
               return TraversalContinuation.breakIf(foundInvokeSpecialToDefaultLibraryMethod);
             });
     return result.shouldContinue();
diff --git a/src/test/java/com/android/tools/r8/debuginfo/NoLineInfoTest.java b/src/test/java/com/android/tools/r8/debuginfo/NoLineInfoTest.java
index 1e47370..2491789 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/NoLineInfoTest.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/NoLineInfoTest.java
@@ -23,6 +23,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
 public class NoLineInfoTest extends TestBase {
@@ -35,7 +36,7 @@
   private final TestParameters parameters;
   private final boolean customSourceFile;
 
-  @Parameterized.Parameters(name = "{0}, custom-sf:{1}")
+  @Parameters(name = "{0}, custom-sf:{1}")
   public static List<Object[]> data() {
     return buildParameters(
         getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
@@ -113,7 +114,7 @@
                   // If debug info remains it is two canonical items and one null pointer.
                   // The presence of 'null' debug info items is for methods with no actual lines at
                   // all.
-                  assertEquals(3, debugInfos.size());
+                  assertEquals(customSourceFile ? 2 : 3, debugInfos.size());
                 }
               }
             })
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceCompanionWithPreambleTest.java b/src/test/java/com/android/tools/r8/retrace/RetraceCompanionWithPreambleTest.java
index eb44cbd..350ece1 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceCompanionWithPreambleTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceCompanionWithPreambleTest.java
@@ -8,6 +8,7 @@
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.MatcherAssert.assertThat;
 
+import com.android.tools.r8.NoVerticalClassMerging;
 import com.android.tools.r8.SingleTestRunResult;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
@@ -81,6 +82,7 @@
         .addKeepMainRule(Main.class)
         .addKeepAttributeSourceFile()
         .addKeepAttributeLineNumberTable()
+        .enableNoVerticalClassMergingAnnotations()
         .setMinApi(parameters)
         .run(parameters.getRuntime(), Main.class)
         .apply(this::checkRunResult)
@@ -129,6 +131,8 @@
         .transform();
   }
 
+  // TODO(b/326461837): Enable vertical class merging for I.
+  @NoVerticalClassMerging
   interface I {
 
     default void foo() {