Merge commit 'f79fefd7dff4f054ec8b8f1991ce4e7bf9c828c0' into dev-release
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFrame.java b/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
index fbeda56..f8b10e0 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
@@ -105,6 +105,24 @@
       return false;
     }
 
+    FrameType map(java.util.function.Function<DexType, DexType> func) {
+      if (isInitialized()) {
+        DexType type = getInitializedType();
+        DexType newType = func.apply(type);
+        if (type != newType) {
+          return initialized(newType);
+        }
+      }
+      if (isUninitializedNew()) {
+        DexType type = getUninitializedNewType();
+        DexType newType = func.apply(type);
+        if (type != newType) {
+          return uninitializedNew(getUninitializedLabel(), newType);
+        }
+      }
+      return this;
+    }
+
     private FrameType() {}
 
     public static FrameType fromMemberType(MemberType memberType, DexItemFactory factory) {
@@ -512,4 +530,16 @@
     }
     return other;
   }
+
+  public CfFrame map(java.util.function.Function<DexType, DexType> func) {
+    Int2ReferenceSortedMap<FrameType> newLocals = new Int2ReferenceAVLTreeMap<>();
+    for (int var : locals.keySet()) {
+      newLocals.put(var, locals.get(var).map(func));
+    }
+    Deque<FrameType> newStack = new ArrayDeque<>();
+    for (FrameType frameType : stack) {
+      newStack.addLast(frameType.map(func));
+    }
+    return new CfFrame(newLocals, newStack);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
index c4bbada..644ed7d 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
@@ -75,9 +75,6 @@
       NamingLens namingLens,
       LensCodeRewriterUtils rewriter,
       MethodVisitor visitor) {
-    if (context.getName().toSourceString().equals("recordNewFieldSignature")) {
-      System.currentTimeMillis();
-    }
     DexCallSite rewrittenCallSite = rewriter.rewriteCallSite(callSite, context);
     DexMethodHandle bootstrapMethod = rewrittenCallSite.bootstrapMethod;
     List<DexValue> bootstrapArgs = rewrittenCallSite.bootstrapArgs;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index 8cd6162..a3fc0e8 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -34,6 +34,7 @@
 import com.android.tools.r8.ir.desugar.backports.SparseArrayMethodRewrites;
 import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeter;
 import com.android.tools.r8.synthesis.SyntheticNaming;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.InternalOptions;
@@ -90,7 +91,10 @@
   @Override
   public boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) {
     return instruction.isInvoke()
-        && getMethodProviderOrNull(instruction.asInvoke().getMethod()) != null;
+        && getMethodProviderOrNull(instruction.asInvoke().getMethod()) != null
+        && !appView
+            .getSyntheticItems()
+            .isSyntheticOfKind(context.getContextType(), SyntheticKind.BACKPORT_WITH_FORWARDING);
   }
 
   public static List<DexMethod> generateListOfBackportedMethods(
@@ -121,7 +125,6 @@
     BackportedMethods.registerSynthesizedCodeReferences(options.itemFactory);
   }
 
-
   private MethodProvider getMethodProviderOrNull(DexMethod method) {
     DexMethod original = appView.graphLens().getOriginalMethodSignature(method);
     assert original != null;
@@ -182,6 +185,9 @@
       if (options.getMinApiLevel().isLessThan(AndroidApiLevel.S)) {
         initializeAndroidSMethodProviders(factory);
       }
+      if (options.getMinApiLevel().isLessThan(AndroidApiLevel.Sv2)) {
+        initializeAndroidSv2MethodProviders(factory);
+      }
 
       // The following providers are currently not implemented at any API level in Android.
       // They however require the Optional/Stream class to be present, either through desugared
@@ -1084,6 +1090,30 @@
               factory.androidUtilSparseArrayMembers.set, SparseArrayMethodRewrites.rewriteSet()));
     }
 
+    private void initializeAndroidSv2MethodProviders(DexItemFactory factory) {
+      DexString name;
+      DexProto proto;
+      DexMethod method;
+      // sun.misc.Unsafe
+
+      // compareAndSwapObject(Object receiver, long offset, Object expect, Object update)
+      name = factory.createString("compareAndSwapObject");
+      proto =
+          factory.createProto(
+              factory.booleanType,
+              factory.objectType,
+              factory.longType,
+              factory.objectType,
+              factory.objectType);
+      method = factory.createMethod(factory.unsafeType, proto, name);
+      addProvider(
+          new StatifyingMethodWithForwardingGenerator(
+              method,
+              BackportedMethods::UnsafeMethods_compareAndSwapObject,
+              "compareAndSwapObject",
+              factory.unsafeType));
+    }
+
     private void initializeJava9MethodProviders(DexItemFactory factory) {
       // Integer
       DexType type = factory.boxedIntType;
@@ -1446,6 +1476,10 @@
       this.methodName = methodName;
     }
 
+    protected SyntheticKind getSyntheticKind() {
+      return SyntheticNaming.SyntheticKind.BACKPORT;
+    }
+
     @Override
     public Collection<CfInstruction> rewriteInvoke(
         CfInvoke invoke,
@@ -1463,7 +1497,7 @@
       return appView
           .getSyntheticItems()
           .createMethod(
-              SyntheticNaming.SyntheticKind.BACKPORT,
+              getSyntheticKind(),
               methodProcessingContext.createUniqueContext(),
               appView,
               builder ->
@@ -1502,6 +1536,20 @@
     }
   }
 
