Merge commit 'f360ce2486bd287908762b33c335c9b4f8a8802a' into dev-release
diff --git a/src/main/java/com/android/tools/r8/AssertionsConfiguration.java b/src/main/java/com/android/tools/r8/AssertionsConfiguration.java
index 7199b77..12e7b48 100644
--- a/src/main/java/com/android/tools/r8/AssertionsConfiguration.java
+++ b/src/main/java/com/android/tools/r8/AssertionsConfiguration.java
@@ -4,12 +4,22 @@
 
 package com.android.tools.r8;
 
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.Reporter;
 
 @Keep
 public class AssertionsConfiguration {
 
-  /** The possible transformations of the javac generated assertion code during compilation. */
+  /**
+   * The simple transformations of the javac generated assertion code during compilation (see {@link
+   * AssertionsConfiguration.Builder#setTransformation(AssertionTransformation)}). For configuring
+   * the transformation to invoke an assertion handler use {@link
+   * AssertionsConfiguration.Builder#setAssertionHandler(MethodReference)}.
+   *
+   * @deprecated As of version 3.3 this enum should not be used.
+   */
+  @Deprecated
   @Keep
   public enum AssertionTransformation {
     /** Unconditionally enable the javac generated assertion code. */
@@ -30,20 +40,51 @@
   }
 
   private final AssertionTransformation transformation;
+  private final MethodReference assertionHandler;
   private final AssertionTransformationScope scope;
   private final String value;
 
   AssertionsConfiguration(
-      AssertionTransformation transformation, AssertionTransformationScope scope, String value) {
+      AssertionTransformation transformation,
+      MethodReference assertionHandler,
+      AssertionTransformationScope scope,
+      String value) {
     this.transformation = transformation;
+    this.assertionHandler = assertionHandler;
     this.scope = scope;
     this.value = value;
+    assert BooleanUtils.xor(transformation != null, assertionHandler != null);
   }
 
+  public boolean isCompileTimeEnabled() {
+    return transformation == AssertionTransformation.ENABLE;
+  }
+
+  public boolean isCompileTimeDisabled() {
+    return transformation == AssertionTransformation.DISABLE;
+  }
+
+  public boolean isPassthrough() {
+    return transformation == AssertionTransformation.PASSTHROUGH;
+  }
+
+  public boolean isAssertionHandler() {
+    return assertionHandler != null;
+  }
+
+  /**
+   * @deprecated As of version 3.3, use one of {@link #isCompileTimeEnabled()} ()}, {@link
+   *     #isCompileTimeDisabled()} ()} or {@link #isPassthrough()} ()}.
+   */
+  @Deprecated
   public AssertionTransformation getTransformation() {
     return transformation;
   }
 
+  public MethodReference getAssertionHandler() {
+    return assertionHandler;
+  }
+
   public AssertionTransformationScope getScope() {
     return scope;
   }
@@ -66,6 +107,7 @@
   public static class Builder {
     Reporter reporter;
     private AssertionTransformation transformation;
+    private MethodReference assertionHandler;
     private AssertionTransformationScope scope;
     private String value;
 
@@ -73,10 +115,17 @@
       this.reporter = reporter;
     }
 
-    /** Set how to handle javac generated assertion code. */
+    /**
+     * Set how to handle javac generated assertion code.
+     *
+     * @deprecated As of version 3.3, use one of {@link #setCompileTimeDisable()}, {@link
+     *     #setCompileTimeDisable()} or {@link #setPassthrough()} ()}.
+     */
+    @Deprecated
     public AssertionsConfiguration.Builder setTransformation(
         AssertionTransformation transformation) {
       this.transformation = transformation;
+      this.assertionHandler = null;
       return this;
     }
 
@@ -84,6 +133,13 @@
      * Unconditionally enable javac generated assertion code in all packages and classes. This
      * corresponds to passing <code>-enableassertions</code> or <code>-ea</code> to the java CLI.
      */
+    public AssertionsConfiguration.Builder setCompileTimeEnable() {
+      setTransformation(AssertionTransformation.ENABLE);
+      return this;
+    }
+
+    /** @deprecated As of version 3.3, replaced by {@link #setCompileTimeEnable()} ()} */
+    @Deprecated
     public AssertionsConfiguration.Builder setEnable() {
       setTransformation(AssertionTransformation.ENABLE);
       return this;
@@ -93,6 +149,13 @@
      * Disable the javac generated assertion code in all packages and classes. This corresponds to
      * passing <code>-disableassertions</code> or <code>-da</code> to the java CLI.
      */
+    public AssertionsConfiguration.Builder setCompileTimeDisable() {
+      setTransformation(AssertionTransformation.DISABLE);
+      return this;
+    }
+
+    /** @deprecated As of version 3.3, replaced by {@link #setCompileTimeDisable()} */
+    @Deprecated
     public AssertionsConfiguration.Builder setDisable() {
       setTransformation(AssertionTransformation.DISABLE);
       return this;
@@ -104,6 +167,18 @@
       return this;
     }
 
+    /**
+     * Rewrite the throwing of <code>java.lang.AssertionError</code> to call the supplied method
+     * <code>assertionHandler</code>. The method must be a reference to a static method taking one
+     * argument of type <code>java.lang.AssertionError</code>. After the assertion handler as been
+     * called, the code continues as if assertions where disabled.
+     */
+    public AssertionsConfiguration.Builder setAssertionHandler(MethodReference assertionHandler) {
+      this.transformation = null;
+      this.assertionHandler = assertionHandler;
+      return this;
+    }
+
     public AssertionsConfiguration.Builder setScopeAll() {
       this.scope = AssertionTransformationScope.ALL;
       this.value = null;
@@ -144,19 +219,20 @@
 
     /** Build and return the {@link AssertionsConfiguration}. */
     public AssertionsConfiguration build() {
-      if (transformation == null) {
-        reporter.error("No transformation specified for building AccertionConfiguration");
+      if (transformation == null && assertionHandler == null) {
+        reporter.error(
+            "No transformation or assertion handler specified for building AssertionConfiguration");
       }
       if (scope == null) {
-        reporter.error("No scope specified for building AccertionConfiguration");
+        reporter.error("No scope specified for building AssertionConfiguration");
       }
       if (scope == AssertionTransformationScope.PACKAGE && value == null) {
-        reporter.error("No package name specified for building AccertionConfiguration");
+        reporter.error("No package name specified for building AssertionConfiguration");
       }
       if (scope == AssertionTransformationScope.CLASS && value == null) {
-        reporter.error("No class name specified for building AccertionConfiguration");
+        reporter.error("No class name specified for building AssertionConfiguration");
       }
-      return new AssertionsConfiguration(transformation, scope, value);
+      return new AssertionsConfiguration(transformation, assertionHandler, scope, value);
     }
 
     /**
@@ -174,14 +250,25 @@
      *
      * <pre>
      *   D8Command command = D8Command.builder()
-     *     .addAssertionsConfiguration(builder -> builder.setEnabled().setScopeAll().build())
+     *     .addAssertionsConfiguration(
+     *         builder -> builder.setCompileTimeEnable().setScopeAll().build())
      *     ...
      *     .build();
      * </pre>
      */
+    public static AssertionsConfiguration compileTimeEnableAllAssertions(
+        AssertionsConfiguration.Builder builder) {
+      return builder.setCompileTimeEnable().setScopeAll().build();
+    }
+
+    /**
+     * @deprecated As of version 3.3, replaced by {@link #compileTimeEnableAllAssertions(Builder)}
+     *     ()}
+     */
+    @Deprecated
     public static AssertionsConfiguration enableAllAssertions(
         AssertionsConfiguration.Builder builder) {
-      return builder.setEnable().setScopeAll().build();
+      return compileTimeEnableAllAssertions(builder);
     }
 
     /**
@@ -190,7 +277,8 @@
      *
      * <pre>
      *   D8Command command = D8Command.builder()
-     *     .addAssertionsConfiguration(AssertionsConfiguration.Builder::disableAllAssertions)
+     *     .addAssertionsConfiguration(
+     *         AssertionsConfiguration.Builder::compileTimeDisableAllAssertions)
      *     ...
      *     .build();
      * </pre>
@@ -199,14 +287,25 @@
      *
      * <pre>
      *   D8Command command = D8Command.builder()
-     *     .addAssertionsConfiguration(builder -> builder.setDisabled().setScopeAll().build())
+     *     .addAssertionsConfiguration(
+     *         builder -> builder.setCompileTimeDisabled().setScopeAll().build())
      *     ...
      *     .build();
      * </pre>
      */
+    public static AssertionsConfiguration compileTimeDisableAllAssertions(
+        AssertionsConfiguration.Builder builder) {
+      return builder.setCompileTimeDisable().setScopeAll().build();
+    }
+
+    /**
+     * @deprecated As of version 3.3, replaced by {@link #compileTimeDisableAllAssertions(Builder)}
+     *     ()}
+     */
+    @Deprecated
     public static AssertionsConfiguration disableAllAssertions(
         AssertionsConfiguration.Builder builder) {
-      return builder.setDisable().setScopeAll().build();
+      return compileTimeDisableAllAssertions(builder);
     }
 
     /**
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index ad3e92b..cd0bc2f 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -210,7 +210,7 @@
             clazz -> {
               ProgramMethod classInitializer = clazz.getProgramClassInitializer();
               if (classInitializer != null) {
-                analysis.processNewlyLiveMethod(classInitializer, clazz);
+                analysis.processNewlyLiveMethod(classInitializer, clazz, null);
               }
             },
             executor);
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index a12020f..8dd34a8 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -507,7 +507,11 @@
     assert internal.assertionsConfiguration == null;
     internal.assertionsConfiguration =
         new AssertionConfigurationWithDefault(
-            AssertionTransformation.DISABLE, getAssertionsConfiguration());
+            AssertionsConfiguration.builder(getReporter())
+                .setTransformation(AssertionTransformation.DISABLE)
+                .setScopeAll()
+                .build(),
+            getAssertionsConfiguration());
 
     internal.outputInspections = InspectorImpl.wrapInspections(getOutputInspections());
 
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index 41f4030..26c99ce 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -202,7 +202,11 @@
     assert internal.assertionsConfiguration == null;
     internal.assertionsConfiguration =
         new AssertionConfigurationWithDefault(
-            AssertionTransformation.DISABLE, getAssertionsConfiguration());
+            AssertionsConfiguration.builder(getReporter())
+                .setTransformation(AssertionTransformation.DISABLE)
+                .setScopeAll()
+                .build(),
+            getAssertionsConfiguration());
 
     if (!DETERMINISTIC_DEBUGGING) {
       assert internal.threadCount == ThreadUtils.NOT_SPECIFIED;
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index b51ee2e..bf04680 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -485,9 +485,6 @@
       }
       ProguardConfiguration.Builder configurationBuilder = parser.getConfigurationBuilder();
       configurationBuilder.setForceProguardCompatibility(forceProguardCompatibility);
-      if (InternalOptions.shouldEnableKeepRuleSynthesisForRecompilation()) {
-        configurationBuilder.enableKeepRuleSynthesisForRecompilation();
-      }
 
       if (proguardConfigurationConsumerForTesting != null) {
         proguardConfigurationConsumerForTesting.accept(configurationBuilder);
@@ -921,9 +918,13 @@
     assert internal.assertionsConfiguration == null;
     internal.assertionsConfiguration =
         new AssertionConfigurationWithDefault(
-            getProgramConsumer() instanceof ClassFileConsumer
-                ? AssertionTransformation.PASSTHROUGH
-                : AssertionTransformation.DISABLE,
+            AssertionsConfiguration.builder(getReporter())
+                .setTransformation(
+                    getProgramConsumer() instanceof ClassFileConsumer
+                        ? AssertionTransformation.PASSTHROUGH
+                        : AssertionTransformation.DISABLE)
+                .setScopeAll()
+                .build(),
             getAssertionsConfiguration());
 
     // TODO(b/171552739): Enable class merging for CF. When compiling libraries, we need to be
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiForHashingClass.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiForHashingClass.java
deleted file mode 100644
index 058201f..0000000
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiForHashingClass.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// 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.graph.DexField;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.utils.AndroidApiLevel;
-import java.util.function.BiConsumer;
-
-/**
- * This is an interface for all generated classes from api-versions.xml for building a database from
- * a serialized hashed format.
- */
-public interface AndroidApiForHashingClass {
-
-  DexType getType();
-
-  AndroidApiLevel getApiLevel();
-
-  void visitMethodsWithApiLevels(BiConsumer<DexMethod, AndroidApiLevel> consumer);
-
-  void visitFieldsWithApiLevels(BiConsumer<DexField, AndroidApiLevel> consumer);
-}
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiForHashingReference.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiForHashingReference.java
new file mode 100644
index 0000000..726b1a0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiForHashingReference.java
@@ -0,0 +1,33 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.androidapi;
+
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.utils.AndroidApiLevel;
+
+/** This interface is used to add additional known references to the api database. */
+class AndroidApiForHashingReference {
+
+  private final DexReference reference;
+
+  private final AndroidApiLevel apiLevel;
+
+  private AndroidApiForHashingReference(DexReference reference, AndroidApiLevel apiLevel) {
+    this.reference = reference;
+    this.apiLevel = apiLevel;
+  }
+
+  static AndroidApiForHashingReference create(DexReference reference, AndroidApiLevel apiLevel) {
+    return new AndroidApiForHashingReference(reference, apiLevel);
+  }
+
+  DexReference getReference() {
+    return reference;
+  }
+
+  AndroidApiLevel getApiLevel() {
+    return apiLevel;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelDatabaseHelper.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelDatabaseHelper.java
new file mode 100644
index 0000000..03727d7
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelDatabaseHelper.java
@@ -0,0 +1,103 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.androidapi;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.util.function.BiConsumer;
+
+class AndroidApiLevelDatabaseHelper {
+
+  static void visitAdditionalKnownApiReferences(
+      DexItemFactory factory, BiConsumer<DexReference, AndroidApiLevel> apiLevelConsumer) {
+    // StringBuilder and StringBuffer lack api definitions for the exact same methods in
+    // api-versions.xml. See b/216587554 for related error.
+    for (DexType type : new DexType[] {factory.stringBuilderType, factory.stringBufferType}) {
+      apiLevelConsumer.accept(
+          factory.createMethod(type, factory.createProto(factory.intType), "capacity"),
+          AndroidApiLevel.B);
+      apiLevelConsumer.accept(
+          factory.createMethod(
+              type, factory.createProto(factory.intType, factory.intType), "codePointAt"),
+          AndroidApiLevel.B);
+      apiLevelConsumer.accept(
+          factory.createMethod(
+              type, factory.createProto(factory.intType, factory.intType), "codePointBefore"),
+          AndroidApiLevel.B);
+      apiLevelConsumer.accept(
+          factory.createMethod(
+              type,
+              factory.createProto(factory.intType, factory.intType, factory.intType),
+              "codePointCount"),
+          AndroidApiLevel.B);
+      apiLevelConsumer.accept(
+          factory.createMethod(
+              type, factory.createProto(factory.voidType, factory.intType), "ensureCapacity"),
+          AndroidApiLevel.B);
+      apiLevelConsumer.accept(
+          factory.createMethod(
+              type,
+              factory.createProto(
+                  factory.voidType,
+                  factory.intType,
+                  factory.intType,
+                  factory.charArrayType,
+                  factory.intType),
+              "getChars"),
+          AndroidApiLevel.B);
+      apiLevelConsumer.accept(
+          factory.createMethod(
+              type, factory.createProto(factory.intType, factory.stringType), "indexOf"),
+          AndroidApiLevel.B);
+      apiLevelConsumer.accept(
+          factory.createMethod(
+              type,
+              factory.createProto(factory.intType, factory.stringType, factory.intType),
+              "indexOf"),
+          AndroidApiLevel.B);
+      apiLevelConsumer.accept(
+          factory.createMethod(
+              type, factory.createProto(factory.intType, factory.stringType), "lastIndexOf"),
+          AndroidApiLevel.B);
+      apiLevelConsumer.accept(
+          factory.createMethod(
+              type,
+              factory.createProto(factory.intType, factory.stringType, factory.intType),
+              "lastIndexOf"),
+          AndroidApiLevel.B);
+      apiLevelConsumer.accept(
+          factory.createMethod(
+              type,
+              factory.createProto(factory.intType, factory.intType, factory.intType),
+              "offsetByCodePoints"),
+          AndroidApiLevel.B);
+      apiLevelConsumer.accept(
+          factory.createMethod(
+              type,
+              factory.createProto(factory.voidType, factory.intType, factory.charType),
+              "setCharAt"),
+          AndroidApiLevel.B);
+      apiLevelConsumer.accept(
+          factory.createMethod(
+              type, factory.createProto(factory.voidType, factory.intType), "setLength"),
+          AndroidApiLevel.B);
+      apiLevelConsumer.accept(
+          factory.createMethod(
+              type, factory.createProto(factory.stringType, factory.intType), "substring"),
+          AndroidApiLevel.B);
+      apiLevelConsumer.accept(
+          factory.createMethod(
+              type,
+              factory.createProto(factory.stringType, factory.intType, factory.intType),
+              "substring"),
+          AndroidApiLevel.B);
+      apiLevelConsumer.accept(
+          factory.createMethod(type, factory.createProto(factory.voidType), "trimToSize"),
+          AndroidApiLevel.B);
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelHashingDatabaseImpl.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelHashingDatabaseImpl.java
index bedc254..f5343da 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelHashingDatabaseImpl.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelHashingDatabaseImpl.java
@@ -41,24 +41,21 @@
   private final Map<DexReference, AndroidApiLevel> ambiguousCache = new IdentityHashMap<>();
 
   public AndroidApiLevelHashingDatabaseImpl(
-      List<AndroidApiForHashingClass> predefinedApiTypeLookup) {
+      List<AndroidApiForHashingReference> predefinedApiTypeLookup) {
     loadData();
     predefinedApiTypeLookup.forEach(
-        apiClass -> {
-          DexType type = apiClass.getType();
-          lookupNonAmbiguousCache.put(type.hashCode(), null);
-          ambiguousCache.put(type, apiClass.getApiLevel());
-          apiClass.visitMethodsWithApiLevels(
-              (method, apiLevel) -> {
-                lookupNonAmbiguousCache.put(method.hashCode(), null);
-                ambiguousCache.put(method, apiLevel);
-              });
-          apiClass.visitFieldsWithApiLevels(
-              (field, apiLevel) -> {
-                lookupNonAmbiguousCache.put(field.hashCode(), null);
-                ambiguousCache.put(field, apiLevel);
-              });
+        predefinedApiReference -> {
+          int hashCode = predefinedApiReference.getReference().hashCode();
+          // Do not use computeIfAbsent since a return value of null implies the key should not be
+          // inserted.
+          if (!lookupNonAmbiguousCache.containsKey(hashCode)) {
+            lookupNonAmbiguousCache.put(hashCode, null);
+            ambiguousCache.put(
+                predefinedApiReference.getReference(), predefinedApiReference.getApiLevel());
+          }
         });
+    assert predefinedApiTypeLookup.stream()
+        .allMatch(added -> added.getApiLevel().isEqualTo(lookupApiLevel(added.getReference())));
   }
 
   private void loadData() {
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
index b4609ce..2e2aa7a 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
@@ -11,8 +11,10 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.ConsumerUtils;
 import com.google.common.collect.ImmutableList;
 import java.util.List;
+import java.util.function.BiConsumer;
 
 public class AndroidApiReferenceLevelCache {
 
@@ -25,7 +27,7 @@
   private AndroidApiReferenceLevelCache(
       AppView<?> appView,
       AndroidApiLevelCompute apiLevelCompute,
-      List<AndroidApiForHashingClass> predefinedApiTypeLookupForHashing) {
+      List<AndroidApiForHashingReference> predefinedApiTypeLookupForHashing) {
     this.appView = appView;
     this.apiLevelCompute = apiLevelCompute;
     factory = appView.dexItemFactory();
@@ -37,11 +39,15 @@
   public static AndroidApiReferenceLevelCache create(
       AppView<?> appView, AndroidApiLevelCompute apiLevelCompute) {
     assert appView.options().apiModelingOptions().enableApiCallerIdentification;
-    ImmutableList.Builder<AndroidApiForHashingClass> builder = ImmutableList.builder();
+    ImmutableList.Builder<AndroidApiForHashingReference> builder = ImmutableList.builder();
+    BiConsumer<DexReference, AndroidApiLevel> addItemToList =
+        ConsumerUtils.andThen(AndroidApiForHashingReference::create, builder::add);
+    AndroidApiLevelDatabaseHelper.visitAdditionalKnownApiReferences(
+        appView.dexItemFactory(), addItemToList);
     appView
         .options()
         .apiModelingOptions()
-        .visitMockedApiLevelsForReferences(appView.dexItemFactory(), builder::add);
+        .visitMockedApiLevelsForReferences(appView.dexItemFactory(), addItemToList);
     return new AndroidApiReferenceLevelCache(appView, apiLevelCompute, builder.build());
   }
 
diff --git a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
index ae4700f..f9b13a4 100644
--- a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
+++ b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
@@ -116,7 +116,7 @@
 
   public ApiReferenceStubber(AppView<? extends AppInfoWithClassHierarchy> appView) {
     this.appView = appView;
-    apiLevelCompute = AndroidApiLevelCompute.create(appView);
+    apiLevelCompute = appView.apiLevelCompute();
     desugaredLibraryConfiguration = appView.options().desugaredLibrarySpecification;
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index 07ebb29..f35751b 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -19,11 +19,14 @@
 import com.android.tools.r8.cf.code.CfReturnVoid;
 import com.android.tools.r8.cf.code.CfTryCatch;
 import com.android.tools.r8.code.Base5Format;
+import com.android.tools.r8.code.CfOrDexInstruction;
 import com.android.tools.r8.errors.InvalidDebugInfoException;
 import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfo;
 import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
+import com.android.tools.r8.graph.bytecodemetadata.BytecodeInstructionMetadata;
+import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadata;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.MemberType;
 import com.android.tools.r8.ir.code.NumberGenerator;
@@ -150,6 +153,7 @@
   private final List<LocalVariableInfo> localVariables;
   private StackMapStatus stackMapStatus = StackMapStatus.NOT_VERIFIED;
   private final com.android.tools.r8.position.Position diagnosticPosition;
+  private final BytecodeMetadata<CfInstruction> metadata;
 
   public CfCode(
       DexType originalHolder, int maxStack, int maxLocals, List<CfInstruction> instructions) {
@@ -187,6 +191,26 @@
       List<CfTryCatch> tryCatchRanges,
       List<LocalVariableInfo> localVariables,
       com.android.tools.r8.position.Position diagnosticPosition) {
+    this(
+        originalHolder,
+        maxStack,
+        maxLocals,
+        instructions,
+        tryCatchRanges,
+        localVariables,
+        diagnosticPosition,
+        BytecodeMetadata.empty());
+  }
+
+  public CfCode(
+      DexType originalHolder,
+      int maxStack,
+      int maxLocals,
+      List<CfInstruction> instructions,
+      List<CfTryCatch> tryCatchRanges,
+      List<LocalVariableInfo> localVariables,
+      com.android.tools.r8.position.Position diagnosticPosition,
+      BytecodeMetadata<CfInstruction> metadata) {
     this.originalHolder = originalHolder;
     this.maxStack = maxStack;
     this.maxLocals = maxLocals;
@@ -194,6 +218,7 @@
     this.tryCatchRanges = tryCatchRanges;
     this.localVariables = localVariables;
     this.diagnosticPosition = diagnosticPosition;
+    this.metadata = metadata;
   }
 
   @Override
@@ -207,6 +232,20 @@
   }
 
   @Override
+  public BytecodeMetadata<CfInstruction> getMetadata() {
+    return metadata;
+  }
+
+  @Override
+  public BytecodeInstructionMetadata getMetadata(CfOrDexInstruction instruction) {
+    return getMetadata(instruction.asCfInstruction());
+  }
+
+  public BytecodeInstructionMetadata getMetadata(CfInstruction instruction) {
+    return metadata.getMetadata(instruction);
+  }
+
+  @Override
   public StructuralMapping<CfCode> getStructuralMapping() {
     throw new Unreachable();
   }
diff --git a/src/main/java/com/android/tools/r8/graph/Code.java b/src/main/java/com/android/tools/r8/graph/Code.java
index e727373..f8d5917 100644
--- a/src/main/java/com/android/tools/r8/graph/Code.java
+++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.dex.MixedSectionCollection;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.bytecodemetadata.BytecodeInstructionMetadata;
+import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadata;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.NumberGenerator;
 import com.android.tools.r8.ir.code.Position;
@@ -31,6 +32,10 @@
         + getClass().getCanonicalName());
   }
 
+  public BytecodeMetadata<? extends CfOrDexInstruction> getMetadata() {
+    return null;
+  }
+
   public BytecodeInstructionMetadata getMetadata(CfOrDexInstruction instruction) {
     return null;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java
index 7689694..a44fc1d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -123,6 +123,11 @@
   }
 
   @Override
+  public BytecodeMetadata<Instruction> getMetadata() {
+    return metadata;
+  }
+
+  @Override
   public BytecodeInstructionMetadata getMetadata(CfOrDexInstruction instruction) {
     return getMetadata(instruction.asDexInstruction());
   }
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java b/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
index 0f27bda..0beeb2f 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
@@ -291,17 +291,16 @@
     assert code.getDebugInfo().isPcBasedInfo();
     PcBasedDebugInfo pcBasedDebugInfo = code.getDebugInfo().asPcBasedInfo();
     // Generate a line event at each throwing instruction.
-    List<DexDebugEvent> events = new ArrayList<>(code.instructions.length + 1);
-    events.add(factory.zeroChangeDefaultEvent);
+    List<DexDebugEvent> events = new ArrayList<>(code.instructions.length);
     int pc = 0;
     int delta = 0;
     for (Instruction instruction : code.instructions) {
-      delta += instruction.getSize();
       if (instruction.canThrow()) {
         DexDebugEventBuilder.addDefaultEventWithAdvancePcIfNecessary(delta, delta, events, factory);
         pc += delta;
         delta = 0;
       }
+      delta += instruction.getSize();
     }
     assert pc + delta - ArrayUtils.last(code.instructions).getSize() <= pcBasedDebugInfo.maxPc;
     return new EventBasedDebugInfo(
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureEnqueuerAnalysis.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureEnqueuerAnalysis.java
index 33f6e4d..ccbb86f 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignatureEnqueuerAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureEnqueuerAnalysis.java
@@ -25,22 +25,24 @@
   }
 
   @Override
-  public void notifyMarkFieldAsReachable(ProgramField field) {
+  public void notifyMarkFieldAsReachable(ProgramField field, EnqueuerWorklist worklist) {
     processSignature(field, field.getContext());
   }
 
   @Override
-  public void processNewlyLiveField(ProgramField field, ProgramDefinition context) {
+  public void processNewlyLiveField(
+      ProgramField field, ProgramDefinition context, EnqueuerWorklist worklist) {
     processSignature(field, context);
   }
 
   @Override
-  public void notifyMarkMethodAsTargeted(ProgramMethod method) {
+  public void notifyMarkMethodAsTargeted(ProgramMethod method, EnqueuerWorklist worklist) {
     processSignature(method, method.getContext());
   }
 
   @Override
-  public void processNewlyLiveMethod(ProgramMethod method, ProgramDefinition context) {
+  public void processNewlyLiveMethod(
+      ProgramMethod method, ProgramDefinition context, EnqueuerWorklist worklist) {
     processSignature(method, context);
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollection.java b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollection.java
index 28ab2ca..745c558 100644
--- a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollection.java
@@ -6,6 +6,7 @@
 
 import java.util.Set;
 import java.util.function.BiConsumer;
+import java.util.function.Consumer;
 
 /**
  * Provides immutable access to {@link ObjectAllocationInfoCollectionImpl}, which stores the set of
@@ -28,6 +29,8 @@
 
   boolean isImmediateInterfaceOfInstantiatedLambda(DexProgramClass clazz);
 
+  void forEachInstantiatedLambdaInterfaces(Consumer<DexType> consumer);
+
   ObjectAllocationInfoCollection rewrittenWithLens(
       DexDefinitionSupplier definitions, GraphLens lens);
 }
diff --git a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
index aa5e4c8..bcad963 100644
--- a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
@@ -242,6 +242,11 @@
     return instantiatedLambdas.keySet();
   }
 
+  @Override
+  public void forEachInstantiatedLambdaInterfaces(Consumer<DexType> consumer) {
+    getInstantiatedLambdaInterfaces().forEach(consumer);
+  }
+
   public void removeAllocationsForPrunedItems(PrunedItems prunedItems) {
     Set<DexType> removedClasses = prunedItems.getRemovedClasses();
     if (removedClasses.isEmpty()) {
diff --git a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
index 972c83a..e6a6cba 100644
--- a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
+++ b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
@@ -234,11 +234,7 @@
       return new Builder();
     }
 
-    public RewrittenTypeInfo(DexType oldType, DexType newType) {
-      this(oldType, newType, null, null);
-    }
-
-    public RewrittenTypeInfo(
+    private RewrittenTypeInfo(
         DexType oldType, DexType newType, DexType castType, SingleValue singleValue) {
       this.castType = castType;
       this.oldType = oldType;
diff --git a/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java b/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java
index 0800ede..2382abc 100644
--- a/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/SubtypingInfo.java
@@ -33,9 +33,15 @@
 
   private final Map<DexType, TypeInfo> typeInfo;
 
-  private SubtypingInfo(Map<DexType, TypeInfo> typeInfo, Map<DexType, Set<DexType>> subtypeMap) {
+  private final DexItemFactory factory;
+
+  private SubtypingInfo(
+      Map<DexType, TypeInfo> typeInfo,
+      Map<DexType, Set<DexType>> subtypeMap,
+      DexItemFactory factory) {
     this.typeInfo = typeInfo;
     this.subtypeMap = subtypeMap;
+    this.factory = factory;
   }
 
   public static SubtypingInfo create(AppView<? extends AppInfoWithClassHierarchy> appView) {
@@ -46,12 +52,12 @@
     return create(appInfo.app().asDirect().allClasses(), appInfo);
   }
 
-  private static SubtypingInfo create(
-      Collection<DexClass> classes, DexDefinitionSupplier definitions) {
+  public static SubtypingInfo create(
+      Collection<? extends DexClass> classes, DexDefinitionSupplier definitions) {
     Map<DexType, TypeInfo> typeInfo = new ConcurrentHashMap<>();
     Map<DexType, Set<DexType>> subtypeMap = new IdentityHashMap<>();
     populateSubtypeMap(classes, subtypeMap, typeInfo, definitions);
-    return new SubtypingInfo(typeInfo, subtypeMap);
+    return new SubtypingInfo(typeInfo, subtypeMap, definitions.dexItemFactory());
   }
 
   private static void populateSuperType(
@@ -111,7 +117,7 @@
   }
 
   private static void populateSubtypeMap(
-      Collection<DexClass> classes,
+      Collection<? extends DexClass> classes,
       Map<DexType, Set<DexType>> map,
       Map<DexType, TypeInfo> typeInfo,
       DexDefinitionSupplier definitionSupplier) {
@@ -225,6 +231,13 @@
     return ImmutableList.of();
   }
 
+  public void forAllInterfaceRoots(Consumer<DexType> fn) {
+    Iterables.filter(
+            getTypeInfo(factory.objectType).directSubtypes,
+            subtype -> getTypeInfo(subtype).isInterface())
+        .forEach(fn);
+  }
+
   private static class TypeInfo {
 
     private final DexType type;
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 86c7e2d..6e6088a 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
@@ -14,6 +14,7 @@
 import com.android.tools.r8.graph.ProgramField;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.shaking.DefaultEnqueuerUseRegistry;
+import com.android.tools.r8.shaking.EnqueuerWorklist;
 
 public class ApiModelAnalysis extends EnqueuerAnalysis {
 
@@ -28,17 +29,20 @@
   }
 
   @Override
-  public void processNewlyLiveField(ProgramField field, ProgramDefinition context) {
+  public void processNewlyLiveField(
+      ProgramField field, ProgramDefinition context, EnqueuerWorklist worklist) {
     computeAndSetApiLevelForDefinition(field);
   }
 
   @Override
-  public void processNewlyLiveMethod(ProgramMethod method, ProgramDefinition context) {
+  public void processNewlyLiveMethod(
+      ProgramMethod method, ProgramDefinition context, EnqueuerWorklist worklist) {
     computeAndSetApiLevelForDefinition(method);
   }
 
   @Override
-  public void processTracedCode(ProgramMethod method, DefaultEnqueuerUseRegistry registry) {
+  public void processTracedCode(
+      ProgramMethod method, DefaultEnqueuerUseRegistry registry, EnqueuerWorklist worklist) {
     assert registry.getMaxApiReferenceLevel().isGreaterThanOrEqualTo(minApiLevel);
     if (appView.options().apiModelingOptions().tracedMethodApiLevelCallback != null) {
       appView
@@ -52,17 +56,18 @@
   }
 
   @Override
-  public void notifyMarkMethodAsTargeted(ProgramMethod method) {
+  public void notifyMarkMethodAsTargeted(ProgramMethod method, EnqueuerWorklist worklist) {
     computeAndSetApiLevelForDefinition(method);
   }
 
   @Override
-  public void notifyMarkFieldAsReachable(ProgramField field) {
+  public void notifyMarkFieldAsReachable(ProgramField field, EnqueuerWorklist worklist) {
     computeAndSetApiLevelForDefinition(field);
   }
 
   @Override
-  public void notifyMarkVirtualDispatchTargetAsLive(LookupTarget target) {
+  public void notifyMarkVirtualDispatchTargetAsLive(
+      LookupTarget target, EnqueuerWorklist worklist) {
     target.accept(
         this::computeAndSetApiLevelForDefinition,
         lookupLambdaTarget -> {
@@ -71,7 +76,8 @@
   }
 
   @Override
-  public void notifyFailedMethodResolutionTarget(DexEncodedMethod method) {
+  public void notifyFailedMethodResolutionTarget(
+      DexEncodedMethod method, EnqueuerWorklist worklist) {
     // We may not trace into failed resolution targets.
     method.setApiLevelForCode(ComputedApiLevel.unknown());
   }
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java
index 45bff3f..a6045e4 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java
@@ -22,6 +22,7 @@
 import com.android.tools.r8.graph.ProgramDefinition;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
+import com.android.tools.r8.shaking.EnqueuerWorklist;
 import com.google.common.collect.ImmutableList;
 import java.util.List;
 import org.objectweb.asm.Opcodes;
@@ -39,7 +40,8 @@
   }
 
   @Override
-  public void processNewlyLiveMethod(ProgramMethod method, ProgramDefinition context) {
+  public void processNewlyLiveMethod(
+      ProgramMethod method, ProgramDefinition context, EnqueuerWorklist worklist) {
     DexEncodedMethod definition = method.getDefinition();
     if (definition.isClassInitializer()) {
       Code code = definition.getCode();
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java
index ceb7e92..eef04d0 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java
@@ -18,27 +18,33 @@
 public abstract class EnqueuerAnalysis {
 
   /** Called when a class is found to be instantiated. */
-  public void processNewlyInstantiatedClass(DexProgramClass clazz, ProgramMethod context) {}
+  public void processNewlyInstantiatedClass(
+      DexProgramClass clazz, ProgramMethod context, EnqueuerWorklist worklist) {}
 
   /** Called when a class is found to be live. */
   public void processNewlyLiveClass(DexProgramClass clazz, EnqueuerWorklist worklist) {}
 
   /** Called when a field is found to be live. */
-  public void processNewlyLiveField(ProgramField field, ProgramDefinition context) {}
+  public void processNewlyLiveField(
+      ProgramField field, ProgramDefinition context, EnqueuerWorklist worklist) {}
 
   /** Called when a method is found to be live. */
-  public void processNewlyLiveMethod(ProgramMethod method, ProgramDefinition context) {}
+  public void processNewlyLiveMethod(
+      ProgramMethod method, ProgramDefinition context, EnqueuerWorklist worklist) {}
 
   /** Called when a method's code has been processed by the registry. */
-  public void processTracedCode(ProgramMethod method, DefaultEnqueuerUseRegistry registry) {}
+  public void processTracedCode(
+      ProgramMethod method, DefaultEnqueuerUseRegistry registry, EnqueuerWorklist worklist) {}
 
-  public void notifyMarkMethodAsTargeted(ProgramMethod method) {}
+  public void notifyMarkMethodAsTargeted(ProgramMethod method, EnqueuerWorklist worklist) {}
 
-  public void notifyMarkFieldAsReachable(ProgramField field) {}
+  public void notifyMarkFieldAsReachable(ProgramField field, EnqueuerWorklist worklist) {}
 
-  public void notifyMarkVirtualDispatchTargetAsLive(LookupTarget target) {}
+  public void notifyMarkVirtualDispatchTargetAsLive(
+      LookupTarget target, EnqueuerWorklist worklist) {}
 
-  public void notifyFailedMethodResolutionTarget(DexEncodedMethod method) {}
+  public void notifyFailedMethodResolutionTarget(
+      DexEncodedMethod method, EnqueuerWorklist worklist) {}
 
   /**
    * Called when the Enqueuer reaches a fixpoint. This may happen multiple times, since each
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java
index cc74d21..0d19daa 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
 import com.android.tools.r8.shaking.Enqueuer;
+import com.android.tools.r8.shaking.EnqueuerWorklist;
 import java.util.IdentityHashMap;
 import java.util.Map;
 
@@ -63,7 +64,8 @@
   }
 
   @Override
-  public void processNewlyInstantiatedClass(DexProgramClass clazz, ProgramMethod context) {
+  public void processNewlyInstantiatedClass(
+      DexProgramClass clazz, ProgramMethod context, EnqueuerWorklist worklist) {
     DexType key = clazz.type;
     DexType objectType = appView.dexItemFactory().objectType;
     if (context == null) {
diff --git a/src/main/java/com/android/tools/r8/graph/bytecodemetadata/BytecodeMetadata.java b/src/main/java/com/android/tools/r8/graph/bytecodemetadata/BytecodeMetadata.java
index ad7dee4..f335b4c 100644
--- a/src/main/java/com/android/tools/r8/graph/bytecodemetadata/BytecodeMetadata.java
+++ b/src/main/java/com/android/tools/r8/graph/bytecodemetadata/BytecodeMetadata.java
@@ -60,5 +60,10 @@
     public BytecodeMetadata<I> build() {
       return backing.isEmpty() ? empty() : new BytecodeMetadata<>(backing);
     }
+
+    public boolean verifyNoMetadata(Instruction irInstruction) {
+      assert bytecodeMetadataProvider.getMetadata(irInstruction) == null;
+      return true;
+    }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIndirectRuntimeTypeChecks.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIndirectRuntimeTypeChecks.java
index bd5b1da..62dda05 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIndirectRuntimeTypeChecks.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIndirectRuntimeTypeChecks.java
@@ -59,6 +59,14 @@
       cache.put(type, true);
       return true;
     }
+    if (clazz.hasClassInitializer()
+        && clazz.getClassInitializer().getOptimizationInfo().mayHaveSideEffects()
+        && clazz.getMethodCollection().hasVirtualMethods(method -> !method.isAbstract())) {
+      // Require interfaces that trigger class initialization side effects to be implement by all
+      // group members.
+      cache.put(type, true);
+      return true;
+    }
     for (DexType parentType : clazz.getInterfaces()) {
       if (computeInterfaceHasDirectOrIndirectRuntimeTypeCheck(parentType)) {
         cache.put(type, true);
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
index d0cfd52..b0ddb4a 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
@@ -132,7 +132,8 @@
    * ProtoMessageInfo} object, and create a mapping from the holder to it.
    */
   @Override
-  public void processNewlyLiveMethod(ProgramMethod method, ProgramDefinition context) {
+  public void processNewlyLiveMethod(
+      ProgramMethod method, ProgramDefinition context, EnqueuerWorklist worklist) {
     if (references.isFindLiteExtensionByNumberMethod(method.getReference())) {
       findLiteExtensionByNumberMethods.add(method);
       return;
diff --git a/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingNop.java b/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingNop.java
index 45fa439..0e09c04 100644
--- a/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingNop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/AlwaysMaterializingNop.java
@@ -42,7 +42,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfNop());
+    builder.add(new CfNop(), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArithmeticBinop.java b/src/main/java/com/android/tools/r8/ir/code/ArithmeticBinop.java
index bf545a0..a4be195 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArithmeticBinop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArithmeticBinop.java
@@ -165,6 +165,6 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfArithmeticBinop(getCfOpcode(), type));
+    builder.add(new CfArithmeticBinop(getCfOpcode(), type), this);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayAccess.java b/src/main/java/com/android/tools/r8/ir/code/ArrayAccess.java
index 9140e87..eb20267 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayAccess.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayAccess.java
@@ -24,6 +24,12 @@
     return inValues.get(INDEX_INDEX);
   }
 
+  public int getIndexOrDefault(int defaultValue) {
+    return index().isConstant()
+        ? index().getConstInstruction().asConstInstruction().asConstNumber().getIntValue()
+        : defaultValue;
+  }
+
   @Override
   public boolean isArrayAccess() {
     return true;
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java b/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
index e1bb5bf..d51e318 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayGet.java
@@ -176,7 +176,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfArrayLoad(type));
+    builder.add(new CfArrayLoad(type), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java b/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
index 4f0995b..3835b3d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayLength.java
@@ -116,7 +116,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfArrayLength());
+    builder.add(new CfArrayLength(), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
index 4108175..9994dde 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
@@ -213,7 +213,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfArrayStore(type));
+    builder.add(new CfArrayStore(type), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
index e34ab51..864e37e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
+++ b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
@@ -264,7 +264,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfCheckCast(type));
+    builder.add(new CfCheckCast(type), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Cmp.java b/src/main/java/com/android/tools/r8/ir/code/Cmp.java
index 2b44f93..a7b25a3 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Cmp.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Cmp.java
@@ -231,7 +231,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfCmp(bias, type));
+    builder.add(new CfCmp(bias, type), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstClass.java b/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
index 93cefd2..716396d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
@@ -197,7 +197,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfConstClass(clazz));
+    builder.add(new CfConstClass(clazz), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java b/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java
index 3973d6e..35cef20 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstMethodHandle.java
@@ -63,7 +63,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfConstMethodHandle(methodHandle));
+    builder.add(new CfConstMethodHandle(methodHandle), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java b/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
index 6f153a6..64359c8 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstMethodType.java
@@ -63,7 +63,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfConstMethodType(methodType));
+    builder.add(new CfConstMethodType(methodType), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
index b849f75..4111b17 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
@@ -173,9 +173,9 @@
   @Override
   public void buildCf(CfBuilder builder) {
     if (outType().isObject()) {
-      builder.add(new CfConstNull());
+      builder.add(new CfConstNull(), this);
     } else {
-      builder.add(new CfConstNumber(value, outType()));
+      builder.add(new CfConstNumber(value, outType()), this);
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstString.java b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
index f873fd8..a3efba8 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstString.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
@@ -144,7 +144,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfConstString(value));
+    builder.add(new CfConstString(value), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java b/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java
index 6817315..ad9be48 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugLocalWrite.java
@@ -69,7 +69,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfStore(outType(), builder.getLocalRegister(outValue())));
+    builder.add(new CfStore(outType(), builder.getLocalRegister(outValue())), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java b/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java
index d4d76da..e5bdb6d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugPosition.java
@@ -86,7 +86,7 @@
   public void buildCf(CfBuilder builder) {
     assert getPosition().isSome() && !getPosition().isSyntheticPosition();
     // All redundant debug positions are removed. Remaining ones must force a pc advance.
-    builder.add(new CfNop());
+    builder.add(new CfNop(), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java b/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java
index 4c8c646..4dc3985 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java
@@ -139,7 +139,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfDexItemBasedConstString(item, nameComputationInfo));
+    builder.add(new CfDexItemBasedConstString(item, nameComputationInfo), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Dup.java b/src/main/java/com/android/tools/r8/ir/code/Dup.java
index 3bdc40c..2c9c3be 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Dup.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Dup.java
@@ -69,9 +69,9 @@
   @Override
   public void buildCf(CfBuilder builder) {
     if (this.inValues.get(0).getType().isWidePrimitive()) {
-      builder.add(new CfStackInstruction(Opcode.Dup2));
+      builder.add(new CfStackInstruction(Opcode.Dup2), this);
     } else {
-      builder.add(new CfStackInstruction(Opcode.Dup));
+      builder.add(new CfStackInstruction(Opcode.Dup), this);
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/Dup2.java b/src/main/java/com/android/tools/r8/ir/code/Dup2.java
index a01b789..7eff1c4 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Dup2.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Dup2.java
@@ -86,7 +86,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfStackInstruction(Opcode.Dup2));
+    builder.add(new CfStackInstruction(Opcode.Dup2), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Goto.java b/src/main/java/com/android/tools/r8/ir/code/Goto.java
index dde7f0d..025dec0 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Goto.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Goto.java
@@ -118,7 +118,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfGoto(builder.getLabel(getTarget())));
+    builder.add(new CfGoto(builder.getLabel(getTarget())), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java b/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java
index c562fe7..468c7bf 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRMetadata.java
@@ -65,6 +65,10 @@
     return get(Opcodes.AND);
   }
 
+  public boolean mayHaveArrayGet() {
+    return get(Opcodes.ARRAY_GET);
+  }
+
   public boolean mayHaveArrayLength() {
     return get(Opcodes.ARRAY_LENGTH);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/If.java b/src/main/java/com/android/tools/r8/ir/code/If.java
index 62cb5a2..ce75f73 100644
--- a/src/main/java/com/android/tools/r8/ir/code/If.java
+++ b/src/main/java/com/android/tools/r8/ir/code/If.java
@@ -268,11 +268,11 @@
   public void buildCf(CfBuilder builder) {
     ValueType ifType = inValues.get(0).outType();
     if (inValues.size() == 1) {
-      builder.add(new CfIf(type, ifType, builder.getLabel(getTrueTarget())));
+      builder.add(new CfIf(type, ifType, builder.getLabel(getTrueTarget())), this);
       return;
     }
     assert inValues.size() == 2;
     assert inValues.get(0).outType() == inValues.get(1).outType();
-    builder.add(new CfIfCmp(type, ifType, builder.getLabel(getTrueTarget())));
+    builder.add(new CfIfCmp(type, ifType, builder.getLabel(getTrueTarget())), this);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Inc.java b/src/main/java/com/android/tools/r8/ir/code/Inc.java
index 3d10cc4..de99253 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Inc.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Inc.java
@@ -83,17 +83,21 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
+    // Check that this instruction does not have any metadata attached, as it might not materialize
+    // as an iinc in CfCode.
+    assert builder.verifyNoMetadata(this);
     Value inValue = inValues.get(0);
     int inRegister = builder.getLocalRegister(inValue);
     int outRegister = builder.getLocalRegister(outValue);
     if (inRegister == outRegister) {
-      builder.add(new CfIinc(inRegister, increment));
+      builder.add(new CfIinc(inRegister, increment), this);
     } else {
       assert inValue.outType() == ValueType.INT;
-      builder.add(new CfLoad(ValueType.INT, inRegister));
-      builder.add(new CfConstNumber(increment, ValueType.INT));
-      builder.add(new CfArithmeticBinop(Opcode.Add, NumericType.INT));
-      builder.add(new CfStore(ValueType.INT, outRegister));
+      builder.add(
+          new CfLoad(ValueType.INT, inRegister),
+          new CfConstNumber(increment, ValueType.INT),
+          new CfArithmeticBinop(Opcode.Add, NumericType.INT),
+          new CfStore(ValueType.INT, outRegister));
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/InitClass.java b/src/main/java/com/android/tools/r8/ir/code/InitClass.java
index aceeab3..80351a6 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InitClass.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InitClass.java
@@ -76,7 +76,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfInitClass(clazz));
+    builder.add(new CfInitClass(clazz), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
index 347ca58..0bc0830 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
@@ -200,7 +200,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfInstanceFieldRead(getField(), builder.resolveField(getField())));
+    builder.add(new CfInstanceFieldRead(getField(), builder.resolveField(getField())), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java b/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java
index 0530403..123eb45 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceOf.java
@@ -109,7 +109,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfInstanceOf(type));
+    builder.add(new CfInstanceOf(type), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
index 95086dc..13cba81 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
@@ -241,7 +241,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfInstanceFieldWrite(getField(), builder.resolveField(getField())));
+    builder.add(new CfInstanceFieldWrite(getField(), builder.resolveField(getField())), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/IntSwitch.java b/src/main/java/com/android/tools/r8/ir/code/IntSwitch.java
index e0e3cdc..601f67f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IntSwitch.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IntSwitch.java
@@ -311,12 +311,12 @@
         }
       }
       assert index == numberOfKeys();
-      builder.add(new CfSwitch(Kind.TABLE, fallthroughLabel, new int[] {min}, labels));
+      builder.add(new CfSwitch(Kind.TABLE, fallthroughLabel, new int[] {min}, labels), this);
     } else {
       for (int index : targetBlockIndices()) {
         labels.add(builder.getLabel(successors.get(index)));
       }
-      builder.add(new CfSwitch(Kind.LOOKUP, fallthroughLabel, this.keys, labels));
+      builder.add(new CfSwitch(Kind.LOOKUP, fallthroughLabel, this.keys, labels), this);
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
index 508b115..1296742 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
@@ -147,7 +147,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfInvokeDynamic(getCallSite()));
+    builder.add(new CfInvokeDynamic(getCallSite()), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
index 0f2e98a..062220b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeDirect.java
@@ -161,7 +161,8 @@
   @Override
   public void buildCf(CfBuilder builder) {
     builder.add(
-        new CfInvoke(org.objectweb.asm.Opcodes.INVOKESPECIAL, getInvokedMethod(), isInterface));
+        new CfInvoke(org.objectweb.asm.Opcodes.INVOKESPECIAL, getInvokedMethod(), isInterface),
+        this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
index 6412e17..153ca1b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
@@ -128,7 +128,8 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfInvoke(org.objectweb.asm.Opcodes.INVOKEINTERFACE, getInvokedMethod(), true));
+    builder.add(
+        new CfInvoke(org.objectweb.asm.Opcodes.INVOKEINTERFACE, getInvokedMethod(), true), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
index 7163fdd..63df06a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
@@ -104,7 +104,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfMultiANewArray(type, arguments().size()));
+    builder.add(new CfMultiANewArray(type, arguments().size()), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
index 425262b..ad8c087 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
@@ -104,7 +104,7 @@
     // To translate InvokePolymorphic back into InvokeVirtual, use the original prototype
     // that is stored in getProto().
     DexMethod method = factory.createMethod(dexMethod.holder, getProto(), dexMethod.name);
-    builder.add(new CfInvoke(org.objectweb.asm.Opcodes.INVOKEVIRTUAL, method, false));
+    builder.add(new CfInvoke(org.objectweb.asm.Opcodes.INVOKEVIRTUAL, method, false), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
index f1f86b3..7aa20ad 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
@@ -155,7 +155,8 @@
   @Override
   public void buildCf(CfBuilder builder) {
     builder.add(
-        new CfInvoke(org.objectweb.asm.Opcodes.INVOKESTATIC, getInvokedMethod(), isInterface));
+        new CfInvoke(org.objectweb.asm.Opcodes.INVOKESTATIC, getInvokedMethod(), isInterface),
+        this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
index 0a0461d..630e177 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeSuper.java
@@ -84,7 +84,8 @@
   @Override
   public void buildCf(CfBuilder builder) {
     builder.add(
-        new CfInvoke(org.objectweb.asm.Opcodes.INVOKESPECIAL, getInvokedMethod(), isInterface));
+        new CfInvoke(org.objectweb.asm.Opcodes.INVOKESPECIAL, getInvokedMethod(), isInterface),
+        this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
index 9179014..b5933de 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
@@ -157,7 +157,8 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfInvoke(org.objectweb.asm.Opcodes.INVOKEVIRTUAL, getInvokedMethod(), false));
+    builder.add(
+        new CfInvoke(org.objectweb.asm.Opcodes.INVOKEVIRTUAL, getInvokedMethod(), false), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Load.java b/src/main/java/com/android/tools/r8/ir/code/Load.java
index eee3bef..fe565bc 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Load.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Load.java
@@ -76,7 +76,7 @@
   @Override
   public void buildCf(CfBuilder builder) {
     Value value = src();
-    builder.add(new CfLoad(value.outType(), builder.getLocalRegister(value)));
+    builder.add(new CfLoad(value.outType(), builder.getLocalRegister(value)), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/LogicalBinop.java b/src/main/java/com/android/tools/r8/ir/code/LogicalBinop.java
index 267db9b..b647ecf 100644
--- a/src/main/java/com/android/tools/r8/ir/code/LogicalBinop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/LogicalBinop.java
@@ -141,6 +141,6 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfLogicalBinop(getCfOpcode(), type));
+    builder.add(new CfLogicalBinop(getCfOpcode(), type), this);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/Monitor.java b/src/main/java/com/android/tools/r8/ir/code/Monitor.java
index f779651..648c9fd 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Monitor.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Monitor.java
@@ -137,7 +137,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfMonitor(type));
+    builder.add(new CfMonitor(type), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Neg.java b/src/main/java/com/android/tools/r8/ir/code/Neg.java
index ad0886a..459c8c5 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Neg.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Neg.java
@@ -108,6 +108,6 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfNeg(type));
+    builder.add(new CfNeg(type), this);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java b/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
index 3be3d3d..3d6fb7d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewArrayEmpty.java
@@ -152,7 +152,7 @@
   @Override
   public void buildCf(CfBuilder builder) {
     assert type.isArrayType();
-    builder.add(new CfNewArray(type));
+    builder.add(new CfNewArray(type), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
index 024f6f2..3aff847 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
@@ -125,7 +125,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfNew(clazz));
+    builder.add(new CfNew(clazz), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewUnboxedEnumInstance.java b/src/main/java/com/android/tools/r8/ir/code/NewUnboxedEnumInstance.java
index 33829cd..b56600f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewUnboxedEnumInstance.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewUnboxedEnumInstance.java
@@ -132,7 +132,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfNewUnboxedEnum(clazz, ordinal));
+    builder.add(new CfNewUnboxedEnum(clazz, ordinal), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java b/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java
index e6b6f8d..5f3b592 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NumberConversion.java
@@ -159,7 +159,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfNumberConversion(from, to));
+    builder.add(new CfNumberConversion(from, to), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Pop.java b/src/main/java/com/android/tools/r8/ir/code/Pop.java
index a2f872e..bf8ee2d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Pop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Pop.java
@@ -79,7 +79,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(CfStackInstruction.popType(inValues.get(0).outType()));
+    builder.add(CfStackInstruction.popType(inValues.get(0).outType()), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Position.java b/src/main/java/com/android/tools/r8/ir/code/Position.java
index 5de6390..653e20e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Position.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Position.java
@@ -153,14 +153,14 @@
 
   public Position getOutermostCallerMatchingOrElse(
       Predicate<Position> predicate, Position defaultValue) {
-    return getOutermostCallerMatchingOrElse(predicate, defaultValue, false);
+    Position outerMostMatching = getOutermostCallerMatching(predicate, false);
+    return outerMostMatching == null ? defaultValue : outerMostMatching;
   }
 
-  private Position getOutermostCallerMatchingOrElse(
-      Predicate<Position> predicate, Position defaultValue, boolean isCallerPosition) {
+  private Position getOutermostCallerMatching(
+      Predicate<Position> predicate, boolean isCallerPosition) {
     if (hasCallerPosition()) {
-      Position position =
-          getCallerPosition().getOutermostCallerMatchingOrElse(predicate, defaultValue, true);
+      Position position = getCallerPosition().getOutermostCallerMatching(predicate, true);
       if (position != null) {
         return position;
       }
@@ -168,7 +168,7 @@
     if (isCallerPosition && predicate.test(this)) {
       return this;
     }
-    return defaultValue;
+    return null;
   }
 
   public Position withOutermostCallerPosition(Position newOutermostCallerPosition) {
@@ -180,14 +180,15 @@
         .build();
   }
 
-  public Position replaceOutermostCallerPosition(Position newOutermostCallerPosition) {
-    if (!hasCallerPosition()) {
-      return newOutermostCallerPosition;
+  public Position replacePosition(Position originalPosition, Position newPosition) {
+    if (this == originalPosition) {
+      return newPosition;
     }
-    return builderWithCopy()
-        .setCallerPosition(
-            getCallerPosition().replaceOutermostCallerPosition(newOutermostCallerPosition))
-        .build();
+    return hasCallerPosition()
+        ? builderWithCopy()
+            .setCallerPosition(callerPosition.replacePosition(originalPosition, newPosition))
+            .build()
+        : this;
   }
 
   @Override
@@ -324,7 +325,8 @@
           .setLine(line)
           .setFile(file)
           .setMethod(method)
-          .setCallerPosition(callerPosition);
+          .setCallerPosition(callerPosition)
+          .setRemoveInnerFramesIfThrowingNpe(isRemoveInnerFramesIfThrowingNpe());
     }
 
     @Override
@@ -389,7 +391,11 @@
 
     @Override
     public PositionBuilder<?, ?> builderWithCopy() {
-      return builder().setLine(line).setMethod(method).setCallerPosition(callerPosition);
+      return builder()
+          .setLine(line)
+          .setMethod(method)
+          .setCallerPosition(callerPosition)
+          .setRemoveInnerFramesIfThrowingNpe(isRemoveInnerFramesIfThrowingNpe());
     }
 
     @Override
@@ -442,7 +448,11 @@
 
     @Override
     public PositionBuilder<?, ?> builderWithCopy() {
-      return builder().setLine(line).setMethod(method).setCallerPosition(callerPosition);
+      return builder()
+          .setLine(line)
+          .setMethod(method)
+          .setCallerPosition(callerPosition)
+          .setRemoveInnerFramesIfThrowingNpe(isRemoveInnerFramesIfThrowingNpe());
     }
 
     @Override
@@ -516,7 +526,8 @@
               .setMethod(method)
               .setCallerPosition(callerPosition)
               .setOutlineCallee(outlineCallee)
-              .setIsOutline(isOutline);
+              .setIsOutline(isOutline)
+              .setRemoveInnerFramesIfThrowingNpe(isRemoveInnerFramesIfThrowingNpe());
       outlinePositions.forEach(outlineCallerPositionBuilder::addOutlinePosition);
       return outlineCallerPositionBuilder;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/code/RecordFieldValues.java b/src/main/java/com/android/tools/r8/ir/code/RecordFieldValues.java
index 00df561..a2949d0 100644
--- a/src/main/java/com/android/tools/r8/ir/code/RecordFieldValues.java
+++ b/src/main/java/com/android/tools/r8/ir/code/RecordFieldValues.java
@@ -73,7 +73,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfRecordFieldValues(fields));
+    builder.add(new CfRecordFieldValues(fields), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Return.java b/src/main/java/com/android/tools/r8/ir/code/Return.java
index e28ee89..1ec1a8e 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Return.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Return.java
@@ -132,7 +132,8 @@
   @Override
   public void buildCf(CfBuilder builder) {
     builder.add(
-        isReturnVoid() ? new CfReturnVoid() : new CfReturn(ValueType.fromType(getReturnType())));
+        isReturnVoid() ? new CfReturnVoid() : new CfReturn(ValueType.fromType(getReturnType())),
+        this);
   }
 
   public static class Builder extends BuilderBase<Builder, Return> {
diff --git a/src/main/java/com/android/tools/r8/ir/code/SafeCheckCast.java b/src/main/java/com/android/tools/r8/ir/code/SafeCheckCast.java
index 72a62cd..b811f86 100644
--- a/src/main/java/com/android/tools/r8/ir/code/SafeCheckCast.java
+++ b/src/main/java/com/android/tools/r8/ir/code/SafeCheckCast.java
@@ -22,7 +22,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfSafeCheckCast(getType()));
+    builder.add(new CfSafeCheckCast(getType()), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
index c7e076c..c4413d0 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticGet.java
@@ -214,7 +214,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfStaticFieldRead(getField(), builder.resolveField(getField())));
+    builder.add(new CfStaticFieldRead(getField(), builder.resolveField(getField())), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
index 339f16f..2230cc7 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
@@ -221,7 +221,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfStaticFieldWrite(getField(), builder.resolveField(getField())));
+    builder.add(new CfStaticFieldWrite(getField(), builder.resolveField(getField())), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Store.java b/src/main/java/com/android/tools/r8/ir/code/Store.java
index 8100111..6714e0d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Store.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Store.java
@@ -77,7 +77,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfStore(outType(), builder.getLocalRegister(outValue)));
+    builder.add(new CfStore(outType(), builder.getLocalRegister(outValue)), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Swap.java b/src/main/java/com/android/tools/r8/ir/code/Swap.java
index 777eec1..87ed900 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Swap.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Swap.java
@@ -67,7 +67,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfStackInstruction(Opcode.Swap));
+    builder.add(new CfStackInstruction(Opcode.Swap), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Throw.java b/src/main/java/com/android/tools/r8/ir/code/Throw.java
index 6f47788..6350255 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Throw.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Throw.java
@@ -83,7 +83,7 @@
 
   @Override
   public void buildCf(CfBuilder builder) {
-    builder.add(new CfThrow());
+    builder.add(new CfThrow(), this);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
index 34142c2..4f4a7b2 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -29,6 +29,8 @@
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadata;
+import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider;
 import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.code.Argument;
 import com.android.tools.r8.ir.code.BasicBlock;
@@ -102,6 +104,9 @@
   private List<InvokeDirect> thisInitializers;
   private Map<NewInstance, CfLabel> newInstanceLabels;
 
+  // Extra information that should be attached to the bytecode instructions.
+  private final BytecodeMetadata.Builder<CfInstruction> bytecodeMetadataBuilder;
+
   // Internal structure maintaining the stack height.
   private static class StackHeightTracker {
     int maxHeight = 0;
@@ -128,10 +133,15 @@
     }
   }
 
-  public CfBuilder(AppView<?> appView, DexEncodedMethod method, IRCode code) {
+  public CfBuilder(
+      AppView<?> appView,
+      DexEncodedMethod method,
+      IRCode code,
+      BytecodeMetadataProvider bytecodeMetadataProvider) {
     this.appView = appView;
     this.method = method;
     this.code = code;
+    this.bytecodeMetadataBuilder = BytecodeMetadata.builder(bytecodeMetadataProvider);
   }
 
   public CfCode build(DeadCodeRemover deadCodeRemover, MethodConversionOptions conversionOptions) {
@@ -383,7 +393,8 @@
         instructions,
         tryCatchRanges,
         localVariablesTable,
-        diagnosticPosition);
+        diagnosticPosition,
+        bytecodeMetadataBuilder.build());
   }
 
   private static boolean isNopInstruction(Instruction instruction, BasicBlock nextBlock) {
@@ -684,11 +695,25 @@
     return registerAllocator.getRegisterForValue(value);
   }
 
-  public void add(CfInstruction instruction) {
+  private void add(CfInstruction instruction) {
     instructions.add(instruction);
   }
 
+  public void add(CfInstruction instruction, Instruction origin) {
+    bytecodeMetadataBuilder.setMetadata(origin, instruction);
+    add(instruction);
+  }
+
+  public void add(CfInstruction... instructions) {
+    Collections.addAll(this.instructions, instructions);
+  }
+
   public void addArgument(Argument argument) {
     // Nothing so far.
   }
+
+  public boolean verifyNoMetadata(Instruction instruction) {
+    assert bytecodeMetadataBuilder.verifyNoMetadata(instruction);
+    return true;
+  }
 }
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 c19a35b..efd8593 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
@@ -1486,7 +1486,13 @@
 
     printMethod(code, "Optimized IR (SSA)", previous);
     timing.begin("Finalize IR");
-    finalizeIR(code, feedback, conversionOptions, bytecodeMetadataProviderBuilder.build(), timing);
+    finalizeIR(
+        context,
+        code,
+        feedback,
+        conversionOptions,
+        bytecodeMetadataProviderBuilder.build(),
+        timing);
     timing.end();
     return timing;
   }
@@ -1579,6 +1585,7 @@
     }
     deadCodeRemover.run(code, timing);
     finalizeIR(
+        code.context(),
         code,
         feedback,
         DefaultMethodConversionOptions.getInstance(),
@@ -1587,6 +1594,7 @@
   }
 
   public void finalizeIR(
+      ProgramMethod method,
       IRCode code,
       OptimizationFeedback feedback,
       MethodConversionOptions conversionOptions,
@@ -1594,7 +1602,7 @@
       Timing timing) {
     code.traceBlocks();
     if (options.isGeneratingClassFiles()) {
-      finalizeToCf(code, feedback, conversionOptions);
+      finalizeToCf(code, feedback, conversionOptions, bytecodeMetadataProvider);
     } else {
       assert options.isGeneratingDex();
       finalizeToDex(code, feedback, conversionOptions, bytecodeMetadataProvider, timing);
@@ -1602,10 +1610,13 @@
   }
 
   private void finalizeToCf(
-      IRCode code, OptimizationFeedback feedback, MethodConversionOptions conversionOptions) {
+      IRCode code,
+      OptimizationFeedback feedback,
+      MethodConversionOptions conversionOptions,
+      BytecodeMetadataProvider bytecodeMetadataProvider) {
     DexEncodedMethod method = code.method();
     assert !method.getCode().isDexCode();
-    CfBuilder builder = new CfBuilder(appView, method, code);
+    CfBuilder builder = new CfBuilder(appView, method, code, bytecodeMetadataProvider);
     CfCode result = builder.build(deadCodeRemover, conversionOptions);
     method.setCode(result, appView);
     markProcessed(code, feedback);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java
index fc60cb9..cd3a6b1 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java
@@ -103,7 +103,8 @@
         apiLevelCompute.computeApiLevelForLibraryReference(
             cfInvoke.getMethod(), ComputedApiLevel.unknown());
     if (appView.computedMinApiLevel().isGreaterThanOrEqualTo(methodApiLevel)
-        || isApiLevelLessThanOrEqualTo9(methodApiLevel)) {
+        || isApiLevelLessThanOrEqualTo9(methodApiLevel)
+        || methodApiLevel.isUnknownApiLevel()) {
       return appView.computedMinApiLevel();
     }
     // Compute the api level of the holder to see if the method will be stubbed.
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 d30f717..6740441 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
@@ -693,9 +693,7 @@
                   DexClassAndMethod companionMethod =
                       helper.ensureDefaultAsMethodOfCompanionClassStub(method);
                   acceptCompanionMethod(method, companionMethod, eventConsumer);
-                  return getInvokeStaticInstructions(
-                      InterfaceDesugaringSyntheticHelper.defaultAsMethodOfCompanionClass(
-                          amendedMethod, appView.dexItemFactory()));
+                  return getInvokeStaticInstructions(companionMethod.getReference());
                 })
             .build();
       }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java
index 3e8b5cb..f712cb4 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java
@@ -227,8 +227,9 @@
                         .resolveMethod(derivedMethod.getMethod(), true)
                         .getResolvedProgramMethod();
                 caseMethod =
-                    InterfaceDesugaringSyntheticHelper.defaultAsMethodOfCompanionClass(
-                        resolvedProgramMethod.getReference(), appView.dexItemFactory());
+                    helper
+                        .ensureDefaultAsMethodOfProgramCompanionClassStub(resolvedProgramMethod)
+                        .getReference();
               }
               extraDispatchCases.put(type, caseMethod);
             });
@@ -276,10 +277,12 @@
         DexEncodedMethod result = subInterfaceClass.lookupVirtualMethod(method.getReference());
         if (result != null && !result.isAbstract()) {
           assert result.isDefaultMethod();
-          extraDispatchCases.put(
-              subInterfaceClass.type,
-              InterfaceDesugaringSyntheticHelper.defaultAsMethodOfCompanionClass(
-                  result.getReference(), appView.dexItemFactory()));
+          DexMethod forward =
+              helper
+                  .ensureDefaultAsMethodOfProgramCompanionClassStub(
+                      new ProgramMethod(subInterfaceClass.asProgramClass(), result))
+                  .getReference();
+          extraDispatchCases.put(subInterfaceClass.type, forward);
         }
       }
     } else {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java
index 8e78a50..ba2c90f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8.ir.optimize;
 
 import com.android.tools.r8.AssertionsConfiguration;
-import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
@@ -32,7 +31,7 @@
 
   private static class ConfigurationEntryWithDexString {
 
-    private AssertionsConfiguration entry;
+    private final AssertionsConfiguration entry;
     private final DexString value;
 
     private ConfigurationEntryWithDexString(
@@ -72,13 +71,29 @@
           throw new Unreachable();
       }
     }
+
+    public boolean isEnabled() {
+      return entry.isCompileTimeEnabled();
+    }
+
+    public boolean isDisabled() {
+      return entry.isCompileTimeDisabled();
+    }
+
+    public boolean isPassthrough() {
+      return entry.isPassthrough();
+    }
+
+    public boolean isAssertionHandler() {
+      return entry.isAssertionHandler();
+    }
   }
 
   private final AppView<?> appView;
   private final DexItemFactory dexItemFactory;
-  private final AssertionTransformation defaultTransformation;
+  private final ConfigurationEntryWithDexString defaultConfiguration;
   private final List<ConfigurationEntryWithDexString> configuration;
-  private final AssertionsConfiguration.AssertionTransformation kotlinTransformation;
+  private final ConfigurationEntryWithDexString kotlinTransformation;
   private final boolean enabled;
 
   public AssertionsRewriter(AppView<?> appView) {
@@ -86,13 +101,15 @@
     this.dexItemFactory = appView.dexItemFactory();
     this.enabled = isEnabled(appView.options());
     if (!enabled) {
-      defaultTransformation = null;
+      defaultConfiguration = null;
       configuration = null;
       kotlinTransformation = null;
       return;
     }
-    // Convert the assertion transformation to the representation used for this rewriter.
-    this.defaultTransformation = appView.options().assertionsConfiguration.defautlTransformation;
+    // Convert the assertion configuration to the representation used for this rewriter.
+    this.defaultConfiguration =
+        new ConfigurationEntryWithDexString(
+            appView.options().assertionsConfiguration.defaultConfiguration, dexItemFactory);
     this.configuration =
         appView.options().assertionsConfiguration.assertionsConfigurations.stream()
             .map(entry -> new ConfigurationEntryWithDexString(entry, appView.dexItemFactory()))
@@ -108,40 +125,40 @@
     return configuration != null && !configuration.isPassthroughAll();
   }
 
-  private AssertionTransformation getTransformationForMethod(DexEncodedMethod method) {
+  private ConfigurationEntryWithDexString getTransformationForMethod(DexEncodedMethod method) {
     return getTransformationForType(method.getHolderType());
   }
 
-  private AssertionTransformation getTransformationForType(DexType type) {
-    AssertionTransformation transformation = defaultTransformation;
+  private ConfigurationEntryWithDexString getTransformationForType(DexType type) {
+    ConfigurationEntryWithDexString result = defaultConfiguration;
     for (ConfigurationEntryWithDexString entry : configuration) {
       switch (entry.entry.getScope()) {
         case ALL:
-          transformation = entry.entry.getTransformation();
+          result = entry;
           break;
         case PACKAGE:
           if (entry.value.size == 0) {
             if (!type.descriptor.contains(dexItemFactory.descriptorSeparator)) {
-              transformation = entry.entry.getTransformation();
+              result = entry;
             }
           } else if (type.descriptor.startsWith(entry.value)) {
-            transformation = entry.entry.getTransformation();
+            result = entry;
           }
           break;
         case CLASS:
           if (type.descriptor.equals(entry.value)) {
-            transformation = entry.entry.getTransformation();
+            result = entry;
           }
           if (isDescriptorForClassOrInnerClass(entry.value, type.descriptor)) {
-            transformation = entry.entry.getTransformation();
+            result = entry;
           }
           break;
         default:
           throw new Unreachable();
       }
     }
-    assert transformation != null;
-    return transformation;
+    assert result != null;
+    return result;
   }
 
   private boolean isDescriptorForClassOrInnerClass(
@@ -310,8 +327,8 @@
   }
 
   private void runInternal(DexEncodedMethod method, IRCode code) {
-    AssertionTransformation transformation = getTransformationForMethod(method);
-    if (transformation == AssertionTransformation.PASSTHROUGH) {
+    ConfigurationEntryWithDexString configuration = getTransformationForMethod(method);
+    if (configuration.isPassthrough()) {
       return;
     }
     DexEncodedMethod clinit;
@@ -338,7 +355,7 @@
         InvokeMethod invoke = current.asInvokeMethod();
         if (invoke.getInvokedMethod() == dexItemFactory.classMethods.desiredAssertionStatus) {
           if (method.getHolderType() == dexItemFactory.kotlin.assertions.type) {
-            rewriteKotlinAssertionEnable(code, transformation, iterator, invoke);
+            rewriteKotlinAssertionEnable(code, configuration, iterator, invoke);
           } else {
             iterator.replaceCurrentInstruction(code.createIntConstant(0, current.getLocalInfo()));
           }
@@ -355,16 +372,13 @@
         if (isInitializerEnablingJavaVmAssertions
             && staticGet.getField().name == dexItemFactory.assertionsDisabled) {
           iterator.replaceCurrentInstruction(
-              code.createIntConstant(
-                  transformation == AssertionTransformation.DISABLE ? 1 : 0,
-                  current.getLocalInfo()));
+              code.createIntConstant(configuration.isDisabled() ? 1 : 0, current.getLocalInfo()));
         }
         // Rewrite kotlin._Assertions.ENABLED getter.
         if (staticGet.getField() == dexItemFactory.kotlin.assertions.enabledField) {
           iterator.replaceCurrentInstruction(
               code.createIntConstant(
-                  kotlinTransformation == AssertionTransformation.DISABLE ? 0 : 1,
-                  current.getLocalInfo()));
+                  kotlinTransformation.isDisabled() ? 0 : 1, current.getLocalInfo()));
         }
       }
     }
@@ -372,10 +386,10 @@
 
   private void rewriteKotlinAssertionEnable(
       IRCode code,
-      AssertionTransformation transformation,
+      ConfigurationEntryWithDexString configuration,
       InstructionListIterator iterator,
       InvokeMethod invoke) {
-    if (iterator.hasNext() && transformation == AssertionTransformation.DISABLE) {
+    if (iterator.hasNext() && configuration.isDisabled()) {
       // Check if the invocation of Class.desiredAssertionStatus() is followed by a static
       // put to kotlin._Assertions.ENABLED, and if so remove both instructions.
       // See comment in ClassInitializerAssertionEnablingAnalysis for the expected instruction
@@ -402,8 +416,7 @@
         iterator.replaceCurrentInstruction(code.createIntConstant(0));
       }
     } else {
-      iterator.replaceCurrentInstruction(
-          code.createIntConstant(transformation == AssertionTransformation.ENABLE ? 1 : 0));
+      iterator.replaceCurrentInstruction(code.createIntConstant(configuration.isEnabled() ? 1 : 0));
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
index a39688a..8182229 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -130,8 +130,9 @@
       WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
     // Do not inline if the inlinee is greater than the api caller level.
     // TODO(b/188498051): We should not force inline lower api method calls.
-    if (reason != Reason.FORCE && !isApiSafeForInlining(method, singleTarget, appView.options())) {
-      whyAreYouNotInliningReporter.reportInlineeHigherApiCall();
+    if (reason != Reason.FORCE
+        && !isApiSafeForInlining(
+            method, singleTarget, appView.options(), whyAreYouNotInliningReporter)) {
       return false;
     }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 834e78d..a658752 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -577,8 +577,7 @@
           && !isSynthesizingNullCheckForReceiverUsingMonitorEnter) {
         SimpleEffectAnalysisResult checksReceiverBeingNull =
             canInlineWithoutSynthesizingNullCheckForReceiver(appView, code);
-        if (!checksReceiverBeingNull.hasResult()
-            || !checksReceiverBeingNull.isPartial()
+        if (checksReceiverBeingNull.isNotSatisfied()
             || (checksReceiverBeingNull.isPartial()
                 && checksReceiverBeingNull.topMostNotSatisfiedBlockSize() > 1)) {
           synthesizeNullCheckForReceiver(appView, code, invoke, code.entryBlock());
@@ -761,13 +760,11 @@
         assert false : "Expected position for inlinee call to receiver";
         return;
       }
+      Position outermostCaller = position.getOutermostCaller();
       Position removeInnerFrame =
-          position
-              .getOutermostCaller()
-              .builderWithCopy()
-              .setRemoveInnerFramesIfThrowingNpe(true)
-              .build();
-      instruction.forceOverwritePosition(position.replaceOutermostCallerPosition(removeInnerFrame));
+          outermostCaller.builderWithCopy().setRemoveInnerFramesIfThrowingNpe(true).build();
+      instruction.forceOverwritePosition(
+          position.replacePosition(outermostCaller, removeInnerFrame));
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
index 50bea08..984b6c1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
@@ -19,6 +19,8 @@
 import com.android.tools.r8.ir.analysis.value.SingleFieldValue;
 import com.android.tools.r8.ir.analysis.value.SingleValue;
 import com.android.tools.r8.ir.analysis.value.objectstate.ObjectState;
+import com.android.tools.r8.ir.code.ArrayGet;
+import com.android.tools.r8.ir.code.ArrayPut;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.FieldGet;
 import com.android.tools.r8.ir.code.FieldInstruction;
@@ -30,6 +32,7 @@
 import com.android.tools.r8.ir.code.InstructionListIterator;
 import com.android.tools.r8.ir.code.InvokeDirect;
 import com.android.tools.r8.ir.code.InvokeStatic;
+import com.android.tools.r8.ir.code.MemberType;
 import com.android.tools.r8.ir.code.NewInstance;
 import com.android.tools.r8.ir.code.Phi;
 import com.android.tools.r8.ir.code.StaticGet;
@@ -47,6 +50,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -88,7 +92,9 @@
 
   public static boolean shouldRun(AppView<?> appView, IRCode code) {
     return appView.options().enableRedundantFieldLoadElimination
-        && (code.metadata().mayHaveFieldInstruction() || code.metadata().mayHaveInitClass());
+        && (code.metadata().mayHaveArrayGet()
+            || code.metadata().mayHaveFieldInstruction()
+            || code.metadata().mayHaveInitClass());
   }
 
   private interface FieldValue {
@@ -97,7 +103,7 @@
       return null;
     }
 
-    void eliminateRedundantRead(InstructionListIterator it, FieldInstruction redundant);
+    void eliminateRedundantRead(InstructionListIterator it, Instruction redundant);
   }
 
   private class ExistingValue implements FieldValue {
@@ -114,9 +120,9 @@
     }
 
     @Override
-    public void eliminateRedundantRead(InstructionListIterator it, FieldInstruction redundant) {
-      affectedValues.addAll(redundant.value().affectedValues());
-      redundant.value().replaceUsers(value);
+    public void eliminateRedundantRead(InstructionListIterator it, Instruction redundant) {
+      affectedValues.addAll(redundant.outValue().affectedValues());
+      redundant.outValue().replaceUsers(value);
       it.removeOrReplaceByDebugLocalRead();
       value.uniquePhiUsers().forEach(Phi::removeTrivialPhi);
     }
@@ -141,13 +147,106 @@
     }
 
     @Override
-    public void eliminateRedundantRead(InstructionListIterator it, FieldInstruction redundant) {
-      affectedValues.addAll(redundant.value().affectedValues());
+    public void eliminateRedundantRead(InstructionListIterator it, Instruction redundant) {
+      affectedValues.addAll(redundant.outValue().affectedValues());
       it.replaceCurrentInstruction(
           value.createMaterializingInstruction(appView.withClassHierarchy(), code, redundant));
     }
   }
 
+  private abstract static class ArraySlot {
+
+    protected final Value array;
+    protected final MemberType memberType;
+
+    private ArraySlot(Value array, MemberType memberType) {
+      this.array = array;
+      this.memberType = memberType;
+    }
+
+    public static ArraySlot create(Value array, Value index, MemberType memberType) {
+      if (index.isDefinedByInstructionSatisfying(Instruction::isConstNumber)) {
+        return new ArraySlotWithConstantIndex(
+            array, index.getDefinition().asConstNumber().getIntValue(), memberType);
+      }
+      return new ArraySlotWithValueIndex(array, index, memberType);
+    }
+
+    public MemberType getMemberType() {
+      return memberType;
+    }
+
+    public abstract boolean maybeHasIndex(int i);
+
+    boolean baseEquals(ArraySlot arraySlot) {
+      return array == arraySlot.array && memberType == arraySlot.memberType;
+    }
+  }
+
+  private static class ArraySlotWithConstantIndex extends ArraySlot {
+
+    private final int index;
+
+    private ArraySlotWithConstantIndex(Value array, int index, MemberType memberType) {
+      super(array, memberType);
+      this.index = index;
+    }
+
+    @Override
+    public boolean maybeHasIndex(int i) {
+      return index == i;
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(array, index, memberType);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+      if (this == other) {
+        return true;
+      }
+      if (other == null || getClass() != other.getClass()) {
+        return false;
+      }
+      ArraySlotWithConstantIndex arraySlot = (ArraySlotWithConstantIndex) other;
+      return index == arraySlot.index && baseEquals(arraySlot);
+    }
+  }
+
+  private static class ArraySlotWithValueIndex extends ArraySlot {
+
+    private final Value index;
+
+    private ArraySlotWithValueIndex(Value array, Value index, MemberType memberType) {
+      super(array, memberType);
+      this.index = index;
+    }
+
+    @Override
+    public boolean maybeHasIndex(int i) {
+      return true;
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(array, index, memberType);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+      if (this == other) {
+        return true;
+      }
+      if (other == null || getClass() != other.getClass()) {
+        return false;
+      }
+      ArraySlotWithValueIndex arraySlot = (ArraySlotWithValueIndex) other;
+      return index == arraySlot.index && baseEquals(arraySlot);
+    }
+  }
+
   private static class FieldAndObject {
     private final DexField field;
     private final Value object;
@@ -217,7 +316,14 @@
         InstructionListIterator it = block.listIterator(code);
         while (it.hasNext()) {
           Instruction instruction = it.next();
-          if (instruction.isFieldInstruction()) {
+          if (instruction.isArrayAccess()) {
+            if (instruction.isArrayGet()) {
+              handleArrayGet(it, instruction.asArrayGet());
+            } else {
+              assert instruction.isArrayPut();
+              handleArrayPut(instruction.asArrayPut());
+            }
+          } else if (instruction.isFieldInstruction()) {
             DexField reference = instruction.asFieldInstruction().getField();
             DexClassAndField field = resolveField(reference);
             if (field == null || field.getDefinition().isVolatile()) {
@@ -446,6 +552,43 @@
     }
   }
 
+  private void handleArrayGet(InstructionListIterator it, ArrayGet arrayGet) {
+    if (arrayGet.outValue().hasLocalInfo()) {
+      return;
+    }
+
+    Value array = arrayGet.array().getAliasedValue();
+    Value index = arrayGet.index().getAliasedValue();
+    ArraySlot arraySlot = ArraySlot.create(array, index, arrayGet.getMemberType());
+    FieldValue replacement = activeState.getArraySlotValue(arraySlot);
+    if (replacement != null) {
+      replacement.eliminateRedundantRead(it, arrayGet);
+      return;
+    }
+
+    activeState.putArraySlotValue(arraySlot, new ExistingValue(arrayGet.outValue()));
+  }
+
+  private void handleArrayPut(ArrayPut arrayPut) {
+    int index = arrayPut.getIndexOrDefault(-1);
+    MemberType memberType = arrayPut.getMemberType();
+
+    // An array-put instruction can potentially write the given array slot on all arrays because of
+    // aliases.
+    if (index < 0) {
+      activeState.removeArraySlotValues(memberType);
+    } else {
+      activeState.removeArraySlotValues(memberType, index);
+    }
+
+    // Update the value of the field to allow redundant load elimination.
+    Value array = arrayPut.array().getAliasedValue();
+    Value indexValue = arrayPut.index().getAliasedValue();
+    ArraySlot arraySlot = ArraySlot.create(array, indexValue, memberType);
+    ExistingValue value = new ExistingValue(arrayPut.value());
+    activeState.putArraySlotValue(arraySlot, value);
+  }
+
   private void handleInstanceGet(
       InstructionListIterator it,
       InstanceGet instanceGet,
@@ -646,6 +789,7 @@
   }
 
   private void killAllNonFinalActiveFields() {
+    activeState.clearArraySlotValues();
     activeState.clearNonFinalInstanceFields();
     activeState.clearNonFinalStaticFields();
     activeState.clearMostRecentFieldWrites();
@@ -801,6 +945,8 @@
 
   static class BlockState {
 
+    private LinkedHashMap<ArraySlot, FieldValue> arraySlotValues;
+
     private LinkedHashMap<FieldAndObject, FieldValue> finalInstanceFieldValues;
 
     private LinkedHashMap<DexField, FieldValue> finalStaticFieldValues;
@@ -826,6 +972,10 @@
     public BlockState(int maxCapacity, BlockState state) {
       this(maxCapacity);
       if (state != null) {
+        if (state.arraySlotValues != null && !state.arraySlotValues.isEmpty()) {
+          arraySlotValues = new LinkedHashMap<>();
+          arraySlotValues.putAll(state.arraySlotValues);
+        }
         if (state.finalInstanceFieldValues != null && !state.finalInstanceFieldValues.isEmpty()) {
           finalInstanceFieldValues = new LinkedHashMap<>();
           finalInstanceFieldValues.putAll(state.finalInstanceFieldValues);
@@ -861,6 +1011,10 @@
       }
     }
 
+    public void clearArraySlotValues() {
+      arraySlotValues = null;
+    }
+
     public void clearMostRecentFieldWrites() {
       clearMostRecentInstanceFieldWrites();
       clearMostRecentStaticFieldWrites();
@@ -902,6 +1056,10 @@
       }
     }
 
+    public FieldValue getArraySlotValue(ArraySlot arraySlot) {
+      return arraySlotValues != null ? arraySlotValues.get(arraySlot) : null;
+    }
+
     public FieldValue getInstanceFieldValue(FieldAndObject field) {
       FieldValue value =
           nonFinalInstanceFieldValues != null ? nonFinalInstanceFieldValues.get(field) : null;
@@ -921,6 +1079,11 @@
     }
 
     public void intersect(BlockState state) {
+      if (arraySlotValues != null && state.arraySlotValues != null) {
+        intersectFieldValues(arraySlotValues, state.arraySlotValues);
+      } else {
+        arraySlotValues = null;
+      }
       if (finalInstanceFieldValues != null && state.finalInstanceFieldValues != null) {
         intersectFieldValues(finalInstanceFieldValues, state.finalInstanceFieldValues);
       } else {
@@ -962,7 +1125,9 @@
     }
 
     public boolean isEmpty() {
-      return isEmpty(finalInstanceFieldValues)
+      return isEmpty(arraySlotValues)
+          && isEmpty(initializedClasses)
+          && isEmpty(finalInstanceFieldValues)
           && isEmpty(finalStaticFieldValues)
           && isEmpty(initializedClasses)
           && isEmpty(nonFinalInstanceFieldValues)
@@ -1008,6 +1173,7 @@
     public void reduceSize(int numberOfItemsToRemove) {
       assert numberOfItemsToRemove > 0;
       assert numberOfItemsToRemove < size();
+      numberOfItemsToRemove = reduceSize(numberOfItemsToRemove, arraySlotValues);
       numberOfItemsToRemove = reduceSize(numberOfItemsToRemove, initializedClasses);
       numberOfItemsToRemove = reduceSize(numberOfItemsToRemove, nonFinalInstanceFieldValues);
       numberOfItemsToRemove = reduceSize(numberOfItemsToRemove, nonFinalStaticFieldValues);
@@ -1035,6 +1201,22 @@
       return reduceSize(numberOfItemsToRemove, map != null ? map.keySet() : null);
     }
 
+    public void removeArraySlotValues(MemberType memberType) {
+      if (arraySlotValues != null) {
+        arraySlotValues.keySet().removeIf(arraySlot -> arraySlot.getMemberType() == memberType);
+      }
+    }
+
+    public void removeArraySlotValues(MemberType memberType, int index) {
+      if (arraySlotValues != null) {
+        arraySlotValues
+            .keySet()
+            .removeIf(
+                arraySlot ->
+                    arraySlot.getMemberType() == memberType && arraySlot.maybeHasIndex(index));
+      }
+    }
+
     public void removeInstanceField(FieldAndObject field) {
       removeFinalInstanceField(field);
       removeNonFinalInstanceField(field);
@@ -1089,6 +1271,14 @@
       }
     }
 
+    public void putArraySlotValue(ArraySlot arraySlot, FieldValue value) {
+      ensureCapacityForNewElement();
+      if (arraySlotValues == null) {
+        arraySlotValues = new LinkedHashMap<>();
+      }
+      arraySlotValues.put(arraySlot, value);
+    }
+
     public void putFinalInstanceField(FieldAndObject field, FieldValue value) {
       ensureCapacityForNewElement();
       if (finalInstanceFieldValues == null) {
@@ -1155,7 +1345,8 @@
     }
 
     public int size() {
-      return size(finalInstanceFieldValues)
+      return size(arraySlotValues)
+          + size(finalInstanceFieldValues)
           + size(finalStaticFieldValues)
           + size(initializedClasses)
           + size(nonFinalInstanceFieldValues)
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/SimpleDominatingEffectAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/SimpleDominatingEffectAnalysis.java
index 8942395..ae95f9e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/SimpleDominatingEffectAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/SimpleDominatingEffectAnalysis.java
@@ -166,18 +166,19 @@
 
   public static class SimpleEffectAnalysisResult {
 
+    private final ResultState result;
     private final List<Instruction> satisfyingInstructions;
     private final List<BasicBlock> topmostNotSatisfiedBlocks;
 
     private SimpleEffectAnalysisResult(
-        List<Instruction> satisfyingInstructions, List<BasicBlock> topmostNotSatisfiedBlocks) {
-
+        ResultState result,
+        List<Instruction> satisfyingInstructions,
+        List<BasicBlock> topmostNotSatisfiedBlocks) {
+      this.result = result;
       this.satisfyingInstructions = satisfyingInstructions;
       this.topmostNotSatisfiedBlocks = topmostNotSatisfiedBlocks;
-    }
-
-    public boolean hasResult() {
-      return true;
+      assert !result.isPartial()
+          || (!satisfyingInstructions.isEmpty() && !topmostNotSatisfiedBlocks.isEmpty());
     }
 
     public void forEachSatisfyingInstruction(Consumer<Instruction> instructionConsumer) {
@@ -196,8 +197,12 @@
       return new SimpleEffectAnalysisResultBuilder();
     }
 
+    public boolean isNotSatisfied() {
+      return result.isNotSatisfied();
+    }
+
     public boolean isPartial() {
-      return !satisfyingInstructions.isEmpty() && !topmostNotSatisfiedBlocks.isEmpty();
+      return result.isPartial();
     }
   }
 
@@ -205,10 +210,10 @@
 
     List<Instruction> satisfyingInstructions = new ArrayList<>();
     List<BasicBlock> failingBlocksForPartialResults = ImmutableList.of();
-    private boolean isFailed;
+    private ResultState result;
 
     public void fail() {
-      isFailed = true;
+      result = ResultState.NOT_SATISFIED;
     }
 
     public void addSatisfyingInstruction(Instruction instruction) {
@@ -219,20 +224,21 @@
       this.failingBlocksForPartialResults = basicBlocks;
     }
 
+    public void setResult(ResultState result) {
+      this.result = result;
+    }
+
     public SimpleEffectAnalysisResult build() {
-      return isFailed
+      return result.isNotComputed()
           ? NO_RESULT
-          : new SimpleEffectAnalysisResult(satisfyingInstructions, failingBlocksForPartialResults);
+          : new SimpleEffectAnalysisResult(
+              result, satisfyingInstructions, failingBlocksForPartialResults);
     }
   }
 
   private static final SimpleEffectAnalysisResult NO_RESULT =
-      new SimpleEffectAnalysisResult(ImmutableList.of(), ImmutableList.of()) {
-        @Override
-        public boolean hasResult() {
-          return false;
-        }
-      };
+      new SimpleEffectAnalysisResult(
+          ResultState.NOT_SATISFIED, ImmutableList.of(), ImmutableList.of());
 
   public static SimpleEffectAnalysisResult run(IRCode code, InstructionAnalysis analysis) {
     SimpleEffectAnalysisResultBuilder builder = SimpleEffectAnalysisResult.builder();
@@ -288,6 +294,7 @@
         }
         node.setState(resultState);
         if (node.getNode().isEntry()) {
+          builder.setResult(resultState.state);
           builder.setFailingBlocksForPartialResults(resultState.failingBlocks);
         }
         return CONTINUE;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
index 5e3b23d..8a3d24e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
@@ -167,23 +167,27 @@
         offsetDiff = 1;
         builder.addArgumentInfo(
             0,
-            new RewrittenPrototypeDescription.RewrittenTypeInfo(
-                from.holder, to.proto.parameters.values[0]));
+            RewrittenTypeInfo.builder()
+                .setOldType(from.getHolderType())
+                .setNewType(to.getParameter(0))
+                .build());
       }
-      for (int i = 0; i < from.proto.parameters.size(); i++) {
-        DexType fromType = from.proto.parameters.values[i];
-        DexType toType = to.proto.parameters.values[i + offsetDiff];
+      for (int i = 0; i < from.getParameters().size(); i++) {
+        DexType fromType = from.getParameter(i);
+        DexType toType = to.getParameter(i + offsetDiff);
         if (fromType != toType) {
           builder.addArgumentInfo(
               i + offsetDiff + toOffset,
-              new RewrittenPrototypeDescription.RewrittenTypeInfo(fromType, toType));
+              RewrittenTypeInfo.builder().setOldType(fromType).setNewType(toType).build());
         }
       }
-      RewrittenPrototypeDescription.RewrittenTypeInfo returnInfo =
-          from.proto.returnType == to.proto.returnType
+      RewrittenTypeInfo returnInfo =
+          from.getReturnType() == to.getReturnType()
               ? null
-              : new RewrittenPrototypeDescription.RewrittenTypeInfo(
-                  from.proto.returnType, to.proto.returnType);
+              : RewrittenTypeInfo.builder()
+                  .setOldType(from.getReturnType())
+                  .setNewType(to.getReturnType())
+                  .build();
       RewrittenPrototypeDescription prototypeChanges =
           RewrittenPrototypeDescription.createForRewrittenTypes(returnInfo, builder.build())
               .withExtraUnusedNullParameters(numberOfExtraNullParameters);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/NopWhyAreYouNotInliningReporter.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/NopWhyAreYouNotInliningReporter.java
index cc7d4ec..d3d3735 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/NopWhyAreYouNotInliningReporter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/NopWhyAreYouNotInliningReporter.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.ir.optimize.inliner;
 
+import com.android.tools.r8.androidapi.ComputedApiLevel;
 import com.android.tools.r8.ir.code.InstancePut;
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InvokeDirect;
@@ -34,6 +35,9 @@
   public void reportCallerNotSubtype() {}
 
   @Override
+  public void reportCallerHasUnknownApiLevel() {}
+
+  @Override
   public void reportClasspathMethod() {}
 
   @Override
@@ -55,7 +59,8 @@
   public void reportInlineeNotSimple() {}
 
   @Override
-  public void reportInlineeHigherApiCall() {}
+  public void reportInlineeHigherApiCall(
+      ComputedApiLevel callerApiLevel, ComputedApiLevel inlineeApiLevel) {}
 
   @Override
   public void reportInlineeRefersToClassesNotInMainDex() {}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java
index 66d6108..7c3e5da 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.ir.optimize.inliner;
 
+import com.android.tools.r8.androidapi.ComputedApiLevel;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.InstancePut;
@@ -53,6 +54,8 @@
 
   public abstract void reportCallerNotSubtype();
 
+  public abstract void reportCallerHasUnknownApiLevel();
+
   public abstract void reportClasspathMethod();
 
   public abstract void reportInaccessible();
@@ -67,7 +70,8 @@
 
   public abstract void reportInlineeNotSimple();
 
-  public abstract void reportInlineeHigherApiCall();
+  public abstract void reportInlineeHigherApiCall(
+      ComputedApiLevel callerApiLevel, ComputedApiLevel inlineeApiLevel);
 
   public abstract void reportInlineeRefersToClassesNotInMainDex();
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java
index 94632b6..f5807dd 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.ir.optimize.inliner;
 
+import com.android.tools.r8.androidapi.ComputedApiLevel;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.InstancePut;
 import com.android.tools.r8.ir.code.Instruction;
@@ -73,6 +74,11 @@
   }
 
   @Override
+  public void reportCallerHasUnknownApiLevel() {
+    print("computed API level for caller is unknown");
+  }
+
+  @Override
   public void reportClasspathMethod() {
     print("inlinee is on the classpath.");
   }
@@ -115,8 +121,20 @@
   }
 
   @Override
-  public void reportInlineeHigherApiCall() {
-    print("inlinee having a higher api call than caller context.");
+  public void reportInlineeHigherApiCall(
+      ComputedApiLevel callerApiLevel, ComputedApiLevel inlineeApiLevel) {
+    assert callerApiLevel.isKnownApiLevel();
+    if (inlineeApiLevel.isUnknownApiLevel()) {
+      print("computed API level for inlinee is unknown");
+    } else {
+      assert inlineeApiLevel.isKnownApiLevel();
+      print(
+          "computed API level for inlinee ("
+              + inlineeApiLevel.asKnownApiLevel().getApiLevel()
+              + ") is higher than caller's ("
+              + callerApiLevel.asKnownApiLevel().getApiLevel()
+              + ")");
+    }
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
index 70e6d04..339f93d 100644
--- a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
@@ -174,7 +174,11 @@
             clazz,
             (superType, superClass, isInterface) -> {
               if (isInterface && superClass.isNotProgramClass()) {
-                frontierStatesForInterfaces.get(superType).add(reservationState);
+                Set<ReservedFieldNamingState> reservedNamingState =
+                    frontierStatesForInterfaces.get(superType);
+                if (reservedNamingState != null) {
+                  reservedNamingState.add(reservationState);
+                }
               }
               return TraversalContinuation.CONTINUE;
             });
diff --git a/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
index 8067817..14f7711 100644
--- a/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/InterfaceMethodNameMinifier.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.SubtypingInfo;
 import com.android.tools.r8.ir.desugar.LambdaDescriptor;
 import com.android.tools.r8.naming.MethodNameMinifier.State;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -360,6 +361,7 @@
   }
 
   private final AppView<AppInfoWithLiveness> appView;
+  private final SubtypingInfo subtypingInfo;
   private final Equivalence<DexMethod> equivalence;
   private final Equivalence<DexEncodedMethod> definitionEquivalence;
   private final MethodNameMinifier.State minifierState;
@@ -371,9 +373,11 @@
   /** A map for caching all interface states. */
   private final Map<DexType, InterfaceReservationState> interfaceStateMap = new HashMap<>();
 
-  InterfaceMethodNameMinifier(AppView<AppInfoWithLiveness> appView, State minifierState) {
+  InterfaceMethodNameMinifier(
+      AppView<AppInfoWithLiveness> appView, State minifierState, SubtypingInfo subtypingInfo) {
     this.appView = appView;
     this.minifierState = minifierState;
+    this.subtypingInfo = subtypingInfo;
     this.equivalence =
         appView.options().getProguardConfiguration().isOverloadAggressively()
             ? MethodSignatureEquivalence.get()
@@ -420,7 +424,7 @@
     // frontier states of classes that implement them. We add the frontier states so that we can
     // reserve the names for later method naming.
     timing.begin("Compute map");
-    computeReservationFrontiersForAllImplementingClasses();
+    computeReservationFrontiersForAllImplementingClasses(interfaces);
     for (DexClass iface : interfaces) {
       InterfaceReservationState inheritanceState = interfaceStateMap.get(iface.type);
       assert inheritanceState != null;
@@ -454,9 +458,6 @@
           // refer to interfaces which has been removed.
           Set<DexEncodedMethod> implementedMethods =
               appView.appInfo().lookupLambdaImplementedMethods(callSite);
-          if (implementedMethods.isEmpty()) {
-            return;
-          }
           for (DexEncodedMethod method : implementedMethods) {
             Wrapper<DexEncodedMethod> wrapped = definitionEquivalence.wrap(method);
             InterfaceMethodGroupState groupState = globalStateMap.get(wrapped);
@@ -636,34 +637,29 @@
     }
   }
 
-  private void computeReservationFrontiersForAllImplementingClasses() {
-    appView
-        .appInfo()
-        .forEachTypeInHierarchyOfLiveProgramClasses(
-            clazz -> {
-              // TODO(b/133091438): Extend the if check to test for !clazz.isLibrary().
-              if (!clazz.isInterface()) {
-                appView
-                    .appInfo()
-                    .implementedInterfaces(clazz.type)
-                    .forEach(
-                        (directlyImplemented, ignoreIsKnownAndReserveInAllCases) -> {
-                          InterfaceReservationState iState =
-                              interfaceStateMap.get(directlyImplemented);
-                          if (iState != null) {
-                            DexType frontierType = minifierState.getFrontier(clazz.type);
-                            iState.addReservationType(frontierType);
-                            // The reservation state should already be added, but if a class is
-                            // extending an interface, we will not visit the class during the
-                            // sub-type traversel.
-                            if (minifierState.getReservationState(clazz.type) == null) {
-                              minifierState.allocateReservationStateAndReserve(
-                                  clazz.type, frontierType);
-                            }
-                          }
-                        });
-              }
-            });
+  private void computeReservationFrontiersForAllImplementingClasses(Iterable<DexClass> interfaces) {
+    interfaces.forEach(
+        iface ->
+            subtypingInfo
+                .subtypes(iface.getType())
+                .forEach(
+                    subType -> {
+                      DexClass subClass = appView.contextIndependentDefinitionFor(subType);
+                      if (subClass == null || subClass.isInterface()) {
+                        return;
+                      }
+                      DexType frontierType = minifierState.getFrontier(subType);
+                      if (minifierState.getReservationState(frontierType) == null) {
+                        // The reservation state should already be added. If it does not exist
+                        // it is because it is not reachable from the type hierarchy of program
+                        // classes and we can therefore disregard this interface.
+                        return;
+                      }
+                      InterfaceReservationState iState = interfaceStateMap.get(iface.getType());
+                      if (iState != null) {
+                        iState.addReservationType(frontierType);
+                      }
+                    }));
   }
 
   private boolean verifyAllCallSitesAreRepresentedIn(List<Wrapper<DexEncodedMethod>> groups) {
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
index b2b9e9f..ca7fa4e 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.MethodAccessInfoCollection;
 import com.android.tools.r8.graph.MethodResolutionResult;
+import com.android.tools.r8.graph.SubtypingInfo;
 import com.android.tools.r8.graph.TopDownClassHierarchyTraversal;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.InternalOptions;
@@ -181,7 +182,10 @@
   }
 
   MethodRenaming computeRenaming(
-      Iterable<DexClass> interfaces, ExecutorService executorService, Timing timing)
+      Iterable<DexClass> interfaces,
+      SubtypingInfo subtypingInfo,
+      ExecutorService executorService,
+      Timing timing)
       throws ExecutionException {
     // Phase 1: Reserve all the names that need to be kept and allocate linked state in the
     //          library part.
@@ -193,7 +197,7 @@
     //          states that may hold an implementation.
     timing.begin("Phase 2");
     InterfaceMethodNameMinifier interfaceMethodNameMinifier =
-        new InterfaceMethodNameMinifier(appView, minifierState);
+        new InterfaceMethodNameMinifier(appView, minifierState, subtypingInfo);
     timing.end();
     timing.begin("Phase 3");
     interfaceMethodNameMinifier.assignNamesToInterfaceMethods(timing, interfaces);
diff --git a/src/main/java/com/android/tools/r8/naming/Minifier.java b/src/main/java/com/android/tools/r8/naming/Minifier.java
index 738be08..26c8d70 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -47,7 +47,7 @@
     assert appView.options().isMinifying();
     SubtypingInfo subtypingInfo = appView.appInfo().computeSubtypingInfo();
     timing.begin("ComputeInterfaces");
-    List<DexClass> interfaces = computeReachableInterfacesWithDeterministicOrder();
+    List<DexClass> interfaces = computeReachableInterfacesWithDeterministicOrder(subtypingInfo);
     timing.end();
     timing.begin("MinifyClasses");
     ClassNameMinifier classNameMinifier =
@@ -67,7 +67,7 @@
     timing.begin("MinifyMethods");
     MethodRenaming methodRenaming =
         new MethodNameMinifier(appView, minifyMembers)
-            .computeRenaming(interfaces, executorService, timing);
+            .computeRenaming(interfaces, subtypingInfo, executorService, timing);
     timing.end();
 
     assert new MinifiedRenaming(appView, classRenaming, methodRenaming, FieldRenaming.empty())
@@ -89,9 +89,16 @@
     return lens;
   }
 
-  private List<DexClass> computeReachableInterfacesWithDeterministicOrder() {
+  private List<DexClass> computeReachableInterfacesWithDeterministicOrder(
+      SubtypingInfo subtypingInfo) {
     List<DexClass> interfaces = new ArrayList<>();
-    appView.appInfo().forEachReachableInterface(interfaces::add);
+    subtypingInfo.forAllInterfaceRoots(
+        type -> {
+          DexClass clazz = appView.contextIndependentDefinitionFor(type);
+          if (clazz != null) {
+            interfaces.add(clazz);
+          }
+        });
     return classesWithDeterministicOrder(interfaces);
   }
 
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
index 7ee0681..43cf17d 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
@@ -8,7 +8,6 @@
 import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.defaultAsMethodOfCompanionClass;
 import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.getInterfaceClassType;
 import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.isCompanionClassType;
-import static com.android.tools.r8.utils.IterableUtils.fromMethod;
 
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
@@ -35,6 +34,7 @@
 import com.android.tools.r8.position.Position;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.SetUtils;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
@@ -93,24 +93,34 @@
   }
 
   public NamingLens run(ExecutorService executorService, Timing timing) throws ExecutionException {
-    AppInfoWithLiveness appInfo = appView.appInfo();
-    SubtypingInfo subtypingInfo = appInfo.computeSubtypingInfo();
-
     ArrayDeque<Map<DexReference, MemberNaming>> nonPrivateMembers = new ArrayDeque<>();
     Set<DexReference> notMappedReferences = new HashSet<>();
-
+    timing.begin("MappingInterfaces");
+    Set<DexClass> classesToBuildSubtypeInformationFor =
+        SetUtils.newIdentityHashSet(appView.app().classes());
+    appView
+        .appInfo()
+        .getObjectAllocationInfoCollection()
+        .forEachInstantiatedLambdaInterfaces(
+            type -> {
+              DexClass lambdaInterface = appView.contextIndependentDefinitionFor(type);
+              if (lambdaInterface != null) {
+                classesToBuildSubtypeInformationFor.add(lambdaInterface);
+              }
+            });
+    appView.appInfo().forEachReferencedClasspathClass(classesToBuildSubtypeInformationFor::add);
+    SubtypingInfo subtypingInfo =
+        SubtypingInfo.create(classesToBuildSubtypeInformationFor, appView);
     timing.begin("MappingInterfaces");
     Set<DexClass> interfaces = new TreeSet<>(Comparator.comparing(DexClass::getType));
-    // For union-find of interface methods we also need to add the library types above live types.
-    appInfo.forEachReachableInterface(
+    subtypingInfo.forAllInterfaceRoots(
         iFace -> {
-          assert iFace.isInterface();
-          interfaces.add(iFace);
-          if (iFace.interfaces.isEmpty()) {
-            computeMapping(iFace.type, nonPrivateMembers, notMappedReferences, subtypingInfo);
+          DexClass iFaceDefinition = appView.definitionFor(iFace);
+          if (iFaceDefinition != null) {
+            interfaces.add(iFaceDefinition);
+            computeMapping(iFace, nonPrivateMembers, notMappedReferences, subtypingInfo);
           }
-        },
-        fromMethod(appInfo::forEachReferencedClasspathClass, DexClass::getType));
+        });
 
     assert nonPrivateMembers.isEmpty();
     timing.end();
@@ -148,7 +158,7 @@
     timing.begin("MinifyMethods");
     MethodRenaming methodRenaming =
         new MethodNameMinifier(appView, nameStrategy)
-            .computeRenaming(interfaces, executorService, timing);
+            .computeRenaming(interfaces, subtypingInfo, executorService, timing);
     // Amend the method renamings with the default interface methods.
     methodRenaming.renaming.putAll(defaultInterfaceMethodImplementationNames);
     methodRenaming.renaming.putAll(additionalMethodNamings);
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
index aa91add..63c2dfc 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
@@ -64,10 +64,9 @@
   private ProguardMapSupplier(ClassNameMapper classNameMapper, InternalOptions options) {
     assert classNameMapper != null;
     this.classNameMapper = classNameMapper.sorted();
-    this.consumer =
-        InternalOptions.assertionsEnabled()
-            ? new ProguardMapChecker(options.proguardMapConsumer)
-            : options.proguardMapConsumer;
+    // TODO(b/217111432): Validate Proguard using ProguardMapChecker without building the entire
+    //  Proguard map in memory.
+    this.consumer = options.proguardMapConsumer;
     this.options = options;
     this.reporter = options.reporter;
   }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
index 96b68f3..f5b55c6 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
@@ -225,7 +225,7 @@
     timing.end();
 
     timing.begin("Compute unused arguments");
-    effectivelyUnusedArgumentsAnalysis.computeEffectivelyUnusedArguments(codeScannerResult);
+    effectivelyUnusedArgumentsAnalysis.computeEffectivelyUnusedArguments();
     effectivelyUnusedArgumentsAnalysis = null;
     timing.end();
   }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/lenscoderewriter/NullCheckInserter.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/lenscoderewriter/NullCheckInserter.java
index 3417bfa..c428a70 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/lenscoderewriter/NullCheckInserter.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/lenscoderewriter/NullCheckInserter.java
@@ -130,9 +130,30 @@
                   .getPosition()
                   .getOutermostCallerMatchingOrElse(
                       Position::isRemoveInnerFramesIfThrowingNpe, invoke.getPosition());
+          if (nullCheckPosition.isRemoveInnerFramesIfThrowingNpe()) {
+            // We've found an outermost removeInnerFrames for an invoke with receiver. Assume we
+            // have call chain: inline -> callerInline -> callerCallerInline
+            // where callerInline.isRemoveInnerFramesIfThrowingNpe() == true.
+            // inline must therefore have an immediate use of the receiver which is tracked in
+            // callerInline such that the frame can be removed when retracing.
+            // When synthesizing a nullCheck for the receiver on inline, the check should actually
+            // fail in the callerInline. We therefore use this as the new position.
+            // Since the exception is now moved out to callerInline, we should no longer strip the
+            // topmost frame if we see an NPE in inline, so we update the position on this invoke to
+            // inline -> callerInline' -> callerCallerInline
+            // where callerInline.isRemoveInnerFramesIfThrowingNpe() == false;
+            Position newCallerPositionTail =
+                nullCheckPosition
+                    .builderWithCopy()
+                    .setRemoveInnerFramesIfThrowingNpe(false)
+                    .build();
+            invoke.forceOverwritePosition(
+                invoke.getPosition().replacePosition(nullCheckPosition, newCallerPositionTail));
+            // We can then use pos2 (newCallerPositionTail) as the new null-check position.
+            nullCheckPosition = newCallerPositionTail;
+          }
           instructionIterator.insertNullCheckInstruction(
               appView, code, blockIterator, receiver, nullCheckPosition);
-
           // Reset the block iterator.
           if (invoke.getBlock().hasCatchHandlers()) {
             BasicBlock splitBlock = invoke.getBlock();
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsAnalysis.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsAnalysis.java
index c1a322b..b0be28f 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsAnalysis.java
@@ -16,7 +16,6 @@
 import com.android.tools.r8.ir.code.InvokeMethod;
 import com.android.tools.r8.ir.code.Value;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodParameter;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference;
 import com.android.tools.r8.optimize.argumentpropagation.utils.ParameterRemovalUtils;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.ListUtils;
@@ -172,11 +171,11 @@
     return effectivelyUnusedConstraints;
   }
 
-  public void computeEffectivelyUnusedArguments(MethodStateCollectionByReference methodStates) {
+  public void computeEffectivelyUnusedArguments() {
     // Build a graph where nodes are method parameters and there is an edge from method parameter p0
     // to method parameter p1 if the removal of p0 depends on the removal of p1.
     EffectivelyUnusedArgumentsGraph dependenceGraph =
-        EffectivelyUnusedArgumentsGraph.create(appView, constraints, methodStates);
+        EffectivelyUnusedArgumentsGraph.create(appView, constraints);
 
     // Remove all unoptimizable method parameters from the graph, as well as all nodes that depend
     // on a node that is unoptimable.
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsGraph.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsGraph.java
index fdc01bc..9fbd408 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsGraph.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsGraph.java
@@ -10,7 +10,6 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
 import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodParameter;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.WorkList;
 import com.android.tools.r8.utils.dfs.DFSStack;
@@ -38,14 +37,13 @@
 
   public static EffectivelyUnusedArgumentsGraph create(
       AppView<AppInfoWithLiveness> appView,
-      Map<MethodParameter, Set<MethodParameter>> constraints,
-      MethodStateCollectionByReference methodStates) {
+      Map<MethodParameter, Set<MethodParameter>> constraints) {
     EffectivelyUnusedArgumentsGraph graph = new EffectivelyUnusedArgumentsGraph(appView);
     constraints.forEach(
         (methodParameter, constraintsForMethodParameter) -> {
           EffectivelyUnusedArgumentsGraphNode node = graph.getOrCreateNode(methodParameter);
           for (MethodParameter constraint : constraintsForMethodParameter) {
-            graph.addConstraintEdge(node, constraint, constraints, methodStates);
+            graph.addConstraintEdge(node, constraint, constraints);
           }
         });
     return graph;
@@ -54,8 +52,7 @@
   void addConstraintEdge(
       EffectivelyUnusedArgumentsGraphNode node,
       MethodParameter constraint,
-      Map<MethodParameter, Set<MethodParameter>> constraints,
-      MethodStateCollectionByReference methodStates) {
+      Map<MethodParameter, Set<MethodParameter>> constraints) {
     ProgramMethod dependencyMethod =
         asProgramMethodOrNull(appView.definitionFor(constraint.getMethod()));
     if (dependencyMethod == null) {
@@ -68,7 +65,7 @@
     // invoke (or we cannot preserve NPE semantics).
     if (dependencyMethod.getDefinition().isInstance()
         && constraint.getIndex() == 0
-        && node.isNullable(methodStates)) {
+        && node.isNullable()) {
       node.setUnoptimizable();
       return;
     }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsGraphNode.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsGraphNode.java
index 2b52686..c093924 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsGraphNode.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsGraphNode.java
@@ -7,10 +7,7 @@
 import static com.android.tools.r8.ir.optimize.info.OptimizationFeedback.getSimpleFeedback;
 
 import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMonomorphicMethodState;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodState;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.ParameterState;
+import com.android.tools.r8.ir.analysis.type.DynamicType;
 import com.google.common.collect.Sets;
 import java.util.BitSet;
 import java.util.Set;
@@ -75,28 +72,13 @@
     return successors;
   }
 
-  boolean isNullable(MethodStateCollectionByReference methodStates) {
+  boolean isNullable() {
     if (method.getDefinition().isInstance() && argumentIndex == 0) {
       return false;
     }
-    MethodState methodState = methodStates.get(method);
-    if (methodState.isBottom()) {
-      // TODO: this means the method is unreachable? what to do in this case?
-      return true;
-    }
-    assert !methodState.isBottom();
-    if (methodState.isUnknown()) {
-      return true;
-    }
-    assert methodState.isMonomorphic();
-    ConcreteMonomorphicMethodState monomorphicMethodState = methodState.asMonomorphic();
-    ParameterState parameterState = monomorphicMethodState.getParameterState(argumentIndex);
-    if (parameterState.isUnknown()) {
-      return true;
-    }
-    assert parameterState.isConcrete();
-    assert parameterState.asConcrete().isReferenceParameter();
-    return parameterState.asConcrete().asReferenceParameter().getNullability().isMaybeNull();
+    DynamicType dynamicType =
+        method.getOptimizationInfo().getArgumentInfos().getDynamicType(argumentIndex);
+    return dynamicType.getNullability().isNullable();
   }
 
   boolean isUnoptimizable() {
diff --git a/src/main/java/com/android/tools/r8/retrace/Retrace.java b/src/main/java/com/android/tools/r8/retrace/Retrace.java
index 14b393c..b4467ea 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retrace.java
@@ -602,7 +602,10 @@
         RetraceStackTraceElementProxy<T, ST> other,
         Function<RetraceStackTraceElementProxy<T, ST>, Boolean> predicate,
         Function<RetraceStackTraceElementProxy<T, ST>, V> getter) {
-      return Comparator.comparing(predicate).thenComparing(getter).compare(one, other) != 0;
+      return Comparator.comparing(predicate)
+              .thenComparing(getter, Comparator.nullsFirst(V::compareTo))
+              .compare(one, other)
+          != 0;
     }
 
     public static <T, ST extends StackTraceElementProxy<T, ST>>
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceStackTraceContextImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceStackTraceContextImpl.java
index 94e2d31..f29af88 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceStackTraceContextImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceStackTraceContextImpl.java
@@ -10,7 +10,6 @@
 import com.android.tools.r8.naming.mappinginformation.RewriteFrameMappingInformation.RewriteAction;
 import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.retrace.RetraceStackTraceContext;
-import com.android.tools.r8.utils.ListUtils;
 import java.util.List;
 import java.util.OptionalInt;
 
@@ -36,12 +35,13 @@
     }
     RetraceStackTraceCurrentEvaluationInformation.Builder builder =
         RetraceStackTraceCurrentEvaluationInformation.builder();
-    MappedRange last = ListUtils.last(mappedRanges);
-    for (RewriteFrameMappingInformation rewriteInformation :
-        last.getRewriteFrameMappingInformation()) {
-      if (evaluateConditions(rewriteInformation.getConditions())) {
-        for (RewriteAction action : rewriteInformation.getActions()) {
-          action.evaluate(builder);
+    for (MappedRange mappedRange : mappedRanges) {
+      for (RewriteFrameMappingInformation rewriteInformation :
+          mappedRange.getRewriteFrameMappingInformation()) {
+        if (evaluateConditions(rewriteInformation.getConditions())) {
+          for (RewriteAction action : rewriteInformation.getActions()) {
+            action.evaluate(builder);
+          }
         }
       }
     }
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 71d4807..78c8797 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -1540,57 +1540,6 @@
     app().asDirect().classpathClasses().forEach(fn);
   }
 
-  /**
-   * Visits all class definitions that are a live program type or a type above it in the hierarchy.
-   *
-   * <p>Any given definition will be visited at most once. No guarantees are places on the order.
-   */
-  public void forEachTypeInHierarchyOfLiveProgramClasses(Consumer<DexClass> fn) {
-    forEachTypeInHierarchyOfLiveProgramClasses(
-        fn,
-        ListUtils.map(liveTypes, t -> definitionFor(t).asProgramClass()),
-        objectAllocationInfoCollection.getInstantiatedLambdaInterfaces(),
-        this);
-  }
-
-  // Split in a static method so it can be used during construction.
-  static void forEachTypeInHierarchyOfLiveProgramClasses(
-      Consumer<DexClass> fn,
-      Collection<DexProgramClass> liveProgramClasses,
-      Set<DexType> lambdaInterfaces,
-      AppInfoWithClassHierarchy appInfo) {
-    Set<DexType> seen = Sets.newIdentityHashSet();
-    liveProgramClasses.forEach(c -> seen.add(c.type));
-    Deque<DexType> worklist = new ArrayDeque<>(lambdaInterfaces);
-    for (DexProgramClass liveProgramClass : liveProgramClasses) {
-      fn.accept(liveProgramClass);
-      DexType superType = liveProgramClass.superType;
-      if (superType != null && seen.add(superType)) {
-        worklist.add(superType);
-      }
-      for (DexType iface : liveProgramClass.interfaces.values) {
-        if (seen.add(iface)) {
-          worklist.add(iface);
-        }
-      }
-    }
-    while (!worklist.isEmpty()) {
-      DexType type = worklist.pop();
-      DexClass clazz = appInfo.definitionFor(type);
-      if (clazz != null) {
-        fn.accept(clazz);
-        if (clazz.superType != null && seen.add(clazz.superType)) {
-          worklist.add(clazz.superType);
-        }
-        for (DexType iface : clazz.interfaces.values) {
-          if (seen.add(iface)) {
-            worklist.add(iface);
-          }
-        }
-      }
-    }
-  }
-
   @Override
   public void forEachInstantiatedSubType(
       DexType type,
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 33b077d..658cfbf 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -209,10 +209,6 @@
       return this == FINAL_TREE_SHAKING;
     }
 
-    public boolean isInitialOrFinalTreeShaking() {
-      return isInitialTreeShaking() || isFinalTreeShaking();
-    }
-
     public boolean isInitialMainDexTracing() {
       return this == INITIAL_MAIN_DEX_TRACING;
     }
@@ -480,7 +476,7 @@
             ? ProguardCompatibilityActions.builder()
             : null;
 
-    if (mode.isInitialOrFinalTreeShaking()) {
+    if (mode.isTreeShaking()) {
       GetArrayOfMissingTypeVerifyErrorWorkaround.register(appView, this);
       InvokeVirtualToInterfaceVerifyErrorWorkaround.register(appView, this);
       if (options.protoShrinking().enableGeneratedMessageLiteShrinking) {
@@ -817,15 +813,9 @@
       return;
     }
     // Add everything if we are not shrinking.
-    assert appView.options().getProguardConfiguration().getKeepAllRule() != null;
-    ProguardKeepRuleBase keepAllRule =
-        appView.options().getProguardConfiguration().getKeepAllRule();
-    KeepClassInfo.Joiner keepClassInfo =
-        KeepClassInfo.newEmptyJoiner().addRule(keepAllRule).disallowShrinking();
-    KeepFieldInfo.Joiner keepFieldInfo =
-        KeepFieldInfo.newEmptyJoiner().addRule(keepAllRule).disallowShrinking();
-    KeepMethodInfo.Joiner keepMethodInfo =
-        KeepMethodInfo.newEmptyJoiner().addRule(keepAllRule).disallowShrinking();
+    KeepClassInfo.Joiner keepClassInfo = KeepClassInfo.newEmptyJoiner().disallowShrinking();
+    KeepFieldInfo.Joiner keepFieldInfo = KeepFieldInfo.newEmptyJoiner().disallowShrinking();
+    KeepMethodInfo.Joiner keepMethodInfo = KeepMethodInfo.newEmptyJoiner().disallowShrinking();
     EnqueuerEvent preconditionEvent = UnconditionalKeepInfoEvent.get();
     for (DexProgramClass clazz : appView.appInfo().classes()) {
       if (appView.getSyntheticItems().isSyntheticClass(clazz)
@@ -845,8 +835,7 @@
       DexProgramClass clazz,
       KeepClassInfo.Joiner minimumKeepInfo,
       EnqueuerEvent preconditionEvent) {
-    assert !minimumKeepInfo.isShrinkingAllowed();
-    assert !minimumKeepInfo.getRules().isEmpty();
+    assert minimumKeepInfo.verifyShrinkingDisallowedWithRule(options);
     DexDefinition precondition = preconditionEvent.getDefinition(appInfo());
     enqueueKeepRuleInstantiatedType(clazz, minimumKeepInfo.getRules(), precondition);
   }
@@ -876,8 +865,7 @@
 
   private void enqueueFieldDueToNoShrinkingRule(
       ProgramField field, KeepFieldInfo.Joiner minimumKeepInfo, EnqueuerEvent preconditionEvent) {
-    assert !minimumKeepInfo.isShrinkingAllowed();
-    assert !minimumKeepInfo.getRules().isEmpty();
+    assert minimumKeepInfo.verifyShrinkingDisallowedWithRule(options);
     DexDefinition precondition = preconditionEvent.getDefinition(appInfo());
     workList.enqueueMarkFieldKeptAction(
         field,
@@ -889,8 +877,7 @@
       ProgramMethod method,
       KeepMethodInfo.Joiner minimumKeepInfo,
       EnqueuerEvent preconditionEvent) {
-    assert !minimumKeepInfo.isShrinkingAllowed();
-    assert !minimumKeepInfo.getRules().isEmpty();
+    assert minimumKeepInfo.verifyShrinkingDisallowedWithRule(options);
     DexDefinition precondition = preconditionEvent.getDefinition(appInfo());
     workList.enqueueMarkMethodKeptAction(
         method,
@@ -2419,7 +2406,8 @@
     // because each analysis may depend on seeing all the (clazz, reason) pairs. Thus, not doing so
     // could lead to nondeterminism.
     analyses.forEach(
-        analysis -> analysis.processNewlyInstantiatedClass(clazz.asProgramClass(), context));
+        analysis ->
+            analysis.processNewlyInstantiatedClass(clazz.asProgramClass(), context, workList));
 
     if (!markInstantiatedClass(clazz, context, instantiationReason, keepReason)) {
       return;
@@ -2799,7 +2787,7 @@
     applyMinimumKeepInfo(field);
 
     // Notify analyses.
-    analyses.forEach(analysis -> analysis.processNewlyLiveField(field, context));
+    analyses.forEach(analysis -> analysis.processNewlyLiveField(field, context, workList));
   }
 
   // Package protected due to entry point from worklist.
@@ -2825,7 +2813,7 @@
 
     traceFieldDefinition(field);
 
-    analyses.forEach(analysis -> analysis.notifyMarkFieldAsReachable(field));
+    analyses.forEach(analysis -> analysis.notifyMarkFieldAsReachable(field, workList));
   }
 
   private void traceFieldDefinition(ProgramField field) {
@@ -3076,7 +3064,7 @@
     target.accept(
         method -> markVirtualDispatchTargetAsLive(method, reason),
         lambda -> markVirtualDispatchTargetAsLive(lambda, reason));
-    analyses.forEach(analysis -> analysis.notifyMarkVirtualDispatchTargetAsLive(target));
+    analyses.forEach(analysis -> analysis.notifyMarkVirtualDispatchTargetAsLive(target, workList));
   }
 
   private void markVirtualDispatchTargetAsLive(
@@ -3156,7 +3144,9 @@
     if (target == null) {
       failedMethodResolutionTargets.add(resolution.getResolvedMethod().getReference());
       analyses.forEach(
-          analyses -> analyses.notifyFailedMethodResolutionTarget(resolution.getResolvedMethod()));
+          analyses ->
+              analyses.notifyFailedMethodResolutionTarget(
+                  resolution.getResolvedMethod(), workList));
       return;
     }
 
@@ -3305,7 +3295,7 @@
       KeepClassInfo.Joiner minimumKeepInfo) {
     if ((options.isShrinking() || mode.isMainDexTracing())
         && !minimumKeepInfo.isShrinkingAllowed()) {
-      assert !minimumKeepInfo.getRules().isEmpty();
+      assert minimumKeepInfo.verifyShrinkingDisallowedWithRule(options);
       enqueueClassDueToNoShrinkingRule(clazz, minimumKeepInfo, preconditionEvent);
     }
   }
@@ -3352,7 +3342,7 @@
       ProgramField field, EnqueuerEvent preconditionEvent, KeepFieldInfo.Joiner minimumKeepInfo) {
     if ((options.isShrinking() || mode.isMainDexTracing())
         && !minimumKeepInfo.isShrinkingAllowed()) {
-      assert !minimumKeepInfo.getRules().isEmpty();
+      assert minimumKeepInfo.verifyShrinkingDisallowedWithRule(options);
       enqueueFieldDueToNoShrinkingRule(field, minimumKeepInfo, preconditionEvent);
     }
   }
@@ -3401,7 +3391,7 @@
       KeepMethodInfo.Joiner minimumKeepInfo) {
     if ((options.isShrinking() || mode.isMainDexTracing())
         && !minimumKeepInfo.isShrinkingAllowed()) {
-      assert !minimumKeepInfo.getRules().isEmpty();
+      assert minimumKeepInfo.verifyShrinkingDisallowedWithRule(options);
       enqueueMethodDueToNoShrinkingRule(method, minimumKeepInfo, preconditionEvent);
 
       if (method.getDefinition().isInstanceInitializer()) {
@@ -4317,7 +4307,7 @@
     }
 
     // Notify analyses.
-    analyses.forEach(analysis -> analysis.processNewlyLiveMethod(method, context));
+    analyses.forEach(analysis -> analysis.processNewlyLiveMethod(method, context, workList));
   }
 
   private void markMethodAsTargeted(ProgramMethod method, KeepReason reason) {
@@ -4337,7 +4327,7 @@
         markMethodAsLiveWithCompatRule(method);
       }
     }
-    analyses.forEach(analysis -> analysis.notifyMarkMethodAsTargeted(method));
+    analyses.forEach(analysis -> analysis.notifyMarkMethodAsTargeted(method, workList));
   }
 
   void traceMethodDefinitionExcludingCode(ProgramMethod method) {
@@ -4368,7 +4358,7 @@
         useRegistryFactory.create(appView, method, this, appView.apiLevelCompute());
     method.registerCodeReferences(registry);
     // Notify analyses.
-    analyses.forEach(analysis -> analysis.processTracedCode(method, registry));
+    analyses.forEach(analysis -> analysis.processTracedCode(method, registry, workList));
   }
 
   private void markReferencedTypesAsLive(ProgramMethod method) {
diff --git a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
index 83055e7..2fe6b81 100644
--- a/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
+++ b/src/main/java/com/android/tools/r8/shaking/GraphReporter.java
@@ -31,6 +31,7 @@
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.references.TypeReference;
 import com.android.tools.r8.utils.DequeUtils;
+import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableList.Builder;
 import com.google.common.collect.Sets;
@@ -45,6 +46,7 @@
 public class GraphReporter {
 
   private final AppView<?> appView;
+  private final InternalOptions options;
   private final GraphConsumer keptGraphConsumer;
   private final CollectingGraphConsumer verificationGraphConsumer;
 
@@ -58,6 +60,7 @@
 
   GraphReporter(AppView<?> appView, GraphConsumer keptGraphConsumer) {
     this.appView = appView;
+    this.options = appView.options();
     if (appView.options().testing.verifyKeptGraphInfo) {
       this.verificationGraphConsumer = new CollectingGraphConsumer(keptGraphConsumer);
       this.keptGraphConsumer = verificationGraphConsumer;
@@ -108,17 +111,17 @@
 
   KeepReasonWitness reportKeepClass(
       DexDefinition precondition, ProguardKeepRuleBase rule, DexProgramClass clazz) {
-    if (keptGraphConsumer == null) {
-      return KeepReasonWitness.INSTANCE;
+    if (keptGraphConsumer != null) {
+      KeepRuleGraphNode ruleNode = getKeepRuleGraphNode(precondition, rule);
+      EdgeKind edgeKind = reportPrecondition(ruleNode);
+      return reportEdge(ruleNode, getClassGraphNode(clazz.type), edgeKind);
     }
-    KeepRuleGraphNode ruleNode = getKeepRuleGraphNode(precondition, rule);
-    EdgeKind edgeKind = reportPrecondition(ruleNode);
-    return reportEdge(ruleNode, getClassGraphNode(clazz.type), edgeKind);
+    return KeepReasonWitness.INSTANCE;
   }
 
   KeepReasonWitness reportKeepClass(
       DexDefinition precondition, Collection<ProguardKeepRuleBase> rules, DexProgramClass clazz) {
-    assert !rules.isEmpty();
+    assert !rules.isEmpty() || !options.isShrinking();
     if (keptGraphConsumer != null) {
       for (ProguardKeepRuleBase rule : rules) {
         reportKeepClass(precondition, rule, clazz);
@@ -129,17 +132,17 @@
 
   KeepReasonWitness reportKeepMethod(
       DexDefinition precondition, ProguardKeepRuleBase rule, DexEncodedMethod method) {
-    if (keptGraphConsumer == null) {
-      return KeepReasonWitness.INSTANCE;
+    if (keptGraphConsumer != null) {
+      KeepRuleGraphNode ruleNode = getKeepRuleGraphNode(precondition, rule);
+      EdgeKind edgeKind = reportPrecondition(ruleNode);
+      return reportEdge(ruleNode, getMethodGraphNode(method.getReference()), edgeKind);
     }
-    KeepRuleGraphNode ruleNode = getKeepRuleGraphNode(precondition, rule);
-    EdgeKind edgeKind = reportPrecondition(ruleNode);
-    return reportEdge(ruleNode, getMethodGraphNode(method.getReference()), edgeKind);
+    return KeepReasonWitness.INSTANCE;
   }
 
   KeepReasonWitness reportKeepMethod(
       DexDefinition precondition, Collection<ProguardKeepRuleBase> rules, DexEncodedMethod method) {
-    assert !rules.isEmpty();
+    assert !rules.isEmpty() || !options.isShrinking();
     if (keptGraphConsumer != null) {
       for (ProguardKeepRuleBase rule : rules) {
         reportKeepMethod(precondition, rule, method);
@@ -150,17 +153,17 @@
 
   KeepReasonWitness reportKeepField(
       DexDefinition precondition, ProguardKeepRuleBase rule, DexEncodedField field) {
-    if (keptGraphConsumer == null) {
-      return KeepReasonWitness.INSTANCE;
+    if (keptGraphConsumer != null) {
+      KeepRuleGraphNode ruleNode = getKeepRuleGraphNode(precondition, rule);
+      EdgeKind edgeKind = reportPrecondition(ruleNode);
+      return reportEdge(ruleNode, getFieldGraphNode(field.getReference()), edgeKind);
     }
-    KeepRuleGraphNode ruleNode = getKeepRuleGraphNode(precondition, rule);
-    EdgeKind edgeKind = reportPrecondition(ruleNode);
-    return reportEdge(ruleNode, getFieldGraphNode(field.getReference()), edgeKind);
+    return KeepReasonWitness.INSTANCE;
   }
 
   KeepReasonWitness reportKeepField(
       DexDefinition precondition, Collection<ProguardKeepRuleBase> rules, DexEncodedField field) {
-    assert !rules.isEmpty();
+    assert !rules.isEmpty() || !options.isShrinking();
     if (keptGraphConsumer != null) {
       for (ProguardKeepRuleBase rule : rules) {
         reportKeepField(precondition, rule, field);
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
index 598f6a7..9b83643 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.graph.EnclosingMethodAttribute;
 import com.android.tools.r8.graph.ProgramDefinition;
 import com.android.tools.r8.shaking.KeepInfo.Builder;
+import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.Sets;
 import java.util.Set;
 import java.util.function.Consumer;
@@ -560,5 +561,11 @@
       assert original.isLessThanOrEquals(joined);
       return joined;
     }
+
+    public boolean verifyShrinkingDisallowedWithRule(InternalOptions options) {
+      assert !isShrinkingAllowed();
+      assert !getRules().isEmpty() || !options.isShrinking();
+      return true;
+    }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
index 2f6de9b..c1f5a95 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
@@ -10,7 +10,6 @@
 import com.android.tools.r8.position.Position;
 import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode;
 import com.android.tools.r8.utils.Reporter;
-import com.android.tools.r8.utils.StringUtils;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Sets;
 import java.nio.file.Path;
@@ -68,12 +67,10 @@
         ProguardPathFilter.builder().disable();
     private boolean forceProguardCompatibility = false;
     private boolean overloadAggressively;
-    private boolean keepRuleSynthesisForRecompilation = false;
     private boolean configurationDebugging = false;
     private boolean dontUseMixedCaseClassnames = false;
     private boolean protoShrinking = false;
     private int maxRemovedAndroidLogLevel = 1;
-    private ProguardKeepRule keepAllRule;
 
     private Builder(DexItemFactory dexItemFactory, Reporter reporter) {
       this.dexItemFactory = dexItemFactory;
@@ -281,10 +278,6 @@
       this.overloadAggressively = overloadAggressively;
     }
 
-    public void enableKeepRuleSynthesisForRecompilation() {
-      this.keepRuleSynthesisForRecompilation = true;
-    }
-
     public void setConfigurationDebugging(boolean configurationDebugging) {
       this.configurationDebugging = configurationDebugging;
     }
@@ -309,24 +302,6 @@
       this.maxRemovedAndroidLogLevel = maxRemovedAndroidLogLevel;
     }
 
-    /**
-     * This synthesizes a set of keep rules that are necessary in order to be able to successfully
-     * recompile the generated dex files with the same keep rules.
-     */
-    public void synthesizeKeepRulesForRecompilation() {
-      List<ProguardConfigurationRule> synthesizedKeepRules = new ArrayList<>();
-      for (ProguardConfigurationRule rule : rules) {
-        ProguardConfigurationUtils.synthesizeKeepRulesForRecompilation(rule, synthesizedKeepRules);
-      }
-      if (rules.addAll(synthesizedKeepRules)) {
-        parsedConfiguration.add(
-            StringUtils.lines(
-                synthesizedKeepRules.stream()
-                    .map(ProguardClassSpecification::toString)
-                    .toArray(String[]::new)));
-      }
-    }
-
     public ProguardConfiguration buildRaw() {
       ProguardConfiguration configuration =
           new ProguardConfiguration(
@@ -369,8 +344,7 @@
               configurationDebugging,
               dontUseMixedCaseClassnames,
               protoShrinking,
-              maxRemovedAndroidLogLevel,
-              keepAllRule);
+              maxRemovedAndroidLogLevel);
 
       reporter.failIfPendingErrors();
 
@@ -382,31 +356,6 @@
         // For Proguard -keepattributes are only applicable when obfuscating.
         keepAttributePatterns.addAll(ProguardKeepAttributes.KEEP_ALL);
       }
-      // If either of the flags -dontshrink or -dontobfuscate, or shrinking or minification is
-      // turned off through the API, then add a match all rule which will apply that.
-      if (!isObfuscating() || !isOptimizing() || !isShrinking()) {
-        ProguardKeepRule rule =
-            ProguardKeepRule.defaultKeepAllRule(
-                modifiers -> {
-                  modifiers
-                      .setAllowsAccessModification(isAccessModificationEnabled())
-                      .setAllowsObfuscation(isObfuscating())
-                      .setAllowsOptimization(isOptimizing())
-                      .setAllowsShrinking(isShrinking());
-
-                  // In non-compatibility mode, adding -dontoptimize does not cause all annotations
-                  // to be retained.
-                  if (!forceProguardCompatibility && isShrinking()) {
-                    modifiers.setAllowsAnnotationRemoval(true);
-                  }
-                });
-        addRule(rule);
-        this.keepAllRule = rule;
-      }
-
-      if (keepRuleSynthesisForRecompilation) {
-        synthesizeKeepRulesForRecompilation();
-      }
 
       if (packageObfuscationMode == PackageObfuscationMode.NONE && obfuscating) {
         packageObfuscationMode = PackageObfuscationMode.MINIFICATION;
@@ -456,7 +405,6 @@
   private final boolean dontUseMixedCaseClassnames;
   private final boolean protoShrinking;
   private final int maxRemovedAndroidLogLevel;
-  private final ProguardKeepRule keepAllRule;
 
   private ProguardConfiguration(
       String parsedConfiguration,
@@ -498,8 +446,7 @@
       boolean configurationDebugging,
       boolean dontUseMixedCaseClassnames,
       boolean protoShrinking,
-      int maxRemovedAndroidLogLevel,
-      ProguardKeepRule keepAllRule) {
+      int maxRemovedAndroidLogLevel) {
     this.parsedConfiguration = parsedConfiguration;
     this.dexItemFactory = factory;
     this.injars = ImmutableList.copyOf(injars);
@@ -540,7 +487,6 @@
     this.dontUseMixedCaseClassnames = dontUseMixedCaseClassnames;
     this.protoShrinking = protoShrinking;
     this.maxRemovedAndroidLogLevel = maxRemovedAndroidLogLevel;
-    this.keepAllRule = keepAllRule;
   }
 
   /**
@@ -720,10 +666,6 @@
     return maxRemovedAndroidLogLevel;
   }
 
-  public ProguardKeepRule getKeepAllRule() {
-    return keepAllRule;
-  }
-
   @Override
   public String toString() {
     StringBuilder builder = new StringBuilder();
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
index 8c0d0b4..bf21d1f 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
@@ -181,21 +181,4 @@
     }
     return false;
   }
-
-  public static void synthesizeKeepRulesForRecompilation(
-      ProguardConfigurationRule rule, List<ProguardConfigurationRule> synthesizedKeepRules) {
-    if (rule.hasInheritanceClassName()) {
-      ProguardTypeMatcher inheritanceClassName = rule.getInheritanceClassName();
-      synthesizedKeepRules.add(
-          ProguardKeepRule.builder()
-              .setOrigin(synthesizedRecompilationOrigin)
-              .setType(ProguardKeepRuleType.KEEP)
-              .setClassType(
-                  rule.getInheritanceIsExtends()
-                      ? ProguardClassType.CLASS
-                      : ProguardClassType.INTERFACE)
-              .setClassNames(ProguardClassNameList.singletonList(inheritanceClassName))
-              .build());
-    }
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java b/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
index 089ccce..8eb6b51 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
@@ -11,22 +11,44 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.LibraryMethod;
 import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.optimize.inliner.NopWhyAreYouNotInliningReporter;
+import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
 
 public class AndroidApiLevelUtils {
 
   public static boolean isApiSafeForInlining(
       ProgramMethod caller, ProgramMethod inlinee, InternalOptions options) {
+    return isApiSafeForInlining(
+        caller, inlinee, options, NopWhyAreYouNotInliningReporter.getInstance());
+  }
+
+  public static boolean isApiSafeForInlining(
+      ProgramMethod caller,
+      ProgramMethod inlinee,
+      InternalOptions options,
+      WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
     if (!options.apiModelingOptions().enableApiCallerIdentification) {
       return true;
     }
     if (caller.getHolderType() == inlinee.getHolderType()) {
       return true;
     }
+    ComputedApiLevel callerApiLevelForCode = caller.getDefinition().getApiLevelForCode();
+    if (callerApiLevelForCode.isUnknownApiLevel()) {
+      whyAreYouNotInliningReporter.reportCallerHasUnknownApiLevel();
+      return false;
+    }
     // For inlining we only measure if the code has invokes into the library.
-    return caller
+    ComputedApiLevel inlineeApiLevelForCode = inlinee.getDefinition().getApiLevelForCode();
+    if (!caller
         .getDefinition()
         .getApiLevelForCode()
-        .isGreaterThanOrEqualTo(inlinee.getDefinition().getApiLevelForCode());
+        .isGreaterThanOrEqualTo(inlineeApiLevelForCode)) {
+      whyAreYouNotInliningReporter.reportInlineeHigherApiCall(
+          callerApiLevelForCode, inlineeApiLevelForCode);
+      return false;
+    }
+    return true;
   }
 
   public static ComputedApiLevel getApiReferenceLevelForMerging(
diff --git a/src/main/java/com/android/tools/r8/utils/AssertionConfigurationWithDefault.java b/src/main/java/com/android/tools/r8/utils/AssertionConfigurationWithDefault.java
index 7dc7b33..df3f944 100644
--- a/src/main/java/com/android/tools/r8/utils/AssertionConfigurationWithDefault.java
+++ b/src/main/java/com/android/tools/r8/utils/AssertionConfigurationWithDefault.java
@@ -11,20 +11,20 @@
 
 public class AssertionConfigurationWithDefault {
 
-  public final AssertionTransformation defautlTransformation;
+  public final AssertionsConfiguration defaultConfiguration;
   public final List<AssertionsConfiguration> assertionsConfigurations;
 
   public AssertionConfigurationWithDefault(
-      AssertionTransformation defautlTransformation,
+      AssertionsConfiguration defautlTransformation,
       List<AssertionsConfiguration> assertionsConfigurations) {
-    this.defautlTransformation = defautlTransformation;
+    this.defaultConfiguration = defautlTransformation;
     assert assertionsConfigurations != null;
     this.assertionsConfigurations = assertionsConfigurations;
   }
 
   public boolean isPassthroughAll() {
     if (assertionsConfigurations.size() == 0) {
-      return defautlTransformation == AssertionTransformation.PASSTHROUGH;
+      return defaultConfiguration.getTransformation() == AssertionTransformation.PASSTHROUGH;
     }
     return assertionsConfigurations.size() == 1
         && assertionsConfigurations.get(0).getScope() == AssertionTransformationScope.ALL
diff --git a/src/main/java/com/android/tools/r8/utils/BooleanUtils.java b/src/main/java/com/android/tools/r8/utils/BooleanUtils.java
index e99ff55..4df05c6 100644
--- a/src/main/java/com/android/tools/r8/utils/BooleanUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/BooleanUtils.java
@@ -28,4 +28,8 @@
   public static Boolean[] falseValues() {
     return FALSE_VALUES;
   }
+
+  public static boolean xor(boolean x, boolean y) {
+    return (x && !y) || (!x && y);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/ConsumerUtils.java b/src/main/java/com/android/tools/r8/utils/ConsumerUtils.java
index 4f652cf..4b20c06 100644
--- a/src/main/java/com/android/tools/r8/utils/ConsumerUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ConsumerUtils.java
@@ -6,6 +6,7 @@
 
 import java.util.Set;
 import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
 import java.util.function.Consumer;
 import java.util.function.Function;
 
@@ -33,6 +34,11 @@
     };
   }
 
+  public static <S, T, R> BiConsumer<S, T> andThen(
+      BiFunction<S, T, R> function, Consumer<R> consumer) {
+    return (s, t) -> consumer.accept(function.apply(s, t));
+  }
+
   public static <T> Consumer<T> emptyConsumer() {
     return ignore -> {};
   }
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 d32fe5c..bc88b16 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -18,7 +18,6 @@
 import com.android.tools.r8.SourceFileProvider;
 import com.android.tools.r8.StringConsumer;
 import com.android.tools.r8.Version;
-import com.android.tools.r8.androidapi.AndroidApiForHashingClass;
 import com.android.tools.r8.androidapi.ComputedApiLevel;
 import com.android.tools.r8.cf.CfVersion;
 import com.android.tools.r8.dex.Marker;
@@ -39,12 +38,12 @@
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexClasspathClass;
 import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexItem;
 import com.android.tools.r8.graph.DexItemFactory;
 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;
@@ -780,10 +779,6 @@
     return desugarSpecificOptions;
   }
 
-  public static boolean shouldEnableKeepRuleSynthesisForRecompilation() {
-    return System.getProperty("com.android.tools.r8.keepRuleSynthesisForRecompilation") != null;
-  }
-
   private static Set<String> getExtensiveLoggingFilter() {
     String property = System.getProperty("com.android.tools.r8.extensiveLoggingFilter");
     if (property != null) {
@@ -1489,12 +1484,6 @@
 
   public static class ApiModelTestingOptions {
 
-    // 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<ClassReference, AndroidApiLevel> classApiMapping = new HashMap<>();
-    public BiConsumer<MethodReference, ComputedApiLevel> tracedMethodApiLevelCallback = null;
-
     public boolean enableApiCallerIdentification =
         System.getProperty("com.android.tools.r8.disableApiModeling") == null;
     public boolean checkAllApiReferencesAreSet =
@@ -1504,54 +1493,28 @@
     public boolean enableOutliningOfMethods =
         System.getProperty("com.android.tools.r8.disableApiModeling") == null;
 
+    // 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<ClassReference, AndroidApiLevel> classApiMapping = new HashMap<>();
+    public BiConsumer<MethodReference, ComputedApiLevel> tracedMethodApiLevelCallback = null;
+
     public void visitMockedApiLevelsForReferences(
-        DexItemFactory factory, Consumer<AndroidApiForHashingClass> consumer) {
+        DexItemFactory factory, BiConsumer<DexReference, AndroidApiLevel> apiLevelConsumer) {
       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(
-                new AndroidApiForHashingClass() {
-                  @Override
-                  public DexType getType() {
-                    return factory.createType(classReference.getDescriptor());
-                  }
-
-                  @Override
-                  public AndroidApiLevel getApiLevel() {
-                    return classApiMapping.getOrDefault(classReference, ANDROID_PLATFORM);
-                  }
-
-                  @Override
-                  public void visitMethodsWithApiLevels(
-                      BiConsumer<DexMethod, AndroidApiLevel> consumer) {
-                    methodApiMapping.forEach(
-                        (methodReference, apiLevel) -> {
-                          if (methodReference.getHolderClass().equals(classReference)) {
-                            consumer.accept(factory.createMethod(methodReference), apiLevel);
-                          }
-                        });
-                  }
-
-                  @Override
-                  public void visitFieldsWithApiLevels(
-                      BiConsumer<DexField, AndroidApiLevel> consumer) {
-                    fieldApiMapping.forEach(
-                        (fieldReference, apiLevel) -> {
-                          if (fieldReference.getHolderClass().equals(classReference)) {
-                            consumer.accept(factory.createField(fieldReference), apiLevel);
-                          }
-                        });
-                  }
-                });
+      classApiMapping.forEach(
+          (classReference, apiLevel) -> {
+            apiLevelConsumer.accept(factory.createType(classReference.getDescriptor()), apiLevel);
+          });
+      fieldApiMapping.forEach(
+          (fieldReference, apiLevel) -> {
+            apiLevelConsumer.accept(factory.createField(fieldReference), apiLevel);
+          });
+      methodApiMapping.forEach(
+          (methodReference, apiLevel) -> {
+            apiLevelConsumer.accept(factory.createMethod(methodReference), apiLevel);
           });
     }
 
diff --git a/src/main/java/com/android/tools/r8/utils/SetUtils.java b/src/main/java/com/android/tools/r8/utils/SetUtils.java
index 624fb10..6be9cc3 100644
--- a/src/main/java/com/android/tools/r8/utils/SetUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/SetUtils.java
@@ -60,7 +60,7 @@
     return result;
   }
 
-  public static <T> Set<T> newIdentityHashSet(Iterable<T> c) {
+  public static <T> Set<T> newIdentityHashSet(Iterable<? extends T> c) {
     Set<T> result = Sets.newIdentityHashSet();
     c.forEach(result::add);
     return result;
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java
index 8531ceb..97ee26b 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java
@@ -116,16 +116,16 @@
               assertFalse(synthesizedMissingNotReferenced.isPresent());
               verifyThat(inspector, parameters, addedOn23).isNotOutlinedFrom(testMethod);
               verifyThat(inspector, parameters, addedOn27)
-                  .isOutlinedFromUntil(testMethod, AndroidApiLevel.O_MR1);
+                  .isOutlinedFromUntil(testMethod, finalLibraryMethodLevel);
               verifyThat(
                       inspector,
                       parameters,
                       LibraryClass.class.getDeclaredMethod("missingAndReferenced"))
-                  .isOutlinedFrom(testMethod);
-              if (parameters.getApiLevel().isLessThan(AndroidApiLevel.O_MR1)) {
-                assertEquals(5, inspector.allClasses().size());
-              } else {
+                  .isNotOutlinedFrom(testMethod);
+              if (parameters.getApiLevel().isLessThan(finalLibraryMethodLevel)) {
                 assertEquals(4, inspector.allClasses().size());
+              } else {
+                assertEquals(3, inspector.allClasses().size());
               }
             });
   }
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodUnknownTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodUnknownTest.java
new file mode 100644
index 0000000..f8b3bf7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodUnknownTest.java
@@ -0,0 +1,87 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.apimodel;
+
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeFalse;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.testing.AndroidBuildVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ApiModelOutlineMethodUnknownTest extends TestBase {
+
+  private static final AndroidApiLevel classApiLevel = AndroidApiLevel.M;
+
+  @Parameter public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    // TODO(b/197078995): Make this work on 12.
+    assumeFalse(
+        parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isEqualTo(Version.V12_0_0));
+    boolean willInvokeLibraryMethods =
+        parameters.isDexRuntime() && parameters.getApiLevel().isGreaterThanOrEqualTo(classApiLevel);
+    testForR8(parameters.getBackend())
+        .addLibraryClasses(LibraryClass.class)
+        .addDefaultRuntimeLibrary(parameters)
+        .addProgramClasses(Main.class)
+        .addKeepMainRule(Main.class)
+        .setMinApi(parameters.getApiLevel())
+        .addAndroidBuildVersion()
+        .apply(setMockApiLevelForClass(LibraryClass.class, classApiLevel))
+        .apply(ApiModelingTestHelper::enableOutliningOfMethods)
+        .apply(ApiModelingTestHelper::disableStubbingOfClasses)
+        .compile()
+        .inspect(
+            inspector -> {
+              // Assert that we did not outline any methods.
+              assertEquals(
+                  0,
+                  inspector.allClasses().stream()
+                      .filter(FoundClassSubject::isCompilerSynthesized)
+                      .count());
+            })
+        .applyIf(willInvokeLibraryMethods, b -> b.addBootClasspathClasses(LibraryClass.class))
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLinesIf(!willInvokeLibraryMethods, "Not calling API")
+        .assertSuccessWithOutputLinesIf(willInvokeLibraryMethods, "LibraryClass::unknownApiLevel");
+  }
+
+  // Only present from api level 23.
+  public static class LibraryClass {
+
+    public static void unknownApiLevel() {
+      System.out.println("LibraryClass::unknownApiLevel");
+    }
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      if (AndroidBuildVersion.VERSION >= 23) {
+        LibraryClass.unknownApiLevel();
+      } else {
+        System.out.println("Not calling API");
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlinePackagePrivateTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlinePackagePrivateTest.java
new file mode 100644
index 0000000..40b302d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlinePackagePrivateTest.java
@@ -0,0 +1,135 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.apimodel;
+
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.D8TestCompileResult;
+import com.android.tools.r8.SingleTestRunResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.testing.AndroidBuildVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ApiModelOutlinePackagePrivateTest extends TestBase {
+
+  private static final AndroidApiLevel classApiLevel = AndroidApiLevel.G;
+  private static final AndroidApiLevel methodApiLevel = AndroidApiLevel.G_MR1;
+
+  @Parameter public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  private boolean willInvokeLibraryMethods() {
+    return parameters.isDexRuntime()
+        && parameters.getApiLevel().isGreaterThanOrEqualTo(classApiLevel);
+  }
+
+  @Test
+  public void testD8BootClassPath() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    assumeTrue(parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0));
+    compileOnD8()
+        .addBootClasspathClasses(LibraryClass.class)
+        .run(parameters.getRuntime(), Main.class)
+        .apply(this::checkResultOnBootClassPath);
+  }
+
+  @Test
+  public void testD8RuntimeClasspath() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    compileOnD8()
+        .addRunClasspathClasses(LibraryClass.class)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLinesIf(!willInvokeLibraryMethods(), "Not calling API")
+        .assertSuccessWithOutputLinesIf(willInvokeLibraryMethods(), "LibraryClass::addedOn10");
+  }
+
+  private D8TestCompileResult compileOnD8() throws Exception {
+    return testForD8(parameters.getBackend())
+        .addLibraryClasses(LibraryClass.class)
+        .addProgramClasses(Main.class)
+        .addAndroidBuildVersion()
+        .setMinApi(parameters.getApiLevel())
+        .compile();
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    // TODO(b/197078995): Make this work on 12.
+    assumeFalse(
+        parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isEqualTo(Version.V12_0_0));
+    testForR8(parameters.getBackend())
+        .addLibraryClasses(LibraryClass.class)
+        .addDefaultRuntimeLibrary(parameters)
+        .addProgramClasses(Main.class)
+        .addKeepMainRule(Main.class)
+        .setMinApi(parameters.getApiLevel())
+        .addKeepAttributeInnerClassesAndEnclosingMethod()
+        .addAndroidBuildVersion()
+        .apply(setMockApiLevelForClass(LibraryClass.class, classApiLevel))
+        .apply(
+            setMockApiLevelForMethod(
+                LibraryClass.class.getDeclaredMethod("addedOn10"), methodApiLevel))
+        .apply(ApiModelingTestHelper::enableOutliningOfMethods)
+        .apply(ApiModelingTestHelper::disableStubbingOfClasses)
+        .compile()
+        .inspect(
+            inspector -> {
+              // Assert that we did not outline any methods.
+              assertEquals(
+                  0,
+                  inspector.allClasses().stream()
+                      .filter(FoundClassSubject::isCompilerSynthesized)
+                      .count());
+            })
+        .applyIf(willInvokeLibraryMethods(), b -> b.addBootClasspathClasses(LibraryClass.class))
+        .run(parameters.getRuntime(), Main.class)
+        .apply(this::checkResultOnBootClassPath);
+  }
+
+  private void checkResultOnBootClassPath(SingleTestRunResult<?> runResult) {
+    runResult
+        .assertSuccessWithOutputLinesIf(!willInvokeLibraryMethods(), "Not calling API")
+        // We are expecting an IllegalAccessError since LibraryClass is on bootclasspath and
+        // for Main to call the package private method it has to be loaded by the same class loader.
+        .assertFailureWithErrorThatThrowsIf(willInvokeLibraryMethods(), IllegalAccessError.class);
+  }
+
+  // Only present from api level 9.
+  public static class LibraryClass {
+
+    static void addedOn10() {
+      System.out.println("LibraryClass::addedOn10");
+    }
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      if (AndroidBuildVersion.VERSION >= 10) {
+        LibraryClass.addedOn10();
+      } else {
+        System.out.println("Not calling API");
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
index 06bfb5b..e5a169b 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
@@ -13,6 +13,7 @@
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoMethodStaticizing;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.horizontalclassmerging.ClassMerger;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -36,6 +37,7 @@
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .enableNoHorizontalClassMergingAnnotations()
+        .enableNoMethodStaticizingAnnotations()
         .setMinApi(parameters.getApiLevel())
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutputLines("changed", "0", "42", "foo", "7", "foo", "print a", "print b")
@@ -71,6 +73,7 @@
   @NoHorizontalClassMerging
   public static class Parent {
     @NeverInline
+    @NoMethodStaticizing
     public void foo() {
       System.out.println("foo");
     }
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritInterfaceWithDefaultTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritInterfaceWithDefaultTest.java
index 1f413bc..02e26dc 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritInterfaceWithDefaultTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritInterfaceWithDefaultTest.java
@@ -10,6 +10,7 @@
 
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoMethodStaticizing;
 import com.android.tools.r8.TestParameters;
 import org.junit.Test;
 
@@ -27,6 +28,7 @@
         .allowStdoutMessages()
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
+        .enableNoMethodStaticizingAnnotations()
         .setMinApi(parameters.getApiLevel())
         .addHorizontallyMergedClassesInspector(
             inspector -> inspector.assertMergedInto(B.class, A.class))
@@ -42,6 +44,7 @@
 
   public interface Interface {
     @NeverInline
+    @NoMethodStaticizing
     default void print() {
       System.out.println("print interface");
     }
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonTrivialClassInitializationAfterMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonTrivialClassInitializationAfterMergingTest.java
new file mode 100644
index 0000000..d5bbb8e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonTrivialClassInitializationAfterMergingTest.java
@@ -0,0 +1,79 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.classmerging.horizontal;
+
+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 org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class NonTrivialClassInitializationAfterMergingTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addKeepClassAndMembersRules(I.class)
+        .addHorizontallyMergedClassesInspector(
+            inspector ->
+                inspector
+                    .applyIf(
+                        !parameters.canUseDefaultAndStaticInterfaceMethods(),
+                        i -> i.assertIsCompleteMergeGroup(A.class, B.class))
+                    .assertNoOtherClassesMerged())
+        .enableNoHorizontalClassMergingAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .run(parameters.getRuntime(), Main.class)
+        .applyIf(
+            parameters.canUseDefaultAndStaticInterfaceMethods(),
+            runResult -> runResult.assertSuccessWithOutputLines("Hello world!"),
+            runResult -> runResult.assertSuccessWithOutput("Hello"));
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      new A();
+      System.out.print("Hello");
+      new B();
+    }
+  }
+
+  interface I {
+
+    Greeter greeter = new Greeter();
+
+    default void foo() {}
+  }
+
+  static class A {}
+
+  // Prints " world!" when initialized due to implementing I.
+  static class B implements I {}
+
+  @NoHorizontalClassMerging
+  static class Greeter {
+
+    static {
+      System.out.println(" world!");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTest.java b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTest.java
index 100567d..33aab91 100644
--- a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTest.java
+++ b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTest.java
@@ -6,6 +6,7 @@
 import static org.junit.Assert.assertEquals;
 
 import com.android.tools.r8.compilerapi.mockdata.MockClass;
+import com.android.tools.r8.compilerapi.mockdata.MockClassWithAssertion;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -70,6 +71,10 @@
     return MockClass.class;
   }
 
+  public Class<?> getMockClassWithAssertion() {
+    return MockClassWithAssertion.class;
+  }
+
   public Path getJava8RuntimeJar() {
     return Paths.get("third_party", "openjdk", "openjdk-rt-1.8", "rt.jar");
   }
diff --git a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
index c2fab32..24e1de1 100644
--- a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
+++ b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
@@ -8,8 +8,10 @@
 import static com.android.tools.r8.ToolHelper.isTestingR8Lib;
 
 import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.compilerapi.assertionconfiguration.AssertionConfigurationTest;
 import com.android.tools.r8.compilerapi.mapid.CustomMapIdTest;
 import com.android.tools.r8.compilerapi.mockdata.MockClass;
+import com.android.tools.r8.compilerapi.mockdata.MockClassWithAssertion;
 import com.android.tools.r8.compilerapi.sourcefile.CustomSourceFileTest;
 import com.android.tools.r8.compilerapi.testsetup.ApiTestingSetUpTest;
 import com.google.common.collect.ImmutableList;
@@ -26,10 +28,13 @@
       Paths.get(ToolHelper.THIRD_PARTY_DIR, "binary_compatibility_tests", DIRNAME, "tests.jar");
 
   private static final List<Class<? extends CompilerApiTest>> CLASSES_FOR_BINARY_COMPATIBILITY =
-      ImmutableList.of(ApiTestingSetUpTest.ApiTest.class);
+      ImmutableList.of(
+          ApiTestingSetUpTest.ApiTest.class,
+          CustomMapIdTest.ApiTest.class,
+          CustomSourceFileTest.ApiTest.class);
 
   private static final List<Class<? extends CompilerApiTest>> CLASSES_PENDING_BINARY_COMPATIBILITY =
-      ImmutableList.of(CustomMapIdTest.ApiTest.class, CustomSourceFileTest.ApiTest.class);
+      ImmutableList.of(AssertionConfigurationTest.ApiTest.class);
 
   private final TemporaryFolder temp;
 
@@ -54,7 +59,7 @@
 
   @Override
   public List<Class<?>> getAdditionalClassesForTests() {
-    return ImmutableList.of(CompilerApiTest.class, MockClass.class);
+    return ImmutableList.of(CompilerApiTest.class, MockClass.class, MockClassWithAssertion.class);
   }
 
   @Override
diff --git a/src/test/java/com/android/tools/r8/compilerapi/assertionconfiguration/AssertionConfigurationTest.java b/src/test/java/com/android/tools/r8/compilerapi/assertionconfiguration/AssertionConfigurationTest.java
new file mode 100644
index 0000000..aa85988
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/compilerapi/assertionconfiguration/AssertionConfigurationTest.java
@@ -0,0 +1,138 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.compilerapi.assertionconfiguration;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.AssertionsConfiguration;
+import com.android.tools.r8.D8;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.ProgramConsumer;
+import com.android.tools.r8.R8;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.compilerapi.CompilerApiTest;
+import com.android.tools.r8.compilerapi.CompilerApiTestRunner;
+import com.android.tools.r8.compilerapi.mockdata.MockClassWithAssertion;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.ThrowingBiConsumer;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.InvokeInstructionSubject;
+import java.nio.file.Path;
+import org.junit.Test;
+
+public class AssertionConfigurationTest extends CompilerApiTestRunner {
+
+  static MethodReference assertionHandler =
+      Reference.methodFromDescriptor(
+          "Lcom/example/SomeClass;", "assertionHandler", "(Ljava/lang/AssertionError;)V");
+
+  public AssertionConfigurationTest(TestParameters parameters) {
+    super(parameters);
+  }
+
+  @Override
+  public Class<? extends CompilerApiTest> binaryTestClass() {
+    return ApiTest.class;
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    ApiTest test = new ApiTest(ApiTest.PARAMETERS);
+    assertEquals(assertionHandler, ApiTest.assertionHandler);
+    runTest(test::runD8);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    ApiTest test = new ApiTest(ApiTest.PARAMETERS);
+    runTest(test::runR8);
+  }
+
+  private boolean invokesAssertionHandler(InstructionSubject instruction) {
+    return instruction.isInvokeStatic()
+        && ((InvokeInstructionSubject) instruction)
+            .invokedMethod()
+            .asMethodReference()
+            .equals(assertionHandler);
+  }
+
+  private void runTest(ThrowingBiConsumer<ProgramConsumer, MethodReference, Exception> test)
+      throws Exception {
+    Path output = temp.newFolder().toPath().resolve("out.jar");
+    test.accept(new DexIndexedConsumer.ArchiveConsumer(output), assertionHandler);
+
+    // TODO(b/209445989): This should be true when the assertion handler support is implemented.
+    assertFalse(
+        new CodeInspector(output)
+            .clazz(MockClassWithAssertion.class)
+            .uniqueMethodWithName("main")
+            .streamInstructions()
+            .anyMatch(this::invokesAssertionHandler));
+  }
+
+  public static class ApiTest extends CompilerApiTest {
+
+    static MethodReference assertionHandler =
+        Reference.methodFromDescriptor(
+            "Lcom/example/SomeClass;", "assertionHandler", "(Ljava/lang/AssertionError;)V");
+
+    public ApiTest(Object parameters) {
+      super(parameters);
+    }
+
+    AssertionsConfiguration buildWithAssertionHandler(AssertionsConfiguration.Builder builder) {
+      AssertionsConfiguration configuration =
+          builder.setAssertionHandler(assertionHandler).setScopeAll().build();
+      assertFalse(configuration.isCompileTimeEnabled());
+      assertFalse(configuration.isCompileTimeDisabled());
+      assertFalse(configuration.isPassthrough());
+      assertTrue(configuration.isAssertionHandler());
+      assertSame(assertionHandler, configuration.getAssertionHandler());
+      return configuration;
+    }
+
+    public void runD8(ProgramConsumer programConsumer, MethodReference assertionHandler)
+        throws Exception {
+      D8.run(
+          D8Command.builder()
+              .addClassProgramData(getBytesForClass(getMockClassWithAssertion()), Origin.unknown())
+              .addLibraryFiles(getJava8RuntimeJar())
+              .addAssertionsConfiguration(this::buildWithAssertionHandler)
+              .setProgramConsumer(programConsumer)
+              .build());
+    }
+
+    public void runR8(ProgramConsumer programConsumer, MethodReference assertionHandler)
+        throws Exception {
+      R8.run(
+          R8Command.builder()
+              .addClassProgramData(getBytesForClass(getMockClassWithAssertion()), Origin.unknown())
+              .addProguardConfiguration(
+                  getKeepMainRules(getMockClassWithAssertion()), Origin.unknown())
+              .addLibraryFiles(getJava8RuntimeJar())
+              .addAssertionsConfiguration(
+                  builder -> builder.setAssertionHandler(assertionHandler).setScopeAll().build())
+              .setProgramConsumer(programConsumer)
+              .build());
+    }
+
+    @Test
+    public void testD8() throws Exception {
+      runD8(DexIndexedConsumer.emptyConsumer(), assertionHandler);
+    }
+
+    @Test
+    public void testR8() throws Exception {
+      runR8(DexIndexedConsumer.emptyConsumer(), assertionHandler);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/compilerapi/mockdata/MockClassWithAssertion.java b/src/test/java/com/android/tools/r8/compilerapi/mockdata/MockClassWithAssertion.java
new file mode 100644
index 0000000..df42fbd
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/compilerapi/mockdata/MockClassWithAssertion.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.compilerapi.mockdata;
+
+// Class to use as data for the compilation.
+public class MockClassWithAssertion {
+
+  public static void main(String[] args) {
+    assert false;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/Regress216178582Test.java b/src/test/java/com/android/tools/r8/debuginfo/Regress216178582Test.java
new file mode 100644
index 0000000..3f384d4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/Regress216178582Test.java
@@ -0,0 +1,105 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.debuginfo;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexDebugEntry;
+import com.android.tools.r8.graph.DexDebugEntryBuilder;
+import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.utils.StringUtils;
+import java.nio.file.Path;
+import java.util.Iterator;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class Regress216178582Test extends TestBase {
+
+  static final String EXPECTED = StringUtils.lines("Hello world!");
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+  }
+
+  public Regress216178582Test(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    Path out =
+        testForR8(parameters.getBackend())
+            .addInnerClasses(Regress216178582Test.class)
+            .setMinApi(parameters.getApiLevel())
+            .addKeepMainRule(TestClass.class)
+            .addKeepAttributeLineNumberTable()
+            .compile()
+            .inspect(
+                inspector -> {
+                  DexEncodedMethod method =
+                      inspector.clazz(TestClass.class).mainMethod().getMethod();
+                  DexCode code = method.getCode().asDexCode();
+                  if (parameters
+                      .getApiLevel()
+                      .isGreaterThanOrEqualTo(apiLevelWithPcAsLineNumberSupport())) {
+                    assertNull(code.getDebugInfo());
+                    return;
+                  }
+                  assertTrue(code.getDebugInfo().isPcBasedInfo());
+                  // Force convert the PC info to events.
+                  code.setDebugInfo(DexDebugInfo.convertToEventBased(code, inspector.getFactory()));
+                  List<DexDebugEntry> entries =
+                      new DexDebugEntryBuilder(method, inspector.getFactory()).build();
+                  Iterator<DexDebugEntry> it = entries.iterator();
+                  int pc = 0;
+                  for (Instruction instruction : code.instructions) {
+                    if (instruction.canThrow()) {
+                      DexDebugEntry next = it.next();
+                      assertEquals(
+                          "Invalid entry "
+                              + next
+                              + " at pc "
+                              + StringUtils.hexString(pc, 2)
+                              + ", in:\n"
+                              + method.codeToString(),
+                          pc,
+                          next.address);
+                    }
+                    pc += instruction.getSize();
+                  }
+                })
+            .writeToZip();
+
+    testForD8(parameters.getBackend())
+        .addProgramFiles(out)
+        .setMinApi(parameters.getApiLevel())
+        .addOptionsModification(o -> o.testing.forceJumboStringProcessing = true)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED);
+  }
+
+  static class TestClass {
+
+    // Use synchronized which will require prolog and epilog of monitor instructions.
+    // These have small sizes and debug event entries which hit a PC between instructions showing
+    // that the event stream is built incorrectly when processing jumbo strings.
+    public static synchronized void main(String[] args) {
+      System.out.println("Hello world!");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/Regress148461139.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/Regress148461139.java
index d2d8c86..f92b58e 100644
--- a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/Regress148461139.java
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/Regress148461139.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.desugaring.interfacemethods;
 
-import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.TestBase;
@@ -36,9 +35,6 @@
         .addProgramClasses(Condition.class)
         .noTreeShaking()
         .compile()
-        .inspectProguardConfiguration(p -> {
-          assertNotNull(p.getKeepAllRule());
-        })
         .inspect(i -> assertTrue(i.clazz(Condition.class).isAbstract()));
   }
 
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/ArgumentInfoCollectionTest.java b/src/test/java/com/android/tools/r8/enumunboxing/ArgumentInfoCollectionTest.java
index b0be3f7..2b5ccbd 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/ArgumentInfoCollectionTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/ArgumentInfoCollectionTest.java
@@ -22,13 +22,33 @@
   public void testCombineRewritten() {
     DexItemFactory factory = new DexItemFactory();
     ArgumentInfoCollection.Builder builder1 = ArgumentInfoCollection.builder();
-    builder1.addArgumentInfo(1, new RewrittenTypeInfo(factory.intType, factory.longType));
-    builder1.addArgumentInfo(3, new RewrittenTypeInfo(factory.intType, factory.longType));
+    builder1.addArgumentInfo(
+        1,
+        RewrittenTypeInfo.builder()
+            .setOldType(factory.intType)
+            .setNewType(factory.longType)
+            .build());
+    builder1.addArgumentInfo(
+        3,
+        RewrittenTypeInfo.builder()
+            .setOldType(factory.intType)
+            .setNewType(factory.longType)
+            .build());
     ArgumentInfoCollection arguments1 = builder1.build();
 
     ArgumentInfoCollection.Builder builder2 = ArgumentInfoCollection.builder();
-    builder2.addArgumentInfo(2, new RewrittenTypeInfo(factory.floatType, factory.doubleType));
-    builder2.addArgumentInfo(4, new RewrittenTypeInfo(factory.floatType, factory.doubleType));
+    builder2.addArgumentInfo(
+        2,
+        RewrittenTypeInfo.builder()
+            .setOldType(factory.floatType)
+            .setNewType(factory.doubleType)
+            .build());
+    builder2.addArgumentInfo(
+        4,
+        RewrittenTypeInfo.builder()
+            .setOldType(factory.floatType)
+            .setNewType(factory.doubleType)
+            .build());
     ArgumentInfoCollection arguments2 = builder2.build();
 
     ArgumentInfoCollection combine = arguments1.combine(arguments2);
@@ -112,8 +132,18 @@
     ArgumentInfoCollection arguments1 = builder1.build();
 
     ArgumentInfoCollection.Builder builder2 = ArgumentInfoCollection.builder();
-    builder2.addArgumentInfo(1, new RewrittenTypeInfo(factory.floatType, factory.doubleType));
-    builder2.addArgumentInfo(2, new RewrittenTypeInfo(factory.floatType, factory.doubleType));
+    builder2.addArgumentInfo(
+        1,
+        RewrittenTypeInfo.builder()
+            .setOldType(factory.floatType)
+            .setNewType(factory.doubleType)
+            .build());
+    builder2.addArgumentInfo(
+        2,
+        RewrittenTypeInfo.builder()
+            .setOldType(factory.floatType)
+            .setNewType(factory.doubleType)
+            .build());
     ArgumentInfoCollection arguments2 = builder2.build();
 
     ArgumentInfoCollection combine = arguments1.combine(arguments2);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/extrasubclasses/AbstractClassAlsoImplementedByMissingClassTest.java b/src/test/java/com/android/tools/r8/ir/optimize/extrasubclasses/AbstractClassAlsoImplementedByMissingClassTest.java
index 65cc34d..b67037d 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/extrasubclasses/AbstractClassAlsoImplementedByMissingClassTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/extrasubclasses/AbstractClassAlsoImplementedByMissingClassTest.java
@@ -72,10 +72,12 @@
     ClassSubject bClassSubject = inspector.clazz(B.class);
     assertThat(bClassSubject.uniqueMethodWithName("kept"), isPresent());
 
-    // A.notKept() and B.notKept() should not be present, because the only invoke instruction
-    // targeting A.notKept() should have been inlined.
+    // A.notKept() should not be present, because the only invoke instruction targeting A.notKept()
+    // should have been inlined.
     assertThat(aClassSubject.uniqueMethodWithName("notKept"), not(isPresent()));
-    assertThat(bClassSubject.uniqueMethodWithName("notKept"), not(isPresent()));
+    // B.notKept() is present because the caller has an invoke to an unknown method giving it api
+    // level unknown.
+    assertThat(bClassSubject.uniqueMethodWithName("notKept"), isPresent());
   }
 
   static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/extrasubclasses/InterfaceAlsoImplementedByMissingClassTest.java b/src/test/java/com/android/tools/r8/ir/optimize/extrasubclasses/InterfaceAlsoImplementedByMissingClassTest.java
index 89586d8..20adeda 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/extrasubclasses/InterfaceAlsoImplementedByMissingClassTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/extrasubclasses/InterfaceAlsoImplementedByMissingClassTest.java
@@ -70,10 +70,12 @@
     ClassSubject aClassSubject = inspector.clazz(A.class);
     assertThat(aClassSubject.uniqueMethodWithName("kept"), isPresent());
 
-    // I.notKept() and A.notKept() should not be present, because the only invoke instruction
-    // targeting I.notKept() should have been inlined.
+    // I.notKept() should not be present, because the only invoke instruction targeting I.notKept()
+    // should have been inlined.
     assertThat(iClassSubject.uniqueMethodWithName("notKept"), not(isPresent()));
-    assertThat(aClassSubject.uniqueMethodWithName("notKept"), not(isPresent()));
+    // A.notKept() is present because the caller has an invoke to an unknown method giving it api
+    // level unknown.
+    assertThat(aClassSubject.uniqueMethodWithName("notKept"), isPresent());
   }
 
   static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldReadForWriteTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldReadForWriteTest.java
index 0c4e336..c0d3be4 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldReadForWriteTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldReadForWriteTest.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8.ir.optimize.membervaluepropagation;
 
 import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import com.android.tools.r8.NoHorizontalClassMerging;
@@ -13,7 +12,6 @@
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ir.optimize.membervaluepropagation.FieldReadForWriteTest.R.anim;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -42,16 +40,7 @@
         .enableNoHorizontalClassMergingAnnotations()
         .setMinApi(parameters.getApiLevel())
         .compile()
-        .inspect(
-            inspector -> {
-              ClassSubject animClassSubject = inspector.clazz(anim.class);
-              if (parameters.isCfRuntime()) {
-                assertThat(animClassSubject, isPresent());
-                assertThat(animClassSubject.uniqueFieldWithName("abc_fade_in"), isPresent());
-              } else {
-                assertThat(animClassSubject, isAbsent());
-              }
-            });
+        .inspect(inspector -> assertThat(inspector.clazz(anim.class), isAbsent()));
   }
 
   static class Main {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/redundantarraygetelimination/RedundantArrayGetEliminationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/redundantarraygetelimination/RedundantArrayGetEliminationTest.java
new file mode 100644
index 0000000..b5c1de5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/redundantarraygetelimination/RedundantArrayGetEliminationTest.java
@@ -0,0 +1,130 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.redundantarraygetelimination;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+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 org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class RedundantArrayGetEliminationTest extends TestBase {
+
+  private static final String EXPECTED_OUTPUT = StringUtils.lines("0", "0", "0", "42", "84");
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    testForD8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .release()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(this::inspect)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepClassAndMembersRules(Main.class)
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(this::inspect)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  private void inspect(CodeInspector inspector) {
+    ClassSubject mainClassSubject = inspector.clazz(Main.class);
+    assertArrayGetCountEquals(1, mainClassSubject.uniqueMethodWithName("testRedundantArrayGet"));
+    assertArrayGetCountEquals(
+        1, mainClassSubject.uniqueMethodWithName("testRedundantArrayGetAfterPutToUnrelatedArray"));
+    assertArrayGetCountEquals(
+        1, mainClassSubject.uniqueMethodWithName("testRedundantArrayGetAfterPutToUnrelatedIndex"));
+    assertArrayGetCountEquals(
+        2, mainClassSubject.uniqueMethodWithName("testNecessaryArrayGetAfterAliasedArrayPut"));
+    assertArrayGetCountEquals(
+        2, mainClassSubject.uniqueMethodWithName("testNecessaryArrayGetAfterExternalSideEffect"));
+  }
+
+  static void assertArrayGetCountEquals(int expected, MethodSubject methodSubject) {
+    assertEquals(
+        expected,
+        methodSubject.streamInstructions().filter(InstructionSubject::isArrayGet).count());
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      int[] ints = new int[] {0, 1};
+      long[] longs = new long[] {0};
+      testRedundantArrayGet(ints, 0);
+      testRedundantArrayGetAfterPutToUnrelatedArray(ints, longs, 0);
+      testRedundantArrayGetAfterPutToUnrelatedIndex(ints, ints);
+      testNecessaryArrayGetAfterAliasedArrayPut(ints, ints, 0, 0);
+      testNecessaryArrayGetAfterExternalSideEffect(ints, 0, () -> ints[0] = 42);
+    }
+
+    static void testRedundantArrayGet(int[] array, int index) {
+      int first = array[index];
+      int second = array[index]; // Redundant.
+      System.out.println(first + second);
+    }
+
+    static void testRedundantArrayGetAfterPutToUnrelatedArray(
+        int[] array, long[] otherArray, int index) {
+      int first = array[index];
+      otherArray[index] = 42;
+      int second = array[index]; // Redundant.
+      System.out.println(first + second);
+    }
+
+    static void testRedundantArrayGetAfterPutToUnrelatedIndex(int[] array, int[] sameArray) {
+      int first = array[0];
+      sameArray[1] = 42;
+      int second = array[0]; // Redundant.
+      System.out.println(first + second);
+    }
+
+    static void testNecessaryArrayGetAfterAliasedArrayPut(
+        int[] array, int[] sameArray, int index, int sameIndex) {
+      int first = array[index];
+      sameArray[sameIndex] = 42;
+      int second = array[index]; // Not redundant.
+      System.out.println(first + second);
+    }
+
+    static void testNecessaryArrayGetAfterExternalSideEffect(
+        int[] array, int index, Runnable runnable) {
+      int first = array[index];
+      runnable.run();
+      int second = array[index]; // Not redundant.
+      System.out.println(first + second);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/InvokeMethodWithNonNullParamCheckTest.java b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/InvokeMethodWithNonNullParamCheckTest.java
index 35b475b..c12a128 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/InvokeMethodWithNonNullParamCheckTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/InvokeMethodWithNonNullParamCheckTest.java
@@ -9,6 +9,7 @@
 import static org.junit.Assert.assertEquals;
 
 import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoMethodStaticizing;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
@@ -68,13 +69,19 @@
             "Caught NullPointerException from testRewriteInvokeVirtualToThrowNull"
                 + "WithDeadCatchHandler");
 
-    testForJvm().addTestClasspath().run(TestClass.class).assertSuccessWithOutput(expected);
+    if (parameters.isCfRuntime()) {
+      testForJvm()
+          .addTestClasspath()
+          .run(parameters.getRuntime(), TestClass.class)
+          .assertSuccessWithOutput(expected);
+    }
 
     CodeInspector inspector =
         testForR8(parameters.getBackend())
             .addInnerClasses(InvokeMethodWithNonNullParamCheckTest.class)
             .addKeepMainRule(TestClass.class)
             .enableInliningAnnotations()
+            .enableNoMethodStaticizingAnnotations()
             .addOptionsModification(
                 options -> {
                   // Avoid that the class inliner inlines testRewriteInvokeVirtualToThrowNullWith-
@@ -371,6 +378,7 @@
   static class Virtual {
 
     @NeverInline
+    @NoMethodStaticizing
     public String throwIfFirstIsNull(Object first) {
       if (first == null) {
         throw new NullPointerException();
@@ -379,6 +387,7 @@
     }
 
     @NeverInline
+    @NoMethodStaticizing
     public String throwIfSecondIsNull(Object first, Object second, Object third) {
       if (second == null) {
         throw new NullPointerException();
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
index 7e12a9f..ad4f8a2 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
@@ -5,6 +5,7 @@
 
 import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_5_0;
 import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.MIN_SUPPORTED_VERSION;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionProperty;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
@@ -248,11 +249,14 @@
     KmPropertySubject familyName = kmClass.kmPropertyWithUniqueName("familyName");
     assertThat(familyName, not(isPresent()));
 
+    FieldSubject ageField = person.uniqueFieldWithName("age");
+    assertThat(ageField, isAbsent());
+
     KmPropertySubject age = kmClass.kmPropertyWithUniqueName("age");
     assertThat(age, isPresent());
     assertThat(age, not(isExtensionProperty()));
-    assertEquals(age.fieldSignature().asString(), "a:I");
-    assertEquals(age.getterSignature().asString(), "getAge()I");
-    assertEquals(age.setterSignature().asString(), "setAge(I)V");
+    assertEquals("age:I", age.fieldSignature().asString());
+    assertEquals("getAge()I", age.getterSignature().asString());
+    assertEquals("setAge(I)V", age.setterSignature().asString());
   }
 }
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterDevirtualizationTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterDevirtualizationTest.java
index 99554bf..5b13f6b 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterDevirtualizationTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterDevirtualizationTest.java
@@ -98,7 +98,7 @@
   public void runOnJvm() throws Throwable {
     Assume.assumeTrue(parameters.isCfRuntime());
     testForJvm()
-        .addProgramClasses(CLASSPATH_CLASSES)
+        .addClasspathClasses(CLASSPATH_CLASSES)
         .addProgramClasses(PROGRAM_CLASSES)
         .run(parameters.getRuntime(), ProgramClass.class)
         .assertSuccessWithOutput(EXPECTED_OUTPUT);
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingMissingInterfaceTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingMissingInterfaceTest.java
new file mode 100644
index 0000000..3eb80ae
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingMissingInterfaceTest.java
@@ -0,0 +1,86 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.naming.applymapping;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ApplyMappingMissingInterfaceTest extends TestBase {
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    R8TestCompileResult libraryCompileResult =
+        testForR8(parameters.getBackend())
+            .addLibraryClasses(LibraryI.class)
+            .addDefaultRuntimeLibrary(parameters)
+            .addProgramClasses(ClassPathI.class)
+            .setMinApi(parameters.getApiLevel())
+            .addKeepClassAndMembersRulesWithAllowObfuscation(ClassPathI.class)
+            .compile();
+
+    testForR8(parameters.getBackend())
+        .addClasspathClasses(ClassPathI.class)
+        .addProgramClasses(ProgramInterface.class, Main.class)
+        .setMinApi(parameters.getApiLevel())
+        .addApplyMapping(libraryCompileResult.getProguardMap())
+        .addKeepMainRule(Main.class)
+        .enableInliningAnnotations()
+        .addDontWarn(LibraryI.class)
+        .compile()
+        .addRunClasspathFiles(libraryCompileResult.writeToZip())
+        .addRunClasspathClasses(LibraryI.class)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("Main::foo", "Main::bar");
+  }
+
+  /* Will be missing on compile time */
+  public interface LibraryI {}
+
+  public interface ClassPathI extends LibraryI {
+
+    void foo();
+  }
+
+  public interface ProgramInterface extends ClassPathI {
+
+    void bar();
+  }
+
+  public static class Main implements ProgramInterface {
+
+    public static void main(String[] args) {
+      new Main().foo();
+      new Main().bar();
+    }
+
+    @Override
+    @NeverInline
+    public void foo() {
+      System.out.println("Main::foo");
+    }
+
+    @Override
+    @NeverInline
+    public void bar() {
+      System.out.println("Main::bar");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/ApplyMappingTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/ApplyMappingTest.java
index 554ea5c..01de767 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/ApplyMappingTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/ApplyMappingTest.java
@@ -199,7 +199,8 @@
   private R8Command.Builder getCommandForInstrumentation(
       Path out, Path flag, Path mainApp, Path instrApp) throws IOException {
     return R8Command.builder()
-        .addLibraryFiles(runtimeJar(backend), mainApp)
+        .addLibraryFiles(runtimeJar(backend))
+        .addClasspathFiles(mainApp)
         .addProgramFiles(instrApp)
         .setOutput(out, outputMode(backend))
         .addProguardConfigurationFiles(flag);
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckInlinedTest.java b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckInlinedTest.java
index 6b7b2d0..99531f1 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckInlinedTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckInlinedTest.java
@@ -4,7 +4,6 @@
 package com.android.tools.r8.naming.retrace;
 
 import static com.android.tools.r8.naming.retrace.StackTrace.isSame;
-import static com.android.tools.r8.naming.retrace.StackTrace.isSameExceptForLineNumbers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -13,12 +12,10 @@
 import com.android.tools.r8.NoMethodStaticizing;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine;
 import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import java.util.List;
-import java.util.Objects;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -70,31 +67,9 @@
             (stackTrace, inspector) -> {
               ClassSubject callerClass = inspector.clazz(Caller.class);
               assertThat(callerClass, isPresent());
-              MethodSubject staticized = callerClass.uniqueMethodWithName("outerCaller");
-              assertThat(staticized, isPresentAndRenamed());
-              // TODO(b/214377135): The stack traces should be the same (when 206427323) is
-              //  resolved.
-              if (throwReceiverNpe && canUseJavaUtilObjectsRequireNonNull(parameters)) {
-                StackTrace requireNonNullFrame =
-                    StackTrace.builder().add(stackTrace.get(0)).build();
-                assertThat(
-                    requireNonNullFrame,
-                    isSameExceptForLineNumbers(
-                        StackTrace.builder()
-                            .add(
-                                StackTraceLine.builder()
-                                    .setClassName(Objects.class.getTypeName())
-                                    .setMethodName("requireNonNull")
-                                    .setFileName("Objects.java")
-                                    .build())
-                            .build()));
-
-                StackTrace stackTraceWithoutRequireNonNull =
-                    StackTrace.builder().add(stackTrace).remove(0).build();
-                assertThat(stackTraceWithoutRequireNonNull, isSame(expectedStackTrace));
-              } else {
-                assertThat(stackTrace, isSame(expectedStackTrace));
-              }
+              MethodSubject outerCaller = callerClass.uniqueMethodWithName("outerCaller");
+              assertThat(outerCaller, isPresentAndRenamed());
+              assertThat(stackTrace, isSame(expectedStackTrace));
             });
   }
 
@@ -119,7 +94,7 @@
 
     static void caller(Foo f) {
       Object inlinable = f.inlinable();
-      System.out.println(inlinable);
+      System.out.println(inlinable == null ? "null" : "some");
     }
 
     @NeverInline
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckSequenceTest.java b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckSequenceTest.java
index bf8856b..be348ec 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckSequenceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckSequenceTest.java
@@ -8,7 +8,6 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.SingleTestRunResult;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine;
@@ -37,17 +36,12 @@
   @Before
   public void setup() throws Exception {
     // Get the expected stack trace by running on the JVM.
-    SingleTestRunResult<?> runResult =
+    expectedStackTrace =
         testForRuntime(parameters)
             .addProgramClasses(Caller.class, Foo.class)
             .run(parameters.getRuntime(), Caller.class)
-            .assertFailureWithErrorThatThrows(NullPointerException.class);
-    if (parameters.isCfRuntime()) {
-      expectedStackTrace = runResult.map(StackTrace::extractFromJvm);
-    } else {
-      expectedStackTrace =
-          StackTrace.extractFromArt(runResult.getStdErr(), parameters.asDexRuntime().getVm());
-    }
+            .assertFailureWithErrorThatThrows(NullPointerException.class)
+            .getStackTrace();
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/CompanionConstructorShakingTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/CompanionConstructorShakingTest.java
index 828a4b5..a728f8c 100644
--- a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/CompanionConstructorShakingTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/CompanionConstructorShakingTest.java
@@ -4,9 +4,9 @@
 
 package com.android.tools.r8.optimize.argumentpropagation;
 
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isStatic;
-import static com.android.tools.r8.utils.codeinspector.Matchers.onlyIf;
 import static junit.framework.TestCase.assertEquals;
 import static org.hamcrest.MatcherAssert.assertThat;
 
@@ -16,7 +16,6 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import org.junit.Test;
@@ -48,19 +47,16 @@
         .compile()
         .inspect(
             inspector -> {
-              boolean isCf = parameters.isCfRuntime();
-
               ClassSubject hostClassSubject = inspector.clazz(Host.class);
               assertThat(hostClassSubject, isPresent());
-              assertEquals(1 + BooleanUtils.intValue(isCf), hostClassSubject.allMethods().size());
-              assertThat(hostClassSubject.clinit(), onlyIf(isCf, isPresent()));
+              assertEquals(1, hostClassSubject.allMethods().size());
+              assertThat(hostClassSubject.clinit(), isAbsent());
               assertThat(hostClassSubject.uniqueMethodWithName("keepHost"), isPresent());
 
               ClassSubject companionClassSubject = inspector.clazz(Host.Companion.class);
               assertThat(companionClassSubject, isPresent());
-              assertEquals(
-                  1 + BooleanUtils.intValue(isCf), companionClassSubject.allMethods().size());
-              assertThat(companionClassSubject.init(), onlyIf(isCf, isPresent()));
+              assertEquals(1, companionClassSubject.allMethods().size());
+              assertThat(companionClassSubject.init(), isAbsent());
 
               MethodSubject greetMethodSubject =
                   companionClassSubject.uniqueMethodWithName("greet");
diff --git a/src/test/java/com/android/tools/r8/resolution/InvokeSuperChainTest.java b/src/test/java/com/android/tools/r8/resolution/InvokeSuperChainTest.java
index 3bd714f..ab23d56 100644
--- a/src/test/java/com/android/tools/r8/resolution/InvokeSuperChainTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/InvokeSuperChainTest.java
@@ -30,6 +30,9 @@
   public void testRuntime() throws Exception {
     // This tests shows that invoke super behaves differently on V5_1_1 and V6_0_1 based on the
     // static receiver on invoke-super, skipping the direct resolution target.
+    // https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12.3 specify that
+    // the static target of an invoke-super should always be an immediate super type and the
+    // generated byte code is therefore invalid according to the spec.
     boolean hasIncorrectSuperInvokeSemantics =
         parameters.isDexRuntimeVersion(Version.V5_1_1)
             || parameters.isDexRuntimeVersion(Version.V6_0_1);
diff --git a/src/test/java/com/android/tools/r8/retrace/StackTraceRegularExpressionParserTests.java b/src/test/java/com/android/tools/r8/retrace/StackTraceRegularExpressionParserTests.java
index b769752..7299dfb 100644
--- a/src/test/java/com/android/tools/r8/retrace/StackTraceRegularExpressionParserTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/StackTraceRegularExpressionParserTests.java
@@ -944,6 +944,108 @@
         });
   }
 
+  /* This is a regression test for b/216359244 */
+  @Test
+  public void testMissingSourceFile() {
+    runRetraceTest(
+        ".*FOO\\s+:\\s+\\[\\d+\\]\\s+%c\\.%m\\(%l\\):.*",
+        new StackTraceForTest() {
+          @Override
+          public List<String> obfuscatedStackTrace() {
+            return ImmutableList.of(
+                "12-24 19:53:19.052 10197 30302 30302 I FOO : [2] huk.g(1): getDownloads()");
+          }
+
+          @Override
+          public String mapping() {
+            return "foo.Bar -> huk:\n" + "  void baz():13:13 -> g\n" + "  void qux():12:12 -> g\n";
+          }
+
+          @Override
+          public List<String> retracedStackTrace() {
+            return ImmutableList.of(
+                "12-24 19:53:19.052 10197 30302 30302 I FOO : [2] foo.Bar.baz(13): getDownloads()",
+                "<OR> 12-24 19:53:19.052 10197 30302 30302 I FOO : [2] foo.Bar.qux(12):"
+                    + " getDownloads()");
+          }
+
+          @Override
+          public List<String> retraceVerboseStackTrace() {
+            return ImmutableList.of(
+                "12-24 19:53:19.052 10197 30302 30302 I FOO : [2] foo.Bar.void baz()(13):"
+                    + " getDownloads()",
+                "<OR> 12-24 19:53:19.052 10197 30302 30302 I FOO : [2] foo.Bar.void qux()(12):"
+                    + " getDownloads()");
+          }
+
+          @Override
+          public int expectedWarnings() {
+            return 0;
+          }
+        });
+  }
+
+  @Test
+  public void testBigLine() {
+    runRetraceTest(
+        RetraceOptions.defaultRegularExpression(),
+        new StackTraceForTest() {
+
+          private final String LINE =
+              "12-16 19:29:11.111 media 453 490 D WVCdm : [for_bar.cpp(334):AddKey] key_data ="
+                  + " (1401)"
+                  + " CAISvgYKmgIKIEIzMkU1QkU3RjlGMThCNzYzQjAwMDAwMDAwMDAwMDAwEuMBeyJ2ZXJzaW9uIjoiM"
+                  + "S4wIiwiZXNuIjoiTkZBTkRST0lEMi1QUlYtQk9SRUFMLUdPT0dMQ0hST01FQ0FTVD1IRC0yMTc5My0"
+                  + "1Nzk0RDMyRTQxRUI5QTc2QzZFQzIyNTdEQzVGNTQ2MTNDNzdBQTdCNzAwNzcyMkREQjVCRDYzRUFER"
+                  + "TZBRUUyIiwic2FsdCI6IjQ5OTI3MDU0ODMyNzI0NjIxMDc0MDM4Nzg3MDkxMjAyNCIsImlzc3VlVGl"
+                  + "tZSI6MTYzOTY2MzE1MDAwMCwibW92aWVJZCI6IjgxMTg0NDE3In0gASgAOMDRAkDA0QJIroztjQYSF"
+                  + "AgBEAAYACDA0QIowNECWABgAXgBGmYSEBnHXFI_ttc_1lja-bpjqHkaUN_81aKusLLERz_xX0vPIR1"
+                  + "I8yu9u9zQndpy1aEFJYtQiB0sed6wAC3c6aeH4oLsGFPiuVgGweP1MGc3yxunqusDpoGe03JGD1VSE"
+                  + "9aB0jSKIAEahgEKEAAAAAAGmcHCAAAAAAAAAAASEN4b9_2XH75TnxdBkpJ4YhoaIOdhQxec7DpUs0D"
+                  + "ixSQXTlvjbXqX3PIOg9DiQLCa4vbzIAIoBToECAEQKkI0CiDuiCOCIxvTlCqkYTKBWzMOfeh9BpKc5"
+                  + "RDdE2omMbOfaRIQRWVqtbR8Qg4JEY0u0CAFRBqGAQoQAAAAAAaZwcMAAAAAAAAAABIQ7iP9449yr2F"
+                  + "_97v4QHerRxognKGBtw2cwGpxKltxU7A_1asVTS0aPaw8dr5e2rP2SW4gAigFOgQIARAqQjQKIB-50"
+                  + "Ou_RGn8rzRDr76FeWpBLYSt99zHVSZK0xnd_anREhC4eACukLOUwL5TQ8w1oOAVGoYBChAAAAAABpn"
+                  + "BwQAAAAAAAAAAEhDsGE2bR6VgRM85PY3YU9qIGiAhD3h7I8Cv5Irr315yecWo0YA1t9_sr0g2z0zsa"
+                  + "6Dq0yACKAE6BAgBECpCNAogQoIDcKUGMhCA-HetsARASXxXJUazkIIaSwCKE9asAEwSEMW4TIMZZkc"
+                  + "r3LYIITelMlIgroztjQY4ABogzw-SGqQ1AYy8EopI2zSvXX_hkFpOoWxCvFQuQEj5oQwigAIRzNhvB"
+                  + "9-YMOy2Muentb574WJKnVqXfEor5mFDIQ3vDELdjbkibaS4THyK2DlUQyp0M3C8n5bQ1JApslsfh5w"
+                  + "SEMvA8Y0sXE9H6xnAKnMJTioZJOTElqPrbbRM5APhI3ohL8rp7u8ydBm9aWqkprDngrU3b1KdHd6J9"
+                  + "YiJRJ4Y55M4qqCdwbCqr57xYAu_IIH_p8erfnhfNgUE2svtZBiLMP37vOMSQAP08_zkMf87VRHBxuI"
+                  + "2rlnGcdgLoJyPgLp2ZBbdLw-b4Nq6uIZQfNABQ1SuM3OZ6uD6eq-NH9FawWvN0EmzWZ88DGiHtgM7h"
+                  + "zqvIb8JNmLInt28us-vw6KQOggKBjE2LjUuMEABSoACAAAAAgAAAQAABAAQZrvQTwAAABMAAAE3AAA"
+                  + "AEAAAAUkAAABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAKjAA"
+                  + "AAAAAAAqMAAAAAAAAAAAAAAAAMAAAGgAAAAEAAAAbIAAAAQAAABxAAAABAAAAIUAAAAEAAAAfIAAAA"
+                  + "gAAACKQAAABAAAAI7AAAAEAAAAk0AAAAQAAACnQAAABAAAAJ7AAAAIAAAArIAAAAQAAACxAAAABAAA"
+                  + "ALWAAAAEAAAAyYAAAAQAAADBAAAACC8nuR7W9i8AuG9xF3K__n1kk9F_G1i25QRKR9YXyssdsadWT";
+
+          @Override
+          public List<String> obfuscatedStackTrace() {
+            return ImmutableList.of(LINE);
+          }
+
+          @Override
+          public String mapping() {
+            return "foo.Bar -> huk:\n" + "  void baz():13:13 -> g\n" + "  void qux():12:12 -> g\n";
+          }
+
+          @Override
+          public List<String> retracedStackTrace() {
+            return ImmutableList.of(LINE);
+          }
+
+          @Override
+          public List<String> retraceVerboseStackTrace() {
+            return ImmutableList.of(LINE);
+          }
+
+          @Override
+          public int expectedWarnings() {
+            return 0;
+          }
+        });
+  }
+
   private TestDiagnosticMessagesImpl runRetraceTest(
       String regularExpression, StackTraceForTest stackTraceForTest) {
     TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
diff --git a/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitializationTriggersIndirectInterfaceInitializationTest.java b/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitializationTriggersIndirectInterfaceInitializationTest.java
index 29960f9..1c7492d 100644
--- a/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitializationTriggersIndirectInterfaceInitializationTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitializationTriggersIndirectInterfaceInitializationTest.java
@@ -7,8 +7,6 @@
 import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
@@ -59,13 +57,9 @@
                 assertThat(iClassSubject, isPresent());
                 assertThat(iClassSubject.clinit(), isPresent());
 
-                // Verify that J is still there.
-                assertThat(jClassSubject, isPresent());
-
-                // Verify that A still implements J.
-                assertThat(aClassSubject, isPresent());
-                assertEquals(1, aClassSubject.getDexProgramClass().getInterfaces().size());
-                assertTrue(aClassSubject.isImplementing(jClassSubject));
+                // Verify that J and A are pruned.
+                assertThat(jClassSubject, isAbsent());
+                assertThat(aClassSubject, isAbsent());
               } else {
                 // All interfaces are gone and the default methods companion call is inlined.
                 assertThat(iClassSubject, isAbsent());
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByTwoMethods.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByTwoMethods.java
index 035ddf7..c238e5d 100644
--- a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByTwoMethods.java
+++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByTwoMethods.java
@@ -7,6 +7,7 @@
 
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoMethodStaticizing;
 import com.android.tools.r8.NoVerticalClassMerging;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
@@ -27,6 +28,7 @@
   public static class A {
 
     @NeverInline
+    @NoMethodStaticizing
     void foo() {
       System.out.println("A.foo!");
     }
@@ -75,6 +77,7 @@
         testForR8(parameters.getBackend())
             .enableGraphInspector()
             .enableNeverClassInliningAnnotations()
+            .enableNoMethodStaticizingAnnotations()
             .enableNoVerticalClassMergingAnnotations()
             .enableInliningAnnotations()
             .addKeepMainRule(TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
index 4465113..40bfdec 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
@@ -7,6 +7,7 @@
 
 import com.android.tools.r8.cf.code.CfArithmeticBinop;
 import com.android.tools.r8.cf.code.CfArrayLength;
+import com.android.tools.r8.cf.code.CfArrayLoad;
 import com.android.tools.r8.cf.code.CfArrayStore;
 import com.android.tools.r8.cf.code.CfCheckCast;
 import com.android.tools.r8.cf.code.CfConstClass;
@@ -368,6 +369,11 @@
   }
 
   @Override
+  public boolean isArrayGet() {
+    return instruction instanceof CfArrayLoad;
+  }
+
+  @Override
   public boolean isArrayPut() {
     return instruction instanceof CfArrayStore;
   }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
index 91f0ea9..00cfc92 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
@@ -4,6 +4,13 @@
 
 package com.android.tools.r8.utils.codeinspector;
 
+import com.android.tools.r8.code.Aget;
+import com.android.tools.r8.code.AgetBoolean;
+import com.android.tools.r8.code.AgetByte;
+import com.android.tools.r8.code.AgetChar;
+import com.android.tools.r8.code.AgetObject;
+import com.android.tools.r8.code.AgetShort;
+import com.android.tools.r8.code.AgetWide;
 import com.android.tools.r8.code.Aput;
 import com.android.tools.r8.code.AputBoolean;
 import com.android.tools.r8.code.AputByte;
@@ -458,6 +465,17 @@
   }
 
   @Override
+  public boolean isArrayGet() {
+    return instruction instanceof Aget
+        || instruction instanceof AgetBoolean
+        || instruction instanceof AgetByte
+        || instruction instanceof AgetChar
+        || instruction instanceof AgetObject
+        || instruction instanceof AgetShort
+        || instruction instanceof AgetWide;
+  }
+
+  @Override
   public boolean isArrayPut() {
     return instruction instanceof Aput
         || instruction instanceof AputBoolean
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
index 9f55b56..4488d83 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
@@ -144,6 +144,8 @@
 
   boolean isArrayLength();
 
+  boolean isArrayGet();
+
   boolean isArrayPut();
 
   boolean isMonitorEnter();
diff --git a/third_party/binary_compatibility_tests/compiler_api_tests.tar.gz.sha1 b/third_party/binary_compatibility_tests/compiler_api_tests.tar.gz.sha1
index ffee6ee..c55bcfe 100644
--- a/third_party/binary_compatibility_tests/compiler_api_tests.tar.gz.sha1
+++ b/third_party/binary_compatibility_tests/compiler_api_tests.tar.gz.sha1
@@ -1 +1 @@
-36741d08e769bd2c5c201698161b3407507c7535
\ No newline at end of file
+bf40dc3b9e57b7759d8da51fb70d9995af1a188d
\ No newline at end of file
diff --git a/tools/retrace.py b/tools/retrace.py
index 14593ab..22753fb 100755
--- a/tools/retrace.py
+++ b/tools/retrace.py
@@ -61,6 +61,11 @@
       default=None,
       action='store_true',
       help='Enables verbose retracing.')
+  parser.add_argument(
+      '--disable-map-validation',
+      default=None,
+      action='store_true',
+      help='Disable validation of map hash.')
   return parser.parse_args()
 
 
@@ -108,7 +113,7 @@
       print(e)
       print('WARNING: Falling back to using local mapping file.')
 
-    if map_path:
+    if map_path and not args.disable_map_validation:
       check_maphash(map_path, maphash)
       return map_path