Avoid IR processing of default instance initializers

Change-Id: I54673b14fe973a7aa9b6f4daae7fd1b6323811c6
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 0263c5f..a0ac288 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -982,7 +982,7 @@
                         } else if (code.isDexCode()) {
                           assert verifyOriginalMethodInDebugInfo(code.asDexCode(), originalMethod);
                         } else {
-                          assert code.isThrowNullCode();
+                          assert code.isDefaultInstanceInitializerCode() || code.isThrowNullCode();
                         }
                       }
                     }));
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index 455d586..a48fb5c 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -182,6 +182,11 @@
   }
 
   @Override
+  public CfWritableCodeKind getCfWritableCodeKind() {
+    return CfWritableCodeKind.DEFAULT;
+  }
+
+  @Override
   public StructuralMapping<CfCode> getStructuralMapping() {
     throw new Unreachable();
   }
diff --git a/src/main/java/com/android/tools/r8/graph/CfWritableCode.java b/src/main/java/com/android/tools/r8/graph/CfWritableCode.java
index fd23726..939695f 100644
--- a/src/main/java/com/android/tools/r8/graph/CfWritableCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfWritableCode.java
@@ -5,12 +5,51 @@
 package com.android.tools.r8.graph;
 
 import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
 import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import org.objectweb.asm.MethodVisitor;
 
 public interface CfWritableCode {
 
+  enum CfWritableCodeKind {
+    DEFAULT,
+    DEFAULT_INSTANCE_INITIALIZER,
+    THROW_NULL
+  }
+
+  default int acceptCompareTo(CfWritableCode code, CompareToVisitor visitor) {
+    CfWritableCodeKind kind = getCfWritableCodeKind();
+    CfWritableCodeKind otherKind = code.getCfWritableCodeKind();
+    if (kind != otherKind) {
+      return kind.compareTo(otherKind);
+    }
+    switch (kind) {
+      case DEFAULT:
+        return asCfCode().acceptCompareTo(code.asCfCode(), visitor);
+      case DEFAULT_INSTANCE_INITIALIZER:
+        return 0;
+      case THROW_NULL:
+        return 0;
+      default:
+        throw new Unreachable();
+    }
+  }
+
+  void acceptHashing(HashingVisitor visitor);
+
+  CfWritableCodeKind getCfWritableCodeKind();
+
+  default boolean isCfCode() {
+    return false;
+  }
+
+  default CfCode asCfCode() {
+    return null;
+  }
+
   void writeCf(
       ProgramMethod method,
       CfVersion classFileVersion,
diff --git a/src/main/java/com/android/tools/r8/graph/Code.java b/src/main/java/com/android/tools/r8/graph/Code.java
index ba7a9c9..6e41fa0 100644
--- a/src/main/java/com/android/tools/r8/graph/Code.java
+++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -62,6 +62,14 @@
     return false;
   }
 
+  public boolean isDefaultInstanceInitializerCode() {
+    return false;
+  }
+
+  public DefaultInstanceInitializerCode asDefaultInstanceInitializerCode() {
+    return null;
+  }
+
   public boolean isDexCode() {
     return false;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java b/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java
new file mode 100644
index 0000000..df2f50a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java
@@ -0,0 +1,524 @@
+// Copyright (c) 2021, 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.graph;
+
+import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfLoad;
+import com.android.tools.r8.cf.code.CfReturnVoid;
+import com.android.tools.r8.code.InvokeDirect;
+import com.android.tools.r8.code.ReturnVoid;
+import com.android.tools.r8.dex.CodeToKeep;
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexCode.Try;
+import com.android.tools.r8.graph.DexCode.TryHandler;
+import com.android.tools.r8.ir.code.CatchHandlers;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.NumberGenerator;
+import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.ir.code.Position.SyntheticPosition;
+import com.android.tools.r8.ir.code.ValueType;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
+import com.android.tools.r8.ir.conversion.SourceCode;
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.ConsumerUtils;
+import com.android.tools.r8.utils.IteratorUtils;
+import com.android.tools.r8.utils.structural.HashingVisitor;
+import com.google.common.collect.ImmutableList;
+import java.nio.ShortBuffer;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * A piece of code on the form:
+ *
+ * <pre>
+ *   aload_0
+ *   invoke-special LSuperClass;-><init>()V
+ *   return
+ * </pre>
+ *
+ * <p>Note that (i) {@code SuperClass} may be different from {@link java.lang.Object} and (ii) the
+ * method holding this code object may have a non-empty proto.
+ */
+public class DefaultInstanceInitializerCode extends Code
+    implements CfWritableCode, DexWritableCode {
+
+  private static final DefaultInstanceInitializerCode INSTANCE =
+      new DefaultInstanceInitializerCode();
+
+  private DefaultInstanceInitializerCode() {}
+
+  public static DefaultInstanceInitializerCode get() {
+    return INSTANCE;
+  }
+
+  public static boolean canonicalizeCodeIfPossible(AppView<?> appView, ProgramMethod method) {
+    if (hasDefaultInstanceInitializerCode(method, appView)) {
+      method.getDefinition().setCode(get(), appView);
+      return true;
+    }
+    return false;
+  }
+
+  public static void uncanonicalizeCode(AppView<?> appView, ProgramMethod method) {
+    uncanonicalizeCode(appView, method, method.getHolder().getSuperType());
+  }
+
+  public static void uncanonicalizeCode(
+      AppView<?> appView, ProgramMethod method, DexType superType) {
+    DexEncodedMethod definition = method.getDefinition();
+    assert definition.getCode().isDefaultInstanceInitializerCode();
+    definition.setCode(get().toCfCode(method, appView.dexItemFactory(), superType), appView);
+  }
+
+  private static boolean hasDefaultInstanceInitializerCode(
+      ProgramMethod method, AppView<?> appView) {
+    if (!method.getDefinition().isInstanceInitializer()) {
+      return false;
+    }
+    Code code = method.getDefinition().getCode();
+    if (!code.isCfCode()) {
+      return false;
+    }
+    CfCode cfCode = code.asCfCode();
+    if (!method.getDefinition().isInstanceInitializer()
+        || !cfCode.getLocalVariables().isEmpty()
+        || !cfCode.getTryCatchRanges().isEmpty()) {
+      return false;
+    }
+    if (cfCode.getInstructions().size() > 6) {
+      // Default instance initializers typically have the following instruction sequence:
+      // [CfLabel, CfPosition, CfLoad, CfInvoke, CfReturnVoid, CfLabel].
+      return false;
+    }
+    DexItemFactory dexItemFactory = appView.dexItemFactory();
+    Iterator<CfInstruction> instructionIterator = cfCode.getInstructions().iterator();
+    // Allow skipping CfPosition instructions in instance initializers that only call Object.<init>.
+    Predicate<CfInstruction> instructionOfInterest =
+        method.getHolder().getSuperType() == dexItemFactory.objectType
+            ? instruction -> !instruction.isLabel() && !instruction.isPosition()
+            : instruction -> !instruction.isLabel();
+    CfLoad load = IteratorUtils.nextUntil(instructionIterator, instructionOfInterest).asLoad();
+    if (load == null || load.getLocalIndex() != 0) {
+      return false;
+    }
+    CfInvoke invoke = instructionIterator.next().asInvoke();
+    if (invoke == null
+        || !invoke.isInvokeConstructor(dexItemFactory)
+        || invoke.getMethod() != getParentConstructor(method, dexItemFactory)) {
+      return false;
+    }
+    return instructionIterator.next().isReturnVoid();
+  }
+
+  @Override
+  public void acceptHashing(HashingVisitor visitor) {
+    visitor.visitInt(getCfWritableCodeKind().hashCode());
+  }
+
+  @Override
+  public IRCode buildIR(ProgramMethod method, AppView<?> appView, Origin origin) {
+    DefaultInstanceInitializerSourceCode source = new DefaultInstanceInitializerSourceCode(method);
+    return IRBuilder.create(method, appView, source, origin).build(method);
+  }
+
+  @Override
+  public IRCode buildInliningIR(
+      ProgramMethod context,
+      ProgramMethod method,
+      AppView<?> appView,
+      GraphLens codeLens,
+      NumberGenerator valueNumberGenerator,
+      Position callerPosition,
+      Origin origin,
+      RewrittenPrototypeDescription protoChanges) {
+    DefaultInstanceInitializerSourceCode source =
+        new DefaultInstanceInitializerSourceCode(method, callerPosition);
+    return IRBuilder.createForInlining(
+            method, appView, codeLens, source, origin, valueNumberGenerator, protoChanges)
+        .build(context);
+  }
+
+  @Override
+  public int codeSizeInBytes() {
+    return InvokeDirect.SIZE + ReturnVoid.SIZE;
+  }
+
+  @Override
+  public void collectIndexedItems(
+      IndexedItemCollection indexedItems,
+      ProgramMethod context,
+      GraphLens graphLens,
+      LensCodeRewriterUtils rewriter) {
+    getParentConstructor(context, rewriter.dexItemFactory()).collectIndexedItems(indexedItems);
+  }
+
+  @Override
+  public void collectMixedSectionItems(MixedSectionCollection mixedItems) {
+    // Intentionally empty.
+  }
+
+  @Override
+  protected int computeHashCode() {
+    return System.identityHashCode(this);
+  }
+
+  @Override
+  protected boolean computeEquals(Object other) {
+    return this == other;
+  }
+
+  @Override
+  public int estimatedDexCodeSizeUpperBoundInBytes() {
+    return codeSizeInBytes();
+  }
+
+  @Override
+  public CfWritableCodeKind getCfWritableCodeKind() {
+    return CfWritableCodeKind.DEFAULT_INSTANCE_INITIALIZER;
+  }
+
+  @Override
+  public DexWritableCodeKind getDexWritableCodeKind() {
+    return DexWritableCodeKind.DEFAULT_INSTANCE_INITIALIZER;
+  }
+
+  @Override
+  public DexDebugInfoForWriting getDebugInfoForWriting() {
+    return null;
+  }
+
+  @Override
+  public TryHandler[] getHandlers() {
+    return new TryHandler[0];
+  }
+
+  @Override
+  public DexString getHighestSortingString() {
+    return null;
+  }
+
+  @Override
+  public int getIncomingRegisterSize(ProgramMethod method) {
+    return getMaxLocals(method);
+  }
+
+  static DexMethod getParentConstructor(DexClassAndMethod method, DexItemFactory dexItemFactory) {
+    return dexItemFactory.createInstanceInitializer(method.getHolder().getSuperType());
+  }
+
+  private int getMaxLocals(ProgramMethod method) {
+    int maxLocals = method.getAccessFlags().isStatic() ? 0 : 1;
+    for (DexType parameter : method.getParameters()) {
+      maxLocals += parameter.getRequiredRegisters();
+    }
+    return maxLocals;
+  }
+
+  private int getMaxStack() {
+    return 1;
+  }
+
+  @Override
+  public int getOutgoingRegisterSize() {
+    return 1;
+  }
+
+  @Override
+  public int getRegisterSize(ProgramMethod method) {
+    return getIncomingRegisterSize(method);
+  }
+
+  @Override
+  public Try[] getTries() {
+    return new Try[0];
+  }
+
+  @Override
+  public boolean isCfWritableCode() {
+    return true;
+  }
+
+  @Override
+  public CfWritableCode asCfWritableCode() {
+    return this;
+  }
+
+  @Override
+  public boolean isDexWritableCode() {
+    return true;
+  }
+
+  @Override
+  public DexWritableCode asDexWritableCode() {
+    return this;
+  }
+
+  @Override
+  public boolean isEmptyVoidMethod() {
+    return false;
+  }
+
+  @Override
+  public boolean isDefaultInstanceInitializerCode() {
+    return true;
+  }
+
+  @Override
+  public DefaultInstanceInitializerCode asDefaultInstanceInitializerCode() {
+    return this;
+  }
+
+  @Override
+  public boolean isSharedCodeObject() {
+    return true;
+  }
+
+  @Override
+  public void registerCodeReferences(ProgramMethod method, UseRegistry registry) {
+    internalRegisterCodeReferences(method, registry);
+  }
+
+  @Override
+  public void registerCodeReferencesForDesugaring(ClasspathMethod method, UseRegistry registry) {
+    internalRegisterCodeReferences(method, registry);
+  }
+
+  private void internalRegisterCodeReferences(DexClassAndMethod method, UseRegistry<?> registry) {
+    registry.registerInvokeDirect(getParentConstructor(method, registry.dexItemFactory()));
+  }
+
+  @Override
+  public DexWritableCode rewriteCodeWithJumboStrings(
+      ProgramMethod method, ObjectToOffsetMapping mapping, DexItemFactory factory, boolean force) {
+    // Intentionally empty. This piece of code does not have any const-string instructions.
+    return this;
+  }
+
+  @Override
+  public void setCallSiteContexts(ProgramMethod method) {
+    // Intentionally empty. This piece of code does not have any call sites.
+  }
+
+  public CfCode toCfCode(ProgramMethod method, DexItemFactory dexItemFactory) {
+    return toCfCode(method, dexItemFactory, method.getHolder().getSuperType());
+  }
+
+  public CfCode toCfCode(ProgramMethod method, DexItemFactory dexItemFactory, DexType supertype) {
+    List<CfInstruction> instructions =
+        Arrays.asList(
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfInvoke(
+                Opcodes.INVOKESPECIAL, dexItemFactory.createInstanceInitializer(supertype), false),
+            new CfReturnVoid());
+    return new CfCode(method.getHolderType(), getMaxStack(), getMaxLocals(method), instructions);
+  }
+
+  @Override
+  public void writeCf(
+      ProgramMethod method,
+      CfVersion classFileVersion,
+      AppView<?> appView,
+      NamingLens namingLens,
+      LensCodeRewriterUtils rewriter,
+      MethodVisitor visitor) {
+    visitor.visitVarInsn(Opcodes.ALOAD, 0);
+    visitor.visitMethodInsn(
+        Opcodes.INVOKESPECIAL,
+        namingLens.lookupInternalName(method.getHolder().getSuperType()),
+        "<init>",
+        "()V",
+        false);
+    visitor.visitInsn(Opcodes.RETURN);
+    visitor.visitEnd();
+    visitor.visitMaxs(getMaxStack(), getMaxLocals(method));
+  }
+
+  @Override
+  public void writeDex(
+      ShortBuffer shortBuffer,
+      ProgramMethod context,
+      GraphLens graphLens,
+      LensCodeRewriterUtils lensCodeRewriter,
+      ObjectToOffsetMapping mapping) {
+    new InvokeDirect(1, getParentConstructor(context, mapping.dexItemFactory()), 0, 0, 0, 0, 0)
+        .write(shortBuffer, context, graphLens, mapping, lensCodeRewriter);
+    new ReturnVoid().write(shortBuffer, context, graphLens, mapping, lensCodeRewriter);
+  }
+
+  @Override
+  public void writeKeepRulesForDesugaredLibrary(CodeToKeep codeToKeep) {
+    // Intentionally empty.
+  }
+
+  @Override
+  public String toString() {
+    return "DefaultInstanceInitializerCode";
+  }
+
+  @Override
+  public String toString(DexEncodedMethod method, ClassNameMapper naming) {
+    return toString();
+  }
+
+  static class DefaultInstanceInitializerSourceCode implements SourceCode {
+
+    private static final List<Consumer<IRBuilder>> instructionBuilders =
+        ImmutableList.of(
+            builder ->
+                builder.add(
+                    com.android.tools.r8.ir.code.InvokeDirect.builder()
+                        .setMethod(
+                            getParentConstructor(
+                                builder.getProgramMethod(), builder.dexItemFactory()))
+                        .setSingleArgument(builder.getReceiverValue())
+                        .build()),
+            IRBuilder::addReturn);
+
+    private final Position position;
+
+    DefaultInstanceInitializerSourceCode(ProgramMethod method) {
+      this(method, null);
+    }
+
+    DefaultInstanceInitializerSourceCode(ProgramMethod method, Position callerPosition) {
+      this.position =
+          SyntheticPosition.builder()
+              .setLine(0)
+              .setMethod(method.getReference())
+              .setCallerPosition(callerPosition)
+              .build();
+    }
+
+    @Override
+    public int instructionCount() {
+      return instructionBuilders.size();
+    }
+
+    @Override
+    public int instructionIndex(int instructionOffset) {
+      return instructionOffset;
+    }
+
+    @Override
+    public int instructionOffset(int instructionIndex) {
+      return instructionIndex;
+    }
+
+    @Override
+    public void buildPrelude(IRBuilder builder) {
+      int firstArgumentRegister = 0;
+      builder.buildArgumentsWithRewrittenPrototypeChanges(
+          firstArgumentRegister, builder.getMethod(), ConsumerUtils.emptyBiConsumer());
+    }
+
+    @Override
+    public void buildInstruction(
+        IRBuilder builder, int instructionIndex, boolean firstBlockInstruction) {
+      instructionBuilders.get(instructionIndex).accept(builder);
+    }
+
+    @Override
+    public void buildPostlude(IRBuilder builder) {
+      // Intentionally empty.
+    }
+
+    @Override
+    public void clear() {
+      // Intentionally empty.
+    }
+
+    @Override
+    public Position getCanonicalDebugPositionAtOffset(int offset) {
+      return null;
+    }
+
+    @Override
+    public CatchHandlers<Integer> getCurrentCatchHandlers(IRBuilder builder) {
+      return null;
+    }
+
+    @Override
+    public Position getCurrentPosition() {
+      return position;
+    }
+
+    @Override
+    public DebugLocalInfo getIncomingLocal(int register) {
+      return null;
+    }
+
+    @Override
+    public DebugLocalInfo getIncomingLocalAtBlock(int register, int blockOffset) {
+      return null;
+    }
+
+    @Override
+    public DebugLocalInfo getOutgoingLocal(int register) {
+      return null;
+    }
+
+    @Override
+    public void setUp() {
+      // Intentionally empty.
+    }
+
+    @Override
+    public int traceInstruction(int instructionIndex, IRBuilder builder) {
+      // This instruction does not close the block.
+      return -1;
+    }
+
+    @Override
+    public boolean verifyCurrentInstructionCanThrow() {
+      return true;
+    }
+
+    @Override
+    public boolean verifyRegister(int register) {
+      return true;
+    }
+
+    @Override
+    public void buildBlockTransfer(
+        IRBuilder builder, int predecessorOffset, int successorOffset, boolean isExceptional) {
+      throw new Unreachable();
+    }
+
+    @Override
+    public int getMoveExceptionRegister(int instructionIndex) {
+      throw new Unreachable();
+    }
+
+    @Override
+    public void resolveAndBuildNewArrayFilledData(
+        int arrayRef, int payloadOffset, IRBuilder builder) {
+      throw new Unreachable();
+    }
+
+    @Override
+    public void resolveAndBuildSwitch(
+        int value, int fallthroughOffset, int payloadOffset, IRBuilder builder) {
+      throw new Unreachable();
+    }
+
+    @Override
+    public boolean verifyLocalInScope(DebugLocalInfo local) {
+      throw new Unreachable();
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java
index c23bf18..8c16e46 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -30,6 +30,7 @@
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.structural.Equatable;
 import com.android.tools.r8.utils.structural.HashCodeVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import com.android.tools.r8.utils.structural.StructuralItem;
 import com.android.tools.r8.utils.structural.StructuralMapping;
 import com.android.tools.r8.utils.structural.StructuralSpecification;
@@ -130,6 +131,11 @@
   }
 
   @Override
+  public DexWritableCodeKind getDexWritableCodeKind() {
+    return DexWritableCodeKind.DEFAULT;
+  }
+
+  @Override
   public StructuralMapping<DexCode> getStructuralMapping() {
     return DexCode::specify;
   }
@@ -319,6 +325,11 @@
   }
 
   @Override
+  public void acceptHashing(HashingVisitor visitor) {
+    visitor.visit(this, getStructuralMapping());
+  }
+
+  @Override
   public int computeHashCode() {
     return incomingRegisterSize * 2
         + registerSize * 3
@@ -531,6 +542,7 @@
     return builder.toString();
   }
 
+  @Override
   public void collectIndexedItems(
       IndexedItemCollection indexedItems,
       ProgramMethod context,
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index afdef0a..d3be3c2 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -304,21 +304,22 @@
   }
 
   private static int compareCodeObject(Code code1, Code code2, CompareToVisitor visitor) {
-    if (code1.isCfCode() && code2.isCfCode()) {
-      return code1.asCfCode().acceptCompareTo(code2.asCfCode(), visitor);
+    if (code1.isCfWritableCode() && code2.isCfWritableCode()) {
+      return code1.asCfWritableCode().acceptCompareTo(code2.asCfWritableCode(), visitor);
     }
-    if (code1.isDexCode() && code2.isDexCode()) {
-      return code1.asDexCode().acceptCompareTo(code2.asDexCode(), visitor);
+    if (code1.isDexWritableCode() && code2.isDexWritableCode()) {
+      return code1.asDexWritableCode().acceptCompareTo(code2.asDexWritableCode(), visitor);
     }
     throw new Unreachable(
         "Unexpected attempt to compare incompatible synthetic objects: " + code1 + " and " + code2);
   }
 
   private static void hashCodeObject(Code code, HashingVisitor visitor) {
-    if (code.isCfCode()) {
-      code.asCfCode().acceptHashing(visitor);
+    if (code.isCfWritableCode()) {
+      code.asCfWritableCode().acceptHashing(visitor);
     } else {
-      code.asDexCode().acceptHashing(visitor);
+      assert code.isDexWritableCode();
+      code.asDexWritableCode().acceptHashing(visitor);
     }
   }
 
@@ -394,6 +395,11 @@
     return null;
   }
 
+  public ProgramMethod asProgramMethod(DexProgramClass holder) {
+    assert getHolderType() == holder.getType();
+    return new ProgramMethod(holder, this);
+  }
+
   public ProgramMethod asProgramMethod(DexDefinitionSupplier definitions) {
     assert getReference().holder.isClassType();
     DexProgramClass clazz = asProgramClassOrNull(definitions.definitionForHolder(getReference()));
diff --git a/src/main/java/com/android/tools/r8/graph/DexWritableCode.java b/src/main/java/com/android/tools/r8/graph/DexWritableCode.java
index 9aea5e4..664b4e8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexWritableCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexWritableCode.java
@@ -5,22 +5,60 @@
 package com.android.tools.r8.graph;
 
 import com.android.tools.r8.dex.CodeToKeep;
+import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.dex.MixedSectionCollection;
+import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexCode.Try;
 import com.android.tools.r8.graph.DexCode.TryHandler;
 import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import java.nio.ShortBuffer;
 
 public interface DexWritableCode {
 
+  enum DexWritableCodeKind {
+    DEFAULT,
+    DEFAULT_INSTANCE_INITIALIZER,
+    THROW_NULL
+  }
+
+  default int acceptCompareTo(DexWritableCode code, CompareToVisitor visitor) {
+    DexWritableCodeKind kind = getDexWritableCodeKind();
+    DexWritableCodeKind otherKind = code.getDexWritableCodeKind();
+    if (kind != otherKind) {
+      return kind.compareTo(otherKind);
+    }
+    switch (kind) {
+      case DEFAULT:
+        return asDexCode().acceptCompareTo(code.asDexCode(), visitor);
+      case DEFAULT_INSTANCE_INITIALIZER:
+        return 0;
+      case THROW_NULL:
+        return 0;
+      default:
+        throw new Unreachable();
+    }
+  }
+
+  void acceptHashing(HashingVisitor visitor);
+
   int codeSizeInBytes();
 
+  void collectIndexedItems(
+      IndexedItemCollection indexedItems,
+      ProgramMethod context,
+      GraphLens graphLens,
+      LensCodeRewriterUtils rewriter);
+
   void collectMixedSectionItems(MixedSectionCollection mixedItems);
 
   void writeKeepRulesForDesugaredLibrary(CodeToKeep codeToKeep);
 
   DexDebugInfoForWriting getDebugInfoForWriting();
 
+  DexWritableCodeKind getDexWritableCodeKind();
+
   DexString getHighestSortingString();
 
   TryHandler[] getHandlers();
@@ -33,6 +71,14 @@
 
   int getOutgoingRegisterSize();
 
+  default boolean isDexCode() {
+    return false;
+  }
+
+  default DexCode asDexCode() {
+    return null;
+  }
+
   /** Rewrites the code to have JumboString bytecode if required by mapping. */
   DexWritableCode rewriteCodeWithJumboStrings(
       ProgramMethod method, ObjectToOffsetMapping mapping, DexItemFactory factory, boolean force);
diff --git a/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java b/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
index 196bc31..04b3eb0 100644
--- a/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
+++ b/src/main/java/com/android/tools/r8/graph/ObjectToOffsetMapping.java
@@ -29,6 +29,7 @@
   private final static int NOT_FOUND = -1;
   private final static int NOT_SET = -2;
 
+  private final AppView<?> appView;
   private final GraphLens graphLens;
   private final NamingLens namingLens;
   private final InitClassLens initClassLens;
@@ -74,6 +75,7 @@
     assert callSites != null;
     assert methodHandles != null;
     assert initClassLens != null;
+    this.appView = appView;
     this.graphLens = graphLens;
     this.namingLens = namingLens;
     this.initClassLens = initClassLens;
@@ -228,6 +230,10 @@
     return map == null ? Collections.emptyList() : map.keySet();
   }
 
+  public DexItemFactory dexItemFactory() {
+    return appView.dexItemFactory();
+  }
+
   public GraphLens getGraphLens() {
     return graphLens;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
index 90278d0..844b45e 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
@@ -64,11 +64,7 @@
     getReference().collectIndexedItems(indexedItems);
     if (definition.hasCode()) {
       Code code = definition.getCode();
-      if (code.isDexCode()) {
-        code.asDexCode().collectIndexedItems(indexedItems, this, graphLens, rewriter);
-      } else {
-        assert code.isThrowNullCode();
-      }
+      code.asDexWritableCode().collectIndexedItems(indexedItems, this, graphLens, rewriter);
     }
     definition.annotations().collectIndexedItems(indexedItems);
     definition.parameterAnnotationsList.collectIndexedItems(indexedItems);
diff --git a/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java b/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java
index c9d875a..28a7ace 100644
--- a/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java
+++ b/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.code.Const4;
 import com.android.tools.r8.code.Throw;
 import com.android.tools.r8.dex.CodeToKeep;
+import com.android.tools.r8.dex.IndexedItemCollection;
 import com.android.tools.r8.dex.MixedSectionCollection;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexCode.Try;
@@ -24,6 +25,7 @@
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.ConsumerUtils;
+import com.android.tools.r8.utils.structural.HashingVisitor;
 import com.google.common.collect.ImmutableList;
 import java.nio.ShortBuffer;
 import java.util.List;
@@ -42,6 +44,11 @@
   }
 
   @Override
+  public void acceptHashing(HashingVisitor visitor) {
+    visitor.visitInt(getCfWritableCodeKind().hashCode());
+  }
+
+  @Override
   public IRCode buildIR(ProgramMethod method, AppView<?> appView, Origin origin) {
     ThrowNullSourceCode source = new ThrowNullSourceCode(method);
     return IRBuilder.create(method, appView, source, origin).build(method);
@@ -69,6 +76,15 @@
   }
 
   @Override
+  public void collectIndexedItems(
+      IndexedItemCollection indexedItems,
+      ProgramMethod context,
+      GraphLens graphLens,
+      LensCodeRewriterUtils rewriter) {
+    // Intentionally empty.
+  }
+
+  @Override
   public void collectMixedSectionItems(MixedSectionCollection mixedItems) {
     // Intentionally empty.
   }
@@ -89,6 +105,16 @@
   }
 
   @Override
+  public CfWritableCodeKind getCfWritableCodeKind() {
+    return CfWritableCodeKind.THROW_NULL;
+  }
+
+  @Override
+  public DexWritableCodeKind getDexWritableCodeKind() {
+    return DexWritableCodeKind.THROW_NULL;
+  }
+
+  @Override
   public DexDebugInfoForWriting getDebugInfoForWriting() {
     return null;
   }
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 2f9a4ee..e16a15f 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -117,7 +117,7 @@
     appView.setHorizontallyMergedClasses(mergedClasses, mode);
 
     HorizontalClassMergerGraphLens horizontalClassMergerGraphLens =
-        createLens(mergedClasses, lensBuilder, syntheticArgumentClass);
+        createLens(mergedClasses, lensBuilder, mode, syntheticArgumentClass);
 
     assert verifyNoCyclesInInterfaceHierarchies(groups);
 
@@ -235,8 +235,9 @@
   private HorizontalClassMergerGraphLens createLens(
       HorizontallyMergedClasses mergedClasses,
       HorizontalClassMergerGraphLens.Builder lensBuilder,
+      Mode mode,
       SyntheticArgumentClass syntheticArgumentClass) {
-    return new TreeFixer(appView, mergedClasses, lensBuilder, syntheticArgumentClass)
+    return new TreeFixer(appView, mergedClasses, lensBuilder, mode, syntheticArgumentClass)
         .fixupTypeReferences();
   }
 
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 2ab6c46..3c97009 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
@@ -375,8 +375,8 @@
         syntheticInitializerConverterBuilder.add(
             new ProgramMethod(group.getTarget(), newInstanceInitializer));
       } else {
-        assert !appView.options().isGeneratingClassFiles()
-            || newInstanceInitializer.getCode().isCfCode();
+        assert appView.options().isGeneratingDex()
+            || newInstanceInitializer.getCode().isCfWritableCode();
       }
     }
   }
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 b85eab3..7a74a2b 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
@@ -7,6 +7,7 @@
 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.DefaultInstanceInitializerCode;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexField;
@@ -19,6 +20,7 @@
 import com.android.tools.r8.graph.DexTypeList;
 import com.android.tools.r8.graph.EnclosingMethodAttribute;
 import com.android.tools.r8.graph.TreeFixerBase;
+import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
 import com.android.tools.r8.ir.conversion.ExtraUnusedNullParameter;
 import com.android.tools.r8.shaking.AnnotationFixer;
 import com.android.tools.r8.utils.ArrayUtils;
@@ -28,8 +30,10 @@
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
 import java.util.Collections;
+import java.util.IdentityHashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 
@@ -42,9 +46,12 @@
 
   private final AppView<? extends AppInfoWithClassHierarchy> appView;
   private final HorizontallyMergedClasses mergedClasses;
+  private final Mode mode;
   private final HorizontalClassMergerGraphLens.Builder lensBuilder;
   private final DexItemFactory dexItemFactory;
   private final SyntheticArgumentClass syntheticArgumentClass;
+
+  private final Map<DexProgramClass, DexType> originalSuperTypes = new IdentityHashMap<>();
   private final BiMap<DexMethodSignature, DexMethodSignature> reservedInterfaceSignatures =
       HashBiMap.create();
 
@@ -52,10 +59,12 @@
       AppView<? extends AppInfoWithClassHierarchy> appView,
       HorizontallyMergedClasses mergedClasses,
       HorizontalClassMergerGraphLens.Builder lensBuilder,
+      Mode mode,
       SyntheticArgumentClass syntheticArgumentClass) {
     super(appView);
     this.appView = appView;
     this.mergedClasses = mergedClasses;
+    this.mode = mode;
     this.lensBuilder = lensBuilder;
     this.syntheticArgumentClass = syntheticArgumentClass;
     this.dexItemFactory = appView.dexItemFactory();
@@ -145,7 +154,11 @@
   }
 
   private void fixupProgramClassSuperTypes(DexProgramClass clazz) {
-    clazz.superType = fixupType(clazz.superType);
+    DexType rewrittenSuperType = fixupType(clazz.getSuperType());
+    if (rewrittenSuperType != clazz.getSuperType()) {
+      originalSuperTypes.put(clazz, clazz.getSuperType());
+      clazz.superType = rewrittenSuperType;
+    }
     clazz.setInterfaces(fixupInterfaces(clazz, clazz.getInterfaces()));
   }
 
@@ -166,7 +179,7 @@
             method -> fixupVirtualMethod(remappedClassVirtualMethods, newMethodReferences, method));
     clazz
         .getMethodCollection()