+  // Version of StatifyingMethodGenerator for backports which will call the method they backport.
+  // Such backports will not go through backporting again as that would cause infinite recursion.
+  private static class StatifyingMethodWithForwardingGenerator extends StatifyingMethodGenerator {
+    StatifyingMethodWithForwardingGenerator(
+        DexMethod method, TemplateMethodFactory factory, String methodName, DexType receiverType) {
+      super(method, factory, methodName, receiverType);
+    }
+
+    @Override
+    protected SyntheticKind getSyntheticKind() {
+      return SyntheticKind.BACKPORT_WITH_FORWARDING;
+    }
+  }
+
   private interface TemplateMethodFactory {
 
     CfCode create(InternalOptions options, DexMethod method);
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 da44c03..8e2f9fb 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
@@ -28,6 +28,7 @@
 import com.android.tools.r8.ir.desugar.LocalStackAllocator;
 import com.android.tools.r8.ir.synthetic.ForwardMethodBuilder;
 import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.google.common.collect.ImmutableList;
 import java.util.Collection;
 
@@ -95,7 +96,8 @@
     ComputedApiLevel methodApiLevel =
         apiLevelCompute.computeApiLevelForLibraryReference(
             cfInvoke.getMethod(), ComputedApiLevel.unknown());
-    if (appView.computedMinApiLevel().isGreaterThanOrEqualTo(methodApiLevel)) {
+    if (appView.computedMinApiLevel().isGreaterThanOrEqualTo(methodApiLevel)
+        || isApiLevelLessThanOrEqualTo9(methodApiLevel)) {
       return appView.computedMinApiLevel();
     }
     // Compute the api level of the holder to see if the method will be stubbed.
@@ -106,6 +108,11 @@
         : appView.computedMinApiLevel();
   }
 
+  private boolean isApiLevelLessThanOrEqualTo9(ComputedApiLevel apiLevel) {
+    return apiLevel.isKnownApiLevel()
+        && apiLevel.asKnownApiLevel().getApiLevel().isLessThanOrEqualTo(AndroidApiLevel.G);
+  }
+
   private Collection<CfInstruction> desugarLibraryCall(
       UniqueContext context,
       CfInvoke invoke,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
index b2fd06d..d7f7109 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
@@ -105,6 +105,7 @@
     factory.createSynthesizedType("Ljava/util/stream/IntStream;");
     factory.createSynthesizedType("Ljava/util/stream/LongStream;");
     factory.createSynthesizedType("Ljava/util/stream/Stream;");
+    factory.createSynthesizedType("Lsun/misc/Unsafe;");
     factory.createSynthesizedType("[Ljava/lang/CharSequence;");
     factory.createSynthesizedType("[Ljava/lang/Class;");
     factory.createSynthesizedType("[Ljava/lang/Object;");
@@ -9077,4 +9078,84 @@
         ImmutableList.of(),
         ImmutableList.of());
   }
+
+  public static CfCode UnsafeMethods_compareAndSwapObject(
+      InternalOptions options, DexMethod method) {
+    CfLabel label0 = new CfLabel();
+    CfLabel label1 = new CfLabel();
+    CfLabel label2 = new CfLabel();
+    CfLabel label3 = new CfLabel();
+    CfLabel label4 = new CfLabel();
+    return new CfCode(
+        method.holder,
+        6,
+        6,
+        ImmutableList.of(
+            label0,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2, 4, 5},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.createType("Lsun/misc/Unsafe;")),
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                new ArrayDeque<>(Arrays.asList())),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfLoad(ValueType.LONG, 2),
+            new CfLoad(ValueType.OBJECT, 4),
+            new CfLoad(ValueType.OBJECT, 5),
+            new CfInvoke(
+                182,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Lsun/misc/Unsafe;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.booleanType,
+                        options.itemFactory.objectType,
+                        options.itemFactory.longType,
+                        options.itemFactory.objectType,
+                        options.itemFactory.objectType),
+                    options.itemFactory.createString("compareAndSwapObject")),
+                false),
+            new CfIf(If.Type.EQ, ValueType.INT, label2),
+            label1,
+            new CfConstNumber(1, ValueType.INT),
+            new CfReturn(ValueType.INT),
+            label2,
+            new CfFrame(
+                new Int2ReferenceAVLTreeMap<>(
+                    new int[] {0, 1, 2, 4, 5},
+                    new FrameType[] {
+                      FrameType.initialized(options.itemFactory.createType("Lsun/misc/Unsafe;")),
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.longType),
+                      FrameType.initialized(options.itemFactory.objectType),
+                      FrameType.initialized(options.itemFactory.objectType)
+                    }),
+                new ArrayDeque<>(Arrays.asList())),
+            new CfLoad(ValueType.OBJECT, 0),
+            new CfLoad(ValueType.OBJECT, 1),
+            new CfLoad(ValueType.LONG, 2),
+            new CfInvoke(
+                182,
+                options.itemFactory.createMethod(
+                    options.itemFactory.createType("Lsun/misc/Unsafe;"),
+                    options.itemFactory.createProto(
+                        options.itemFactory.objectType,
+                        options.itemFactory.objectType,
+                        options.itemFactory.longType),
+                    options.itemFactory.createString("getObject")),
+                false),
+            new CfLoad(ValueType.OBJECT, 4),
+            new CfIfCmp(If.Type.EQ, ValueType.OBJECT, label0),
+            label3,
+            new CfConstNumber(0, ValueType.INT),
+            new CfReturn(ValueType.INT),
+            label4),
+        ImmutableList.of(),
+        ImmutableList.of());
+  }
 }
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 0e3dfdb..50bea08 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
@@ -29,6 +29,7 @@
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InstructionListIterator;
 import com.android.tools.r8.ir.code.InvokeDirect;
+import com.android.tools.r8.ir.code.InvokeStatic;
 import com.android.tools.r8.ir.code.NewInstance;
 import com.android.tools.r8.ir.code.Phi;
 import com.android.tools.r8.ir.code.StaticGet;
@@ -243,13 +244,12 @@
             }
           } else if (instruction.isInvokeDirect()) {
             handleInvokeDirect(instruction.asInvokeDirect());
+          } else if (instruction.isInvokeStatic()) {
+            handleInvokeStatic(instruction.asInvokeStatic());
           } else if (instruction.isInvokeMethod() || instruction.isInvokeCustom()) {
             killAllNonFinalActiveFields();
           } else if (instruction.isNewInstance()) {
-            NewInstance newInstance = instruction.asNewInstance();
-            if (newInstance.clazz.classInitializationMayHaveSideEffectsInContext(appView, method)) {
-              killAllNonFinalActiveFields();
-            }
+            handleNewInstance(instruction.asNewInstance());
           } else {
             // If the current instruction could trigger a method invocation, it could also cause
             // field values to change. In that case, it must be handled above.
@@ -258,6 +258,7 @@
             // Clear the field writes.
             if (instruction.instructionInstanceCanThrow(appView, method)) {
               activeState.clearMostRecentFieldWrites();
+              activeState.clearMostRecentInitClass();
             }
 
             // If this assertion fails for a new instruction we need to determine if that
@@ -394,6 +395,23 @@
         });
   }
 
