[ApiModel] Outline NewInstance and <init> to new library classes

Bug: b/243883589
Change-Id: Iefe0a9851d546b0a2c16a12fdde3a79c676ddecc
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 c62d786..e323b6b 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
@@ -62,6 +62,7 @@
 import com.android.tools.r8.ir.optimize.IdempotentFunctionCallCanonicalizer;
 import com.android.tools.r8.ir.optimize.Inliner;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
+import com.android.tools.r8.ir.optimize.InstanceInitializerOutliner;
 import com.android.tools.r8.ir.optimize.NaturalIntLoopRemover;
 import com.android.tools.r8.ir.optimize.RedundantFieldLoadAndStoreElimination;
 import com.android.tools.r8.ir.optimize.ReflectionOptimizer;
@@ -143,6 +144,7 @@
   private final ServiceLoaderRewriter serviceLoaderRewriter;
   private final EnumValueOptimizer enumValueOptimizer;
   private final EnumUnboxer enumUnboxer;
+  private final InstanceInitializerOutliner instanceInitializerOutliner;
 
   public final AssumeInserter assumeInserter;
   private final DynamicTypeOptimization dynamicTypeOptimization;
@@ -227,6 +229,7 @@
       this.enumValueOptimizer = null;
       this.enumUnboxer = EnumUnboxer.empty();
       this.assumeInserter = null;
+      this.instanceInitializerOutliner = null;
       return;
     }
     this.instructionDesugaring =
@@ -237,6 +240,12 @@
         options.processCovariantReturnTypeAnnotations
             ? new CovariantReturnTypeAnnotationTransformer(this, appView.dexItemFactory())
             : null;
