Extend the use of LIR past horizontal class merging

Change-Id: I251f28dc2e20bd084840bde35d7e3446520a3aa9
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index f9694e5..ecb29de 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -730,7 +730,7 @@
 
       GenericSignatureContextBuilder genericContextBuilderBeforeFinalMerging = null;
       if (appView.hasCfByteCodePassThroughMethods()) {
-        LirConverter.finalizeLirToOutputFormat(appView, timing, executorService);
+        LirConverter.rewriteLirWithLens(appView, timing, executorService);
       } else {
         // Perform repackaging.
         if (appView.hasLiveness()) {
@@ -743,16 +743,14 @@
         // Rewrite LIR with lens to allow building IR from LIR in class mergers.
         LirConverter.rewriteLirWithLens(appView, timing, executorService);
         appView.clearCodeRewritings(executorService, timing);
+        assert appView.dexItemFactory().verifyNoCachedTypeElements();
 
         if (appView.hasLiveness()) {
           VerticalClassMerger.createForFinalClassMerging(appView.withLiveness())
               .runIfNecessary(executorService, timing);
+          assert appView.dexItemFactory().verifyNoCachedTypeElements();
         }
 
-        // TODO(b/225838009): Move further down.
-        LirConverter.finalizeLirToOutputFormat(appView, timing, executorService);
-        assert appView.dexItemFactory().verifyNoCachedTypeElements();
-
         genericContextBuilderBeforeFinalMerging = GenericSignatureContextBuilder.create(appView);
 
         // Run horizontal class merging. This runs even if shrinking is disabled to ensure
@@ -764,8 +762,13 @@
                 finalRuntimeTypeCheckInfoBuilder != null
                     ? finalRuntimeTypeCheckInfoBuilder.build(appView.graphLens())
                     : null);
+        assert appView.dexItemFactory().verifyNoCachedTypeElements();
       }
 
+      // TODO(b/225838009): Move further down.
+      LirConverter.finalizeLirToOutputFormat(appView, timing, executorService);
+      assert appView.dexItemFactory().verifyNoCachedTypeElements();
+
       // Perform minification.
       if (options.getProguardConfiguration().hasApplyMappingFile()) {
         timing.begin("apply-mapping");
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
index 1b9b0b0..2ae1e75 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
@@ -89,6 +89,10 @@
     return new AppInfoWithClassHierarchy(WITNESS, appInfo);
   }
 
+  public final AppInfoWithClassHierarchy rebuildWithClassHierarchy(DexApplication application) {
+    return rebuildWithClassHierarchy(getSyntheticItems().commit(application));
+  }
+
   public final AppInfoWithClassHierarchy rebuildWithClassHierarchy(CommittedItems commit) {
     return new AppInfoWithClassHierarchy(
         commit, getClassToFeatureSplitMap(), getMainDexInfo(), getMissingClasses());
diff --git a/src/main/java/com/android/tools/r8/graph/lens/GraphLens.java b/src/main/java/com/android/tools/r8/graph/lens/GraphLens.java
index 05bf1b7..7c01711 100644
--- a/src/main/java/com/android/tools/r8/graph/lens/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/lens/GraphLens.java
@@ -20,6 +20,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
+import com.android.tools.r8.horizontalclassmerging.HorizontalClassMergerGraphLens;
 import com.android.tools.r8.ir.code.InvokeType;
 import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
 import com.android.tools.r8.ir.optimize.CustomLensCodeRewriter;
@@ -425,6 +426,10 @@
     return false;
   }
 
+  public HorizontalClassMergerGraphLens asHorizontalClassMergerGraphLens() {
+    return null;
+  }
+
   public abstract boolean isIdentityLens();
 
   public abstract boolean isIdentityLensForFields(GraphLens codeLens);
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 7139797..e754435 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -27,6 +27,7 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.PrunedItems;
 import com.android.tools.r8.horizontalclassmerging.code.ClassInitializerMerger;
+import com.android.tools.r8.horizontalclassmerging.code.ClassInitializerMerger.IRProvider;
 import com.android.tools.r8.horizontalclassmerging.code.SyntheticInitializerConverter;
 import com.android.tools.r8.ir.analysis.value.NumberFromIntervalValue;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