+  private void handleInvokeStatic(InvokeStatic invoke) {
+    if (appView.hasClassHierarchy()) {
+      ProgramMethod resolvedMethod =
+          appView
+              .appInfo()
+              .withClassHierarchy()
+              .unsafeResolveMethodDueToDexFormat(invoke.getInvokedMethod())
+              .getResolvedProgramMethod();
+      if (resolvedMethod != null) {
+        markClassAsInitialized(resolvedMethod.getHolderType());
+        markMostRecentInitClassForRemoval(resolvedMethod.getHolderType());
+      }
+    }
+
+    killAllNonFinalActiveFields();
+  }
+
   private void handleInitClass(InstructionListIterator instructionIterator, InitClass initClass) {
     assert !initClass.outValue().hasAnyUsers();
 
@@ -406,11 +424,28 @@
     }
 
     DexType clazz = initClass.getClassValue();
-    if (!activeState.markClassAsInitialized(clazz)) {
+    if (markClassAsInitialized(clazz)) {
+      if (release) {
+        activeState.setMostRecentInitClass(initClass);
+      }
+    } else {
       instructionIterator.removeOrReplaceByDebugLocalRead();
     }
   }
 
+  private boolean markClassAsInitialized(DexType type) {
+    return activeState.markClassAsInitialized(type);
+  }
+
+  private void markMostRecentInitClassForRemoval(DexType initializedType) {
+    InitClass mostRecentInitClass = activeState.getMostRecentInitClass();
+    if (mostRecentInitClass != null && mostRecentInitClass.getClassValue() == initializedType) {
+      instructionsToRemove
+          .computeIfAbsent(mostRecentInitClass.getBlock(), ignoreKey(Sets::newIdentityHashSet))
+          .add(mostRecentInitClass);
+    }
+  }
+
   private void handleInstanceGet(
       InstructionListIterator it,
       InstanceGet instanceGet,
@@ -431,9 +466,18 @@
     }
 
     activeState.putNonFinalInstanceField(fieldAndObject, new ExistingValue(instanceGet.value()));
+    activeState.clearMostRecentInitClass();
     clearMostRecentInstanceFieldWrite(instanceGet, field);
   }
 
+  private void handleNewInstance(NewInstance newInstance) {
+    markClassAsInitialized(newInstance.getType());
+    markMostRecentInitClassForRemoval(newInstance.getType());
+    if (newInstance.getType().classInitializationMayHaveSideEffectsInContext(appView, method)) {
+      killAllNonFinalActiveFields();
+    }
+  }
+
   private void clearMostRecentInstanceFieldWrite(InstanceGet instanceGet, DexClassAndField field) {
     // If the instruction can throw, we need to clear all most-recent-writes, since subsequent field
     // writes (if any) are not guaranteed to be executed.
@@ -494,6 +538,8 @@
         }
       }
     }
+
+    activeState.clearMostRecentInitClass();
   }
 
   private void handleStaticGet(
@@ -501,6 +547,8 @@
       StaticGet staticGet,
       DexClassAndField field,
       AssumeRemover assumeRemover) {
+    markClassAsInitialized(field.getHolderType());
+
     if (staticGet.outValue().hasLocalInfo()) {
       killNonFinalActiveFields(staticGet);
       clearMostRecentStaticFieldWrite(staticGet, field);
@@ -532,6 +580,9 @@
         applyObjectState(staticGet.outValue(), singleFieldValue.getObjectState());
       }
     }
+
+    markMostRecentInitClassForRemoval(field.getHolderType());
+    activeState.clearMostRecentInitClass();
   }
 
   private void clearMostRecentStaticFieldWrite(StaticGet staticGet, DexClassAndField field) {
@@ -545,6 +596,8 @@
   }
 
   private void handleStaticPut(StaticPut staticPut, DexClassAndField field) {
+    markClassAsInitialized(field.getHolderType());
+
     // A field put on a different class can cause <clinit> to run and change static field values.
     killNonFinalActiveFields(staticPut);
 
@@ -573,6 +626,9 @@
         }
       }
     }
+
+    markMostRecentInitClassForRemoval(field.getHolderType());
+    activeState.clearMostRecentInitClass();
   }
 
   private void applyObjectState(Value value, ObjectState objectState) {
@@ -593,6 +649,7 @@
     activeState.clearNonFinalInstanceFields();
     activeState.clearNonFinalStaticFields();
     activeState.clearMostRecentFieldWrites();
+    activeState.clearMostRecentInitClass();
   }
 
   private void killNonFinalActiveFields(Instruction instruction) {
@@ -714,6 +771,7 @@
       }
       if (!block.hasUniqueSuccessorWithUniquePredecessor()) {
         state.clearMostRecentFieldWrites();
+        state.clearMostRecentInitClass();
       }
       ensureCapacity(state);
       activeStateAtExit.put(block, state);
@@ -753,6 +811,8 @@
 
     private LinkedHashMap<DexField, FieldValue> nonFinalStaticFieldValues;
 
+    private InitClass mostRecentInitClass;
+
     private LinkedHashMap<FieldAndObject, InstancePut> mostRecentInstanceFieldWrites;
 
     private LinkedHashMap<DexField, StaticPut> mostRecentStaticFieldWrites;
@@ -787,6 +847,7 @@
           nonFinalStaticFieldValues = new LinkedHashMap<>();
           nonFinalStaticFieldValues.putAll(state.nonFinalStaticFieldValues);
         }
+        mostRecentInitClass = state.mostRecentInitClass;
         if (state.mostRecentInstanceFieldWrites != null
             && !state.mostRecentInstanceFieldWrites.isEmpty()) {
           mostRecentInstanceFieldWrites = new LinkedHashMap<>();
@@ -885,6 +946,7 @@
       } else {
         nonFinalStaticFieldValues = null;
       }