-        .replaceAllDirectMethods(method -> fixupDirectMethod(newMethodReferences, method));
+        .replaceAllDirectMethods(method -> fixupDirectMethod(newMethodReferences, clazz, method));
 
     Set<DexField> newFieldReferences = Sets.newIdentityHashSet();
     DexEncodedField[] instanceFields = clazz.clearInstanceFields();
@@ -220,7 +233,7 @@
     Set<DexMethodSignature> newDirectMethods = new LinkedHashSet<>();
     iface
         .getMethodCollection()
-        .replaceDirectMethods(method -> fixupDirectMethod(newDirectMethods, method));
+        .replaceDirectMethods(method -> fixupDirectMethod(newDirectMethods, iface, method));
     iface.getMethodCollection().replaceVirtualMethods(this::fixupVirtualInterfaceMethod);
 
     assert !iface.hasInstanceFields();
@@ -263,7 +276,7 @@
   }
 
   private DexEncodedMethod fixupDirectMethod(
-      Set<DexMethodSignature> newMethods, DexEncodedMethod method) {
+      Set<DexMethodSignature> newMethods, DexProgramClass clazz, DexEncodedMethod method) {
     DexMethod originalMethodReference = method.getReference();
 
     // Fix all type references in the method prototype.
@@ -299,6 +312,17 @@
     boolean changed = newMethods.add(newMethodReference.getSignature());
     assert changed;
 
+    // Convert out of DefaultInstanceInitializerCode, since this piece of code will require lens
+    // code rewriting.
+    if (mode.isInitial()
+        && method.hasCode()
+        && method.getCode().isDefaultInstanceInitializerCode()
+        && mergedClasses.hasBeenMergedOrIsMergeTarget(clazz.getSuperType())) {
+      DexType originalSuperType = originalSuperTypes.getOrDefault(clazz, clazz.getSuperType());
+      DefaultInstanceInitializerCode.uncanonicalizeCode(
+          appView, method.asProgramMethod(clazz), originalSuperType);
+    }
+
     return fixupProgramMethod(newMethodReference, method);
   }
 
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIllegalInlining.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIllegalInlining.java
index fce04fa..6e88f4d 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIllegalInlining.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIllegalInlining.java
@@ -34,19 +34,20 @@
     }
 
     // For non-jar/cf code we currently cannot guarantee that markForceInline() will succeed.