@@ -112,11 +113,10 @@
       ClassMergerSharedData classMergerSharedData,
       ProfileCollectionAdditions profileCollectionAdditions,
       SyntheticInitializerConverter.Builder syntheticInitializerConverterBuilder) {
-    mergeInstanceInitializers(
-        classMergerSharedData, profileCollectionAdditions, syntheticInitializerConverterBuilder);
+    mergeInstanceInitializers(classMergerSharedData, profileCollectionAdditions);
     mergeStaticClassInitializers(syntheticInitializerConverterBuilder);
     group.forEach(this::mergeDirectMethods);
-    if (!classInitializerMerger.isEmpty() && classInitializerMerger.isTrivialMerge()) {
+    if (classInitializerMerger.size() > 1 && classInitializerMerger.isTrivialMerge()) {
       classInitializerMerger.setObsolete();
     }
     instanceInitializerMergers.setObsolete();
@@ -128,6 +128,13 @@
       return;
     }
 
+    if (classInitializerMerger.isSingleton()) {
+      DexEncodedMethod classInitializer =
+          classInitializerMerger.moveSingleton(group, dexItemFactory);
+      classMethodsBuilder.addDirectMethod(classInitializer);
+      return;
+    }
+
     // Synthesize a new class initializer with a fresh synthetic original name.
     DexMethod newMethodReference =
         dexItemFactory.createClassInitializer(group.getTarget().getType());
@@ -144,17 +151,15 @@
             .setMethod(newMethodReference)
             .setAccessFlags(MethodAccessFlags.createForClassInitializer())
             .setCode(classInitializerMerger.getCode(newMethodReference))
-            .setClassFileVersion(classInitializerMerger.getCfVersion())
+            .setClassFileVersion(classInitializerMerger.getCfVersion(appView.options()))
             .setApiLevelForDefinition(apiReferenceLevel)
             .setApiLevelForCode(apiReferenceLevel)
             .build();
     classMethodsBuilder.addDirectMethod(definition);
 
-    // In case we didn't synthesize CF code, we register the class initializer for conversion to dex
-    // after merging.
-    if (!definition.getCode().isCfCode()) {
-      assert appView.options().isGeneratingDex();
-      assert mode.isFinal();
+    // Convert the synthetic code object to LIR before exiting class merging.
+    if (mode.isFinal()) {
+      assert definition.getCode() instanceof IRProvider;
       syntheticInitializerConverterBuilder.addClassInitializer(
           new ProgramMethod(group.getTarget(), definition));
     }
@@ -204,15 +209,10 @@
 
   void mergeInstanceInitializers(
       ClassMergerSharedData classMergerSharedData,
-      ProfileCollectionAdditions profileCollectionAdditions,
-      SyntheticInitializerConverter.Builder syntheticInitializerConverterBuilder) {
+      ProfileCollectionAdditions profileCollectionAdditions) {
     instanceInitializerMergers.forEach(
         merger ->
-            merger.merge(
-                classMergerSharedData,
-                profileCollectionAdditions,
-                classMethodsBuilder,
-                syntheticInitializerConverterBuilder));
+            merger.merge(classMergerSharedData, profileCollectionAdditions, classMethodsBuilder));
   }
 
   void mergeMethods(
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 91ca758..2c5dee9 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -12,13 +12,14 @@
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.Code;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.PrunedItems;
 import com.android.tools.r8.horizontalclassmerging.code.SyntheticInitializerConverter;
+import com.android.tools.r8.ir.conversion.LirConverter;
 import com.android.tools.r8.ir.conversion.MethodConversionOptions;
 import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
 import com.android.tools.r8.profile.art.ArtProfileCompletenessChecker;
@@ -102,9 +103,9 @@
   }
 
   private MutableMethodConversionOptions getConversionOptions() {
-    return mode == ClassMergerMode.INITIAL
+    return mode.isInitial()
         ? MethodConversionOptions.forPreLirPhase(appView)
-        : MethodConversionOptions.forPostLirPhase(appView);
+        : MethodConversionOptions.forLirPhase(appView);
   }
 
   private void run(
@@ -135,7 +136,7 @@
     ProfileCollectionAdditions profileCollectionAdditions =
         ProfileCollectionAdditions.create(appView);
     SyntheticInitializerConverter.Builder syntheticInitializerConverterBuilder =
-        SyntheticInitializerConverter.builder(appView, codeProvider, mode);
+        SyntheticInitializerConverter.builder(appView, codeProvider);
     List<VirtuallyMergedMethodsKeepInfo> virtuallyMergedMethodsKeepInfos = new ArrayList<>();
     PrunedItems prunedItems =
         applyClassMergers(
@@ -177,7 +178,6 @@
 
     // Finalize synthetic code.
     transformIncompleteCode(groups, horizontalClassMergerGraphLens, executorService);
-    syntheticInitializerConverter.convertInstanceInitializers(executorService);
 
     // Must rewrite AppInfoWithLiveness before pruning the merged classes, to ensure that allocation
     // sites, fields accesses, etc. are correctly transferred to the target classes.
@@ -187,8 +187,23 @@
       KeepInfoCollection keepInfo = appView.getKeepInfo();
       keepInfo.mutate(mutator -> mutator.removeKeepInfoForMergedClasses(prunedItems));
       assert appView.hasClassHierarchy();
-      appView.rewriteWithLensAndApplication(
-          horizontalClassMergerGraphLens, newApplication.toDirect(), executorService, timing);
+      if (mode.isInitial()) {
+        appView.rewriteWithLensAndApplication(
+            horizontalClassMergerGraphLens, newApplication.toDirect(), executorService, timing);
+      } else {
+        appView.rewriteWithLens(horizontalClassMergerGraphLens, executorService, timing);
+        LirConverter.rewriteLirWithLens(appView.withClassHierarchy(), timing, executorService);
+        if (appView.hasLiveness()) {
+          appView
+              .withLiveness()
+              .setAppInfo(appView.appInfoWithLiveness().rebuildWithLiveness(newApplication));
+        } else {
+          appView
+              .withClassHierarchy()
+              .setAppInfo(
+                  appView.appInfoWithClassHierarchy().rebuildWithClassHierarchy(newApplication));
+        }
+      }
     } else {
       assert mode.isFinal();
       SyntheticItems syntheticItems = appView.appInfo().getSyntheticItems();
@@ -297,11 +312,14 @@
                 // This should be changed to generate non-null LirCode always.
                 IncompleteHorizontalClassMergerCode code =
                     (IncompleteHorizontalClassMergerCode) method.getDefinition().getCode();
-                CfCode cfCode =
-                    code.toCfCode(
-                        appView.withClassHierarchy(), method, horizontalClassMergerGraphLens);
-                if (cfCode != null) {
-                  method.setCode(cfCode, appView);
+                Code newCode =
+                    mode.isInitial()
+                        ? code.toCfCode(
+                            appView.withClassHierarchy(), method, horizontalClassMergerGraphLens)
+                        : code.toLirCode(
+                            appView.withClassHierarchy(), method, horizontalClassMergerGraphLens);
+                if (newCode != null) {
+                  method.setCode(newCode, appView);
                 }
               });
         },
@@ -399,6 +417,7 @@
           syntheticInitializerConverterBuilder,
           virtuallyMergedMethodsKeepInfoConsumer);
     }