+    if (appView.options().desugarState.isOn()
+        && appView.options().apiModelingOptions().enableOutliningOfMethods) {
+      this.instanceInitializerOutliner = new InstanceInitializerOutliner(appView);
+    } else {
+      this.instanceInitializerOutliner = null;
+    }
     if (appView.enableWholeProgramOptimizations()) {
       assert appView.appInfo().hasLiveness();
       assert appView.rootSet() != null;
@@ -358,6 +367,10 @@
     reportNestDesugarDependencies();
     clearNestAttributes();
 
+    if (instanceInitializerOutliner != null) {
+      processSimpleSynthesizeMethods(instanceInitializerOutliner.getSynthesizedMethods(), executor);
+    }
+
     application = commitPendingSyntheticItemsD8(appView, application);
 
     postProcessingDesugaringForD8(methodProcessor, interfaceProcessor, executor);
@@ -780,10 +793,15 @@
     builder.setHighestSortingString(highestSortingString);
 
     if (serviceLoaderRewriter != null) {
-      processSynthesizedServiceLoaderMethods(
+      processSimpleSynthesizeMethods(
           serviceLoaderRewriter.getServiceLoadMethods(), executorService);
     }
 
+    if (instanceInitializerOutliner != null) {
+      processSimpleSynthesizeMethods(
+          instanceInitializerOutliner.getSynthesizedMethods(), executorService);
+    }
+
     // Update optimization info for all synthesized methods at once.
     feedback.updateVisibleOptimizationInfo();
 
@@ -865,14 +883,14 @@
     return onWaveDoneActions != null;
   }
 
-  private void processSynthesizedServiceLoaderMethods(
+  private void processSimpleSynthesizeMethods(
       List<ProgramMethod> serviceLoadMethods, ExecutorService executorService)
       throws ExecutionException {
     ThreadUtils.processItems(
-        serviceLoadMethods, this::forEachSynthesizedServiceLoaderMethod, executorService);
+        serviceLoadMethods, this::processAndFinalizeSimpleSynthesiedMethod, executorService);
   }
 
-  private void forEachSynthesizedServiceLoaderMethod(ProgramMethod method) {
+  private void processAndFinalizeSimpleSynthesiedMethod(ProgramMethod method) {
     IRCode code = method.buildIR(appView);
     assert code != null;
     codeRewriter.rewriteMoveResult(code);
@@ -1208,6 +1226,11 @@
       timing.end();
     }
 
+    if (instanceInitializerOutliner != null) {
+      instanceInitializerOutliner.rewriteInstanceInitializers(
+          code, context, methodProcessingContext);
+    }
+
     previous = printMethod(code, "IR after disable assertions (SSA)", previous);
 
     // Update the IR code if collected call site optimization info has something useful.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
index 6137206..67ec80a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
@@ -41,7 +41,7 @@
         }
       case INVOKE_CONSTRUCTOR:
         {
-          forwardMethodBuilder.setConstructorTarget(target, appView.dexItemFactory());
+          forwardMethodBuilder.setConstructorTargetWithNewInstance(target);
           break;
         }
       default:
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InstanceInitializerOutliner.java b/src/main/java/com/android/tools/r8/ir/optimize/InstanceInitializerOutliner.java
new file mode 100644
index 0000000..32bff94
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InstanceInitializerOutliner.java
@@ -0,0 +1,216 @@
+// Copyright (c) 2022, 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.ir.optimize;
+
+import com.android.tools.r8.androidapi.ComputedApiLevel;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
+import com.android.tools.r8.graph.AppView;
+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.DexProto;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeDirect;
+import com.android.tools.r8.ir.code.InvokeStatic;
+import com.android.tools.r8.ir.code.NewInstance;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.synthetic.ForwardMethodBuilder;
+import com.android.tools.r8.ir.synthetic.NewInstanceSourceCode;
+import com.android.tools.r8.shaking.ComputeApiLevelUseRegistry;
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * The InstanceInitializerOutliner will outline instance initializers and their NewInstance source.
+ * Unlike the ApiInvokeOutlinerDesugaring that works on CF, this works on IR to properly replace the
+ * users of the NewInstance call.
+ */
+public class InstanceInitializerOutliner {
+
+  private final AppView<?> appView;
+  private final DexItemFactory factory;
+
+  private final List<ProgramMethod> synthesizedMethods = new ArrayList<>();
+
+  public InstanceInitializerOutliner(AppView<?> appView) {
+    this.appView = appView;
+    this.factory = appView.dexItemFactory();
+  }
+
+  public List<ProgramMethod> getSynthesizedMethods() {
+    return synthesizedMethods;
+  }
+
+  public void rewriteInstanceInitializers(
+      IRCode code, ProgramMethod context, MethodProcessingContext methodProcessingContext) {
+    // Do not outline from already synthesized methods.
+    if (context.getDefinition().isD8R8Synthesized()) {
+      return;
+    }
+    Map<NewInstance, Value> rewrittenNewInstances = new IdentityHashMap<>();
+    ComputedApiLevel minApiLevel = appView.computedMinApiLevel();
+    InstructionListIterator iterator = code.instructionListIterator();
+    // Scan over the code to find <init> calls that needs to be outlined.
+    while (iterator.hasNext()) {
+      Instruction instruction = iterator.next();
+      InvokeDirect invokeDirect = instruction.asInvokeDirect();
+      if (invokeDirect == null) {
+        continue;
+      }
+      DexMethod invokedConstructor = invokeDirect.getInvokedMethod();
+      if (!invokedConstructor.isInstanceInitializer(factory)) {
+        continue;
+      }
+      NewInstance newInstance = invokeDirect.getFirstOperand().getDefinition().asNewInstance();
+      if (newInstance == null) {
+        // We could not find a new instance call associated with the init, this is probably a
+        // constructor call to the super class.
+        continue;
+      }
+      ComputedApiLevel apiReferenceLevel =
+          appView
+              .apiLevelCompute()
+              .computeApiLevelForLibraryReference(invokedConstructor, minApiLevel);
+      if (minApiLevel.isGreaterThanOrEqualTo(apiReferenceLevel)) {
+        continue;
+      }
+      DexEncodedMethod synthesizedInstanceInitializer =
+          createSynthesizedInstanceInitializer(
+              invokeDirect.getInvokedMethod(), apiReferenceLevel, methodProcessingContext);
+      List<Value> arguments = instruction.inValues();
+      InvokeStatic outlinedMethodInvoke =
+          InvokeStatic.builder()
+              .setMethod(synthesizedInstanceInitializer.getReference())
+              .setPosition(instruction)
+              .setFreshOutValue(code, newInstance.getOutType())
+              .setArguments(arguments.subList(1, arguments.size()))
+              .build();
+      iterator.replaceCurrentInstruction(outlinedMethodInvoke);
+      rewrittenNewInstances.put(newInstance, outlinedMethodInvoke.outValue());
+    }
+    if (rewrittenNewInstances.isEmpty()) {
+      return;
+    }
+    // Scan over NewInstance calls that needs to be outlined. We insert a call to a synthetic method
+    // with a NewInstance to preserve class-init semantics.
+    // TODO(b/244284945): If we know that arguments to an init cannot change class initializer
+    //  semantics we can avoid inserting the NewInstance outline.
+    iterator = code.instructionListIterator();
+    while (iterator.hasNext()) {
+      Instruction instruction = iterator.next();
+      if (!instruction.isNewInstance()) {
+        continue;
+      }
+      NewInstance newInstance = instruction.asNewInstance();
+      Value newOutlineInstanceValue = rewrittenNewInstances.get(newInstance);
+      if (newOutlineInstanceValue == null) {
+        continue;
+      }
+      ComputedApiLevel classApiLevel =
+          appView
+              .apiLevelCompute()
+              .computeApiLevelForLibraryReference(newInstance.getType(), minApiLevel);
+      assert classApiLevel.isKnownApiLevel();
+      DexEncodedMethod synthesizedNewInstance =
+          createSynthesizedNewInstance(
+              newInstance.getType(), classApiLevel, methodProcessingContext);
+      InvokeStatic outlinedStaticInit =
+          InvokeStatic.builder()
+              .setMethod(synthesizedNewInstance.getReference())
+              .setPosition(instruction)
+              .build();
+      newInstance.outValue().replaceUsers(newOutlineInstanceValue);
+      iterator.replaceCurrentInstruction(outlinedStaticInit);
+    }
+    // Outlining of instance initializers will in most cases change the api level of the context
+    // since all other soft verification issues has been outlined. To ensure that we do not inline
+    // the outline again in R8 - but allow inlining of other calls to min api level methods, we have
+    // to recompute the api level.
+    recomputeApiLevel(context, code);
+  }
+
+  private void recomputeApiLevel(ProgramMethod context, IRCode code) {
+    DexEncodedMethod definition = context.getDefinition();
+    if (!definition.getApiLevelForCode().isKnownApiLevel()) {
+      // This is either D8 or the api level is unknown.
+      return;
+    }
+    ComputeApiLevelUseRegistry registry =
+        new ComputeApiLevelUseRegistry(appView, context, appView.apiLevelCompute());
+    code.registerCodeReferences(context, registry);
+    ComputedApiLevel maxApiReferenceLevel = registry.getMaxApiReferenceLevel();
+    assert maxApiReferenceLevel.isKnownApiLevel();
+    definition.setApiLevelForCode(maxApiReferenceLevel);
+  }
+
+  private DexEncodedMethod createSynthesizedNewInstance(
+      DexType targetType,
+      ComputedApiLevel computedApiLevel,
+      MethodProcessingContext methodProcessingContext) {
+    DexProto proto = appView.dexItemFactory().createProto(factory.voidType);
+    ProgramMethod method =
+        appView
+            .getSyntheticItems()
+            .createMethod(
+                kinds -> kinds.API_MODEL_OUTLINE,
+                methodProcessingContext.createUniqueContext(),
+                appView,
+                builder ->
+                    builder
+                        .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+                        .setProto(proto)
+                        .setApiLevelForDefinition(appView.computedMinApiLevel())
+                        .setApiLevelForCode(computedApiLevel)
+                        .setCode(
+                            m ->
+                                NewInstanceSourceCode.create(appView, m.getHolderType(), targetType)
+                                    .generateCfCode()));
+    synchronized (synthesizedMethods) {
+      synthesizedMethods.add(method);
+    }
+    return method.getDefinition();
+  }
+
+  private DexEncodedMethod createSynthesizedInstanceInitializer(
+      DexMethod targetMethod,
+      ComputedApiLevel computedApiLevel,
+      MethodProcessingContext methodProcessingContext) {
+    DexProto proto =
+        appView
+            .dexItemFactory()
+            .createProto(targetMethod.getHolderType(), targetMethod.getParameters());
+    ProgramMethod method =
+        appView
+            .getSyntheticItems()
+            .createMethod(
+                kinds -> kinds.API_MODEL_OUTLINE,
+                methodProcessingContext.createUniqueContext(),
+                appView,
+                builder ->
+                    builder
+                        .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+                        .setProto(proto)
+                        .setApiLevelForDefinition(appView.computedMinApiLevel())
+                        .setApiLevelForCode(computedApiLevel)
+                        .setCode(
+                            m ->
+                                ForwardMethodBuilder.builder(appView.dexItemFactory())
+                                    .setConstructorTargetWithNewInstance(targetMethod)
+                                    .setStaticSource(m)
+                                    .build()));
+
+    synchronized (synthesizedMethods) {
+      synthesizedMethods.add(method);
+    }
+    return method.getDefinition();
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/ExceptionThrowingSourceCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/ExceptionThrowingSourceCode.java
deleted file mode 100644
index 080b519..0000000
--- a/src/main/java/com/android/tools/r8/ir/synthetic/ExceptionThrowingSourceCode.java
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (c) 2019, 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.ir.synthetic;
-
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProto;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.code.Invoke.Type;
-import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.code.ValueType;
-import com.android.tools.r8.ir.conversion.IRBuilder;
-import java.util.Collections;
-
-// Source code representing simple forwarding method.
-public final class ExceptionThrowingSourceCode extends SyntheticSourceCode {
-
-  private static final int register = 0;
-  private final DexType exceptionType;
-
-  public ExceptionThrowingSourceCode(
-      DexType receiver, DexMethod method, Position callerPosition, DexType exceptionType) {
-    super(receiver, method, callerPosition);
-    this.exceptionType = exceptionType;
-  }
-
-  @Override
-  protected void prepareInstructions() {
-    add(builder -> build(builder, exceptionType));
-  }
-
-  public static void build(IRBuilder builder, DexType exceptionType) {
-    DexItemFactory factory = builder.appView.dexItemFactory();
-    DexProto initProto = factory.createProto(factory.voidType);
-    DexMethod initMethod =
-        factory.createMethod(exceptionType, initProto, factory.constructorMethodName);
-    builder.addNewInstance(register, exceptionType);
-    builder.addInvoke(
-        Type.DIRECT,
-        initMethod,
-        initMethod.proto,
-        Collections.singletonList(ValueType.OBJECT),
-        Collections.singletonList(register),
-        false /* isInterface */);
-    builder.addThrow(register);
-  }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodBuilder.java b/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodBuilder.java
index 90065e6..8d50c17 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodBuilder.java
@@ -142,7 +142,7 @@
     return this;
   }
 
-  public ForwardMethodBuilder setConstructorTarget(DexMethod method, DexItemFactory factory) {
+  public ForwardMethodBuilder setConstructorTargetWithNewInstance(DexMethod method) {
     assert method.isInstanceInitializer(factory);
     targetMethod = method;
     isConstructorDelegate = true;
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/NewInstanceSourceCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/NewInstanceSourceCode.java
new file mode 100644
index 0000000..02b32bc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/NewInstanceSourceCode.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2022, 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.ir.synthetic;
+
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfNew;
+import com.android.tools.r8.cf.code.CfReturnVoid;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.DexType;
+import java.util.ArrayList;
+import java.util.List;
+
+// Source code representing a simple call to NewInstance.
+public final class NewInstanceSourceCode extends SyntheticCfCodeProvider {
+
+  private final DexType newInstanceType;
+
+  private NewInstanceSourceCode(AppView<?> appView, DexType holder, DexType newInstanceType) {
+    super(appView, holder);
+    this.newInstanceType = newInstanceType;
+  }
+
+  public static NewInstanceSourceCode create(
+      AppView<?> appView, DexType holder, DexType newInstanceType) {
+    return new NewInstanceSourceCode(appView, holder, newInstanceType);
+  }
+
+  @Override
+  public CfCode generateCfCode() {
+    List<CfInstruction> instructions = new ArrayList<>();
+    instructions.add(new CfNew(newInstanceType));
+    instructions.add(new CfReturnVoid());
+    return standardCfCodeFromInstructions(instructions);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ComputeApiLevelUseRegistry.java b/src/main/java/com/android/tools/r8/shaking/ComputeApiLevelUseRegistry.java
new file mode 100644
index 0000000..89d124e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ComputeApiLevelUseRegistry.java
@@ -0,0 +1,157 @@
+// Copyright (c) 2022, 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.shaking;
+
+import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
+import com.android.tools.r8.androidapi.ComputedApiLevel;
+import com.android.tools.r8.dex.code.CfOrDexInstruction;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
+import java.util.ListIterator;
+
+public class ComputeApiLevelUseRegistry extends UseRegistry<ProgramMethod> {
+
+  protected final AppView<?> appView;
+  private final AndroidApiLevelCompute apiLevelCompute;
+  private ComputedApiLevel maxApiReferenceLevel;
+
+  public ComputeApiLevelUseRegistry(
+      AppView<?> appView, ProgramMethod context, AndroidApiLevelCompute apiLevelCompute) {
+    super(appView, context);
+    this.appView = appView;
+    this.apiLevelCompute = apiLevelCompute;
+    maxApiReferenceLevel = appView.computedMinApiLevel();
+  }
+
+  @Override
+  public void registerInitClass(DexType clazz) {
+    // Intentionally empty since init class as synthesized.
+  }
+
+  @Override
+  public void registerInvokeVirtual(DexMethod invokedMethod) {
+    setMaxApiReferenceLevel(invokedMethod);
+  }
+
+  @Override
+  public void registerInvokeDirect(DexMethod invokedMethod) {
+    setMaxApiReferenceLevel(invokedMethod);
+  }
+
+  @Override
+  public void registerInvokeStatic(DexMethod invokedMethod) {
+    setMaxApiReferenceLevel(invokedMethod);
+  }
+
+  @Override
+  public void registerInvokeInterface(DexMethod invokedMethod) {
+    setMaxApiReferenceLevel(invokedMethod);
+  }
+
+  @Override
+  public void registerInvokeSuper(DexMethod invokedMethod) {
+    setMaxApiReferenceLevel(invokedMethod);
+  }
+
+  @Override
+  public void registerInstanceFieldRead(DexField field) {
+    setMaxApiReferenceLevel(field);
+  }
+
+  @Override
+  public void registerInstanceFieldReadFromMethodHandle(DexField field) {
+    setMaxApiReferenceLevel(field);
+  }
+
+  @Override
+  public void registerInstanceFieldWrite(DexField field) {
+    setMaxApiReferenceLevel(field);
+  }
+
+  @Override
+  public void registerInstanceFieldWriteFromMethodHandle(DexField field) {
+    setMaxApiReferenceLevel(field);
+  }
+
+  @Override
+  public void registerNewInstance(DexType type) {
+    setMaxApiReferenceLevel(type);
+  }
+
+  @Override
+  public void registerStaticFieldRead(DexField field) {
+    setMaxApiReferenceLevel(field);
+  }
+
+  @Override
+  public void registerStaticFieldReadFromMethodHandle(DexField field) {
+    setMaxApiReferenceLevel(field);
+  }
+
+  @Override
+  public void registerStaticFieldWrite(DexField field) {
+    setMaxApiReferenceLevel(field);
+  }
+
+  @Override
+  public void registerStaticFieldWriteFromMethodHandle(DexField field) {
+    setMaxApiReferenceLevel(field);
+  }
+
+  @Override
+  public void registerConstClass(
+      DexType type,
+      ListIterator<? extends CfOrDexInstruction> iterator,
+      boolean ignoreCompatRules) {
+    setMaxApiReferenceLevel(type);
+  }
+
+  @Override
+  public void registerCheckCast(DexType type, boolean ignoreCompatRules) {
+    // CheckCast are not causing soft verification issues
+  }
+
+  @Override
+  public void registerSafeCheckCast(DexType type) {
+    // CheckCast are not causing soft verification issues
+  }
+
+  @Override
+  public void registerTypeReference(DexType type) {
+    // Type references are OK as long as we do not have a use on them
+  }
+
+  @Override
+  public void registerInstanceOf(DexType type) {
+    // InstanceOf are not causing soft verification issues
+  }
+
+  @Override
+  public void registerExceptionGuard(DexType guard) {
+    setMaxApiReferenceLevel(guard);
+  }
+
+  @Override
+  public void registerMethodHandle(DexMethodHandle methodHandle, MethodHandleUse use) {
+    super.registerMethodHandle(methodHandle, use);
+  }
+
+  private void setMaxApiReferenceLevel(DexReference reference) {
+    maxApiReferenceLevel =
+        maxApiReferenceLevel.max(
+            apiLevelCompute.computeApiLevelForLibraryReference(
+                reference, apiLevelCompute.getPlatformApiLevelOrUnknown(appView)));
+  }
+
+  public ComputedApiLevel getMaxApiReferenceLevel() {
+    return maxApiReferenceLevel;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
index 192b16a..f7ffc42 100644
--- a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
@@ -7,7 +7,6 @@
 import static com.android.tools.r8.ir.desugar.records.RecordRewriterHelper.isInvokeDynamicOnRecord;
 
 import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
-import com.android.tools.r8.androidapi.ComputedApiLevel;
 import com.android.tools.r8.dex.code.CfOrDexInstruction;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
@@ -18,29 +17,23 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexMethodHandle;
 import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.UseRegistry;
 import java.util.ListIterator;
 
-public class DefaultEnqueuerUseRegistry extends UseRegistry<ProgramMethod> {
+public class DefaultEnqueuerUseRegistry extends ComputeApiLevelUseRegistry {
 
   protected final AppView<? extends AppInfoWithClassHierarchy> appView;
   protected final Enqueuer enqueuer;
-  private final AndroidApiLevelCompute apiLevelCompute;
-  private ComputedApiLevel maxApiReferenceLevel;
 
   public DefaultEnqueuerUseRegistry(
       AppView<? extends AppInfoWithClassHierarchy> appView,
       ProgramMethod context,
       Enqueuer enqueuer,
       AndroidApiLevelCompute apiLevelCompute) {
-    super(appView, context);
+    super(appView, context, apiLevelCompute);
     this.appView = appView;
     this.enqueuer = enqueuer;
-    this.apiLevelCompute = apiLevelCompute;
-    maxApiReferenceLevel = appView.computedMinApiLevel();
   }
 
   public DexProgramClass getContextHolder() {
@@ -53,6 +46,7 @@
 
   @Override
   public void registerInitClass(DexType clazz) {
+    super.registerInitClass(clazz);
     enqueuer.traceInitClass(clazz, getContext());
   }
 
@@ -64,90 +58,90 @@
 
   @Override
   public void registerInvokeVirtual(DexMethod invokedMethod) {
-    setMaxApiReferenceLevel(invokedMethod);
+    super.registerInvokeVirtual(invokedMethod);
     enqueuer.traceInvokeVirtual(invokedMethod, getContext());
   }
 
   @Override
   public void registerInvokeDirect(DexMethod invokedMethod) {
-    setMaxApiReferenceLevel(invokedMethod);
+    super.registerInvokeDirect(invokedMethod);
     enqueuer.traceInvokeDirect(invokedMethod, getContext());
   }
 
   @Override
   public void registerInvokeStatic(DexMethod invokedMethod) {
-    setMaxApiReferenceLevel(invokedMethod);
+    super.registerInvokeStatic(invokedMethod);
     enqueuer.traceInvokeStatic(invokedMethod, getContext());
   }
 
   @Override
   public void registerInvokeInterface(DexMethod invokedMethod) {
-    setMaxApiReferenceLevel(invokedMethod);
+    super.registerInvokeInterface(invokedMethod);
     enqueuer.traceInvokeInterface(invokedMethod, getContext());
   }
 
   @Override
   public void registerInvokeSuper(DexMethod invokedMethod) {
-    setMaxApiReferenceLevel(invokedMethod);
+    super.registerInvokeSuper(invokedMethod);
     enqueuer.traceInvokeSuper(invokedMethod, getContext());
   }
 
   @Override
   public void registerInstanceFieldRead(DexField field) {
-    setMaxApiReferenceLevel(field);
+    super.registerInstanceFieldRead(field);
     enqueuer.traceInstanceFieldRead(field, getContext());
   }
 
   @Override
   public void registerInstanceFieldReadFromMethodHandle(DexField field) {
-    setMaxApiReferenceLevel(field);
+    super.registerInstanceFieldReadFromMethodHandle(field);
     enqueuer.traceInstanceFieldReadFromMethodHandle(field, getContext());
   }
 
   private void registerInstanceFieldReadFromRecordMethodHandle(DexField field) {
-    setMaxApiReferenceLevel(field);
+    super.registerInstanceFieldWriteFromMethodHandle(field);
     enqueuer.traceInstanceFieldReadFromRecordMethodHandle(field, getContext());
   }
 
   @Override
   public void registerInstanceFieldWrite(DexField field) {
-    setMaxApiReferenceLevel(field);
+    super.registerInstanceFieldWrite(field);
     enqueuer.traceInstanceFieldWrite(field, getContext());
   }
 
   @Override
   public void registerInstanceFieldWriteFromMethodHandle(DexField field) {
-    setMaxApiReferenceLevel(field);
+    super.registerInstanceFieldWriteFromMethodHandle(field);
     enqueuer.traceInstanceFieldWriteFromMethodHandle(field, getContext());
   }
 
   @Override
   public void registerNewInstance(DexType type) {
-    setMaxApiReferenceLevel(type);
+    super.registerNewInstance(type);
     enqueuer.traceNewInstance(type, getContext());
   }
 
   @Override
   public void registerStaticFieldRead(DexField field) {
-    setMaxApiReferenceLevel(field);
+    super.registerStaticFieldRead(field);
     enqueuer.traceStaticFieldRead(field, getContext());
   }
 
   @Override
   public void registerStaticFieldReadFromMethodHandle(DexField field) {
-    setMaxApiReferenceLevel(field);
+    super.registerStaticFieldReadFromMethodHandle(field);
     enqueuer.traceStaticFieldReadFromMethodHandle(field, getContext());
   }
 
   @Override
   public void registerStaticFieldWrite(DexField field) {
-    setMaxApiReferenceLevel(field);
+    super.registerStaticFieldWrite(field);
     enqueuer.traceStaticFieldWrite(field, getContext());
   }
 
   @Override
   public void registerStaticFieldWriteFromMethodHandle(DexField field) {
-    setMaxApiReferenceLevel(field);
+    super.registerStaticFieldWriteFromMethodHandle(field);
     enqueuer.traceStaticFieldWriteFromMethodHandle(field, getContext());
   }
 
@@ -156,32 +150,37 @@
       DexType type,
       ListIterator<? extends CfOrDexInstruction> iterator,
       boolean ignoreCompatRules) {
+    super.registerConstClass(type, iterator, ignoreCompatRules);
     enqueuer.traceConstClass(type, getContext(), iterator, ignoreCompatRules);
   }
 
   @Override
   public void registerCheckCast(DexType type, boolean ignoreCompatRules) {
+    super.registerCheckCast(type, ignoreCompatRules);
     enqueuer.traceCheckCast(type, getContext(), ignoreCompatRules);
   }
 
   @Override
   public void registerSafeCheckCast(DexType type) {
+    super.registerSafeCheckCast(type);
     enqueuer.traceSafeCheckCast(type, getContext());
   }
 
   @Override
   public void registerTypeReference(DexType type) {
+    super.registerTypeReference(type);
     enqueuer.traceTypeReference(type, getContext());
   }
 
   @Override
   public void registerInstanceOf(DexType type) {
+    super.registerInstanceOf(type);
     enqueuer.traceInstanceOf(type, getContext());
   }
 
   @Override
   public void registerExceptionGuard(DexType guard) {
-    setMaxApiReferenceLevel(guard);
+    super.registerExceptionGuard(guard);
     enqueuer.traceExceptionGuard(guard, getContext());
   }
 
@@ -219,15 +218,4 @@
       }
     }
   }
-
-  private void setMaxApiReferenceLevel(DexReference reference) {
-    maxApiReferenceLevel =
-        maxApiReferenceLevel.max(
-            apiLevelCompute.computeApiLevelForLibraryReference(
-                reference, apiLevelCompute.getPlatformApiLevelOrUnknown(appView)));
-  }
-
-  public ComputedApiLevel getMaxApiReferenceLevel() {
-    return maxApiReferenceLevel;
-  }
 }
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java
index 57eaa67..e826dd7 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8.apimodel;
 
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
-import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
 import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethodWithName;
@@ -60,7 +59,6 @@
         .setMinApi(parameters.getApiLevel())
         .addAndroidBuildVersion()
         .apply(setMockApiLevelForClass(LibraryClass.class, classApiLevel))
-        .apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, classApiLevel))
         .apply(setMockApiLevelForMethod(addedOn23(), methodApiLevel))
         // TODO(b/213552119): Remove when enabled by default.
         .apply(ApiModelingTestHelper::enableApiCallerIdentification)
@@ -162,7 +160,7 @@
   // Only present from api level 19.
   public static class LibraryClass {
 
-    public void addedOn23() {
+    public static void addedOn23() {
       System.out.println("LibraryClass::addedOn23");
     }
   }
@@ -171,12 +169,9 @@
 
     @NeverInline
     public static void test() {
-      if (AndroidBuildVersion.VERSION >= 19) {
-        LibraryClass libraryClass = new LibraryClass();
-        if (AndroidBuildVersion.VERSION >= 23) {
-          libraryClass.addedOn23();
-          libraryClass.addedOn23();
-        }
+      if (AndroidBuildVersion.VERSION >= 23) {
+        LibraryClass.addedOn23();
+        LibraryClass.addedOn23();
       }
       System.out.println("Hello World");
     }
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java
index 9793b9b..978d068 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java
@@ -35,7 +35,6 @@
 public class ApiModelOutlineHorizontalMergingTest extends TestBase {
 
   private final AndroidApiLevel libraryClassApiLevel = AndroidApiLevel.K;
-  private final AndroidApiLevel otherLibraryClassApiLevel = AndroidApiLevel.K;
   private final AndroidApiLevel firstMethodApiLevel = AndroidApiLevel.M;
   private final AndroidApiLevel secondMethodApiLevel = AndroidApiLevel.O_MR1;
 
@@ -62,10 +61,10 @@
         .apply(
             setMockApiLevelForMethod(
                 LibraryClass.class.getMethod("addedOn27"), secondMethodApiLevel))
-        .apply(setMockApiLevelForClass(OtherLibraryClass.class, otherLibraryClassApiLevel))
+        .apply(setMockApiLevelForClass(OtherLibraryClass.class, libraryClassApiLevel))
         .apply(
             setMockApiLevelForDefaultInstanceInitializer(
-                OtherLibraryClass.class, otherLibraryClassApiLevel))
+                OtherLibraryClass.class, libraryClassApiLevel))
         .apply(
             setMockApiLevelForMethod(
                 OtherLibraryClass.class.getMethod("addedOn23"), firstMethodApiLevel))
@@ -101,7 +100,11 @@
         .inspect(
             inspector -> {
               // TODO(b/187675788): Update when horizontal merging is enabled for D8 for debug mode.
-              if (parameters.getApiLevel().isLessThan(firstMethodApiLevel)) {
+              if (parameters.getApiLevel().isLessThan(libraryClassApiLevel)) {
+                // We have generated 4 outlines two having api level 23 and two having api level 27
+                // and 2 outlines for each instance initializer.
+                assertEquals(11, inspector.allClasses().size());
+              } else if (parameters.getApiLevel().isLessThan(firstMethodApiLevel)) {
                 // We have generated 4 outlines two having api level 23 and two having api level 27.
                 assertEquals(7, inspector.allClasses().size());
               } else if (parameters.getApiLevel().isLessThan(secondMethodApiLevel)) {
@@ -166,8 +169,12 @@
       assertEquals(3, inspector.allClasses().size());
     } else if (parameters.getApiLevel().isLessThan(firstMethodApiLevel)) {
       // We have generated 4 outlines two having api level 23 and two having api level 27.
+      // If less than the library api level then we have synthesized two instance initializer
+      // outlines as well.
       // Check that the levels are horizontally merged.
-      assertEquals(5, inspector.allClasses().size());
+      assertEquals(
+          parameters.getApiLevel().isLessThan(libraryClassApiLevel) ? 6 : 5,
+          inspector.allClasses().size());
       assertEquals(2, outlinedAddedOn23.size());
       assertTrue(
           outlinedAddedOn23.stream()
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceInitializerSuperTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceInitializerSuperTest.java
new file mode 100644
index 0000000..a362bd3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceInitializerSuperTest.java
@@ -0,0 +1,165 @@
+// Copyright (c) 2022, 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.apimodel;
+
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.SingleTestRunResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.testing.AndroidBuildVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ApiModelOutlineInstanceInitializerSuperTest extends TestBase {
+
+  private static final AndroidApiLevel classApiLevel = AndroidApiLevel.M;
+
+  private static final String[] EXPECTED = new String[] {"Hello ", "World!"};
+
+  @Parameter public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  private void setupTestBuilder(TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder) throws Exception {
+    testBuilder
+        .addLibraryClasses(LibraryClass.class)
+        .addDefaultRuntimeLibrary(parameters)
+        .addProgramClasses(Main.class, ProgramExtendsLibraryClass.class)
+        .setMinApi(parameters.getApiLevel())
+        .addAndroidBuildVersion()
+        .apply(setMockApiLevelForClass(LibraryClass.class, classApiLevel))
+        .apply(
+            setMockApiLevelForMethod(
+                LibraryClass.class.getDeclaredConstructor(String.class), classApiLevel))
+        .apply(setMockApiLevelForMethod(LibraryClass.class.getMethod("print"), classApiLevel))
+        .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+        .apply(ApiModelingTestHelper::enableOutliningOfMethods)
+        .apply(ApiModelingTestHelper::disableStubbingOfClasses);
+  }
+
+  public boolean addToBootClasspath() {
+    return parameters.isDexRuntime()
+        && parameters.getApiLevel().isGreaterThanOrEqualTo(classApiLevel);
+  }
+
+  @Test
+  public void testD8Debug() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    testForD8()
+        .setMode(CompilationMode.DEBUG)
+        .apply(this::setupTestBuilder)
+        .compile()
+        .inspect(inspector -> inspect(inspector, false))
+        .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
+        .run(parameters.getRuntime(), Main.class)
+        .apply(this::checkOutput);
+  }
+
+  @Test
+  public void testD8Release() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    testForD8()
+        .setMode(CompilationMode.RELEASE)
+        .apply(this::setupTestBuilder)
+        .compile()
+        .inspect(inspector -> inspect(inspector, false))
+        .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
+        .run(parameters.getRuntime(), Main.class)
+        .apply(this::checkOutput);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .apply(this::setupTestBuilder)
+        .addKeepMainRule(Main.class)
+        .addKeepClassAndMembersRules(ProgramExtendsLibraryClass.class)
+        .addDontObfuscate()
+        .compile()
+        .inspect(inspector -> inspect(inspector, true))
+        .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
+        .run(parameters.getRuntime(), Main.class)
+        .apply(this::checkOutput);
+  }
+
+  private void inspect(CodeInspector inspector, boolean isR8) throws Exception {
+    // Because each of the outline context also have a super call, R8 will inline back the outline
+    // because the super call has the same api level as the outlinee.
+    verifyThat(inspector, parameters, LibraryClass.class.getMethod("print"))
+        .isOutlinedFromUntil(
+            ProgramExtendsLibraryClass.class.getMethod("print"),
+            isR8 ? AndroidApiLevel.B : classApiLevel);
+    verifyThat(inspector, parameters, LibraryClass.class.getDeclaredConstructor(String.class))
+        .isOutlinedFromUntil(
+            ProgramExtendsLibraryClass.class.getDeclaredConstructor(String.class),
+            isR8 ? AndroidApiLevel.B : classApiLevel);
+  }
+
+  private void checkOutput(SingleTestRunResult<?> runResult) {
+    if (parameters.isDexRuntime()
+        && parameters.getApiLevel().isGreaterThanOrEqualTo(classApiLevel)) {
+      runResult.assertSuccessWithOutputLines(EXPECTED);
+    } else {
+      runResult.assertSuccessWithOutputLines("Not calling API");
+    }
+  }
+
+  // Only present from api level 23.
+  public static class LibraryClass {
+
+    private final String arg;
+
+    public LibraryClass(String arg) {
+      this.arg = arg;
+    }
+
+    public void print() {
+      System.out.println(arg);
+    }
+  }
+
+  public static class ProgramExtendsLibraryClass extends LibraryClass {
+
+    private final LibraryClass otherArg;
+
+    public ProgramExtendsLibraryClass(String arg) {
+      super(arg); // <-- this cannot be outlined
+      otherArg = new LibraryClass("World!"); // <-- this should be outlined.
+    }
+
+    @Override
+    public void print() {
+      super.print();
+      otherArg.print();
+    }
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      if (AndroidBuildVersion.VERSION >= 23) {
+        new ProgramExtendsLibraryClass("Hello ").print();
+      } else {
+        System.out.println("Not calling API");
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceInitializerTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceInitializerTest.java
index ea08c59..b0a5fb7 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceInitializerTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceInitializerTest.java
@@ -113,11 +113,11 @@
   private void inspect(CodeInspector inspector, boolean isR8) throws Exception {
     Method mainMethod = Main.class.getMethod("main", String[].class);
     verifyThat(inspector, parameters, Argument.class.getDeclaredConstructor(String.class))
-        .isOutlinedFromUntil(mainMethod, AndroidApiLevel.B);
+        .isOutlinedFromUntil(mainMethod, classApiLevel);
     verifyThat(inspector, parameters, LibraryClass.class.getDeclaredConstructor(Argument.class))
-        .isOutlinedFromUntil(mainMethod, AndroidApiLevel.B);
+        .isOutlinedFromUntil(mainMethod, classApiLevel);
     verifyThat(inspector, parameters, LibraryClass.class.getMethod("print"))
-        .isOutlinedFromUntil(mainMethod, isR8 ? AndroidApiLevel.B : classApiLevel);
+        .isOutlinedFromUntil(mainMethod, classApiLevel);
   }
 
   private void checkOutput(SingleTestRunResult<?> runResult) {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodAndStubClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodAndStubClassTest.java
index 0b2f270..3504527 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodAndStubClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodAndStubClassTest.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8.apimodel;
 
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
-import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
 import static org.junit.Assume.assumeTrue;
@@ -55,8 +54,9 @@
         .apply(ApiModelingTestHelper::enableApiCallerIdentification)
         .apply(ApiModelingTestHelper::enableStubbingOfClasses)
         .apply(ApiModelingTestHelper::enableOutliningOfMethods)
+        // We only model the class and not the default initializer, otherwise we outline the new
+        // instance call and remove the last reference in non-outlined code.
         .apply(setMockApiLevelForClass(LibraryClass.class, libraryClassLevel))
-        .apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, libraryClassLevel))
         .apply(setMockApiLevelForMethod(apiMethod(), libraryMethodLevel));
   }
 
@@ -123,7 +123,7 @@
   public static class LibraryClass {
 
     // Only present from api level 30
-    public void foo() {
+    public static void foo() {
       System.out.println("LibraryClass::foo");
     }
   }
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java
index 21db603..22f79df 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8.apimodel;
 
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
-import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
 import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethodWithName;
@@ -64,9 +63,6 @@
         .setMinApi(parameters.getApiLevel())
         .addAndroidBuildVersion()
         .apply(setMockApiLevelForClass(LibraryClass.class, initialLibraryMockLevel))
-        .apply(
-            setMockApiLevelForDefaultInstanceInitializer(
-                LibraryClass.class, initialLibraryMockLevel))
         .apply(setMockApiLevelForMethod(addedOn23(), initialLibraryMockLevel))
         .apply(setMockApiLevelForMethod(addedOn27(), finalLibraryMethodLevel))
         // TODO(b/213552119): Remove when enabled by default.
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
index 178c115..574a8b5 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
@@ -6,6 +6,7 @@
 
 import static com.android.tools.r8.utils.codeinspector.CodeMatchers.accessesField;
 import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
+import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethodWithHolderAndName;
 import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethodWithName;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
@@ -28,6 +29,7 @@
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import com.google.common.collect.ImmutableList;
 import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.util.Collections;
@@ -334,7 +336,7 @@
       assertThat(target, not(CodeMatchers.invokesMethod(candidate)));
     }
 
-    void isOutlinedFromUntil(Method method, AndroidApiLevel apiLevel) {
+    void isOutlinedFromUntil(Executable method, AndroidApiLevel apiLevel) {
       if (parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(apiLevel)) {
         isOutlinedFrom(method);
       } else {
@@ -342,7 +344,7 @@
       }
     }
 
-    void isOutlinedFrom(Method method) {
+    void isOutlinedFrom(Executable method) {
       // Check that the call is in a synthetic class.
       List<FoundMethodSubject> outlinedMethod =
           inspector.allClasses().stream()
@@ -350,18 +352,20 @@
               .filter(
                   methodSubject ->
                       methodSubject.isSynthetic()
-                          && invokesMethodWithName(methodOfInterest.getMethodName())
+                          && invokesMethodWithHolderAndName(
+                                  methodOfInterest.getHolderClass().getTypeName(),
+                                  methodOfInterest.getMethodName())
                               .matches(methodSubject))
               .collect(Collectors.toList());
       assertEquals(1, outlinedMethod.size());
       // Assert that method invokes the outline
-      MethodSubject caller = inspector.method(method);
+      MethodSubject caller = inspector.method(Reference.methodFromMethod(method));
       assertThat(caller, isPresent());
       assertThat(caller, invokesMethod(outlinedMethod.get(0)));
     }
 
-    void isNotOutlinedFrom(Method method) {
-      MethodSubject caller = inspector.method(method);
+    void isNotOutlinedFrom(Executable method) {
+      MethodSubject caller = inspector.method(Reference.methodFromMethod(method));
       assertThat(caller, isPresent());
       assertThat(caller, invokesMethodWithName(methodOfInterest.getMethodName()));
     }