Merge commit '348064ccc3ffcfff8c51db26b1c98e3396d46529' into dev-release
diff --git a/build.gradle b/build.gradle
index 7f0d08f..1dc777d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -492,21 +492,29 @@
     }
 }
 
-task downloadDeps {
+task downloadCloudDeps() {
     cloudDependencies.each { entry ->
         entry.value.each { entryFile ->
             dependsOn "${getDownloadDepsTaskName(entry.key, entryFile)}"
         }
     }
-    if (!project.hasProperty('no_internal')) {
-        x20Dependencies.each { entry ->
-            entry.value.each { entryFile ->
-                dependsOn "${getDownloadDepsTaskName(entry.key, entryFile)}"
-            }
+}
+
+task downloadX20Deps() {
+    x20Dependencies.each { entry ->
+        entry.value.each { entryFile ->
+            dependsOn "${getDownloadDepsTaskName(entry.key, entryFile)}"
         }
     }
 }
 
+task downloadDeps {
+    dependsOn downloadCloudDeps
+    if (!project.hasProperty('no_internal')) {
+        dependsOn downloadX20Deps
+    }
+}
+
 allprojects {
     sourceCompatibility = JavaVersion.VERSION_1_8
     targetCompatibility = JavaVersion.VERSION_1_8
@@ -538,6 +546,8 @@
     }
 }
 
+compileJava.dependsOn downloadCloudDeps
+
 sourceSets.configureEach { sourceSet ->
     tasks.named(sourceSet.compileJavaTaskName).configure {
         // Default disable errorprone (enabled and setup below).
@@ -727,6 +737,7 @@
 }
 
 task repackageDepsNew(type: ShadowJar) {
+    dependsOn downloadCloudDeps
     configurations = [project.configurations.runtimeClasspath]
     mergeServiceFiles(it)
     exclude { it.getRelativePath().getPathString().endsWith("module-info.class") }
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 2fadb7d..ac5cd11 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -44,7 +44,6 @@
 import com.android.tools.r8.utils.ExceptionUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.InternalOptions.DesugarState;
-import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
 import com.android.tools.r8.utils.LineNumberOptimizer;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.android.tools.r8.utils.StringUtils;
@@ -348,8 +347,6 @@
   private static ProguardMapSupplier finalizeApplication(
       AndroidApp inputApp, AppView<AppInfo> appView, NamingLens namingLens) {
     SyntheticFinalization.finalize(appView);
-    // TODO(b/37830524): Once D8 supports PC mapping this will need to be run for that too.
-    assert appView.options().lineNumberOptimization == LineNumberOptimization.OFF;
     if (appView.options().proguardMapConsumer == null) {
       return null;
     }
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiClass.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiClass.java
index f33f5bd..ee0ffb2 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiClass.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiClass.java
@@ -26,6 +26,8 @@
 
   public abstract AndroidApiLevel getApiLevel();
 
+  public abstract int getMemberCount();
+
   public abstract TraversalContinuation visitFields(
       BiFunction<FieldReference, AndroidApiLevel, TraversalContinuation> visitor);
 
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
new file mode 100644
index 0000000..124ea3f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
@@ -0,0 +1,150 @@
+// 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.androidapi;
+
+import com.android.tools.r8.apimodel.AndroidApiDatabaseBuilder;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMember;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.utils.TraversalContinuation;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+public class AndroidApiReferenceLevelCache {
+
+  private static final int BUILD_CACHE_TRESHOLD = 20;
+
+  private final Map<DexType, AndroidApiClass> apiTypeLookup;
+  private final Map<DexReference, AndroidApiLevel> apiMemberLookup = new IdentityHashMap<>();
+  private final AppView<?> appView;
+
+  private AndroidApiReferenceLevelCache(AppView<?> appView) {
+    this.appView = appView;
+    this.apiTypeLookup = new IdentityHashMap<>();
+  }
+
+  private AndroidApiReferenceLevelCache(
+      AppView<?> appView, Map<DexType, AndroidApiClass> apiTypeLookup) {
+    this.appView = appView;
+    this.apiTypeLookup = apiTypeLookup;
+  }
+
+  public static AndroidApiReferenceLevelCache create(AppView<?> appView) {
+    if (!appView.options().apiModelingOptions().enableApiCallerIdentification) {
+      // If enableApiCallerIdentification is not enabled then override lookup to always return
+      // AndroidApiLevel.B.
+      return new AndroidApiReferenceLevelCache(appView) {
+        @Override
+        public AndroidApiLevel lookup(DexReference reference) {
+          return AndroidApiLevel.B;
+        }
+      };
+    }
+    // The apiTypeLookup is build lazily except for the mocked api types that we define in tests
+    // externally.
+    Map<DexType, AndroidApiClass> apiTypeLookup = new IdentityHashMap<>();
+    appView
+        .options()
+        .apiModelingOptions()
+        .visitMockedApiReferences(
+            (classReference, androidApiClass) ->
+                apiTypeLookup.put(
+                    appView.dexItemFactory().createType(classReference.getDescriptor()),
+                    androidApiClass));
+    return new AndroidApiReferenceLevelCache(appView, apiTypeLookup);
+  }
+
+  public AndroidApiLevel lookupMax(DexReference reference, AndroidApiLevel minApiLevel) {
+    return lookup(reference).max(minApiLevel);
+  }
+
+  public AndroidApiLevel lookup(DexReference reference) {
+    DexType contextType = reference.getContextType();
+    assert !contextType.isArrayType();
+    if (contextType.isPrimitiveType() || contextType.isVoidType()) {
+      return AndroidApiLevel.B;
+    }
+    DexClass clazz = appView.definitionFor(contextType);
+    if (clazz == null) {
+      return AndroidApiLevel.UNKNOWN;
+    }
+    if (!clazz.isLibraryClass()) {
+      return appView.options().minApiLevel;
+    }
+    AndroidApiClass androidApiClass =
+        apiTypeLookup.computeIfAbsent(
+            contextType, type -> AndroidApiDatabaseBuilder.buildClass(type.asClassReference()));
+    if (androidApiClass == null) {
+      // This is a library class but we have no api model for it. This happens if using an older
+      // version of R8 to compile a new target. We simply have to disallow inlining of methods
+      // that has such references.
+      return AndroidApiLevel.UNKNOWN;
+    }
+    if (reference.isDexType()) {
+      return androidApiClass.getApiLevel();
+    }
+    return androidApiClass.getMemberCount() > BUILD_CACHE_TRESHOLD
+        ? findMemberByCaching(reference, androidApiClass)
+        : findMemberByIteration(reference.asDexMember(), androidApiClass);
+  }
+
+  private AndroidApiLevel findMemberByIteration(
+      DexMember<?, ?> reference, AndroidApiClass apiClass) {
+    DexItemFactory factory = appView.dexItemFactory();
+    // Similar to the case for api classes we are unable to find, if the member
+    // is unknown we have to be conservative.
+    Box<AndroidApiLevel> apiLevelBox = new Box<>(AndroidApiLevel.UNKNOWN);
+    reference.apply(
+        field ->
+            apiClass.visitFields(
+                (fieldReference, apiLevel) -> {
+                  if (factory.createField(fieldReference) == field) {
+                    apiLevelBox.set(apiLevel);
+                    return TraversalContinuation.BREAK;
+                  }
+                  return TraversalContinuation.CONTINUE;
+                }),
+        method ->
+            apiClass.visitMethods(
+                (methodReference, apiLevel) -> {
+                  if (factory.createMethod(methodReference) == method) {
+                    apiLevelBox.set(apiLevel);
+                    return TraversalContinuation.BREAK;
+                  }
+                  return TraversalContinuation.CONTINUE;
+                }));
+    return apiLevelBox.get();
+  }
+
+  private AndroidApiLevel findMemberByCaching(DexReference reference, AndroidApiClass apiClass) {
+    buildCacheForMembers(reference.getContextType(), apiClass);
+    return apiMemberLookup.getOrDefault(reference, AndroidApiLevel.UNKNOWN);
+  }
+
+  private void buildCacheForMembers(DexType context, AndroidApiClass apiClass) {
+    assert apiClass.getMemberCount() > BUILD_CACHE_TRESHOLD;
+    // Use the context type as a token for us having build a cache for it.
+    if (apiMemberLookup.containsKey(context)) {
+      return;
+    }
+    DexItemFactory factory = appView.dexItemFactory();
+    apiClass.visitFields(
+        (fieldReference, apiLevel) -> {
+          apiMemberLookup.put(factory.createField(fieldReference), apiLevel);
+          return TraversalContinuation.CONTINUE;
+        });
+    apiClass.visitMethods(
+        (methodReference, apiLevel) -> {
+          apiMemberLookup.put(factory.createMethod(methodReference), apiLevel);
+          return TraversalContinuation.CONTINUE;
+        });
+    apiMemberLookup.put(context, AndroidApiLevel.UNKNOWN);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
index 271fd68..e054ffa 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
@@ -69,7 +69,7 @@
   public abstract <T> T apply(
       Function<DexEncodedField, T> fieldConsumer, Function<DexEncodedMethod, T> methodConsumer);
 
-  public void apply(
+  public void accept(
       Consumer<DexEncodedField> fieldConsumer, Consumer<DexEncodedMethod> methodConsumer) {
     apply(
         field -> {
diff --git a/src/main/java/com/android/tools/r8/graph/DexMember.java b/src/main/java/com/android/tools/r8/graph/DexMember.java
index ae9d9fb..bffc842 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMember.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMember.java
@@ -5,7 +5,7 @@
 
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.google.common.collect.Iterables;
-import java.util.Map;
+import java.util.function.BiFunction;
 import java.util.function.Function;
 
 public abstract class DexMember<D extends DexEncodedMember<D, R>, R extends DexMember<D, R>>
@@ -64,12 +64,11 @@
   }
 
   public AndroidApiLevel computeApiLevelForReferencedTypes(
-      AppView<?> appView, Map<DexReference, AndroidApiLevel> apiLevelMap) {
-    AndroidApiLevel minApiLevel = appView.options().minApiLevel;
-    AndroidApiLevel apiLevel = minApiLevel;
+      AppView<?> appView, BiFunction<DexReference, AndroidApiLevel, AndroidApiLevel> computeMax) {
+    AndroidApiLevel computedLevel = appView.options().minApiLevel;
     for (DexType type : getReferencedBaseTypes(appView.dexItemFactory())) {
-      apiLevel = apiLevel.max(apiLevelMap.getOrDefault(type, minApiLevel));
+      computedLevel = computeMax.apply(type, computedLevel);
     }
-    return apiLevel;
+    return computedLevel;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 0ebd1da..d46c103 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -15,11 +15,13 @@
 import com.android.tools.r8.graph.GenericSignature.ClassSignature;
 import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
 import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
+import com.android.tools.r8.ir.optimize.info.MemberOptimizationInfo;
 import com.android.tools.r8.kotlin.KotlinClassLevelInfo;
 import com.android.tools.r8.naming.NamingLens;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.synthesis.SyntheticMarker;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.TraversalContinuation;
 import com.android.tools.r8.utils.structural.Ordered;
 import com.android.tools.r8.utils.structural.StructuralItem;
@@ -32,6 +34,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
+import java.util.function.BiFunction;
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
@@ -834,4 +837,26 @@
   public ChecksumSupplier getChecksumSupplier() {
     return checksumSupplier;
   }
+
+  public AndroidApiLevel getApiReferenceLevel(
+      AndroidApiLevel minApiLevel,
+      BiFunction<DexReference, AndroidApiLevel, AndroidApiLevel> apiLevelLookup) {
+    // The api level of a class is the max level of it's members, super class and interfaces.
+    AndroidApiLevel computedApiLevel = minApiLevel;
+    for (DexType superType : allImmediateSupertypes()) {
+      computedApiLevel = apiLevelLookup.apply(superType, computedApiLevel);
+      if (computedApiLevel == AndroidApiLevel.UNKNOWN) {
+        return AndroidApiLevel.UNKNOWN;
+      }
+    }
+    for (DexEncodedMember<?, ?> member : members()) {
+      MemberOptimizationInfo<?> optimizationInfo = member.getOptimizationInfo();
+      assert optimizationInfo.hasApiReferenceLevel();
+      computedApiLevel = optimizationInfo.getApiReferenceLevel(computedApiLevel);
+      if (computedApiLevel == AndroidApiLevel.UNKNOWN) {
+        return AndroidApiLevel.UNKNOWN;
+      }
+    }
+    return computedApiLevel;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureCorrectnessHelper.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureCorrectnessHelper.java
index a3cf3a1..03f4259 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignatureCorrectnessHelper.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureCorrectnessHelper.java
@@ -236,7 +236,7 @@
         // We represent no signature as object.
       } else if (context.superType
           != appView.graphLens().lookupClassType(classSignature.superClassSignature().type())) {
-        assert mode.doNotVerify();
+        assert mode.doNotVerify() : "Super type inconsistency in generic signature";
         return INVALID_SUPER_TYPE;
       }
       signatureEvaluationResult =
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java
index f93437e..65840c5 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java
@@ -4,10 +4,10 @@
 
 package com.android.tools.r8.graph.analysis;
 
+import com.android.tools.r8.androidapi.AndroidApiReferenceLevelCache;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexEncodedMember;
 import com.android.tools.r8.graph.DexMember;
-import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.ProgramDefinition;
 import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -16,19 +16,17 @@
 import com.android.tools.r8.ir.optimize.info.MemberOptimizationInfo;
 import com.android.tools.r8.shaking.DefaultEnqueuerUseRegistry;
 import com.android.tools.r8.utils.AndroidApiLevel;
-import java.util.Map;
 
 public class ApiModelAnalysis extends EnqueuerAnalysis {
 
   private final AppView<?> appView;
   private final AndroidApiLevel minApiLevel;
-  private final Map<DexReference, AndroidApiLevel> referenceToApiLevelMap;
+  private final AndroidApiReferenceLevelCache referenceLevelCache;
 
-  public ApiModelAnalysis(
-      AppView<?> appView, Map<DexReference, AndroidApiLevel> referenceToApiLevelMap) {
+  public ApiModelAnalysis(AppView<?> appView, AndroidApiReferenceLevelCache referenceLevelCache) {
     this.appView = appView;
     this.minApiLevel = appView.options().minApiLevel;
-    this.referenceToApiLevelMap = referenceToApiLevelMap;
+    this.referenceLevelCache = referenceLevelCache;
   }
 
   @Override
@@ -46,6 +44,13 @@
   @Override
   public void processTracedCode(ProgramMethod method, DefaultEnqueuerUseRegistry registry) {
     assert registry.getMaxApiReferenceLevel().isGreaterThanOrEqualTo(minApiLevel);
+    if (appView.options().apiModelingOptions().tracedMethodApiLevelCallback != null) {
+      appView
+          .options()
+          .apiModelingOptions()
+          .tracedMethodApiLevelCallback
+          .accept(method.getMethodReference(), registry.getMaxApiReferenceLevel());
+    }
     setApiLevelForMember(method.getDefinition(), registry.getMaxApiReferenceLevel());
   }
 
@@ -54,7 +59,7 @@
     // swap the default optimization info for one with that marks the api level to be min api.
     MemberOptimizationInfo<?> optimizationInfo = member.getOptimizationInfo();
     if (!optimizationInfo.isMutableOptimizationInfo() && apiLevel == minApiLevel) {
-      member.apply(
+      member.accept(
           field -> {
             field.setMinApiOptimizationInfo(DefaultFieldOptimizationWithMinApiInfo.getInstance());
           },
@@ -66,7 +71,7 @@
           optimizationInfo.hasApiReferenceLevel()
               ? apiLevel.max(optimizationInfo.getApiReferenceLevel(minApiLevel))
               : apiLevel;
-      member.apply(
+      member.accept(
           field -> {
             field.getMutableOptimizationInfo().setApiReferenceLevel(maxApiLevel);
           },
@@ -77,6 +82,6 @@
   }
 
   private AndroidApiLevel computeApiLevelForReferencedTypes(DexMember<?, ?> member) {
-    return member.computeApiLevelForReferencedTypes(appView, referenceToApiLevelMap);
+    return member.computeApiLevelForReferencedTypes(appView, referenceLevelCache::lookupMax);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
index 0d3bf0d..4abddc6 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
@@ -21,6 +21,7 @@
 import com.android.tools.r8.horizontalclassmerging.policies.NoDeadLocks;
 import com.android.tools.r8.horizontalclassmerging.policies.NoDefaultInterfaceMethodCollisions;
 import com.android.tools.r8.horizontalclassmerging.policies.NoDefaultInterfaceMethodMerging;
+import com.android.tools.r8.horizontalclassmerging.policies.NoDifferentApiReferenceLevel;
 import com.android.tools.r8.horizontalclassmerging.policies.NoDirectRuntimeTypeChecks;
 import com.android.tools.r8.horizontalclassmerging.policies.NoEnums;
 import com.android.tools.r8.horizontalclassmerging.policies.NoIllegalInlining;
@@ -199,6 +200,7 @@
         new SameParentClass(),
         new SyntheticItemsPolicy(appView, mode),
         new RespectPackageBoundaries(appView),
+        new NoDifferentApiReferenceLevel(appView),
         new PreventClassMethodAndDefaultMethodCollisions(appView));
   }
 
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDifferentApiReferenceLevel.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDifferentApiReferenceLevel.java
new file mode 100644
index 0000000..97d8d21
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDifferentApiReferenceLevel.java
@@ -0,0 +1,42 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.horizontalclassmerging.policies;
+
+import com.android.tools.r8.androidapi.AndroidApiReferenceLevelCache;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.horizontalclassmerging.MultiClassSameReferencePolicy;
+import com.android.tools.r8.utils.AndroidApiLevel;
+
+public class NoDifferentApiReferenceLevel extends MultiClassSameReferencePolicy<AndroidApiLevel> {
+
+  private final AndroidApiReferenceLevelCache apiReferenceLevelCache;
+  private final AndroidApiLevel minApi;
+  // TODO(b/188388130): Remove when stabilized.
+  private final boolean enableApiCallerIdentification;
+
+  public NoDifferentApiReferenceLevel(AppView<?> appView) {
+    apiReferenceLevelCache = AndroidApiReferenceLevelCache.create(appView);
+    minApi = appView.options().minApiLevel;
+    enableApiCallerIdentification =
+        appView.options().apiModelingOptions().enableApiCallerIdentification;
+  }
+
+  @Override
+  public boolean shouldSkipPolicy() {
+    return !enableApiCallerIdentification;
+  }
+
+  @Override
+  public String getName() {
+    return "NoDifferentApiReferenceLevel";
+  }
+
+  @Override
+  public AndroidApiLevel getMergeKey(DexProgramClass clazz) {
+    assert enableApiCallerIdentification;
+    return clazz.getApiReferenceLevel(minApi, apiReferenceLevelCache::lookupMax);
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/NullSimpleInliningConstraint.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/NullSimpleInliningConstraint.java
index 2dce46e..19607ee 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/inlining/NullSimpleInliningConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/NullSimpleInliningConstraint.java
@@ -28,9 +28,13 @@
 
   @Override
   public boolean isSatisfied(InvokeMethod invoke) {
-    Value argument = getArgument(invoke);
-    assert argument.getType().isReferenceType();
-    return argument.getType().isDefinitelyNull();
+    // Take the root value to also deal with the following case, which may happen in dead code,
+    // where v1 is actually guaranteed to be null, despite the value's type being non-null:
+    //   v0 <- ConstNumber 0
+    //   v1 <- AssumeNotNull v0
+    Value argumentRoot = getArgument(invoke).getAliasedValue();
+    assert argumentRoot.getType().isReferenceType();
+    return argumentRoot.getType().isDefinitelyNull();
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java
index f8aae86..ecd958d 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java
@@ -20,7 +20,7 @@
 import com.android.tools.r8.shaking.EnqueuerUseRegistryFactory;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import java.util.ListIterator;
-import java.util.Map;
+import java.util.function.BiFunction;
 
 public class ProtoEnqueuerUseRegistry extends DefaultEnqueuerUseRegistry {
 
@@ -32,7 +32,7 @@
       AppView<? extends AppInfoWithClassHierarchy> appView,
       ProgramMethod currentMethod,
       Enqueuer enqueuer,
-      Map<DexReference, AndroidApiLevel> apiLevelReferenceMap) {
+      BiFunction<DexReference, AndroidApiLevel, AndroidApiLevel> apiLevelReferenceMap) {
     super(appView, currentMethod, enqueuer, apiLevelReferenceMap);
     this.references = appView.protoShrinker().references;
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Assume.java b/src/main/java/com/android/tools/r8/ir/code/Assume.java
index 4674e14..9c70c92 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Assume.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Assume.java
@@ -240,6 +240,16 @@
   }
 
   @Override
+  public Value getNonNullInput() {
+    return src();
+  }
+
+  @Override
+  public boolean throwsOnNullInput() {
+    return true;
+  }
+
+  @Override
   public boolean verifyTypes(AppView<?> appView, VerifyTypesHelper verifyTypesHelper) {
     assert super.verifyTypes(appView, verifyTypesHelper);
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
index df8f00e..a6a4704 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
@@ -6,6 +6,7 @@
 import static com.android.tools.r8.ir.code.IRCode.INSTRUCTION_NUMBER_DELTA;
 
 import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DebugLocalInfo;
 import com.android.tools.r8.graph.DebugLocalInfo.PrintLevel;
@@ -1499,6 +1500,19 @@
     return block;
   }
 
+  public boolean isInstructionBeforeThrowingInstruction(Instruction instruction) {
+    assert instruction.getBlock() == this;
+    for (Instruction candidate : getInstructions()) {
+      if (candidate == instruction) {
+        return true;
+      }
+      if (candidate.instructionTypeCanThrow()) {
+        return false;
+      }
+    }
+    throw new Unreachable();
+  }
+
   public boolean isTrivialGoto() {
     return instructions.size() == 1 && exit().isGoto();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/CatchHandlers.java b/src/main/java/com/android/tools/r8/ir/code/CatchHandlers.java
index 95ee7e0..6005e1f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/CatchHandlers.java
+++ b/src/main/java/com/android/tools/r8/ir/code/CatchHandlers.java
@@ -25,6 +25,10 @@
       this.guard = guard;
       this.target = target;
     }
+
+    public T getTarget() {
+      return target;
+    }
   }
 
   private final List<DexType> guards;
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 3f52f3d..a9e34e2 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
@@ -1168,7 +1168,8 @@
       ProgramMethod method,
       CfInstructionDesugaringEventConsumer desugaringEventConsumer,
       MethodProcessingContext methodProcessingContext) {
-    if (options.desugarState.isOff() || !method.getDefinition().getCode().isCfCode()) {
+    // Due to some mandatory desugarings, we need to run desugaring even if desugaring is disabled.
+    if (!method.getDefinition().getCode().isCfCode()) {
       return false;
     }
     instructionDesugaring.scan(method, desugaringEventConsumer);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
index 78291d7..148ea56 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
@@ -28,7 +28,7 @@
     if (appView.options().isGeneratingClassFiles()) {
       return NonEmptyCfInstructionDesugaringCollection.createForCfToCfNonDesugar(appView);
     }
-    return empty();
+    return NonEmptyCfInstructionDesugaringCollection.createForCfToDexNonDesugar(appView);
   }
 
   public static CfInstructionDesugaringCollection empty() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
index ba7cbc0b..723e8b1 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
@@ -43,6 +43,12 @@
 
   NonEmptyCfInstructionDesugaringCollection(AppView<?> appView) {
     this.appView = appView;
+    if (appView.options().desugarState.isOff()) {
+      this.nestBasedAccessDesugaring = null;
+      this.recordRewriter = null;
+      this.desugaredLibraryRetargeter = null;
+      return;
+    }
     this.nestBasedAccessDesugaring = NestBasedAccessDesugaring.create(appView);
     BackportedMethodRewriter backportedMethodRewriter = null;
     desugaredLibraryRetargeter =
@@ -85,22 +91,25 @@
     }
   }
 
-  // TODO(b/145775365): special constructor for cf-to-cf compilations with desugaring disabled.
-  //  This should be removed once we can represent invoke-special instructions in the IR.
-  private NonEmptyCfInstructionDesugaringCollection(
-      AppView<?> appView, InvokeSpecialToSelfDesugaring invokeSpecialToSelfDesugaring) {
-    this.appView = appView;
-    this.nestBasedAccessDesugaring = null;
-    this.desugaredLibraryRetargeter = null;
-    this.recordRewriter = null;
-    desugarings.add(invokeSpecialToSelfDesugaring);
-  }
-
   static NonEmptyCfInstructionDesugaringCollection createForCfToCfNonDesugar(AppView<?> appView) {
     assert appView.options().desugarState.isOff();
     assert appView.options().isGeneratingClassFiles();
-    return new NonEmptyCfInstructionDesugaringCollection(
-        appView, new InvokeSpecialToSelfDesugaring(appView));
+    NonEmptyCfInstructionDesugaringCollection desugaringCollection =
+        new NonEmptyCfInstructionDesugaringCollection(appView);
+    // TODO(b/145775365): special constructor for cf-to-cf compilations with desugaring disabled.
+    //  This should be removed once we can represent invoke-special instructions in the IR.
+    desugaringCollection.desugarings.add(new InvokeSpecialToSelfDesugaring(appView));
+    return desugaringCollection;
+  }
+
+  static NonEmptyCfInstructionDesugaringCollection createForCfToDexNonDesugar(AppView<?> appView) {
+    assert appView.options().desugarState.isOff();
+    assert appView.options().isGeneratingDex();
+    NonEmptyCfInstructionDesugaringCollection desugaringCollection =
+        new NonEmptyCfInstructionDesugaringCollection(appView);
+    desugaringCollection.desugarings.add(new InvokeSpecialToSelfDesugaring(appView));
+    desugaringCollection.desugarings.add(new InvokeToPrivateRewriter());
+    return desugaringCollection;
   }
 
   private void ensureCfCode(ProgramMethod method) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
index 97af733..55598d1 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
@@ -735,10 +735,18 @@
       // method is expected to be in the current class since it is private, but desugaring
       // may move some methods or their code into other classes.
       assert invokeNeedsRewriting(invokedMethod, DIRECT);
-      return rewriteInvoke.apply(
-          directTarget.getDefinition().isPrivateMethod()
-              ? privateAsMethodOfCompanionClass(directTarget)
-              : defaultAsMethodOfCompanionClass(directTarget));
+      DexMethod companionMethod;
+      if (directTarget.getDefinition().isPrivateMethod()) {
+        companionMethod =
+            directTarget.isProgramMethod()
+                ? ensurePrivateAsMethodOfProgramCompanionClassStub(directTarget.asProgramMethod())
+                    .getReference()
+                // TODO(b/183998768): Why does this not create a stub on the class path?
+                : privateAsMethodOfCompanionClass(directTarget);
+      } else {
+        companionMethod = defaultAsMethodOfCompanionClass(directTarget);
+      }
+      return rewriteInvoke.apply(companionMethod);
     } else {
       // The method can be a default method in the interface hierarchy.
       DexClassAndMethod virtualTarget =
@@ -1196,6 +1204,35 @@
     }
   }
 
+  ProgramMethod ensurePrivateAsMethodOfProgramCompanionClassStub(ProgramMethod method) {
+    DexMethod companionMethod =
+        privateAsMethodOfCompanionClass(method.getReference(), appView.dexItemFactory());
+    DexEncodedMethod definition = method.getDefinition();
+    return InterfaceProcessor.ensureCompanionMethod(
+        method.getHolder(),
+        companionMethod.getName(),
+        companionMethod.getProto(),
+        appView,
+        methodBuilder -> {
+          MethodAccessFlags newFlags = definition.getAccessFlags().copy();
+          assert newFlags.isPrivate();
+          newFlags.promoteToPublic();
+          newFlags.promoteToStatic();
+          methodBuilder
+              .setAccessFlags(newFlags)
+              .setGenericSignature(definition.getGenericSignature())
+              .setAnnotations(definition.annotations())
+              .setParameterAnnotationsList(definition.getParameterAnnotations())
+              // TODO(b/183998768): Once R8 desugars in the enqueuer this should set an invalid
+              //  code to ensure it is never used before desugared and installed.
+              .setCode(
+                  ignored ->
+                      appView.enableWholeProgramOptimizations()
+                          ? definition.getCode()
+                          : InvalidCode.getInstance());
+        });
+  }
+
   // Represent a static interface method as a method of companion class.
   final DexMethod staticAsMethodOfCompanionClass(DexClassAndMethod method) {
     DexItemFactory dexItemFactory = appView.dexItemFactory();
@@ -1299,7 +1336,7 @@
               // TODO(b/183998768): Once R8 desugars in the enqueuer this should set an invalid
               //  code to ensure it is never used before desugared and installed.
               .setCode(
-                  m ->
+                  ignored ->
                       appView.enableWholeProgramOptimizations()
                           ? definition.getCode()
                           : InvalidCode.getInstance());
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
index ab61be0..799b584 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
@@ -288,71 +288,41 @@
 
       getPostProcessingInterfaceInfo(iface).setHasNonClinitDirectMethods();
 
-      MethodAccessFlags originalFlags = method.getAccessFlags();
-      MethodAccessFlags newFlags = originalFlags.copy();
-      if (originalFlags.isPrivate()) {
-        newFlags.promoteToPublic();
-      }
-
-      DexMethod oldMethod = method.getReference();
+      ProgramMethod companion;
       if (isStaticMethod(definition)) {
-        assert originalFlags.isPrivate() || originalFlags.isPublic()
+        assert definition.isPrivate() || definition.isPublic()
             : "Static interface method "
                 + method.toSourceString()
                 + " is expected to "
                 + "either be public or private in "
                 + iface.origin;
-        ProgramMethod companion = rewriter.ensureStaticAsMethodOfProgramCompanionClassStub(method);
-        // TODO(b/183998768): R8 should also install an "invalid code" object until the actual code
-        //  moves.
-        assert appView.enableWholeProgramOptimizations()
-            || InvalidCode.isInvalidCode(companion.getDefinition().getCode());
-        if (definition.hasClassFileVersion()) {
-          companion.getDefinition().downgradeClassFileVersion(definition.getClassFileVersion());
+        companion = rewriter.ensureStaticAsMethodOfProgramCompanionClassStub(method);
+      } else {
+        assert definition.isPrivate();
+        companion = rewriter.ensurePrivateAsMethodOfProgramCompanionClassStub(method);
+        Code code = definition.getCode();
+        if (code == null) {
+          throw new CompilationError(
+              "Code is missing for private instance "
+                  + "interface method: "
+                  + method.getReference().toSourceString(),
+              iface.origin);
         }
-        companion.getDefinition().setCode(definition.getCode(), appView);
-        getPostProcessingInterfaceInfo(iface).moveMethod(oldMethod, companion.getReference());
-        definition.setCode(InvalidCode.getInstance(), appView);
-        continue;
+        DexEncodedMethod.setDebugInfoWithFakeThisParameter(
+            code, companion.getReference().getArity(), appView);
       }
 
-      assert originalFlags.isPrivate();
-
-      newFlags.promoteToStatic();
-
-      DexMethod companionMethod =
-          InterfaceMethodRewriter.privateAsMethodOfCompanionClass(
-              oldMethod, appView.dexItemFactory());
-
-      Code code = definition.getCode();
-      if (code == null) {
-        throw new CompilationError(
-            "Code is missing for private instance "
-                + "interface method: "
-                + oldMethod.toSourceString(),
-            iface.origin);
+      // TODO(b/183998768): R8 should also install an "invalid code" object until the actual code
+      //  moves.
+      assert appView.enableWholeProgramOptimizations()
+          || InvalidCode.isInvalidCode(companion.getDefinition().getCode());
+      if (definition.hasClassFileVersion()) {
+        companion.getDefinition().downgradeClassFileVersion(definition.getClassFileVersion());
       }
-
-      DexEncodedMethod.setDebugInfoWithFakeThisParameter(code, companionMethod.getArity(), appView);
-
-      ensureCompanionMethod(
-          iface,
-          companionMethod.getName(),
-          companionMethod.getProto(),
-          appView,
-          methodBuilder ->
-              methodBuilder
-                  .setAccessFlags(newFlags)
-                  .setGenericSignature(definition.getGenericSignature())
-                  .setAnnotations(definition.annotations())
-                  .setParameterAnnotationsList(definition.getParameterAnnotations())
-                  .setCode(ignored -> definition.getCode())
-                  .setOnBuildConsumer(
-                      implMethod -> {
-                        implMethod.copyMetadata(definition);
-                        getPostProcessingInterfaceInfo(iface)
-                            .moveMethod(oldMethod, companionMethod);
-                      }));
+      companion.getDefinition().setCode(definition.getCode(), appView);
+      getPostProcessingInterfaceInfo(iface)
+          .moveMethod(method.getReference(), companion.getReference());
+      definition.setCode(InvalidCode.getInstance(), appView);
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 36d70df..2a1ec57 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -44,6 +44,7 @@
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.Binop;
 import com.android.tools.r8.ir.code.CatchHandlers;
+import com.android.tools.r8.ir.code.CatchHandlers.CatchHandler;
 import com.android.tools.r8.ir.code.CheckCast;
 import com.android.tools.r8.ir.code.ConstInstruction;
 import com.android.tools.r8.ir.code.ConstNumber;
@@ -3108,6 +3109,7 @@
     Set<BasicBlock> blocksToRemove = Sets.newIdentityHashSet();
     ListIterator<BasicBlock> blockIterator = code.listIterator();
     ProgramMethod context = code.context();
+    boolean hasUnlinkedCatchHandlers = false;
     while (blockIterator.hasNext()) {
       BasicBlock block = blockIterator.next();
       if (block.getNumber() != 0 && block.getPredecessors().isEmpty()) {
@@ -3123,7 +3125,19 @@
           Value inValue = instruction.getNonNullInput();
           if (inValue.isAlwaysNull(appView)) {
             // Insert `throw null` after the instruction if it is not guaranteed to throw an NPE.
-            if (instruction.isInstanceFieldInstruction()) {
+            if (instruction.isAssume()) {
+              // If this assume is in a block with catch handlers, then the out-value can have
+              // usages in the catch handler if the block's throwing instruction comes after the
+              // assume instruction. In this case, the catch handler is also guaranteed to be dead,
+              // so we detach it from the current block.
+              if (block.hasCatchHandlers()
+                  && block.isInstructionBeforeThrowingInstruction(instruction)) {
+                for (CatchHandler<BasicBlock> catchHandler : block.getCatchHandlers()) {
+                  catchHandler.getTarget().unlinkCatchHandler();
+                }
+                hasUnlinkedCatchHandlers = true;
+              }
+            } else if (instruction.isInstanceFieldInstruction()) {
               InstanceFieldInstruction instanceFieldInstruction =
                   instruction.asInstanceFieldInstruction();
               if (instanceFieldInstruction.instructionInstanceCanThrow(
@@ -3183,6 +3197,9 @@
       }
     }
     code.removeBlocks(blocksToRemove);
+    if (hasUnlinkedCatchHandlers) {
+      affectedValues.addAll(code.removeUnreachableBlocks());
+    }
     assert code.getUnreachableBlocks().isEmpty();
     if (!affectedValues.isEmpty()) {
       new TypeAnalysis(appView).narrowing(affectedValues);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index 5f6ad1a..e653838 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -742,8 +742,12 @@
     Set<BasicBlock> seen = Sets.newIdentityHashSet();
     Set<Instruction> users = eligibleInstance.uniqueUsers();
     for (Instruction user : users) {
+      if (!user.hasBlock()) {
+        continue;
+      }
+
       BasicBlock block = user.getBlock();
-      if (block == null || !seen.add(block)) {
+      if (!seen.add(block)) {
         continue;
       }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedback.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedback.java
index 26516df..ede372a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedback.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedback.java
@@ -29,7 +29,7 @@
     default void fixup(DexEncodedMember<?, ?> member) {
       MemberOptimizationInfo<?> optimizationInfo = member.getOptimizationInfo();
       if (optimizationInfo.isMutableOptimizationInfo()) {
-        member.apply(
+        member.accept(
             field -> {
               fixup(field, optimizationInfo.asMutableFieldOptimizationInfo());
             },
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 da86fe5..8a3a7e3 100644
--- a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
@@ -19,21 +19,21 @@
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import java.util.ListIterator;
-import java.util.Map;
+import java.util.function.BiFunction;
 
 public class DefaultEnqueuerUseRegistry extends UseRegistry {
 
   protected final AppView<? extends AppInfoWithClassHierarchy> appView;
   private final ProgramMethod context;
   protected final Enqueuer enqueuer;
-  private final Map<DexReference, AndroidApiLevel> apiReferenceMapping;
+  private final BiFunction<DexReference, AndroidApiLevel, AndroidApiLevel> apiReferenceMapping;
   private AndroidApiLevel maxApiReferenceLevel;
 
   public DefaultEnqueuerUseRegistry(
       AppView<? extends AppInfoWithClassHierarchy> appView,
       ProgramMethod context,
       Enqueuer enqueuer,
-      Map<DexReference, AndroidApiLevel> apiReferenceMapping) {
+      BiFunction<DexReference, AndroidApiLevel, AndroidApiLevel> apiReferenceMapping) {
     super(appView.dexItemFactory());
     this.appView = appView;
     this.context = context;
@@ -188,14 +188,13 @@
 
   private void setMaxApiReferenceLevel(DexReference reference) {
     if (reference.isDexMember()) {
-      this.maxApiReferenceLevel =
+      maxApiReferenceLevel =
           maxApiReferenceLevel.max(
               reference
                   .asDexMember()
                   .computeApiLevelForReferencedTypes(appView, apiReferenceMapping));
     }
-    this.maxApiReferenceLevel =
-        maxApiReferenceLevel.max(apiReferenceMapping.getOrDefault(reference, maxApiReferenceLevel));
+    maxApiReferenceLevel = apiReferenceMapping.apply(reference, maxApiReferenceLevel);
   }
 
   public AndroidApiLevel getMaxApiReferenceLevel() {
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index abb6523..ea9b2d7 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -15,6 +15,7 @@
 import static java.util.Collections.emptySet;
 
 import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.androidapi.AndroidApiReferenceLevelCache;
 import com.android.tools.r8.cf.code.CfInstruction;
 import com.android.tools.r8.cf.code.CfInvoke;
 import com.android.tools.r8.code.CfOrDexInstruction;
@@ -120,7 +121,6 @@
 import com.android.tools.r8.shaking.ScopedDexMethodSet.AddMethodIfMoreVisibleResult;
 import com.android.tools.r8.synthesis.SyntheticItems.SynthesizingContextOracle;
 import com.android.tools.r8.utils.Action;
-import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.IteratorUtils;
 import com.android.tools.r8.utils.ListUtils;
@@ -255,7 +255,7 @@
 
   private final Set<DexReference> identifierNameStrings = Sets.newIdentityHashSet();
 
-  private final Map<DexReference, AndroidApiLevel> referenceToApiLevelMap;
+  private final AndroidApiReferenceLevelCache apiReferenceLevelCache;
 
   /**
    * Tracks the dependency between a method and the super-method it calls, if any. Used to make
@@ -473,10 +473,7 @@
     } else {
       desugaredLibraryWrapperAnalysis = null;
     }
-    referenceToApiLevelMap = new IdentityHashMap<>();
-    if (options.apiModelingOptions().enableApiCallerIdentification) {
-      options.apiModelingOptions().appendToApiLevelMap(referenceToApiLevelMap, dexItemFactory);
-    }
+    apiReferenceLevelCache = AndroidApiReferenceLevelCache.create(appView);
   }
 
   private AppInfoWithClassHierarchy appInfo() {
@@ -3028,7 +3025,7 @@
       registerAnalysis(new GenericSignatureEnqueuerAnalysis(enqueuerDefinitionSupplier));
     }
     if (appView.options().apiModelingOptions().enableApiCallerIdentification) {
-      registerAnalysis(new ApiModelAnalysis(appView, referenceToApiLevelMap));
+      registerAnalysis(new ApiModelAnalysis(appView, apiReferenceLevelCache));
     }
     if (mode.isInitialTreeShaking()) {
       // This is simulating the effect of the "root set" applied rules.
@@ -3944,7 +3941,7 @@
 
   void traceCode(ProgramMethod method) {
     DefaultEnqueuerUseRegistry registry =
-        useRegistryFactory.create(appView, method, this, referenceToApiLevelMap);
+        useRegistryFactory.create(appView, method, this, apiReferenceLevelCache::lookupMax);
     method.registerCodeReferences(registry);
     // Notify analyses.
     analyses.forEach(analysis -> analysis.processTracedCode(method, registry));
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerUseRegistryFactory.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerUseRegistryFactory.java
index 6bd8473..def9a41 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerUseRegistryFactory.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerUseRegistryFactory.java
@@ -9,7 +9,7 @@
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.utils.AndroidApiLevel;
-import java.util.Map;
+import java.util.function.BiFunction;
 
 public interface EnqueuerUseRegistryFactory {
 
@@ -17,5 +17,5 @@
       AppView<? extends AppInfoWithClassHierarchy> appView,
       ProgramMethod currentMethod,
       Enqueuer enqueuer,
-      Map<DexReference, AndroidApiLevel> apiLevelReferenceMap);
+      BiFunction<DexReference, AndroidApiLevel, AndroidApiLevel> apiLevelReferenceMap);
 }
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 d43f32a..230af5f 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -8,6 +8,7 @@
 import static com.android.tools.r8.ir.code.Invoke.Type.DIRECT;
 import static com.android.tools.r8.ir.code.Invoke.Type.STATIC;
 
+import com.android.tools.r8.androidapi.AndroidApiReferenceLevelCache;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
@@ -53,6 +54,7 @@
 import com.android.tools.r8.ir.synthetic.AbstractSynthesizedCode;
 import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode;
 import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.Box;
 import com.android.tools.r8.utils.FieldSignatureEquivalence;
 import com.android.tools.r8.utils.MethodSignatureEquivalence;
@@ -117,7 +119,8 @@
     UNHANDLED_INVOKE_DIRECT,
     UNHANDLED_INVOKE_SUPER,
     UNSAFE_INLINING,
-    UNSUPPORTED_ATTRIBUTES;
+    UNSUPPORTED_ATTRIBUTES,
+    API_REFERENCE_LEVEL;
 
     public void printLogMessageForClass(DexClass clazz) {
       Log.info(VerticalClassMerger.class, getMessageForClass(clazz));
@@ -180,6 +183,9 @@
         case UNSUPPORTED_ATTRIBUTES:
           message = "since inner-class attributes are not supported";
           break;
+        case API_REFERENCE_LEVEL:
+          message = "since source class references a higher api-level than target";
+          break;
         default:
           assert false;
       }
@@ -201,6 +207,7 @@
   private final MethodPoolCollection methodPoolCollection;
   private final Timing timing;
   private Collection<DexMethod> invokes;
+  private final AndroidApiReferenceLevelCache apiReferenceLevelCache;
 
   private final OptimizationFeedback feedback = OptimizationFeedbackSimple.getInstance();
 
@@ -235,6 +242,7 @@
     this.executorService = executorService;
     this.methodPoolCollection = new MethodPoolCollection(appView, subtypingInfo);
     this.lensBuilder = new VerticalClassMergerGraphLens.Builder(appView.dexItemFactory());
+    this.apiReferenceLevelCache = AndroidApiReferenceLevelCache.create(appView);
     this.timing = timing;
 
     Iterable<DexProgramClass> classes = application.classesWithDeterministicOrder();
@@ -419,6 +427,21 @@
       }
       return false;
     }
+    // Only merge if api reference level of source class is equal to target class.
+    if (appView.options().apiModelingOptions().enableApiCallerIdentification) {
+      AndroidApiLevel sourceApiLevel =
+          sourceClass.getApiReferenceLevel(
+              appView.options().minApiLevel, apiReferenceLevelCache::lookupMax);
+      AndroidApiLevel targetApiLevel =
+          targetClass.getApiReferenceLevel(
+              appView.options().minApiLevel, apiReferenceLevelCache::lookupMax);
+      if (sourceApiLevel != targetApiLevel) {
+        if (Log.ENABLED) {
+          AbortReason.API_REFERENCE_LEVEL.printLogMessageForClass(sourceClass);
+        }
+        return false;
+      }
+    }
     return true;
   }
 
diff --git a/src/main/java/com/android/tools/r8/utils/EntryUtils.java b/src/main/java/com/android/tools/r8/utils/EntryUtils.java
new file mode 100644
index 0000000..e1d28b1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/EntryUtils.java
@@ -0,0 +1,15 @@
+// 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.utils;
+
+import java.util.Map.Entry;
+import java.util.function.BiFunction;
+
+public class EntryUtils {
+
+  public static <K, V, R> R accept(Entry<K, V> entry, BiFunction<K, V, R> consumer) {
+    return consumer.apply(entry.getKey(), entry.getValue());
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 0a1fcc7..ff1643f 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.ProgramConsumer;
 import com.android.tools.r8.StringConsumer;
 import com.android.tools.r8.Version;
+import com.android.tools.r8.androidapi.AndroidApiClass;
 import com.android.tools.r8.cf.CfVersion;
 import com.android.tools.r8.dex.Marker;
 import com.android.tools.r8.dex.Marker.Backend;
@@ -37,7 +38,6 @@
 import com.android.tools.r8.graph.DexLibraryClass;
 import com.android.tools.r8.graph.DexMethod;
 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.classmerging.VerticallyMergedClasses;
@@ -52,10 +52,10 @@
 import com.android.tools.r8.naming.MapVersion;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.position.Position;
+import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.references.FieldReference;
 import com.android.tools.r8.references.MethodReference;
 import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.references.TypeReference;
 import com.android.tools.r8.repackaging.Repackaging.DefaultRepackagingConfiguration;
 import com.android.tools.r8.repackaging.Repackaging.RepackagingConfiguration;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -84,10 +84,12 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 import java.util.TreeSet;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
 import java.util.function.BiPredicate;
 import java.util.function.Consumer;
 import java.util.function.Function;
@@ -1319,21 +1321,68 @@
     // A mapping from references to the api-level introducing them.
     public Map<MethodReference, AndroidApiLevel> methodApiMapping = new HashMap<>();
     public Map<FieldReference, AndroidApiLevel> fieldApiMapping = new HashMap<>();
-    public Map<TypeReference, AndroidApiLevel> typeApiMapping = new HashMap<>();
+    public Map<ClassReference, AndroidApiLevel> classApiMapping = new HashMap<>();
+    public BiConsumer<MethodReference, AndroidApiLevel> tracedMethodApiLevelCallback = null;
 
     public boolean enableApiCallerIdentification = false;
 
-    public void appendToApiLevelMap(
-        Map<DexReference, AndroidApiLevel> apiLevelMap, DexItemFactory factory) {
-      methodApiMapping.forEach(
-          (methodReference, apiLevel) ->
-              apiLevelMap.put(factory.createMethod(methodReference), apiLevel));
-      fieldApiMapping.forEach(
-          (fieldReference, apiLevel) ->
-              apiLevelMap.put(factory.createField(fieldReference), apiLevel));
-      typeApiMapping.forEach(
-          (typeReference, apiLevel) ->
-              apiLevelMap.put(factory.createType(typeReference.getDescriptor()), apiLevel));
+    public void visitMockedApiReferences(BiConsumer<ClassReference, AndroidApiClass> consumer) {
+      if (methodApiMapping.isEmpty() && fieldApiMapping.isEmpty() && classApiMapping.isEmpty()) {
+        return;
+      }
+      Set<ClassReference> classReferences = new HashSet<>(classApiMapping.keySet());
+      methodApiMapping
+          .keySet()
+          .forEach(methodReference -> classReferences.add(methodReference.getHolderClass()));
+      fieldApiMapping
+          .keySet()
+          .forEach(methodReference -> classReferences.add(methodReference.getHolderClass()));
+      classReferences.forEach(
+          classReference -> {
+            consumer.accept(
+                classReference,
+                new AndroidApiClass(classReference) {
+                  @Override
+                  public AndroidApiLevel getApiLevel() {
+                    return classApiMapping.getOrDefault(classReference, AndroidApiLevel.B);
+                  }
+
+                  @Override
+                  public int getMemberCount() {
+                    return 0;
+                  }
+
+                  @Override
+                  public TraversalContinuation visitFields(
+                      BiFunction<FieldReference, AndroidApiLevel, TraversalContinuation> visitor) {
+                    for (Entry<FieldReference, AndroidApiLevel> entry :
+                        fieldApiMapping.entrySet()) {
+                      if (!entry.getKey().getHolderClass().equals(classReference)) {
+                        continue;
+                      }
+                      if (EntryUtils.accept(entry, visitor).shouldBreak()) {
+                        return TraversalContinuation.BREAK;
+                      }
+                    }
+                    return TraversalContinuation.CONTINUE;
+                  }
+
+                  @Override
+                  public TraversalContinuation visitMethods(
+                      BiFunction<MethodReference, AndroidApiLevel, TraversalContinuation> visitor) {
+                    for (Entry<MethodReference, AndroidApiLevel> entry :
+                        methodApiMapping.entrySet()) {
+                      if (!entry.getKey().getHolderClass().equals(classReference)) {
+                        continue;
+                      }
+                      if (EntryUtils.accept(entry, visitor).shouldBreak()) {
+                        return TraversalContinuation.BREAK;
+                      }
+                    }
+                    return TraversalContinuation.CONTINUE;
+                  }
+                });
+          });
     }
   }
 
@@ -1652,7 +1701,7 @@
   }
 
   public boolean canUseDexPcAsDebugInformation() {
-    return !debug && hasMinApi(AndroidApiLevel.O);
+    return lineNumberOptimization == LineNumberOptimization.ON && hasMinApi(AndroidApiLevel.O);
   }
 
   public boolean isInterfaceMethodDesugaringEnabled() {
diff --git a/src/test/java/com/android/tools/r8/L8TestBuilder.java b/src/test/java/com/android/tools/r8/L8TestBuilder.java
index 567d471..072da5a 100644
--- a/src/test/java/com/android/tools/r8/L8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/L8TestBuilder.java
@@ -134,6 +134,11 @@
     return this;
   }
 
+  public L8TestBuilder setDesugaredLibraryConfiguration(StringResource configuration) {
+    this.desugaredLibraryConfiguration = configuration;
+    return this;
+  }
+
   public L8TestBuilder setDisableL8AnnotationRemoval(boolean disableL8AnnotationRemoval) {
     return addOptionsModifier(
         options -> options.testing.disableL8AnnotationRemoval = disableL8AnnotationRemoval);
diff --git a/src/test/java/com/android/tools/r8/LibraryDesugaringTestConfiguration.java b/src/test/java/com/android/tools/r8/LibraryDesugaringTestConfiguration.java
index 79dc2c9..737b0ff 100644
--- a/src/test/java/com/android/tools/r8/LibraryDesugaringTestConfiguration.java
+++ b/src/test/java/com/android/tools/r8/LibraryDesugaringTestConfiguration.java
@@ -227,10 +227,12 @@
     }
     String finalGeneratedKeepRules = generatedKeepRules;
     try {
+      assert desugaredLibraryConfigurationResources.size() == 1 : "There can be only one";
       return L8TestBuilder.create(minApiLevel, Backend.DEX, state)
           .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
           .setDesugarJDKLibs(desugarJdkLibs)
           .setDesugarJDKLibsConfiguration(customConversions)
+          .setDesugaredLibraryConfiguration(desugaredLibraryConfigurationResources.get(0))
           .applyIf(
               mode == CompilationMode.RELEASE,
               builder -> {
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index e51cf99..d41987d 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -1758,6 +1758,10 @@
     return AndroidApiLevel.N;
   }
 
+  public static AndroidApiLevel apiLevelWithPrivateInterfaceMethodsSupport() {
+    return AndroidApiLevel.N;
+  }
+
   public static AndroidApiLevel apiLevelWithInvokeCustomSupport() {
     return AndroidApiLevel.O;
   }
diff --git a/src/test/java/com/android/tools/r8/TestBuilder.java b/src/test/java/com/android/tools/r8/TestBuilder.java
index 8f5466c..656955e 100644
--- a/src/test/java/com/android/tools/r8/TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestBuilder.java
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
+import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
 import com.android.tools.r8.TestBase.Backend;
 import com.android.tools.r8.debug.DebugTestConfig;
 import com.android.tools.r8.errors.Unimplemented;
@@ -158,6 +159,25 @@
 
   public abstract T addClasspathFiles(Collection<Path> files);
 
+  public T addClasspathClassFileData(byte[]... classes) {
+    return addClasspathClassFileData(Arrays.asList(classes));
+  }
+
+  public T addClasspathClassFileData(Collection<byte[]> classes) {
+    Path out;
+    try {
+      out = getState().getNewTempFolder().resolve("cp.jar");
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+    ArchiveConsumer consumer = new ArchiveConsumer(out);
+    for (byte[] bytes : classes) {
+      consumer.accept(ByteDataView.of(bytes), TestBase.extractClassDescriptor(bytes), null);
+    }
+    consumer.finished(null);
+    return addClasspathFiles(out);
+  }
+
   public final T addTestingAnnotationsAsProgramClasses() {
     return addProgramClasses(getTestingAnnotations());
   }
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGenerator.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGenerator.java
index bc25cd4..9aa8f4d 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGenerator.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGenerator.java
@@ -94,10 +94,12 @@
               .setClassDescriptor(getApiClassDescriptor(apiClass))
               .addMethodTransformer(getInitTransformer(apiClass))
               .addMethodTransformer(getApiLevelTransformer(apiClass))
+              .addMethodTransformer(getGetMemberCountTransformer(apiClass))
               .addMethodTransformer(getVisitFieldsTransformer(apiClass))
               .addMethodTransformer(getVisitMethodsTransformer(apiClass))
               .removeMethods(MethodPredicate.onName("placeHolderForInit"))
               .removeMethods(MethodPredicate.onName("placeHolderForGetApiLevel"))
+              .removeMethods(MethodPredicate.onName("placeHolderForGetMemberCount"))
               .removeMethods(MethodPredicate.onName("placeHolderForVisitFields"))
               .removeMethods(MethodPredicate.onName("placeHolderForVisitMethods"))
               .transform());
@@ -170,6 +172,16 @@
         });
   }
 
+  // The transformer below changes AndroidApiDatabaseClassTemplate.getMemberCount from:
+  //     return placeHolderForGetMemberCount();
+  // into
+  //    return <memberCount>;
+  private static MethodTransformer getGetMemberCountTransformer(ParsedApiClass apiClass) {
+    return replaceCode(
+        "placeHolderForGetMemberCount",
+        transformer -> transformer.visitLdcInsn(apiClass.getMemberCount()));
+  }
+
   // The transformer below changes AndroidApiDatabaseClassTemplate.visitFields from:
   //     placeHolder();
   //     return TraversalContinuation.CONTINUE;
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGeneratorTest.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGeneratorTest.java
index d85e3e5..6d46bfd 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGeneratorTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGeneratorTest.java
@@ -225,12 +225,7 @@
     ZipUtils.unzip(
         ToolHelper.DEPS.toString(),
         tempDeps.toFile(),
-        entry -> {
-          if (entry.getName().startsWith("com/android/tools/r8/apimodel/")) {
-            return false;
-          }
-          return true;
-        });
+        entry -> !entry.getName().startsWith("com/android/tools/r8/apimodel/"));
     Path modifiedDeps = Files.createTempFile("modified_deps", ".jar");
     ZipUtils.zip(modifiedDeps, tempDeps);
     return modifiedDeps;
@@ -242,6 +237,7 @@
         apiClass -> {
           expected.add(apiClass.getClassReference().getDescriptor());
           expected.add(apiClass.getApiLevel().getName());
+          expected.add(apiClass.getMemberCount() + "");
           BooleanBox added = new BooleanBox(false);
           apiClass.visitFieldReferences(
               (apiLevel, fieldReferences) -> {
@@ -292,6 +288,7 @@
             if (apiClass != null) {
               System.out.println(descriptor);
               System.out.println(apiClass.getApiLevel().getName());
+              System.out.println(apiClass.getMemberCount());
               apiClass.visitFields(
                   (reference, apiLevel) -> {
                     System.out.println(reference.getFieldType().getDescriptor());
@@ -322,6 +319,7 @@
             if (apiClass != null) {
               System.out.println(descriptor);
               System.out.println(apiClass.getApiLevel().getName());
+              System.out.println(apiClass.getMemberCount());
               apiClass.visitFields(
                   (reference, apiLevel) -> {
                     System.out.println(reference.getFieldType().getDescriptor());
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseClassTemplate.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseClassTemplate.java
index 2598898..faefd62 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseClassTemplate.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseClassTemplate.java
@@ -27,6 +27,12 @@
   }
 
   @Override
+  public int getMemberCount() {
+    // Code added dynamically in AndroidApiDatabaseBuilderGenerator.
+    return placeHolderForGetMemberCount();
+  }
+
+  @Override
   public TraversalContinuation visitFields(
       BiFunction<FieldReference, AndroidApiLevel, TraversalContinuation> visitor) {
     // Code added dynamically in AndroidApiDatabaseBuilderGenerator.
@@ -50,6 +56,10 @@
     return null;
   }
 
+  private static int placeHolderForGetMemberCount() {
+    return 0;
+  }
+
   private static void placeHolderForVisitFields() {}
 
   private static void placeHolderForVisitMethods() {}
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiVersionsXmlParser.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiVersionsXmlParser.java
index c904dff..dbb71d3 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiVersionsXmlParser.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiVersionsXmlParser.java
@@ -135,6 +135,11 @@
     private final TreeMap<AndroidApiLevel, List<FieldReference>> fieldReferences = new TreeMap<>();
     private final Map<AndroidApiLevel, List<MethodReference>> methodReferences = new TreeMap<>();
 
+    private ParsedApiClass(ClassReference classReference, AndroidApiLevel apiLevel) {
+      this.classReference = classReference;
+      this.apiLevel = apiLevel;
+    }
+
     public ClassReference getClassReference() {
       return classReference;
     }
@@ -143,9 +148,8 @@
       return apiLevel;
     }
 
-    private ParsedApiClass(ClassReference classReference, AndroidApiLevel apiLevel) {
-      this.classReference = classReference;
-      this.apiLevel = apiLevel;
+    public int getMemberCount() {
+      return fieldReferences.size() + methodReferences.size();
     }
 
     private void register(FieldReference reference, AndroidApiLevel apiLevel) {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiFieldsTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiFieldsTest.java
index 5d0c0b8..cb837ac 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiFieldsTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiFieldsTest.java
@@ -4,7 +4,7 @@
 
 package com.android.tools.r8.apimodel;
 
-import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForType;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
 
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.TestBase;
@@ -41,11 +41,17 @@
         .setMinApi(parameters.getApiLevel())
         .addKeepMainRule(Main.class)
         .enableInliningAnnotations()
-        // TODO(b/138781768): Should not be merged
         .addHorizontallyMergedClassesInspector(
-            inspector -> inspector.assertClassesMerged(A.class, B.class))
+            inspector -> {
+              if (parameters.isDexRuntime()
+                  && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L_MR1)) {
+                inspector.assertClassesMerged(A.class, B.class);
+              } else {
+                inspector.assertNoClassesMerged();
+              }
+            })
         .apply(ApiModelingTestHelper::enableApiCallerIdentification)
-        .apply(setMockApiLevelForType(Api.class, AndroidApiLevel.L_MR1))
+        .apply(setMockApiLevelForClass(Api.class, AndroidApiLevel.L_MR1))
         .compile()
         .addRunClasspathClasses(Api.class)
         .run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiMethodsTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiMethodsTest.java
index f174546..0eeb405 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiMethodsTest.java
@@ -42,9 +42,15 @@
         .setMinApi(parameters.getApiLevel())
         .addKeepMainRule(Main.class)
         .enableInliningAnnotations()
-        // TODO(b/138781768): Should not be merged
         .addHorizontallyMergedClassesInspector(
-            inspector -> inspector.assertClassesMerged(A.class, B.class))
+            inspector -> {
+              if (parameters.isDexRuntime()
+                  && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L_MR1)) {
+                inspector.assertClassesMerged(A.class, B.class);
+              } else {
+                inspector.assertNoClassesMerged();
+              }
+            })
         .apply(ApiModelingTestHelper::enableApiCallerIdentification)
         .apply(setMockApiLevelForMethod(apiMethod, AndroidApiLevel.L_MR1))
         .compile()
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineMethodWithApiTypeTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineMethodWithApiTypeTest.java
index f6996e9..03b2f3e 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineMethodWithApiTypeTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineMethodWithApiTypeTest.java
@@ -4,7 +4,7 @@
 
 package com.android.tools.r8.apimodel;
 
-import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForType;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.CoreMatchers.not;
@@ -46,7 +46,7 @@
         .setMinApi(parameters.getApiLevel())
         .addKeepMainRule(Main.class)
         .enableNoHorizontalClassMergingAnnotations()
-        .apply(setMockApiLevelForType(ApiType.class, AndroidApiLevel.L_MR1))
+        .apply(setMockApiLevelForClass(ApiType.class, AndroidApiLevel.L_MR1))
         .apply(ApiModelingTestHelper::enableApiCallerIdentification)
         .compile()
         .addRunClasspathClasses(ApiType.class)
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfDefaultInterfaceMethodsTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfDefaultInterfaceMethodsTest.java
index a19ecec..cd86ade 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfDefaultInterfaceMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfDefaultInterfaceMethodsTest.java
@@ -53,9 +53,9 @@
               assertThat(aSubject, isPresent());
               aSubject.forAllMethods(
                   method -> {
-                    // TODO(b/191013385): callApiLevel is merged into A, but not with method
-                    // implementation.
-                    // TODO(b/191013233): Check that the bridge is supposed to stay in R8.
+                    // TODO(b/191013385): noApiCall is inlined into Main, but the synthesized
+                    //  A.callApiLevel that dispatches to $CC is not. This should change after
+                    //  desugaring is moved up to the enqueuer.
                     if (method
                             .getOriginalName()
                             .equals(ApiCaller.class.getTypeName() + ".callApiLevel")
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInstanceFieldTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInstanceFieldTest.java
index c7b2b1c..a1ecb6a 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInstanceFieldTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInstanceFieldTest.java
@@ -4,15 +4,16 @@
 
 package com.android.tools.r8.apimodel;
 
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForField;
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
+import static com.android.tools.r8.utils.AndroidApiLevel.L_MR1;
 
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.NoHorizontalClassMerging;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.AndroidApiLevel;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import org.junit.Test;
@@ -47,12 +48,11 @@
         .addKeepMainRule(Main.class)
         .enableInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
-        .apply(setMockApiLevelForField(apiField, AndroidApiLevel.L_MR1))
+        .apply(setMockApiLevelForField(apiField, L_MR1))
+        .apply(setMockApiLevelForDefaultInstanceInitializer(Api.class, L_MR1))
         .apply(ApiModelingTestHelper::enableApiCallerIdentification)
         .compile()
-        .inspect(
-            verifyThat(parameters, apiCaller)
-                .inlinedIntoFromApiLevel(apiCallerCaller, AndroidApiLevel.L_MR1))
+        .inspect(verifyThat(parameters, apiCaller).inlinedIntoFromApiLevel(apiCallerCaller, L_MR1))
         .addRunClasspathClasses(Api.class)
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutputLines("Hello World!");
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelSuperTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelSuperTest.java
index 7d6230b..e1cf2e2 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelSuperTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelSuperTest.java
@@ -4,16 +4,19 @@
 
 package com.android.tools.r8.apimodel;
 
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.addTracedApiReferenceLevelCallBack;
+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.Assert.assertEquals;
 
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.NoHorizontalClassMerging;
-import com.android.tools.r8.NoVerticalClassMerging;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import java.lang.reflect.Method;
 import org.junit.Test;
@@ -41,25 +44,36 @@
     Method apiCaller = ApiCaller.class.getDeclaredMethod("apiLevel22");
     Method apiCallerCaller = A.class.getDeclaredMethod("noApiCall");
     testForR8(parameters.getBackend())
-        .addInnerClasses(getClass())
+        .addProgramClasses(ApiCaller.class, A.class, Main.class)
+        .addLibraryClasses(Api.class)
+        .addDefaultRuntimeLibrary(parameters)
         .setMinApi(parameters.getApiLevel())
         .addKeepMainRule(Main.class)
         .enableInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
         .enableNeverClassInliningAnnotations()
-        .enableNoVerticalClassMergingAnnotations()
         .apply(setMockApiLevelForMethod(apiMethod, AndroidApiLevel.L_MR1))
+        .apply(setMockApiLevelForDefaultInstanceInitializer(Api.class, AndroidApiLevel.L_MR1))
         .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+        .apply(
+            addTracedApiReferenceLevelCallBack(
+                (method, apiLevel) -> {
+                  if (Reference.methodFromMethod(apiCaller).equals(method)) {
+                    if (parameters.isCfRuntime()) {
+                      assertEquals(AndroidApiLevel.L_MR1, apiLevel);
+                    } else {
+                      assertEquals(AndroidApiLevel.L_MR1.max(parameters.getApiLevel()), apiLevel);
+                    }
+                  }
+                }))
         .compile()
-        .inspect(
-            verifyThat(parameters, apiCaller)
-                .inlinedIntoFromApiLevel(apiCallerCaller, AndroidApiLevel.L_MR1))
+        // We do not inline overrides calling super.
+        .inspect(verifyThat(parameters, apiCaller).notInlinedInto(apiCallerCaller))
         .addRunClasspathClasses(Api.class)
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutputLines("A::noApiCall", "ApiCaller::apiLevel22", "Api::apiLevel22");
   }
 
-  @NoVerticalClassMerging
   public static class Api {
 
     void apiLevel22() {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelVirtualTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelVirtualTest.java
index d87ed82..f09f09d 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelVirtualTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelVirtualTest.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.apimodel;
 
 import static com.android.tools.r8.apimodel.ApiModelNoInliningOfHigherApiLevelVirtualTest.ApiCaller.callVirtualMethod;
+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;
 
@@ -48,6 +49,7 @@
         .enableInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
         .apply(setMockApiLevelForMethod(apiMethod, AndroidApiLevel.L_MR1))
+        .apply(setMockApiLevelForDefaultInstanceInitializer(Api.class, AndroidApiLevel.L_MR1))
         .apply(ApiModelingTestHelper::enableApiCallerIdentification)
         .compile()
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfStaticInterfaceMethodsTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfStaticInterfaceMethodsTest.java
index 5ed1708..1e2fd44 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfStaticInterfaceMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfStaticInterfaceMethodsTest.java
@@ -5,17 +5,18 @@
 package com.android.tools.r8.apimodel;
 
 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.AndroidApiLevel.L_MR1;
+import static com.android.tools.r8.utils.AndroidApiLevel.O;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
 
+import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.CodeMatchers;
-import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
 import java.lang.reflect.Method;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -29,7 +30,7 @@
 
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withAllRuntimesAndApiLevels().build();
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
   }
 
   public ApiModelNoInliningOfStaticInterfaceMethodsTest(TestParameters parameters) {
@@ -38,38 +39,52 @@
 
   @Test
   public void testR8() throws Exception {
-    Method apiMethod = Api.class.getDeclaredMethod("apiLevel22");
-    // Method apiCaller = ApiCaller.class.getDeclaredMethod("callApiLevel");
+    Method apiMethod22 = Api.class.getDeclaredMethod("apiLevel22");
+    Method apiMethod26 = Api.class.getDeclaredMethod("apiLevel26");
     testForR8(parameters.getBackend())
         .addProgramClasses(Main.class, A.class, ApiCaller.class)
         .addLibraryClasses(Api.class)
         .addDefaultRuntimeLibrary(parameters)
         .setMinApi(parameters.getApiLevel())
         .addKeepMainRule(Main.class)
-        .apply(setMockApiLevelForMethod(apiMethod, L_MR1))
+        .apply(setMockApiLevelForMethod(apiMethod22, L_MR1))
+        .apply(setMockApiLevelForMethod(apiMethod26, O))
         .apply(ApiModelingTestHelper::enableApiCallerIdentification)
         .noMinification()
+        .enableInliningAnnotations()
         .compile()
         .inspect(
             inspector -> {
               // The call to the api is moved to $-CC (or stays) and is then merged if allowed.
-              if (parameters.isDexRuntime()
-                  && parameters.getApiLevel().isGreaterThanOrEqualTo(L_MR1)) {
-                assertEquals(1, inspector.allClasses().size());
+              Method callApiLevel22 = ApiCaller.class.getDeclaredMethod("callApiLevel22");
+              Method callApiLevel26 = ApiCaller.class.getDeclaredMethod("callApiLevel26");
+              Method noApiCallTo22 = A.class.getDeclaredMethod("noApiCallTo22");
+              Method noApiCallTo26 = A.class.getDeclaredMethod("noApiCallTo26");
+              if (!parameters.canUseDefaultAndStaticInterfaceMethods()) {
+                ClassSubject companion = inspector.companionClassFor(ApiCaller.class);
+                assertThat(companion, isPresent());
+                FoundClassSubject foundCompanion = companion.asFoundClassSubject();
+                verifyThat(parameters, callApiLevel22)
+                    .setHolder(foundCompanion)
+                    .inlinedIntoFromApiLevel(noApiCallTo22, L_MR1);
+                verifyThat(parameters, callApiLevel26)
+                    .setHolder(foundCompanion)
+                    .inlinedIntoFromApiLevel(noApiCallTo26, O);
               } else {
-                assertEquals(2, inspector.allClasses().size());
-                ClassSubject aSubject = inspector.clazz(A.class);
-                assertThat(aSubject, isPresent());
-                // TODO(b/191008231): Should not invoke api here but stay on the CC class.
-                assertEquals(1, aSubject.allMethods().size());
-                MethodSubject callApiLevel = aSubject.uniqueMethodWithName("callApiLevel");
-                assertThat(callApiLevel, isPresent());
-                assertThat(callApiLevel, CodeMatchers.invokesMethodWithName("apiLevel22"));
+                verifyThat(parameters, callApiLevel22)
+                    .inlinedIntoFromApiLevel(noApiCallTo22, L_MR1);
+                verifyThat(parameters, callApiLevel26).inlinedIntoFromApiLevel(noApiCallTo26, O);
               }
             })
         .addRunClasspathClasses(Api.class)
         .run(parameters.getRuntime(), Main.class)
-        .assertSuccessWithOutputLines("A::noApiCall", "ApiCaller::callApiLevel", "Api::apiLevel22");
+        .assertSuccessWithOutputLines(
+            "A::noApiCallTo22",
+            "ApiCaller::callApiLevel22",
+            "Api::apiLevel22",
+            "A::noApiCallTo26",
+            "ApiCaller::callApiLevel26",
+            "Api::apiLevel26");
   }
 
   public static class Api {
@@ -77,27 +92,44 @@
     public static void apiLevel22() {
       System.out.println("Api::apiLevel22");
     }
-  }
 
-  public interface ApiCaller {
-    static void callApiLevel() {
-      System.out.println("ApiCaller::callApiLevel");
-      Api.apiLevel22();
+    public static void apiLevel26() {
+      System.out.println("Api::apiLevel26");
     }
   }
 
-  public static class A implements ApiCaller {
+  public interface ApiCaller {
+    static void callApiLevel22() {
+      System.out.println("ApiCaller::callApiLevel22");
+      Api.apiLevel22();
+    }
 
-    public static void noApiCall() {
-      System.out.println("A::noApiCall");
-      ApiCaller.callApiLevel();
+    static void callApiLevel26() {
+      System.out.println("ApiCaller::callApiLevel26");
+      Api.apiLevel26();
+    }
+  }
+
+  public static class A {
+
+    @NeverInline
+    public static void noApiCallTo22() {
+      System.out.println("A::noApiCallTo22");
+      ApiCaller.callApiLevel22();
+    }
+
+    @NeverInline
+    public static void noApiCallTo26() {
+      System.out.println("A::noApiCallTo26");
+      ApiCaller.callApiLevel26();
     }
   }
 
   public static class Main {
 
     public static void main(String[] args) {
-      A.noApiCall();
+      A.noApiCallTo22();
+      A.noApiCallTo26();
     }
   }
 }
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingSubReferenceApiTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingSubReferenceApiTest.java
new file mode 100644
index 0000000..f01e1b0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingSubReferenceApiTest.java
@@ -0,0 +1,119 @@
+// 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.apimodel;
+
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
+import static com.android.tools.r8.utils.AndroidApiLevel.L_MR1;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static junit.framework.TestCase.assertEquals;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
+import java.lang.reflect.Method;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ApiModelNoVerticalMergingSubReferenceApiTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public ApiModelNoVerticalMergingSubReferenceApiTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test()
+  public void testR8() throws Exception {
+    Method apiMethod = Api.class.getDeclaredMethod("apiLevel22");
+    testForR8(parameters.getBackend())
+        .addProgramClasses(Main.class, Base.class, Sub.class)
+        .addLibraryClasses(Api.class)
+        .addDefaultRuntimeLibrary(parameters)
+        .setMinApi(parameters.getApiLevel())
+        .addKeepMainRule(Main.class)
+        .apply(setMockApiLevelForMethod(apiMethod, L_MR1))
+        .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+        .enableInliningAnnotations()
+        .enableNeverClassInliningAnnotations()
+        .addVerticallyMergedClassesInspector(
+            inspector -> {
+              if (parameters.isDexRuntime()
+                  && parameters.getApiLevel().isGreaterThanOrEqualTo(L_MR1)) {
+                inspector.assertMergedIntoSubtype(Base.class);
+              } else {
+                inspector.assertNoClassesMerged();
+              }
+            })
+        .noMinification()
+        .compile()
+        .inspect(
+            inspector -> {
+              ClassSubject base = inspector.clazz(Base.class);
+              if (parameters.isDexRuntime()
+                  && parameters.getApiLevel().isGreaterThanOrEqualTo(L_MR1)) {
+                assertThat(base, not(isPresent()));
+                ClassSubject sub = inspector.clazz(Sub.class);
+                List<FoundMethodSubject> callApis =
+                    sub.allMethods(
+                        method ->
+                            method.getOriginalName().equals(Base.class.getTypeName() + ".callApi"));
+                // TODO(b/191013233): Remove synthetic bridge. Remove noMinification after fixed.
+                assertEquals(2, callApis.size());
+              } else {
+                assertThat(base, isPresent());
+              }
+            })
+        .addRunClasspathClasses(Api.class)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("Sub::callCallApi", "Base::callApi", "Api::apiLevel22");
+  }
+
+  public static class Api {
+
+    public static void apiLevel22() {
+      System.out.println("Api::apiLevel22");
+    }
+  }
+
+  public static class Base {
+
+    public void callApi() {
+      System.out.println("Base::callApi");
+    }
+  }
+
+  @NeverClassInline
+  public static class Sub extends Base {
+
+    @NeverInline
+    public void callCallApi() {
+      System.out.println("Sub::callCallApi");
+      callApi();
+      Api.apiLevel22();
+    }
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      new Sub().callCallApi();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingTest.java
index e7c41d8..b29e694 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingTest.java
@@ -52,20 +52,33 @@
         .apply(ApiModelingTestHelper::enableApiCallerIdentification)
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
+        .addVerticallyMergedClassesInspector(
+            inspector -> {
+              if (parameters.isDexRuntime()
+                  && parameters.getApiLevel().isGreaterThanOrEqualTo(L_MR1)) {
+                inspector.assertMergedIntoSubtype(Base.class);
+              } else {
+                inspector.assertNoClassesMerged();
+              }
+            })
         .noMinification()
         .compile()
         .inspect(
             inspector -> {
-              // TODO(b/191013385): Prevent Base merging with Sub.
               ClassSubject base = inspector.clazz(Base.class);
-              assertThat(base, not(isPresent()));
-              ClassSubject sub = inspector.clazz(Sub.class);
-              List<FoundMethodSubject> callApis =
-                  sub.allMethods(
-                      method ->
-                          method.getOriginalName().equals(Base.class.getTypeName() + ".callApi"));
-              // TODO(b/191013233): Remove synthetic bridge.
-              assertEquals(2, callApis.size());
+              if (parameters.isDexRuntime()
+                  && parameters.getApiLevel().isGreaterThanOrEqualTo(L_MR1)) {
+                assertThat(base, not(isPresent()));
+                ClassSubject sub = inspector.clazz(Sub.class);
+                List<FoundMethodSubject> callApis =
+                    sub.allMethods(
+                        method ->
+                            method.getOriginalName().equals(Base.class.getTypeName() + ".callApi"));
+                // TODO(b/191013233): Remove synthetic bridge. Remove noMinification after fixed.
+                assertEquals(2, callApis.size());
+              } else {
+                assertThat(base, isPresent());
+              }
             })
         .addRunClasspathClasses(Api.class)
         .run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelVerticalMergingOfSuperClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelVerticalMergingOfSuperClassTest.java
index 80e213e..db7dc06 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelVerticalMergingOfSuperClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelVerticalMergingOfSuperClassTest.java
@@ -4,16 +4,21 @@
 
 package com.android.tools.r8.apimodel;
 
-import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForType;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.addTracedApiReferenceLevelCallBack;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
+import static com.android.tools.r8.utils.AndroidApiLevel.L_MR1;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
 
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -44,13 +49,37 @@
         .addKeepMainRule(Main.class)
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
-        .apply(setMockApiLevelForType(Api.class, AndroidApiLevel.L_MR1))
+        .apply(setMockApiLevelForClass(Api.class, AndroidApiLevel.L_MR1))
+        .apply(setMockApiLevelForDefaultInstanceInitializer(Api.class, AndroidApiLevel.L_MR1))
         .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+        .apply(
+            addTracedApiReferenceLevelCallBack(
+                (methodReference, apiLevel) -> {
+                  if (methodReference.getMethodName().equals("<init>")
+                      && methodReference
+                          .getHolderClass()
+                          .equals(Reference.classFromClass(Api.class))) {
+                    assertEquals(AndroidApiLevel.L_MR1, apiLevel);
+                  }
+                }))
+        .addVerticallyMergedClassesInspector(
+            inspector -> {
+              if (parameters.isDexRuntime()
+                  && parameters.getApiLevel().isGreaterThanOrEqualTo(L_MR1)) {
+                inspector.assertMergedIntoSubtype(A.class);
+              } else {
+                inspector.assertNoClassesMerged();
+              }
+            })
         .compile()
         .inspect(
             inspector -> {
-              // TODO(b/138781768): We should not merge A into B.
-              assertThat(inspector.clazz(A.class), not(isPresent()));
+              if (parameters.isDexRuntime()
+                  && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L_MR1)) {
+                assertThat(inspector.clazz(A.class), not(isPresent()));
+              } else {
+                assertThat(inspector.clazz(A.class), isPresent());
+              }
             })
         .addRunClasspathClasses(Api.class)
         .run(parameters.getRuntime(), Main.class)
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 33d1deb..4e02aff 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
@@ -11,14 +11,18 @@
 import com.android.tools.r8.TestCompilerBuilder;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ThrowableConsumer;
+import com.android.tools.r8.references.MethodReference;
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.ThrowingConsumer;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.CodeMatchers;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
+import java.util.function.BiConsumer;
 
 public abstract class ApiModelingTestHelper {
 
@@ -36,6 +40,23 @@
   }
 
   static <T extends TestCompilerBuilder<?, ?, ?, ?, ?>>
+      ThrowableConsumer<T> setMockApiLevelForDefaultInstanceInitializer(
+          Class<?> clazz, AndroidApiLevel apiLevel) {
+    return compilerBuilder -> {
+      compilerBuilder.addOptionsModification(
+          options -> {
+            options
+                .apiModelingOptions()
+                .methodApiMapping
+                .put(
+                    Reference.method(
+                        Reference.classFromClass(clazz), "<init>", ImmutableList.of(), null),
+                    apiLevel);
+          });
+    };
+  }
+
+  static <T extends TestCompilerBuilder<?, ?, ?, ?, ?>>
       ThrowableConsumer<T> setMockApiLevelForField(Field field, AndroidApiLevel apiLevel) {
     return compilerBuilder -> {
       compilerBuilder.addOptionsModification(
@@ -48,14 +69,14 @@
     };
   }
 
-  static <T extends TestCompilerBuilder<?, ?, ?, ?, ?>> ThrowableConsumer<T> setMockApiLevelForType(
-      Class<?> clazz, AndroidApiLevel apiLevel) {
+  static <T extends TestCompilerBuilder<?, ?, ?, ?, ?>>
+      ThrowableConsumer<T> setMockApiLevelForClass(Class<?> clazz, AndroidApiLevel apiLevel) {
     return compilerBuilder -> {
       compilerBuilder.addOptionsModification(
           options -> {
             options
                 .apiModelingOptions()
-                .typeApiMapping
+                .classApiMapping
                 .put(Reference.classFromClass(clazz), apiLevel);
           });
     };
@@ -68,28 +89,50 @@
         });
   }
 
+  static <T extends TestCompilerBuilder<?, ?, ?, ?, ?>>
+      ThrowableConsumer<T> addTracedApiReferenceLevelCallBack(
+          BiConsumer<MethodReference, AndroidApiLevel> consumer) {
+    return compilerBuilder -> {
+      compilerBuilder.addOptionsModification(
+          options -> {
+            options.apiModelingOptions().tracedMethodApiLevelCallback = consumer;
+          });
+    };
+  }
+
   static ApiModelingMethodVerificationHelper verifyThat(TestParameters parameters, Method method) {
-    return new ApiModelingMethodVerificationHelper(parameters, method);
+    return new ApiModelingMethodVerificationHelper(parameters, Reference.methodFromMethod(method));
   }
 
   public static class ApiModelingMethodVerificationHelper {
 
-    private final Method methodOfInterest;
+    private final MethodReference methodOfInterest;
     private final TestParameters parameters;
 
-    public ApiModelingMethodVerificationHelper(TestParameters parameters, Method methodOfInterest) {
+    private ApiModelingMethodVerificationHelper(
+        TestParameters parameters, MethodReference methodOfInterest) {
       this.methodOfInterest = methodOfInterest;
       this.parameters = parameters;
     }
 
-    protected ThrowingConsumer<CodeInspector, Exception> inlinedIntoFromApiLevel(
+    public ApiModelingMethodVerificationHelper setHolder(FoundClassSubject classSubject) {
+      return new ApiModelingMethodVerificationHelper(
+          parameters,
+          Reference.method(
+              classSubject.getFinalReference(),
+              methodOfInterest.getMethodName(),
+              methodOfInterest.getFormalTypes(),
+              methodOfInterest.getReturnType()));
+    }
+
+    ThrowingConsumer<CodeInspector, Exception> inlinedIntoFromApiLevel(
         Method method, AndroidApiLevel apiLevel) {
       return parameters.isDexRuntime() && parameters.getApiLevel().isGreaterThanOrEqualTo(apiLevel)
           ? inlinedInto(method)
           : notInlinedInto(method);
     }
 
-    private ThrowingConsumer<CodeInspector, Exception> notInlinedInto(Method method) {
+    ThrowingConsumer<CodeInspector, Exception> notInlinedInto(Method method) {
       return inspector -> {
         MethodSubject candidate = inspector.method(methodOfInterest);
         assertThat(candidate, isPresent());
@@ -99,7 +142,7 @@
       };
     }
 
-    public ThrowingConsumer<CodeInspector, Exception> inlinedInto(Method method) {
+    ThrowingConsumer<CodeInspector, Exception> inlinedInto(Method method) {
       return inspector -> {
         MethodSubject candidate = inspector.method(methodOfInterest);
         if (!candidate.isPresent()) {
diff --git a/src/test/java/com/android/tools/r8/debuginfo/EnsureNoDebugInfoEmittedForPcOnlyTestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/EnsureNoDebugInfoEmittedForPcOnlyTestRunner.java
index c2f1811..a91f006 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/EnsureNoDebugInfoEmittedForPcOnlyTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/EnsureNoDebugInfoEmittedForPcOnlyTestRunner.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.debuginfo;
 
 import static com.android.tools.r8.naming.retrace.StackTrace.isSameExceptForFileNameAndLineNumber;
+import static com.android.tools.r8.utils.InternalOptions.LineNumberOptimization.ON;
 import static junit.framework.TestCase.assertEquals;
 import static junit.framework.TestCase.assertNull;
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -72,6 +73,11 @@
         .addProgramClasses(MAIN)
         .setMinApi(parameters.getApiLevel())
         .internalEnableMappingOutput()
+        // TODO(b/191038746): Enable LineNumberOptimization for release builds for DEX PC Output.
+        .applyIf(
+            apiLevelSupportsPcOutput(),
+            builder ->
+                builder.addOptionsModification(options -> options.lineNumberOptimization = ON))
         .run(parameters.getRuntime(), MAIN)
         .inspectFailure(
             inspector -> {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ReleasedVersionsSmokeTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ReleasedVersionsSmokeTest.java
index a81de0a..8713609 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ReleasedVersionsSmokeTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ReleasedVersionsSmokeTest.java
@@ -35,6 +35,14 @@
           "GMT",
           "1000",
           "Hello, world");
+  private static final String expectedOutput_1_0_9 =
+      StringUtils.lines(
+          "true",
+          "Caught java.time.format.DateTimeParseException",
+          "true",
+          "1970-01-02T10:17:36.789Z",
+          "1000",
+          "Hello, world");
 
   @Parameters(name = "{0}, {1}")
   public static List<Object[]> data() {
@@ -61,12 +69,9 @@
                 .withKeepRuleConsumer()
                 .setMode(CompilationMode.DEBUG)
                 .build())
-        .compile()
-        .run(parameters.getRuntime(), TestClass.class)
-        .applyIf(
-            configuration.equals(Configuration.RELEASED_1_1_5),
-            r -> r.assertSuccessWithOutput(expectedOutput),
-            r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class));
+        .run(parameters.getRuntime(), TestClass.class, configuration.name())
+        .assertSuccessWithOutput(
+            configuration != Configuration.RELEASED_1_0_9 ? expectedOutput : expectedOutput_1_0_9);
   }
 
   @Test
@@ -82,14 +87,15 @@
                 .withKeepRuleConsumer()
                 .setMode(CompilationMode.RELEASE)
                 .build())
-        .compile()
-        .run(parameters.getRuntime(), TestClass.class)
-        .assertSuccessWithOutput(expectedOutput);
+        .run(parameters.getRuntime(), TestClass.class, configuration.name())
+        .assertSuccessWithOutput(
+            configuration != Configuration.RELEASED_1_0_9 ? expectedOutput : expectedOutput_1_0_9);
   }
 
   static class TestClass {
 
     public static void main(String[] args) {
+      String configurationVersion = args[0];
       System.out.println(Clock.systemDefaultZone().getZone().equals(ZoneId.systemDefault()));
       try {
         java.time.LocalDate.parse("");
@@ -100,9 +106,12 @@
       System.out.println(
           java.util.Date.from(new java.util.Date(123456789).toInstant()).toInstant());
 
-      java.util.TimeZone timeZone = java.util.TimeZone.getTimeZone(ZoneId.of("GMT"));
-      System.out.println(timeZone.getID());
-      System.out.println(timeZone.toZoneId().getId());
+      // Support for this was added in 1.0.10.
+      if (!configurationVersion.equals("RELEASED_1_0_9")) {
+        java.util.TimeZone timeZone = java.util.TimeZone.getTimeZone(ZoneId.of("GMT"));
+        System.out.println(timeZone.getID());
+        System.out.println(timeZone.toZoneId().getId());
+      }
 
       System.out.println(Duration.ofMillis(1000).toMillis());
 
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestPrivateInterfaceMethodsTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestPrivateInterfaceMethodsTest.java
index ae2b6ae..52bc03e 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestPrivateInterfaceMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestPrivateInterfaceMethodsTest.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.utils.StringUtils;
+import java.nio.file.Path;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -47,7 +48,7 @@
         .addProgramClasses(TestClass.class)
         .addProgramClassFileData(getClassWithNest(I.class), getClassWithNest(J.class))
         .run(parameters.getRuntime(), TestClass.class)
-        // TODO(191115349): Nest desugar does not downgrade the classfile version.
+        // TODO(b/191115349): Nest desugar does not downgrade the classfile version.
         .applyIf(
             c ->
                 DesugarTestConfiguration.isNotJavac(c)
@@ -56,6 +57,38 @@
             r -> r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class));
   }
 
+  @Test
+  public void testOnClasspath() throws Exception {
+    byte[] bytesI = getClassWithNest(I.class);
+    byte[] bytesJ = getClassWithNest(J.class);
+    Path outI =
+        testForD8(parameters.getBackend())
+            .addProgramClassFileData(bytesI)
+            .addClasspathClassFileData(bytesJ)
+            .setMinApi(parameters.getApiLevel())
+            .compile()
+            .writeToZip();
+    Path outJ =
+        testForD8(parameters.getBackend())
+            .addProgramClassFileData(bytesJ)
+            .addClasspathClassFileData(bytesI)
+            .setMinApi(parameters.getApiLevel())
+            .compile()
+            .writeToZip();
+    Path outTestClass =
+        testForD8(parameters.getBackend())
+            .addProgramClasses(TestClass.class)
+            .addClasspathClassFileData(bytesI, bytesJ)
+            .setMinApi(parameters.getApiLevel())
+            .compile()
+            .writeToZip();
+    testForD8(parameters.getBackend())
+        .addProgramFiles(outI, outJ, outTestClass)
+        .setMinApi(parameters.getApiLevel())
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
   interface I {
     default /* will be private */ void foo() {
       System.out.println("Hello world!");
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureVerticalMergeTest.java b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureVerticalMergeTest.java
new file mode 100644
index 0000000..b8d02f6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureVerticalMergeTest.java
@@ -0,0 +1,102 @@
+// 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.genericsignature;
+
+import static org.hamcrest.CoreMatchers.containsString;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import java.lang.reflect.Type;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class GenericSignatureVerticalMergeTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  public GenericSignatureVerticalMergeTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test(expected = CompilationFailedException.class)
+  public void testR8() throws Exception {
+    testForR8Compat(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .setMinApi(parameters.getApiLevel())
+        .addKeepMainRule(Main.class)
+        .addKeepClassRules(I.class, J.class)
+        .addKeepClassAndMembersRulesWithAllowObfuscation(Base.class)
+        .addKeepAttributeInnerClassesAndEnclosingMethod()
+        .addKeepAttributeSignature()
+        .enableInliningAnnotations()
+        .enableNeverClassInliningAnnotations()
+        .enableNoVerticalClassMergingAnnotations()
+        .addVerticallyMergedClassesInspector(
+            inspector -> inspector.assertMergedIntoSubtype(A.class))
+        .compileWithExpectedDiagnostics(
+            diagnostics -> {
+              diagnostics.assertErrorMessageThatMatches(
+                  containsString("Super type inconsistency in generic signature"));
+            });
+  }
+
+  public interface I<T> {
+
+    T t();
+  }
+
+  @NoVerticalClassMerging
+  public static class Base<T> {}
+
+  public static class A<S, T> extends Base<S> implements I<T> {
+
+    @Override
+    @NeverInline
+    public T t() {
+      System.out.println("I::t");
+      return null;
+    }
+  }
+
+  public interface J<R> {
+
+    void r(R r);
+  }
+
+  @NeverClassInline
+  public static class B<X> extends A<String, X> implements J<X> {
+
+    @Override
+    @NeverInline
+    public void r(X x) {
+      System.out.println("B::r");
+    }
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      System.out.println(B.class.getGenericSuperclass());
+      for (Type genericInterface : B.class.getGenericInterfaces()) {
+        System.out.println(genericInterface);
+      }
+      B<String> b = new B<>();
+      b.r(b.t());
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/regress/b191296688/A.java b/src/test/java/com/android/tools/r8/regress/b191296688/A.java
new file mode 100644
index 0000000..8b6b5fa
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b191296688/A.java
@@ -0,0 +1,10 @@
+// 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.regress.b191296688;
+
+public class A {
+  public void doIt(Runnable r) {
+    r.run();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/regress/b191296688/B.kt b/src/test/java/com/android/tools/r8/regress/b191296688/B.kt
new file mode 100644
index 0000000..a0ed49a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b191296688/B.kt
@@ -0,0 +1,16 @@
+// 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.regress.b191296688
+
+class B {
+  fun doIt() {
+    A().doIt(this::proceed)
+  }
+
+  private fun proceed() {
+    println("hep")
+  }
+}
+
+fun main() = B().doIt()
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/regress/b191296688/Regress191296688.java b/src/test/java/com/android/tools/r8/regress/b191296688/Regress191296688.java
new file mode 100644
index 0000000..dc7458c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b191296688/Regress191296688.java
@@ -0,0 +1,95 @@
+// 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.regress.b191296688;
+
+import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
+import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
+import static com.android.tools.r8.utils.codeinspector.CodeMatchers.isInvokeWithTarget;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime;
+import com.android.tools.r8.TestRuntime.CfRuntime;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class Regress191296688 extends KotlinTestBase {
+
+  private static final String PKG = Regress191296688.class.getPackage().getName();
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}, {1}")
+  public static Collection<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withDexRuntimes().withAllApiLevels().build(),
+        getKotlinTestParameters()
+            .withTargetVersion(KotlinTargetVersion.JAVA_8)
+            .withCompiler(ToolHelper.getKotlinC_1_5_0_m2())
+            .build());
+  }
+
+  public Regress191296688(TestParameters parameters, KotlinTestParameters kotlinParameters) {
+    super(kotlinParameters);
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testRegress191296688() throws Exception {
+    Path aLib = temp.newFolder().toPath().resolve("alib.jar");
+    writeClassesToJar(aLib, A.class);
+    String folder = DescriptorUtils.getBinaryNameFromJavaType(PKG);
+    CfRuntime cfRuntime = TestRuntime.getCheckedInJdk9();
+    Path ktClasses =
+        kotlinc(cfRuntime, kotlinc, targetVersion)
+            .addSourceFiles(getKotlinFileInTest(folder, "B"))
+            .addClasspathFiles(aLib)
+            .compile();
+    Path desugaredJar =
+        testForD8(Backend.CF)
+            .addLibraryFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+            .addProgramFiles(ktClasses)
+            .addProgramClasses(A.class)
+            .setMinApi(parameters.getApiLevel())
+            .compile()
+            .inspect(this::verifyVirtualCallToPrivate)
+            .writeToZip();
+    testForD8()
+        .addProgramFiles(desugaredJar)
+        .setMinApi(parameters.getApiLevel())
+        .disableDesugaring()
+        .run(parameters.getRuntime(), PKG + ".BKt")
+        .assertSuccessWithOutputLines("hep");
+  }
+
+  private void verifyVirtualCallToPrivate(CodeInspector inspector) {
+    ClassSubject bClassSubject = inspector.clazz(PKG + ".B");
+    MethodSubject proceedMethodSubject = bClassSubject.uniqueMethodWithName("proceed");
+    assertThat(proceedMethodSubject, isPresent());
+    assertTrue(
+        bClassSubject.allMethods().stream()
+            .anyMatch(
+                method ->
+                    method
+                        .streamInstructions()
+                        .filter(InstructionSubject::isInvokeVirtual)
+                        .anyMatch(isInvokeWithTarget(proceedMethodSubject))));
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
index 8179e48..689fce0 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
@@ -80,6 +80,8 @@
   public HorizontallyMergedClassesInspector assertMergedInto(Class<?> from, Class<?> target) {
     assertEquals(
         horizontallyMergedClasses.getMergeTargetOrDefault(toDexType(from)), toDexType(target));
+    seen.add(toDexType(from).asClassReference());
+    seen.add(toDexType(target).asClassReference());
     return this;
   }
 
diff --git a/third_party/android_jar/api-database.tar.gz.sha1 b/third_party/android_jar/api-database.tar.gz.sha1
index 84abf49..6336e23 100644
--- a/third_party/android_jar/api-database.tar.gz.sha1
+++ b/third_party/android_jar/api-database.tar.gz.sha1
@@ -1 +1 @@
-e4da4b29079ac393e0012e7676dcca0799841e29
\ No newline at end of file
+be72aeca006f1aba8b1fe4d9c3ff4c0e76259960
\ No newline at end of file