-    if (code == null || !code.isCfCode()) {
+    if (code == null) {
       return true;
     }
 
-    CfCode cfCode = code.asCfCode();
-
-    ConstraintWithTarget constraint =
-        cfCode.computeInliningConstraint(method, appView, appView.graphLens(), method);
-    if (constraint == ConstraintWithTarget.NEVER) {
+    if (code.isCfCode()) {
+      CfCode cfCode = code.asCfCode();
+      ConstraintWithTarget constraint =
+          cfCode.computeInliningConstraint(method, appView, appView.graphLens(), method);
+      return constraint == ConstraintWithTarget.NEVER;
+    } else if (code.isDefaultInstanceInitializerCode()) {
+      return false;
+    } else {
       return true;
     }
-
-    return false;
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index eb30bd3..513719e 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -1169,7 +1169,7 @@
     assert code.verifyTypes(appView);
     assert code.isConsistentSSA();
 
-    if (appView.isCfByteCodePassThrough(method)) {
+    if (shouldPassThrough(context)) {
       // If the code is pass trough, do not finalize by overwriting the existing code.
       assert appView.enableWholeProgramOptimizations();
       timing.begin("Collect optimization info");
@@ -1183,6 +1183,7 @@
           BytecodeMetadataProvider.builder(),
           timing);
       timing.end();
+      markProcessed(code, feedback);
       return timing;
     }
 
@@ -1516,6 +1517,15 @@
     return timing;
   }
 
+  private boolean shouldPassThrough(ProgramMethod method) {
+    if (appView.isCfByteCodePassThrough(method.getDefinition())) {
+      return true;
+    }
+    Code code = method.getDefinition().getCode();
+    assert !code.isThrowNullCode();
+    return code.isDefaultInstanceInitializerCode();
+  }
+
   // Compute optimization info summary for the current method unless it is pinned
   // (in that case we should not be making any assumptions about the behavior of the method).
   public void collectOptimizationInfo(
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java
index dd5b328..c411187 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriterUtils.java
@@ -61,6 +61,10 @@
     this.rewrittenCallSiteCache = null;
   }
 
+  public DexItemFactory dexItemFactory() {
+    return definitions.dexItemFactory();
+  }
+
   public DexCallSite rewriteCallSite(DexCallSite callSite, ProgramMethod context) {
     if (rewrittenCallSiteCache == null) {
       return rewriteCallSiteInternal(callSite, context);
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java b/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
index f00e19c..2e21467 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
@@ -176,7 +176,7 @@
               instructions);
       code.asCfCode().setInstructions(newInstructions);
     } else {
-      assert code.isThrowNullCode();
+      assert code.isDefaultInstanceInitializerCode() || code.isThrowNullCode();
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
index f837095..db5dcdc 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -4,6 +4,7 @@
 package com.android.tools.r8.shaking;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DefaultInstanceInitializerCode;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMember;
@@ -296,15 +297,16 @@
         firstUnreachableIndex(methods, method -> appInfo.isLiveMethod(method.getReference()));
     // Return the original array if all methods are used.
     if (firstUnreachable == -1) {
+      for (DexEncodedMethod method : methods) {
+        canonicalizeCode(method.asProgramMethod(clazz));
+      }
       return null;
     }
     ArrayList<DexEncodedMethod> reachableMethods = new ArrayList<>(methods.size());
-    for (int i = 0; i < firstUnreachable; i++) {
-      reachableMethods.add(methods.get(i));
-    }
-    for (int i = firstUnreachable; i < methods.size(); i++) {
+    for (int i = 0; i < methods.size(); i++) {
       DexEncodedMethod method = methods.get(i);
       if (appInfo.isLiveMethod(method.getReference())) {
+        canonicalizeCode(method.asProgramMethod(clazz));
         reachableMethods.add(method);
       } else if (options.configurationDebugging) {
         // Keep the method but rewrite its body, if it has one.
@@ -339,6 +341,12 @@
         : reachableMethods.toArray(DexEncodedMethod.EMPTY_ARRAY);
   }
 
+  private void canonicalizeCode(ProgramMethod method) {
+    if (method.getDefinition().hasCode()) {
+      DefaultInstanceInitializerCode.canonicalizeCodeIfPossible(appView, method);
+    }
+  }
+
   private DexEncodedField[] reachableFields(List<DexEncodedField> fields) {
     AppInfoWithLiveness appInfo = appView.appInfo();
     Predicate<DexEncodedField> isReachableOrReferencedField =
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index e157ff8..c53f8ea 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -15,6 +15,7 @@
 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.DefaultInstanceInitializerCode;
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexClassAndMethod;
@@ -1119,6 +1120,12 @@
       // Rewrite generic signatures before we merge fields.
       rewriteGenericSignatures(target, source, directMethods.values(), virtualMethods.values());
 
+      // Convert out of DefaultInstanceInitializerCode, since this piece of code will require lens
+      // code rewriting.
+      target.forEachProgramInstanceInitializerMatching(
+          method -> method.getCode().isDefaultInstanceInitializerCode(),
+          method -> DefaultInstanceInitializerCode.uncanonicalizeCode(appView, method));
+
       // Step 2: Merge fields
       Set<DexString> existingFieldNames = new HashSet<>();
       for (DexEncodedField field : target.fields()) {
@@ -1891,6 +1898,8 @@
           return AbortReason.MAIN_DEX_ROOT_OUTSIDE_REFERENCE;
         }
         return null;
+      } else if (code.isDefaultInstanceInitializerCode()) {
+        return null;
       }
       // For non-jar/cf code we currently cannot guarantee that markForceInline() will succeed.
     }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/B135918413.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/B135918413.java
index be5c5a2..c9cc17b 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/B135918413.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/B135918413.java
@@ -30,7 +30,7 @@
 
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withAllRuntimes().build();
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
   }
 
   public B135918413(TestParameters parameters) {
@@ -43,7 +43,7 @@
         .addInnerClasses(B135918413.class)
         .addKeepMainRule(TestClass.class)
         .enableInliningAnnotations()
-        .setMinApi(parameters.getRuntime())
+        .setMinApi(parameters.getApiLevel())
         .compile()
         .inspect(this::inspect)
         .run(parameters.getRuntime(), TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageMissingSuperTypeTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageMissingSuperTypeTest.java
index af4995f..19138f1 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageMissingSuperTypeTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageMissingSuperTypeTest.java
@@ -9,6 +9,7 @@
 
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
 import com.android.tools.r8.TestParameters;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -53,6 +54,7 @@
         .addDontWarn(MissingSuperType.class)
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
+        .enableNoHorizontalClassMergingAnnotations()
         .compile()
         .inspect(
             inspector -> {
@@ -77,6 +79,7 @@
   }
 
   @NeverClassInline
+  @NoHorizontalClassMerging
   public static class ClassWithSuperCall extends MissingSuperType {
 
     @Override
@@ -88,6 +91,7 @@
   }
 
   @NeverClassInline
+  @NoHorizontalClassMerging
   public static class ClassWithoutSuperCall extends MissingSuperType {
 
     @Override