+      assert mostRecentInitClass == null;
       assert mostRecentInstanceFieldWrites == null;
       assert mostRecentStaticFieldWrites == null;
     }
@@ -899,10 +961,6 @@
       initializedClasses.removeIf(not(other::contains));
     }
 
-    public boolean isClassInitialized(DexType clazz) {
-      return initializedClasses != null && initializedClasses.contains(clazz);
-    }
-
     public boolean isEmpty() {
       return isEmpty(finalInstanceFieldValues)
           && isEmpty(finalStaticFieldValues)
@@ -1082,6 +1140,20 @@
       nonFinalStaticFieldValues.put(field, value);
     }
 
+    public InitClass getMostRecentInitClass() {
+      return mostRecentInitClass;
+    }
+
+    public void setMostRecentInitClass(InitClass initClass) {
+      mostRecentInitClass = initClass;
+    }
+
+    public InitClass clearMostRecentInitClass() {
+      InitClass result = mostRecentInitClass;
+      mostRecentInitClass = null;
+      return result;
+    }
+
     public int size() {
       return size(finalInstanceFieldValues)
           + size(finalStaticFieldValues)
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
index 834e879..de68c75 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -197,11 +197,12 @@
   }
 
   private static DexType firstLibraryClass(DexDefinitionSupplier definitions, DexType bottom) {
-    DexClass searchClass = definitions.definitionFor(bottom);
-    while (searchClass.isProgramClass()) {
-      searchClass = definitions.definitionFor(searchClass.superType);
+    DexClass searchClass = definitions.contextIndependentDefinitionFor(bottom);
+    while (searchClass != null && searchClass.isProgramClass()) {
+      searchClass =
+          definitions.definitionFor(searchClass.getSuperType(), searchClass.asProgramClass());
     }
-    return searchClass.type;
+    return searchClass != null ? searchClass.getType() : null;
   }
 
   private MethodResolutionResult resolveMethodOnClass(DexMethod method) {
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
index fc487f7..c80a94b 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -43,6 +43,7 @@
     ENUM_UNBOXING_CHECK_NOT_ZERO_METHOD("CheckNotZero", 27, true),
     RECORD_HELPER("Record", 9, true),
     BACKPORT("Backport", 10, true),
+    BACKPORT_WITH_FORWARDING("BackportWithForwarding", 34, true),
     STATIC_INTERFACE_CALL("StaticInterfaceCall", 11, true),
     TO_STRING_IF_NOT_NULL("ToStringIfNotNull", 12, true),
     THROW_CCE_IF_NOT_NULL("ThrowCCEIfNotNull", 13, true),
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 7ce8109..0bfc079 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -1469,10 +1469,14 @@
     public Map<ClassReference, AndroidApiLevel> classApiMapping = new HashMap<>();
     public BiConsumer<MethodReference, ComputedApiLevel> tracedMethodApiLevelCallback = null;
 
-    public boolean enableApiCallerIdentification = true;
-    public boolean checkAllApiReferencesAreSet = true;
-    public boolean enableStubbingOfClasses = false;
-    public boolean enableOutliningOfMethods = false;
+    public boolean enableApiCallerIdentification =
+        System.getProperty("com.android.tools.r8.disableApiModeling") == null;
+    public boolean checkAllApiReferencesAreSet =
+        System.getProperty("com.android.tools.r8.disableApiModeling") == null;
+    public boolean enableStubbingOfClasses =
+        System.getProperty("com.android.tools.r8.disableApiModeling") == null;
+    public boolean enableOutliningOfMethods =
+        System.getProperty("com.android.tools.r8.disableApiModeling") == null;
 
     public void visitMockedApiLevelsForReferences(
         DexItemFactory factory, Consumer<AndroidApiForHashingClass> consumer) {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiMethodsTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiMethodsTest.java
index 0eeb405..5730230c 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiMethodsTest.java
@@ -52,6 +52,8 @@
               }
             })
         .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+        // We are testing that we do not inline/merge higher api-levels
+        .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
         .apply(setMockApiLevelForMethod(apiMethod, AndroidApiLevel.L_MR1))
         .compile()
         .addRunClasspathClasses(Api.class)
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineInSameClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineInSameClassTest.java
index 8d4e2f1..f351779 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineInSameClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineInSameClassTest.java
@@ -52,6 +52,8 @@
         .addKeepMainRule(Main.class)
         .apply(setMockApiLevelForMethod(apiMethod, AndroidApiLevel.L_MR1))
         .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+        // We are testing that we do not inline/merge higher api-levels