+    appView.dexItemFactory().clearTypeElementsCache();
     return prunedItemsBuilder.build();
   }
 
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 24e3f18..92931c2 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
@@ -70,6 +70,11 @@
   }
 
   @Override
+  public HorizontalClassMergerGraphLens asHorizontalClassMergerGraphLens() {
+    return this;
+  }
+
+  @Override
   protected Iterable<DexType> internalGetOriginalTypes(DexType previous) {
     return IterableUtils.prependSingleton(previous, mergedClasses.getSourcesFor(previous));
   }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteHorizontalClassMergerCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteHorizontalClassMergerCode.java
index 7ca989f..0d66d8b 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteHorizontalClassMergerCode.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteHorizontalClassMergerCode.java
@@ -17,6 +17,7 @@
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
+import com.android.tools.r8.lightir.LirCode;
 import com.android.tools.r8.utils.RetracerForCodePrinting;
 
 public abstract class IncompleteHorizontalClassMergerCode extends Code {
@@ -36,6 +37,11 @@
       ProgramMethod method,
       HorizontalClassMergerGraphLens lens);
 
+  public abstract LirCode<Integer> toLirCode(
+      AppView<? extends AppInfoWithClassHierarchy> appView,
+      ProgramMethod method,
+      HorizontalClassMergerGraphLens lens);
+
   @Override
   public final Code getCodeAsInlining(
       DexMethod caller,
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteMergedInstanceInitializerCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteMergedInstanceInitializerCode.java
index 95a4920..2ce9bac 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteMergedInstanceInitializerCode.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteMergedInstanceInitializerCode.java
@@ -4,6 +4,8 @@
 
 package com.android.tools.r8.horizontalclassmerging;
 
+import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
+
 import com.android.tools.r8.cf.code.CfConstClass;
 import com.android.tools.r8.cf.code.CfConstNull;
 import com.android.tools.r8.cf.code.CfConstNumber;
@@ -23,20 +25,34 @@
 import com.android.tools.r8.graph.CfCodeWithLens;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.lens.GraphLens;
 import com.android.tools.r8.graph.lens.MethodLookupResult;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.analysis.value.SingleConstValue;
 import com.android.tools.r8.ir.analysis.value.SingleDexItemBasedStringValue;
+import com.android.tools.r8.ir.code.IRMetadata;
 import com.android.tools.r8.ir.code.Position;
 import com.android.tools.r8.ir.code.Position.SyntheticPosition;
+import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.conversion.ExtraParameter;
 import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfo;
+import com.android.tools.r8.lightir.LirBuilder;
+import com.android.tools.r8.lightir.LirCode;
+import com.android.tools.r8.lightir.LirEncodingStrategy;
+import com.android.tools.r8.lightir.LirStrategy;
+import com.android.tools.r8.utils.Box;
 import com.android.tools.r8.utils.IntBox;
+import com.android.tools.r8.utils.ListUtils;
 import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
+import java.util.function.Consumer;
 import org.objectweb.asm.Opcodes;
 
 /**
@@ -174,6 +190,127 @@
         instructionBuilder.build());
   }
 
+  @Override
+  public LirCode<Integer> toLirCode(
+      AppView<? extends AppInfoWithClassHierarchy> appView,
+      ProgramMethod method,
+      HorizontalClassMergerGraphLens lens) {
+    LirEncodingStrategy<Value, Integer> strategy =
+        LirStrategy.getDefaultStrategy().getEncodingStrategy();
+    LirBuilder<Value, Integer> lirBuilder =
+        LirCode.builder(
+                method.getReference(),
+                method.getDefinition().isD8R8Synthesized(),
+                strategy,
+                appView.options())
+            .setMetadata(IRMetadata.unknown());
+
+    int instructionIndex = 0;
+    List<Value> argumentValues = new ArrayList<>();
+
+    // Add receiver argument.
+    DexType receiverType = method.getHolderType();
+    TypeElement receiverTypeElement = receiverType.toTypeElement(appView, definitelyNotNull());
+    Value receiverValue = Value.createNoDebugLocal(instructionIndex, receiverTypeElement);
+    argumentValues.add(receiverValue);
+    strategy.defineValue(receiverValue, receiverValue.getNumber());
+    lirBuilder.addArgument(receiverValue.getNumber(), false);
+    instructionIndex++;
+
+    // Add non-receiver arguments.
+    for (; instructionIndex < method.getDefinition().getNumberOfArguments(); instructionIndex++) {
+      DexType argumentType = method.getArgumentType(instructionIndex);
+      TypeElement argumentTypeElement = argumentType.toTypeElement(appView);
+      Value argumentValue = Value.createNoDebugLocal(instructionIndex, argumentTypeElement);
+      argumentValues.add(argumentValue);
+      strategy.defineValue(argumentValue, argumentValue.getNumber());
+      lirBuilder.addArgument(argumentValue.getNumber(), argumentType.isBooleanType());
+    }
+
+    // Assign class id.
+    if (classIdField != null) {
+      Value classIdValue = argumentValues.get(argumentValues.size() - 1 - extraNulls);
+      lirBuilder.addInstancePut(
+          lens.getNextFieldSignature(classIdField), receiverValue, classIdValue);
+      instructionIndex++;
+    }
+
+    // Assign each field.
+    instructionIndex =
+        addLirInstructionsForInstanceFieldAssignments(
+            appView,
+            method,
+            lirBuilder,
+            strategy,
+            argumentValues,
+            instructionIndex,
+            instanceFieldAssignmentsPre,
+            lens);
+
+    // Load constructor arguments.
+    MethodLookupResult parentConstructorLookup =
+        lens.lookupInvokeDirect(parentConstructor, method, appView.codeLens());
+    List<Value> parentConstructorArgumentValues = new ArrayList<>();
+    parentConstructorArgumentValues.add(receiverValue);
+    int parentConstructorArgumentIndex = 0;
+    for (InstanceFieldInitializationInfo initializationInfo : parentConstructorArguments) {
+      instructionIndex =
+          addLirInstructionsForInitializationInfo(
+              appView,
+              lirBuilder,
+              strategy,
+              initializationInfo,
+              argumentValues,
+              instructionIndex,
+              parentConstructorLookup.getReference().getParameter(parentConstructorArgumentIndex),
+              parentConstructorArgumentValues::add);
+      parentConstructorArgumentIndex++;
+    }
+
+    for (ExtraParameter extraParameter :
+        parentConstructorLookup.getPrototypeChanges().getExtraParameters()) {
+      instructionIndex =
+          addLirInstructionsForInitializationInfo(
+              appView,
+              lirBuilder,
+              strategy,
+              extraParameter.getValue(appView),
+              argumentValues,
+              instructionIndex,
+              parentConstructorLookup.getReference().getParameter(parentConstructorArgumentIndex),
+              parentConstructorArgumentValues::add);
+      parentConstructorArgumentIndex++;
+    }
+
+    // Invoke parent constructor.
+    lirBuilder.addInvokeDirect(
+        parentConstructorLookup.getReference(), parentConstructorArgumentValues, false);
+    instructionIndex++;
+
+    // Assign each field.
+    addLirInstructionsForInstanceFieldAssignments(
+        appView,
+        method,
+        lirBuilder,
+        strategy,
+        argumentValues,
+        instructionIndex,
+        instanceFieldAssignmentsPost,
+        lens);
+
+    // Return.
+    lirBuilder.addReturnVoid();
+    instructionIndex++;
+
+    return new LirCode<>(lirBuilder.build()) {
+
+      @Override
+      public GraphLens getCodeLens(AppView<?> appView) {
+        return lens;
+      }
+    };
+  }
+
   private static void addCfInstructionsForInstanceFieldAssignments(
       AppView<? extends AppInfoWithClassHierarchy> appView,
       ProgramMethod method,
@@ -260,6 +397,117 @@
     }
   }
 
+  private static int addLirInstructionsForInstanceFieldAssignments(
+      AppView<? extends AppInfoWithClassHierarchy> appView,
+      ProgramMethod method,
+      LirBuilder<Value, Integer> lirBuilder,
+      LirEncodingStrategy<Value, Integer> strategy,
+      List<Value> argumentValues,
+      int instructionIndex,
+      Map<DexField, InstanceFieldInitializationInfo> instanceFieldAssignments,
+      HorizontalClassMergerGraphLens lens) {
+    for (Entry<DexField, InstanceFieldInitializationInfo> entry :
+        instanceFieldAssignments.entrySet()) {
+      DexField field = entry.getKey();
+      InstanceFieldInitializationInfo initializationInfo = entry.getValue();
+
+      // Load the field value and then set the field.
+      Box<Value> fieldValueBox = new Box<>();
+      instructionIndex =
+          addLirInstructionsForInitializationInfo(
+              appView,
+              lirBuilder,
+              strategy,
+              initializationInfo,
+              argumentValues,
+              instructionIndex,
+              field.getType(),
+              fieldValueBox::set);
+      Value fieldValue = fieldValueBox.get();
+
+      // Insert a check to ensure the program continues to type check according to Java type
+      // checking. Otherwise, instance initializer merging may cause open interfaces. If
+      // <init>(A) and <init>(B) both have the behavior `this.i = arg; this.j = arg` where the
+      // type of `i` is I and the type of `j` is J, and both A and B implements I and J, then
+      // the constructors are merged into a single constructor <init>(java.lang.Object), which
+      // is no longer strictly type checking. Note that no choice of parameter type would solve
+      // this.
+      DexField rewrittenField = lens.getNextFieldSignature(field);
+      if (initializationInfo.isArgumentInitializationInfo()) {
+        int argumentIndex = initializationInfo.asArgumentInitializationInfo().getArgumentIndex();
+        if (argumentIndex > 0) {
+          DexType argumentType = method.getArgumentType(argumentIndex);
+          if (argumentType.isClassType()
+              && !appView.appInfo().isSubtype(argumentType, rewrittenField.getType())) {
+            TypeElement newFieldValueTypeElement =
+                rewrittenField.getType().toTypeElement(appView, fieldValue.getType().nullability());
+            Value newFieldValue =
+                Value.createNoDebugLocal(instructionIndex, newFieldValueTypeElement);
+            strategy.defineValue(newFieldValue, newFieldValue.getNumber());
+            lirBuilder.addSafeCheckCast(rewrittenField.getType(), fieldValue);
+            fieldValue = newFieldValue;
+            instructionIndex++;
+          }
+        }
+      }
+
+      lirBuilder.addInstancePut(rewrittenField, ListUtils.first(argumentValues), fieldValue);
+      instructionIndex++;
+    }
+    return instructionIndex;
+  }
+
+  private static int addLirInstructionsForInitializationInfo(
+      AppView<? extends AppInfoWithClassHierarchy> appView,
+      LirBuilder<Value, Integer> lirBuilder,
+      LirEncodingStrategy<Value, Integer> strategy,
+      InstanceFieldInitializationInfo initializationInfo,
+      List<Value> argumentValues,
+      int instructionIndex,
+      DexType type,
+      Consumer<Value> valueConsumer) {
+    Value value;
+    if (initializationInfo.isArgumentInitializationInfo()) {
+      int argumentIndex = initializationInfo.asArgumentInitializationInfo().getArgumentIndex();
+      value = argumentValues.get(argumentIndex);
+    } else {
+      assert initializationInfo.isSingleValue();
+      assert initializationInfo.asSingleValue().isSingleConstValue();
+      SingleConstValue singleConstValue = initializationInfo.asSingleValue().asSingleConstValue();
+      TypeElement valueTypeElement;
+      if (singleConstValue.isSingleConstClassValue()) {
+        DexType classType = singleConstValue.asSingleConstClassValue().getType();
+        lirBuilder.addConstClass(classType, false);
+        valueTypeElement = TypeElement.classClassType(appView, definitelyNotNull());
+      } else if (singleConstValue.isSingleDexItemBasedStringValue()) {
+        SingleDexItemBasedStringValue dexItemBasedStringValue =
+            singleConstValue.asSingleDexItemBasedStringValue();
+        lirBuilder.addDexItemBasedConstString(
+            dexItemBasedStringValue.getItem(), dexItemBasedStringValue.getNameComputationInfo());
+        valueTypeElement = TypeElement.stringClassType(appView, definitelyNotNull());
+      } else if (singleConstValue.isNull()) {
+        assert type.isReferenceType();
+        lirBuilder.addConstNull();
+        valueTypeElement = TypeElement.getNull();
+      } else if (singleConstValue.isSingleNumberValue()) {
+        assert type.isPrimitiveType();
+        long numberValue = singleConstValue.asSingleNumberValue().getValue();
+        lirBuilder.addConstNumber(ValueType.fromDexType(type), numberValue);
+        valueTypeElement = type.toTypeElement(appView);
+      } else {
+        assert singleConstValue.isSingleStringValue();
+        DexString string = singleConstValue.asSingleStringValue().getDexString();
+        lirBuilder.addConstString(string);
+        valueTypeElement = TypeElement.stringClassType(appView, definitelyNotNull());
+      }
+      value = Value.createNoDebugLocal(instructionIndex, valueTypeElement);
+      strategy.defineValue(value, value.getNumber());
+      instructionIndex++;
+    }
+    valueConsumer.accept(value);
+    return instructionIndex;
+  }
+
   @Override
   public String toString() {
     return "IncompleteMergedInstanceInitializerCode";
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteVirtuallyMergedMethodCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteVirtuallyMergedMethodCode.java
index 378b433..05088a9 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteVirtuallyMergedMethodCode.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteVirtuallyMergedMethodCode.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.cf.code.CfSwitch;
 import com.android.tools.r8.cf.code.CfSwitch.Kind;
 import com.android.tools.r8.cf.code.frame.FrameType;
+import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.CfCode;
@@ -25,6 +26,7 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.horizontalclassmerging.VirtualMethodMerger.SuperMethodReference;
 import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.lightir.LirCode;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.IterableUtils;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
@@ -157,6 +159,14 @@
         lens, originalMethod.getHolderType(), maxStack, maxLocals, instructions);
   }
 
+  @Override
+  public LirCode<Integer> toLirCode(
+      AppView<? extends AppInfoWithClassHierarchy> appView,
+      ProgramMethod method,
+      HorizontalClassMergerGraphLens lens) {
+    throw new Unreachable();
+  }
+
   private static CfFrame createCfFrameForSwitchCase(ProgramMethod representative, int localsSize) {
     CfFrame.Builder builder =
         CfFrame.builder().allocateStack(representative.getDefinition().getNumberOfArguments());
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
index 66b8a1e..829d715 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
@@ -21,7 +21,6 @@
 import com.android.tools.r8.graph.MethodAccessFlags;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.horizontalclassmerging.code.ConstructorEntryPointSynthesizedCode;
-import com.android.tools.r8.horizontalclassmerging.code.SyntheticInitializerConverter;
 import com.android.tools.r8.ir.conversion.ExtraConstantIntParameter;
 import com.android.tools.r8.ir.conversion.ExtraParameter;
 import com.android.tools.r8.ir.conversion.ExtraUnusedParameter;
@@ -299,8 +298,7 @@
   void merge(
       ClassMergerSharedData classMergerSharedData,
       ProfileCollectionAdditions profileCollectionAdditions,
-      ClassMethodsBuilder classMethodsBuilder,
-      SyntheticInitializerConverter.Builder syntheticInitializerConverterBuilder) {
+      ClassMethodsBuilder classMethodsBuilder) {
     ProgramMethod representative = ListUtils.first(instanceInitializers);
 
     // Create merged instance initializer reference.
@@ -390,16 +388,10 @@
     }
     classMethodsBuilder.addDirectMethod(newInstanceInitializer);
 
-    if (mode.isFinal()) {
-      if (appView.options().isGeneratingDex() && !newInstanceInitializer.getCode().isDexCode()) {
-        syntheticInitializerConverterBuilder.addInstanceInitializer(
-            new ProgramMethod(group.getTarget(), newInstanceInitializer));
-      } else {
-        assert appView.options().isGeneratingDex()
-            || newInstanceInitializer.getCode().isCfWritableCode()
-            || newInstanceInitializer.getCode().isIncompleteHorizontalClassMergerCode();
-      }
-    }
+    assert mode.isInitial()
+        || newInstanceInitializer.getCode().isDefaultInstanceInitializerCode()
+        || newInstanceInitializer.getCode().isLirCode()
+        || newInstanceInitializer.getCode().isIncompleteHorizontalClassMergerCode();
   }
 
   void setObsolete() {
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 d54026f..21e4584 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
@@ -19,6 +19,7 @@
 import com.android.tools.r8.graph.ClasspathMethod;
 import com.android.tools.r8.graph.Code;
 import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -36,6 +37,7 @@
 import com.android.tools.r8.ir.code.Return;
 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;
@@ -84,18 +86,23 @@
     return new IRProvider(classInitializers);
   }
 
-  public CfVersion getCfVersion() {
-    ProgramMethod classInitializer = ListUtils.first(classInitializers);
-    if (classInitializers.size() == 1) {
-      DexEncodedMethod method = classInitializer.getDefinition();
-      return method.hasClassFileVersion() ? method.getClassFileVersion() : null;
+  public CfVersion getCfVersion(InternalOptions options) {
+    return options.isGeneratingClassFiles() ? CfVersionUtils.max(classInitializers) : null;
+  }
+
+  public boolean isSingleton() {
+    return classInitializers.size() == 1;
+  }
+
+  public DexEncodedMethod moveSingleton(HorizontalMergeGroup group, DexItemFactory dexItemFactory) {
+    assert isSingleton();
+    ProgramMethod method = ListUtils.first(classInitializers);
+    DexEncodedMethod definition = method.getDefinition();
+    if (method.getHolder() == group.getTarget()) {
+      return definition;
     }
-    if (classInitializer.getDefinition().getCode().isCfCode()) {
-      assert IterableUtils.allIdentical(
-          classInitializers, method -> method.getDefinition().getCode().isCfCode());
-      return CfVersionUtils.max(classInitializers);
-    }
-    return null;
+    DexMethod newReference = method.getReference().withHolder(group.getTarget(), dexItemFactory);
+    return definition.toTypeSubstitutedMethodAsInlining(newReference, dexItemFactory);
   }
 
   public boolean isTrivialMerge() {
@@ -115,6 +122,10 @@
     classInitializers.forEach(classInitializer -> classInitializer.getDefinition().setObsolete());
   }
 
+  public int size() {
+    return classInitializers.size();
+  }
+
   public static class Builder {
 
     private final ImmutableList.Builder<ProgramMethod> classInitializers = ImmutableList.builder();
@@ -195,7 +206,7 @@
    * Provides a piece of {@link IRCode} that is the concatenation of a collection of class
    * initializers.
    */
-  static class IRProvider extends Code {
+  public static class IRProvider extends Code {
 
     private final ImmutableList<ProgramMethod> classInitializers;
 
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ConstructorEntryPointSynthesizedCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ConstructorEntryPointSynthesizedCode.java
index fda1515..fde81de 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ConstructorEntryPointSynthesizedCode.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ConstructorEntryPointSynthesizedCode.java
@@ -27,6 +27,7 @@
 import com.android.tools.r8.ir.conversion.MethodConversionOptions;
 import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
 import com.android.tools.r8.ir.conversion.SourceCode;
+import com.android.tools.r8.lightir.LirCode;
 import com.android.tools.r8.utils.RetracerForCodePrinting;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
 import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
@@ -84,6 +85,14 @@
   }
 
   @Override
+  public LirCode<Integer> toLirCode(
+      AppView<? extends AppInfoWithClassHierarchy> appView,
+      ProgramMethod method,
+      HorizontalClassMergerGraphLens lens) {
+    throw new Unreachable();
+  }
+
+  @Override
   public final boolean isEmptyVoidMethod() {
     return false;
   }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java
index 2b7d783..55a073a 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java
@@ -4,13 +4,9 @@
 
 package com.android.tools.r8.horizontalclassmerging.code;
 
-import com.android.tools.r8.classmerging.ClassMergerMode;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.Code;
-import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.lens.GraphLens;
 import com.android.tools.r8.horizontalclassmerging.IRCodeProvider;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.conversion.IRConverter;
@@ -18,10 +14,8 @@
 import com.android.tools.r8.synthesis.SyntheticItems.GlobalSyntheticsStrategy;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
-import com.google.common.collect.Sets;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 
@@ -33,73 +27,33 @@
 
   private final AppView<?> appView;
   private final IRCodeProvider codeProvider;
-  private final ClassMergerMode mode;
 
   private final List<ProgramMethod> classInitializers;
 
-  // Classes with one or more instance initializers that need to have their code processed into dex.
-  private final Set<DexProgramClass> instanceInitializers;
-
   private SyntheticInitializerConverter(
-      AppView<?> appView,
-      IRCodeProvider codeProvider,
-      ClassMergerMode mode,
-      List<ProgramMethod> classInitializers,
-      Set<DexProgramClass> instanceInitializers) {
+      AppView<?> appView, IRCodeProvider codeProvider, List<ProgramMethod> classInitializers) {
     this.appView = appView;
     this.codeProvider = codeProvider;
-    this.mode = mode;
     this.classInitializers = classInitializers;
-    this.instanceInitializers = instanceInitializers;
   }
 
-  public static Builder builder(
-      AppView<?> appView, IRCodeProvider codeProvider, ClassMergerMode mode) {
-    return new Builder(appView, codeProvider, mode);
+  public static Builder builder(AppView<?> appView, IRCodeProvider codeProvider) {
+    return new Builder(appView, codeProvider);
   }
 
   public void convertClassInitializers(ExecutorService executorService) throws ExecutionException {
     if (!classInitializers.isEmpty()) {
+      assert appView.dexItemFactory().verifyNoCachedTypeElements();
       IRConverter converter = new IRConverter(createAppViewForConversion());
       ThreadUtils.processItems(
           classInitializers,
           method -> processMethod(method, converter),
           appView.options().getThreadingModule(),
           executorService);
+      appView.dexItemFactory().clearTypeElementsCache();
     }
   }
 
-  public void convertInstanceInitializers(ExecutorService executorService)
-      throws ExecutionException {
-    if (!instanceInitializers.isEmpty()) {
-      IRConverter converter = new IRConverter(createAppViewForConversion());
-      ThreadUtils.processItems(
-          instanceInitializers,
-          clazz -> processInstanceInitializers(clazz, converter),
-          appView.options().getThreadingModule(),
-          executorService);
-    }
-  }
-
-  private void processInstanceInitializers(DexProgramClass clazz, IRConverter converter) {
-    assert appView.options().isGeneratingDex();
-    assert mode.isFinal();
-    clazz.forEachProgramInstanceInitializerMatching(
-        method -> method.getCode().isCfCode(),
-        method -> {
-          GraphLens codeLens = method.getDefinition().getCode().getCodeLens(appView);
-          assert codeLens != appView.codeLens();
-
-          // Convert to dex.
-          processMethod(method, converter);
-
-          // Recover code lens.
-          Code code = method.getDefinition().getCode();
-          assert code.isDexCode();
-          method.setCode(code.asDexCode().withCodeLens(codeLens), appView);
-        });
-  }
-
   private AppView<AppInfo> createAppViewForConversion() {
     assert appView.enableWholeProgramOptimizations();
     assert appView.hasClassHierarchy();
@@ -129,22 +83,19 @@
   }
 
   public boolean isEmpty() {
-    return classInitializers.isEmpty() && instanceInitializers.isEmpty();
+    return classInitializers.isEmpty();
   }
 
   public static class Builder {
 
     private final AppView<?> appView;
     private final IRCodeProvider codeProvider;
-    private final ClassMergerMode mode;
 
     private final List<ProgramMethod> classInitializers = new ArrayList<>();
-    private final Set<DexProgramClass> instanceInitializers = Sets.newIdentityHashSet();
 
-    private Builder(AppView<?> appView, IRCodeProvider codeProvider, ClassMergerMode mode) {
+    private Builder(AppView<?> appView, IRCodeProvider codeProvider) {
       this.appView = appView;
       this.codeProvider = codeProvider;
-      this.mode = mode;
     }
 
     public Builder addClassInitializer(ProgramMethod method) {
@@ -152,17 +103,8 @@
       return this;
     }
 
-    public Builder addInstanceInitializer(ProgramMethod method) {
-      // Record that the holder has an instance initializer that needs processing to dex. We avoid
-      // storing the collection of exact initializers that need processing, since that requires lens
-      // code rewriting after the fixup has been made.
-      this.instanceInitializers.add(method.getHolder());
-      return this;
-    }
-
     public SyntheticInitializerConverter build() {
-      return new SyntheticInitializerConverter(
-          appView, codeProvider, mode, classInitializers, instanceInitializers);
+      return new SyntheticInitializerConverter(appView, codeProvider, classInitializers);
     }
   }
 }
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 5f5a9c9..5eb58db 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
@@ -149,7 +149,6 @@
     assert !appView.getSyntheticItems().hasPendingSyntheticClasses();
     assert verifyLirOnly(appView);
     appView.testing().exitLirSupportedPhase();
-    LensCodeRewriterUtils rewriterUtils = new LensCodeRewriterUtils(appView, true);
     DeadCodeRemover deadCodeRemover = new DeadCodeRemover(appView);
     String output = appView.options().isGeneratingClassFiles() ? "CF" : "DEX";
     timing.begin("LIR->IR->" + output);
@@ -157,7 +156,7 @@
         appView.appInfo().classes(),
         clazz ->
             clazz.forEachProgramMethod(
-                m -> finalizeLirMethodToOutputFormat(m, deadCodeRemover, appView, rewriterUtils)),
+                m -> finalizeLirMethodToOutputFormat(m, deadCodeRemover, appView)),
         appView.options().getThreadingModule(),
         executorService);
     timing.end();
@@ -170,18 +169,12 @@
   private static void finalizeLirMethodToOutputFormat(
       ProgramMethod method,
       DeadCodeRemover deadCodeRemover,
-      AppView<? extends AppInfoWithClassHierarchy> appView,
-      LensCodeRewriterUtils rewriterUtils) {
+      AppView<? extends AppInfoWithClassHierarchy> appView) {
     Code code = method.getDefinition().getCode();
     if (!(code instanceof LirCode)) {
       return;
     }
     Timing onThreadTiming = Timing.empty();
-    LirCode<Integer> lirCode = code.asLirCode();
-    LirCode<Integer> rewrittenLirCode = lirCode.rewriteWithLens(method, appView, rewriterUtils);
-    if (ObjectUtils.notIdentical(lirCode, rewrittenLirCode)) {
-      method.setCode(rewrittenLirCode, appView);
-    }
     IRCode irCode = method.buildIR(appView, MethodConversionOptions.forPostLirPhase(appView));
     assert irCode.verifyInvokeInterface(appView);
     ConstResourceNumberRemover constResourceNumberRemover = new ConstResourceNumberRemover(appView);
diff --git a/src/main/java/com/android/tools/r8/lightir/LirCode.java b/src/main/java/com/android/tools/r8/lightir/LirCode.java
index a571e49..91a85d7 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirCode.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirCode.java
@@ -644,20 +644,13 @@
 
   @Override
   public int estimatedDexCodeSizeUpperBoundInBytes() {
-    throw new Unimplemented();
+    return getEstimatedDexSizeForInlining();
   }
 
   @Override
   public int getEstimatedSizeForInliningIfLessThanOrEquals(int threshold) {
     if (useDexEstimationStrategy) {
-      LirSizeEstimation<EV> estimation = new LirSizeEstimation<>(this);
-      for (LirInstructionView view : this) {
-        estimation.onInstructionView(view);
-        if (estimation.getSizeEstimate() > threshold) {
-          return -1;
-        }
-      }
-      return estimation.getSizeEstimate();
+      return getEstimatedDexSizeForInliningIfLessThanOrEquals(threshold);
     } else {
       // TODO(b/225838009): Currently the size estimation for CF has size one for each instruction
       //  (even switches!) and ignores stack instructions, thus loads to arguments are not included.
@@ -671,6 +664,21 @@
     }
   }
 
+  private int getEstimatedDexSizeForInlining() {
+    return getEstimatedDexSizeForInliningIfLessThanOrEquals(Integer.MAX_VALUE);
+  }
+
+  private int getEstimatedDexSizeForInliningIfLessThanOrEquals(int threshold) {
+    LirSizeEstimation<EV> estimation = new LirSizeEstimation<>(this);
+    for (LirInstructionView view : this) {
+      estimation.onInstructionView(view);
+      if (estimation.getSizeEstimate() > threshold) {
+        return -1;
+      }
+    }
+    return estimation.getSizeEstimate();
+  }
+
   public Position getPreamblePosition(DexMethod method, boolean isD8R8Synthesized) {
     if (positionTable.length > 0 && positionTable[0].fromInstructionIndex == 0) {
       return positionTable[0].getPosition(method, isD8R8Synthesized);
diff --git a/src/main/java/com/android/tools/r8/lightir/LirLensCodeRewriter.java b/src/main/java/com/android/tools/r8/lightir/LirLensCodeRewriter.java
index b4a6cb7..2d7dcee 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirLensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirLensCodeRewriter.java
@@ -22,6 +22,7 @@
 import com.android.tools.r8.graph.lens.GraphLens;
 import com.android.tools.r8.graph.lens.MethodLookupResult;
 import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
+import com.android.tools.r8.horizontalclassmerging.HorizontalClassMergerGraphLens;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.IRMetadata;
 import com.android.tools.r8.ir.code.IROpcodeUtils;
@@ -85,8 +86,8 @@
 
   public void onFieldReference(DexField field) {
     FieldLookupResult result = graphLens.lookupFieldResult(field, codeLens);
-    assert !result.hasReadCastType();
-    assert !result.hasWriteCastType();
+    assert !result.hasReadCastType() || graphLens.isHorizontalClassMergerGraphLens();
+    assert !result.hasWriteCastType() || graphLens.isHorizontalClassMergerGraphLens();
     addRewrittenMapping(field, result.getReference());
   }
 
@@ -172,6 +173,32 @@
   }
 
   @Override
+  public void onInstanceGet(DexField field, EV object) {
+    onFieldGet(field);
+  }
+
+  @Override
+  public void onStaticGet(DexField field) {
+    onFieldGet(field);
+  }
+
+  private void onFieldGet(DexField field) {
+    if (hasPotentialNonTrivialFieldGetRewriting(field)) {
+      hasNonTrivialRewritings = true;
+    }
+  }
+
+  private boolean hasPotentialNonTrivialFieldGetRewriting(DexField field) {
+    HorizontalClassMergerGraphLens horizontalClassMergerLens =
+        graphLens.asHorizontalClassMergerGraphLens();
+    if (horizontalClassMergerLens != null) {
+      FieldLookupResult result = horizontalClassMergerLens.lookupFieldResult(field, codeLens);
+      return result.hasReadCastType();
+    }
+    return false;
+  }
+
+  @Override
   public void onInstancePut(DexField field, EV object, EV value) {
     onFieldPut(field);
   }
@@ -188,6 +215,12 @@
   }
 
   private boolean hasPotentialNonTrivialFieldPutRewriting(DexField field) {
+    HorizontalClassMergerGraphLens horizontalClassMergerLens =
+        graphLens.asHorizontalClassMergerGraphLens();
+    if (horizontalClassMergerLens != null) {
+      FieldLookupResult result = horizontalClassMergerLens.lookupFieldResult(field, codeLens);
+      return result.hasWriteCastType();
+    }
     VerticalClassMergerGraphLens verticalClassMergerLens = graphLens.asVerticalClassMergerLens();
     if (verticalClassMergerLens != null
         && verticalClassMergerLens.hasInterfaceBeenMergedIntoClass(field.getType())) {
@@ -360,9 +393,9 @@
 
     // If there are potential method rewritings then we need to iterate the instructions as the
     // rewriting is instruction-sensitive (i.e., may be dependent on the invoke type).
-    boolean hasPotentialNonTrivialFieldPutRewriting =
-        hasFieldReference && graphLens.isVerticalClassMergerLens();
-    if (hasPotentialNonTrivialFieldPutRewriting || hasPotentialRewrittenMethod) {
+    boolean hasPotentialNonTrivialFieldAccessRewriting =
+        hasFieldReference && graphLens.isClassMergerLens();
+    if (hasPotentialNonTrivialFieldAccessRewriting || hasPotentialRewrittenMethod) {
       for (LirInstructionView view : code) {
         view.accept(this);
         if (hasNonTrivialRewritings) {
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 64d6556..5d13246 100644
--- a/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/verticalclassmerging/VerticalClassMerger.java
@@ -138,6 +138,7 @@
     // Finally update the code lens to signal that the code is fully up to date.
     markRewrittenWithLens(executorService, timing);
 
+    appView.dexItemFactory().clearTypeElementsCache();
     appView.notifyOptimizationFinishedForTesting();
   }