+        .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
         .compile()
         .inspect(
             inspector -> {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoClassInliningMethodTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoClassInliningMethodTest.java
index 22ecf86..013d98f 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoClassInliningMethodTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoClassInliningMethodTest.java
@@ -48,6 +48,8 @@
         .enableNoHorizontalClassMergingAnnotations()
         .apply(setMockApiLevelForMethod(apiMethod, AndroidApiLevel.L_MR1))
         .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+        // We are testing that we do not inline/merge higher api-levels
+        .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
         .compile()
         .inspect(
             inspector -> {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoDesugaredLibraryReferenceTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoDesugaredLibraryReferenceTest.java
index 45d2bec..6e8d3b0 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoDesugaredLibraryReferenceTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoDesugaredLibraryReferenceTest.java
@@ -47,6 +47,8 @@
         .setMinApi(parameters.getApiLevel())
         .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
         .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+        // We are testing that we do not inline/merge higher api-levels
+        .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
         .apply(
             ApiModelingTestHelper.addTracedApiReferenceLevelCallBack(
                 (reference, apiLevel) -> {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfDefaultInterfaceMethodsTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfDefaultInterfaceMethodsTest.java
index a580fb1..9f88bbb 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfDefaultInterfaceMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfDefaultInterfaceMethodsTest.java
@@ -48,6 +48,8 @@
         .addKeepMainRule(Main.class)
         .apply(setMockApiLevelForMethod(apiMethod, L_MR1))
         .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+        // We are testing that we do not inline/merge higher api-levels
+        .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
         .noMinification()
         .compile()
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInterfaceTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInterfaceTest.java
index d39b038..22a7321 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInterfaceTest.java
@@ -50,6 +50,8 @@
         .enableNoHorizontalClassMergingAnnotations()
         .apply(setMockApiLevelForMethod(apiMethod, AndroidApiLevel.L_MR1))
         .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+        // We are testing that we do not inline/merge higher api-levels
+        .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutputLines("A::noApiCall", "ApiCaller::callInterfaceMethod")
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelIntoLowerDirectTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelIntoLowerDirectTest.java
index 98aa240..84ce168 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelIntoLowerDirectTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelIntoLowerDirectTest.java
@@ -46,6 +46,8 @@
         .apply(setMockApiLevelForMethod(apiLevel21, AndroidApiLevel.L))
         .apply(setMockApiLevelForMethod(apiLevel22, AndroidApiLevel.L_MR1))
         .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+        // We are testing that we do not inline/merge higher api-levels
+        .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutputLines("A::apiLevel21", "B::apiLevel22")
         .inspect(
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelStaticTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelStaticTest.java
index 16f24fa..1b7713d 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelStaticTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelStaticTest.java
@@ -48,6 +48,8 @@
         .enableNoHorizontalClassMergingAnnotations()
         .apply(setMockApiLevelForMethod(apiMethod, AndroidApiLevel.L_MR1))
         .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+        // We are testing that we do not inline/merge higher api-levels
+        .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
         .compile()
         .inspect(
             inspector ->
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelVirtualTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelVirtualTest.java
index 2311f12..c4a6c58 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelVirtualTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelVirtualTest.java
@@ -53,6 +53,8 @@
         .apply(setMockApiLevelForClass(Api.class, AndroidApiLevel.L_MR1))
         .apply(setMockApiLevelForDefaultInstanceInitializer(Api.class, AndroidApiLevel.L_MR1))
         .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+        // We are testing that we do not inline/merge higher api-levels
+        .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
         .compile()
         .inspect(
             inspector ->
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfStaticInterfaceMethodsTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfStaticInterfaceMethodsTest.java
index 498ef4c..22714a3 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfStaticInterfaceMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfStaticInterfaceMethodsTest.java
@@ -50,6 +50,8 @@
         .apply(setMockApiLevelForMethod(apiMethod22, L_MR1))
         .apply(setMockApiLevelForMethod(apiMethod26, O))
         .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+        // We are testing that we do not inline/merge higher api-levels
+        .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
         .noMinification()
         .enableInliningAnnotations()
         .compile()
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoMockForOutlineTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoMockForOutlineTest.java
deleted file mode 100644
index a4cbd4b..0000000
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoMockForOutlineTest.java
+++ /dev/null
@@ -1,118 +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.apimodel;
-
-import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
-import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
-import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
-import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assume.assumeFalse;
-
-import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ToolHelper.DexVm.Version;
-import com.android.tools.r8.testing.AndroidBuildVersion;
-import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import java.lang.reflect.Method;
-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 ApiModelNoMockForOutlineTest extends TestBase {
-
-  private final AndroidApiLevel classApiLevel = AndroidApiLevel.K;
-  private final AndroidApiLevel methodApiLevel = AndroidApiLevel.M;
-
-  @Parameter public TestParameters parameters;
-
-  @Parameters(name = "{0}")
-  public static TestParametersCollection data() {
-    return getTestParameters().withAllRuntimesAndApiLevels().build();
-  }
-
-  @Test
-  public void testR8() throws Exception {
-    assumeFalse(
-        parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isEqualTo(Version.V12_0_0));
-    boolean isMethodApiLevel =
-        parameters.isDexRuntime()
-            && parameters.getApiLevel().isGreaterThanOrEqualTo(methodApiLevel);
-    Method methodOn23 = LibraryClass.class.getDeclaredMethod("methodOn23");
-    Method mainMethod = Main.class.getDeclaredMethod("main", String[].class);
-    testForR8(parameters.getBackend())
-        .addProgramClasses(Main.class)
-        .addLibraryClasses(LibraryClass.class)
-        .addDefaultRuntimeLibrary(parameters)
-        .setMinApi(parameters.getApiLevel())
-        .addKeepMainRule(Main.class)
-        .addAndroidBuildVersion()
-        .apply(setMockApiLevelForClass(LibraryClass.class, classApiLevel))
-        .apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, classApiLevel))
-        .apply(setMockApiLevelForMethod(methodOn23, methodApiLevel))
-        .apply(ApiModelingTestHelper::enableOutliningOfMethods)
-        .apply(ApiModelingTestHelper::enableStubbingOfClasses)
-        .enableInliningAnnotations()
-        .compile()
-        .applyIf(
-            parameters.isDexRuntime()
-                && parameters
-                    .getRuntime()
-                    .maxSupportedApiLevel()
-                    .isGreaterThanOrEqualTo(classApiLevel),
-            b -> b.addBootClasspathClasses(LibraryClass.class))
-        .run(parameters.getRuntime(), Main.class)
-        .assertSuccessWithOutputLinesIf(!isMethodApiLevel, "Hello World")
-        .assertSuccessWithOutputLinesIf(isMethodApiLevel, "LibraryClass::methodOn23", "Hello World")
-        .inspect(
-            inspector -> {
-              assertThat(inspector.method(mainMethod), isPresent());
-              verifyThat(inspector, parameters, methodOn23)
-                  .isOutlinedFromUntil(mainMethod, methodApiLevel);
-              verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(classApiLevel);
-              if (parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(classApiLevel)) {
-                // We never trace outlined method for stubs so this holds by default.
-                ClassSubject mockedLibraryClass = inspector.clazz(LibraryClass.class);
-                assertThat(mockedLibraryClass, isPresent());
-                MethodSubject mockedMethodOn23 =
-                    mockedLibraryClass.uniqueMethodWithName("methodOn23");
-                assertThat(mockedMethodOn23, isAbsent());
-              }
-            });
-  }
-
-  // Only present from api level 19.
-  public static class LibraryClass {
-
-    public void methodOn23() {
-      System.out.println("LibraryClass::methodOn23");
-    }
-  }
-
-  public static class Main {
-
-    @NeverInline
-    public static Object create() {
-      return AndroidBuildVersion.VERSION >= 23 ? new LibraryClass() : null;
-    }
-
-    public static void main(String[] args) {
-      Object libraryClass = create();
-      if (libraryClass != null) {
-        ((LibraryClass) libraryClass).methodOn23();
-      }
-      System.out.println("Hello World");
-    }
-  }
-}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingSubReferenceApiTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingSubReferenceApiTest.java
index 98b4e92..da967e8 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingSubReferenceApiTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingSubReferenceApiTest.java
@@ -50,6 +50,8 @@
         .addKeepMainRule(Main.class)
         .apply(setMockApiLevelForMethod(apiMethod, L_MR1))
         .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+        // We are testing that we do not inline/merge higher api-levels
+        .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .addVerticallyMergedClassesInspector(
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingTest.java
index b7cd0b1..7276937 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingTest.java
@@ -50,6 +50,8 @@
         .addKeepMainRule(Main.class)
         .apply(setMockApiLevelForMethod(apiMethod, L_MR1))
         .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+        // We are testing that we do not inline/merge higher api-levels
+        .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
         .enableInliningAnnotations()
         .enableNeverClassInliningAnnotations()
         .addVerticallyMergedClassesInspector(
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java
index 0760cad..c4dcd42 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java
@@ -64,6 +64,7 @@
         .apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, classApiLevel))
         .apply(setMockApiLevelForMethod(adeddOn23, methodApiLevel))
         .apply(ApiModelingTestHelper::enableOutliningOfMethods)
+        .apply(ApiModelingTestHelper::disableStubbingOfClasses)
         .enableInliningAnnotations()
         .compile()
         .applyIf(
@@ -79,7 +80,6 @@
             isMethodApiLevel, "LibraryClass::addedOn23", "LibraryClass::addedOn23", "Hello World")
         .inspect(
             inspector -> {
-              // No need to check further on CF.
               int classCount =
                   parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(methodApiLevel)
                       ? 4
@@ -90,7 +90,7 @@
                   .isOutlinedFromUntil(testMethod, methodApiLevel);
               if (parameters.isDexRuntime()
                   && parameters.getApiLevel().isLessThan(methodApiLevel)) {
-                // Verify that we invoke the synthesized outline addedOn23 twice.
+                // Verify that we invoke the synthesized outline, addedOn23, twice.
                 Optional<FoundMethodSubject> synthesizedAddedOn23 =
                     inspector.allClasses().stream()
                         .flatMap(clazz -> clazz.allMethods().stream())
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java
index 2426afb..ce21ee4 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java
@@ -80,6 +80,7 @@
             setMockApiLevelForMethod(
                 OtherLibraryClass.class.getMethod("addedOn27"), secondMethodApiLevel))
         .apply(ApiModelingTestHelper::enableOutliningOfMethods)
+        .apply(ApiModelingTestHelper::disableStubbingOfClasses)
         .enableInliningAnnotations()
         .compile()
         .applyIf(
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 7755822..8531ceb 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java
@@ -71,6 +71,7 @@
         .apply(setMockApiLevelForMethod(addedOn23, initialLibraryMockLevel))
         .apply(setMockApiLevelForMethod(addedOn27, finalLibraryMethodLevel))
         .apply(ApiModelingTestHelper::enableOutliningOfMethods)
+        .apply(ApiModelingTestHelper::disableStubbingOfClasses)
         .enableInliningAnnotations()
         .compile()
         .applyIf(
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelTypeReferenceInvokeTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelTypeReferenceInvokeTest.java
index 25f0c3a..79df3a4 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelTypeReferenceInvokeTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelTypeReferenceInvokeTest.java
@@ -47,6 +47,8 @@
         .apply(setMockApiLevelForClass(LibraryClass.class, AndroidApiLevel.M))
         .apply(setMockApiLevelForMethod(apiMethod, AndroidApiLevel.M))
         .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+        // We are testing that we do not inline/merge higher api-levels
+        .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
         .addKeepMainRule(Main.class)
         .enableInliningAnnotations()
         .addAndroidBuildVersion()
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchLinkInterfaceTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchLinkInterfaceTest.java
index d4127e8..f28c3c6 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchLinkInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchLinkInterfaceTest.java
@@ -52,6 +52,8 @@
         .setMinApi(parameters.getApiLevel())
         .addKeepMainRule(Main.class)
         .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+        // We are testing that we do not inline/merge higher api-levels
+        .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
         .apply(
             addTracedApiReferenceLevelCallBack(
                 (method, apiLevel) -> {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
index 7077a21..b9cf537 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
@@ -133,6 +133,22 @@
         });
   }
 
+  public static void disableOutliningAndStubbing(
+      TestCompilerBuilder<?, ?, ?, ?, ?> compilerBuilder) {
+    disableStubbingOfClasses(compilerBuilder);
+    disableOutlining(compilerBuilder);
+  }
+
+  public static void disableStubbingOfClasses(TestCompilerBuilder<?, ?, ?, ?, ?> compilerBuilder) {
+    compilerBuilder.addOptionsModification(
+        options -> options.apiModelingOptions().enableStubbingOfClasses = false);
+  }
+
+  public static void disableOutlining(TestCompilerBuilder<?, ?, ?, ?, ?> compilerBuilder) {
+    compilerBuilder.addOptionsModification(
+        options -> options.apiModelingOptions().enableOutliningOfMethods = false);
+  }
+
   static <T extends TestCompilerBuilder<?, ?, ?, ?, ?>>
       ThrowableConsumer<T> addTracedApiReferenceLevelCallBack(
           BiConsumer<MethodReference, AndroidApiLevel> consumer) {
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
index 407165c..3f95a85 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
@@ -23,6 +23,7 @@
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.apimodel.ApiModelingTestHelper;
 import com.android.tools.r8.ir.optimize.Inliner.Reason;
 import com.android.tools.r8.jasmin.JasminBuilder;
 import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
@@ -327,7 +328,8 @@
     runTest(
         testForR8(parameters.getBackend())
             .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
-            .allowUnusedProguardConfigurationRules(),
+            .allowUnusedProguardConfigurationRules()
+            .apply(ApiModelingTestHelper::disableOutlining),
         main,
         programFiles,
         preservedClassNames::contains);
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java
index 7f77265..0c6d576 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java
@@ -42,26 +42,34 @@
   private final Set<String> ignoredInvokes = new HashSet<>();
 
   private static class ClassInfo {
+    private final String name;
     private final Class<?> clazz;
     private final List<byte[]> classFileData;
 
+    private ClassInfo(String name) {
+      this.name = name;
+      this.clazz = null;
+      this.classFileData = null;
+    }
+
     private ClassInfo(Class<?> clazz) {
+      this.name = clazz.getName();
       this.clazz = clazz;
       this.classFileData = null;
     }
 
     private ClassInfo(byte[] classFileData) {
-      this.clazz = null;
-      this.classFileData = ImmutableList.of(classFileData);
+      this(ImmutableList.of(classFileData));
     }
 
     private ClassInfo(List<byte[]> classFileData) {
+      this.name = extractClassName(classFileData.get(0));
       this.clazz = null;
       this.classFileData = classFileData;
     }
 
     String getName() {
-      return clazz != null ? clazz.getName() : extractClassName(classFileData.get(0));
+      return name;
     }
 
     TestBuilder<?, ?> addAsProgramClass(TestBuilder<?, ?> builder) throws IOException {
@@ -79,6 +87,11 @@
   }
 
   AbstractBackportTest(
+      TestParameters parameters, String className, List<byte[]> testClassFileData) {
+    this(parameters, new ClassInfo(className), new ClassInfo(testClassFileData), null, null);
+  }
+
+  AbstractBackportTest(
       TestParameters parameters, byte[] targetClassFileData, List<byte[]> testClassFileData) {
     this(
         parameters,
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/UnsafeBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/UnsafeBackportTest.java
new file mode 100644
index 0000000..dbe597d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/UnsafeBackportTest.java
@@ -0,0 +1,90 @@
+// 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.desugar.backports;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class UnsafeBackportTest extends AbstractBackportTest {
+
+  private static final String UNSAFE_TYPE_NAME = "sun.misc.Unsafe";
+  private static final String UNSAFE_DESCRIPTOR =
+      DescriptorUtils.javaTypeToDescriptor(UNSAFE_TYPE_NAME);
+
+  @Parameters(name = "{0}")
+  public static Iterable<?> data() {
+    return getTestParameters()
+        .withDexRuntimesStartingFromExcluding(Version.V4_0_4)
+        .withAllApiLevels()
+        .build();
+  }
+
+  public UnsafeBackportTest(TestParameters parameters) throws IOException {
+    super(
+        parameters,
+        "sun.misc.Unsafe",
+        ImmutableList.of(UnsafeBackportTest.getTestRunner(), UnsafeBackportTest.getA()));
+
+    ignoreInvokes("objectFieldOffset");
+
+    // sun.misc.Unsafe issue is on API 31.
+    registerTarget(AndroidApiLevel.Sv2, 3);
+  }
+
+  public static class UnsafeStub {
+
+    boolean compareAndSwapObject(Object receiver, long offset, Object expect, Object update) {
+      throw new RuntimeException("Stub called.");
+    }
+
+    public long objectFieldOffset(Field field) {
+      throw new RuntimeException("Stub called.");
+    }
+  }
+
+  private static byte[] getTestRunner() throws IOException {
+    return transformer(TestRunner.class)
+        .setReturnType(MethodPredicate.onName("getUnsafe"), UNSAFE_TYPE_NAME)
+        .replaceClassDescriptorInMethodInstructions(descriptor(UnsafeStub.class), UNSAFE_DESCRIPTOR)
+        .transform();
+  }
+
+  private static byte[] getA() throws IOException {
+    return transformer(A.class).transform();
+  }
+
+  public static class A {
+    public String field;
+  }
+
+  public static class TestRunner extends MiniAssert {
+
+    private static UnsafeStub getUnsafe() throws Exception {
+      Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
+      Field f = unsafeClass.getDeclaredField("theUnsafe");
+      f.setAccessible(true);
+      return (UnsafeStub) f.get(null);
+    }
+
+    public static void main(String[] args) throws Exception {
+      UnsafeStub theUnsafe = getUnsafe();
+      A x = new A();
+      long offset = theUnsafe.objectFieldOffset(A.class.getField("field"));
+      assertTrue(theUnsafe.compareAndSwapObject(x, offset, null, "A"));
+      assertTrue(theUnsafe.compareAndSwapObject(x, offset, "A", "B"));
+      assertFalse(theUnsafe.compareAndSwapObject(x, offset, "A", "B"));
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/SyntheticDistributionTest.java b/src/test/java/com/android/tools/r8/dexsplitter/SyntheticDistributionTest.java
index 61b0fd7..3e86cc9 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/SyntheticDistributionTest.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/SyntheticDistributionTest.java
@@ -16,6 +16,7 @@
 import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.ThrowableConsumer;
 import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.apimodel.ApiModelingTestHelper;
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
@@ -98,6 +99,8 @@
                     "keptApplyLambda", MyFunction.class, String.class)))
         .enableInliningAnnotations()
         .noMinification()
+        // BaseDexClassLoader was introduced at api level 14.
+        .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
         .setMinApi(parameters.getApiLevel());
   }
 
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
index 5e79870..ce75bd9 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
@@ -50,7 +50,8 @@
           OptionalMethods.class,
           ShortMethods.class,
           StreamMethods.class,
-          StringMethods.class);
+          StringMethods.class,
+          UnsafeMethods.class);
 
   protected final TestParameters parameters;
 
@@ -101,6 +102,34 @@
     }
   }
 
+  private static CfInstruction rewriteToUnsafe(
+      DexItemFactory itemFactory, CfInstruction instruction) {
+    // Rewrite references to UnsafeStub to sun.misc.Unsafe.
+    if (instruction.isInvoke()) {
+      String name = instruction.asInvoke().getMethod().getName().toString();
+      if (name.equals("compareAndSwapObject") || name.equals("getObject")) {
+        CfInvoke invoke = instruction.asInvoke();
+        return new CfInvoke(
+            invoke.getOpcode(),
+            itemFactory.createMethod(
+                itemFactory.createType("Lsun/misc/Unsafe;"),
+                invoke.getMethod().getProto(),
+                itemFactory.createString(name)),
+            invoke.isInterface());
+      }
+    }
+    if (instruction.isFrame()) {
+      return instruction
+          .asFrame()
+          .map(
+              type ->
+                  (type.getTypeName().endsWith("$UnsafeStub"))
+                      ? itemFactory.createType("Lsun/misc/Unsafe;")
+                      : type);
+    }
+    return instruction;
+  }
+
   @Override
   protected CfCode getCode(String holderName, String methodName, CfCode code) {
     if (methodName.endsWith("Stub")) {
@@ -113,6 +142,12 @@
               .map(instruction -> rewriteToJava9API(factory, instruction))
               .collect(Collectors.toList()));
     }
+    if (holderName.equals("UnsafeMethods") && methodName.equals("compareAndSwapObject")) {
+      code.setInstructions(
+          code.getInstructions().stream()
+              .map(instruction -> rewriteToUnsafe(factory, instruction))
+              .collect(Collectors.toList()));
+    }
     return code;
   }
 
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/UnsafeMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/UnsafeMethods.java
new file mode 100644
index 0000000..95dab48
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/UnsafeMethods.java
@@ -0,0 +1,31 @@
+// 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.desugar.backports;
+
+public final class UnsafeMethods {
+  // Stub out sun.misc.Unsafe to avoid compiler issues with referring to sun.misc.Unsafe.
+  private static class UnsafeStub {
+
+    public boolean compareAndSwapObject(
+        Object receiver, long offset, Object expect, Object update) {
+      throw new RuntimeException("Stub called.");
+    }
+
+    public Object getObject(Object receiver, long offset) {
+      throw new RuntimeException("Stub called.");
+    }
+  }
+
+  // Workaround Android S issue with compareAndSwapObject (b/211646483).
+  public static boolean compareAndSwapObject(
+      UnsafeStub unsafe, Object receiver, long offset, Object expect, Object update) {
+    do {
+      if (unsafe.compareAndSwapObject(receiver, offset, expect, update)) {
+        return true;
+      }
+    } while (unsafe.getObject(receiver, offset) == expect);
+    return false;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InterfaceInvokeWithObjectReceiverInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InterfaceInvokeWithObjectReceiverInliningTest.java
index cc1ded1..d74eb4b 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InterfaceInvokeWithObjectReceiverInliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InterfaceInvokeWithObjectReceiverInliningTest.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
 import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
 import java.io.IOException;
 import java.util.List;
 import org.junit.Test;
@@ -23,6 +24,8 @@
 @RunWith(Parameterized.class)
 public class InterfaceInvokeWithObjectReceiverInliningTest extends TestBase {
 
+  private static final String EXPECTED = StringUtils.lines("0", "0");
+
   @Parameter(0)
   public boolean enableInlining;
 
@@ -48,7 +51,7 @@
         .addProgramClasses(I.class, A.class)
         .addProgramClassFileData(getTransformedMain())
         .run(parameters.getRuntime(), Main.class)
-        .assertSuccessWithOutputLines("0");
+        .assertSuccessWithOutput(EXPECTED);
   }
 
   @Test
@@ -75,7 +78,7 @@
         .setMinApi(parameters.getApiLevel())
         .compile()
         .run(parameters.getRuntime(), Main.class)
-        .assertSuccessWithOutputLines("0", "0");
+        .assertSuccessWithOutput(EXPECTED);
   }
 
   private static byte[] getTransformedMain() throws IOException {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantInitClassBeforeInvokeStaticTest.java b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantInitClassBeforeInvokeStaticTest.java
new file mode 100644
index 0000000..d7e4296
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantInitClassBeforeInvokeStaticTest.java
@@ -0,0 +1,78 @@
+// 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.redundantfieldloadelimination;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static junit.framework.TestCase.assertEquals;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import 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 RedundantInitClassBeforeInvokeStaticTest 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)
+        .enableInliningAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspect(
+            inspector -> {
+              ClassSubject greeterClassSubject = inspector.clazz(Greeter.class);
+              assertThat(greeterClassSubject, isPresent());
+              assertThat(greeterClassSubject.uniqueMethodWithName("hello"), isAbsent());
+              assertThat(greeterClassSubject.uniqueMethodWithName("world"), isPresent());
+              assertEquals(0, greeterClassSubject.allFields().size());
+            })
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("Hello world!");
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      // The inlining of hello() will materialize an init-class instruction for Greeter. This
+      // instruction should be removed since it immediately precedes another instruction that
+      // initializes Greeter.
+      Greeter.hello();
+      Greeter.world();
+    }
+  }
+
+  static class Greeter {
+
+    static {
+      System.out.print("Hello");
+    }
+
+    static void hello() {}
+
+    @NeverInline
+    static void world() {
+      System.out.println(" world!");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithFeatureSplitTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithFeatureSplitTest.java
index 1183c1f..87fe5f0 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageWithFeatureSplitTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithFeatureSplitTest.java
@@ -12,6 +12,7 @@
 
 import com.android.tools.r8.NeverInline;
 import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.apimodel.ApiModelingTestHelper;
 import com.android.tools.r8.dexsplitter.SplitterTestBase.RunInterface;
 import com.android.tools.r8.dexsplitter.SplitterTestBase.SplitRunner;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -45,6 +46,8 @@
         .addFeatureSplitRuntime()
         .addKeepFeatureMainRule(FeatureMain.class)
         .apply(this::configureRepackaging)
+        // BaseDexClassLoader was introduced at api level 14.
+        .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
         .enableInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
         .compile()