Merge commit '9e1b041d190874407fea685fe7dc6e4701d5d79e' into dev-release
diff --git a/infra/config/global/generated/cr-buildbucket.cfg b/infra/config/global/generated/cr-buildbucket.cfg
index ebe76f2..73e65b4 100644
--- a/infra/config/global/generated/cr-buildbucket.cfg
+++ b/infra/config/global/generated/cr-buildbucket.cfg
@@ -24,7 +24,7 @@
       swarming_tags: "vpython:native-python-wrapper"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -53,7 +53,7 @@
       swarming_tags: "vpython:native-python-wrapper"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -82,7 +82,7 @@
       swarming_tags: "vpython:native-python-wrapper"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -118,7 +118,7 @@
       swarming_tags: "vpython:native-python-wrapper"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -156,7 +156,7 @@
       swarming_tags: "vpython:native-python-wrapper"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -190,7 +190,7 @@
       swarming_tags: "vpython:native-python-wrapper"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -222,7 +222,7 @@
       swarming_tags: "vpython:native-python-wrapper"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -255,7 +255,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -292,7 +292,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -329,7 +329,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -366,7 +366,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -403,7 +403,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -440,7 +440,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -477,7 +477,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -514,7 +514,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -551,7 +551,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -588,7 +588,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -625,7 +625,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -662,7 +662,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -699,7 +699,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -736,7 +736,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -773,7 +773,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -810,7 +810,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -847,7 +847,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -884,7 +884,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -921,7 +921,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -958,7 +958,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -995,7 +995,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -1031,7 +1031,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -1067,7 +1067,7 @@
       dimensions: "cores:2"
       dimensions: "cpu:x86-64"
       dimensions: "internal:true"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -1100,7 +1100,7 @@
       dimensions: "cores:2"
       dimensions: "cpu:x86-64"
       dimensions: "internal:true"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -1133,7 +1133,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -1169,7 +1169,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -1205,7 +1205,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -1241,7 +1241,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -1277,7 +1277,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -1313,7 +1313,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -1349,7 +1349,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -1385,7 +1385,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -1420,7 +1420,7 @@
       swarming_tags: "vpython:native-python-wrapper"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -1457,7 +1457,7 @@
       swarming_tags: "vpython:native-python-wrapper"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -1495,7 +1495,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -1531,7 +1531,7 @@
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
       dimensions: "normal:true"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -1566,7 +1566,7 @@
       swarming_tags: "vpython:native-python-wrapper"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
@@ -1598,7 +1598,7 @@
       swarming_tags: "vpython:native-python-wrapper"
       dimensions: "cores:8"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Ubuntu-16.04"
+      dimensions: "os:Ubuntu-18.04"
       dimensions: "pool:luci.r8.ci"
       exe {
         cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build_limited/scripts/slave"
diff --git a/infra/config/global/main.star b/infra/config/global/main.star
index 73e0802..8574aa5 100755
--- a/infra/config/global/main.star
+++ b/infra/config/global/main.star
@@ -170,7 +170,7 @@
   if windows:
     dimensions["os"] = "Windows-10"
   else:
-    dimensions["os"] = "Ubuntu-16.04"
+    dimensions["os"] = "Ubuntu-18.04"
   if internal:
     dimensions["internal"] = "true"
   if normal:
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 9c659f5..dc99fda 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -35,7 +35,6 @@
 import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagator;
 import com.android.tools.r8.optimize.interfaces.collection.OpenClosedInterfacesCollection;
 import com.android.tools.r8.profile.art.ArtProfileCollection;
-import com.android.tools.r8.retrace.internal.RetraceUtils;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.shaking.AssumeInfoCollection;
 import com.android.tools.r8.shaking.KeepClassInfo;
@@ -983,9 +982,7 @@
   }
 
   public void addPrunedClassSourceFile(DexType prunedType, String sourceFile) {
-    if (!RetraceUtils.hasPredictableSourceFileName(prunedType.toSourceString(), sourceFile)) {
-      sourceFileForPrunedTypes.put(prunedType, sourceFile);
-    }
+    sourceFileForPrunedTypes.put(prunedType, sourceFile);
   }
 
   public String getPrunedClassSourceFileInfo(DexType dexType) {
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLens.java b/src/main/java/com/android/tools/r8/graph/GraphLens.java
index 188211d..46d91ef 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -386,6 +386,26 @@
     return false;
   }
 
+  // Predicate to see if a method definition is only changed by repackaging or synthetic
+  // finalization indicating that it is a simple renaming.
+  public final boolean isSimpleRenaming(DexMethod method) {
+    DexMethod methodToCompareAgainst = method;
+    DexMethod original = method;
+    GraphLens current = this;
+    while (current.isNonIdentityLens()) {
+      NonIdentityGraphLens nonIdentityLens = current.asNonIdentityLens();
+      original = nonIdentityLens.getPreviousMethodSignature(original);
+      if (current.isSimpleRenamingLens()) {
+        methodToCompareAgainst = original;
+      } else if (methodToCompareAgainst != original) {
+        return false;
+      }
+      assert nonIdentityLens.getPrevious() != null;
+      current = nonIdentityLens.getPrevious();
+    }
+    return true;
+  }
+
   public abstract String lookupPackageName(String pkg);
 
   public DexType lookupClassType(DexType type) {
@@ -572,6 +592,10 @@
     return false;
   }
 
+  public boolean isSimpleRenamingLens() {
+    return false;
+  }
+
   public abstract boolean isIdentityLens();
 
   public boolean isMemberRebindingLens() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InvokeToPrivateRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InvokeToPrivateRewriter.java
new file mode 100644
index 0000000..65f23dc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InvokeToPrivateRewriter.java
@@ -0,0 +1,76 @@
+// 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.ir.desugar;
+
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * If an invoke-virtual targets a private method in the current class overriding will not apply (see
+ * JVM 11 spec on method selection 5.4.6. In previous jvm specs this was not explicitly stated, but
+ * derived from method resolution 5.4.3.3 and overriding 5.4.5).
+ *
+ * <p>An invoke-interface can in the same way target a private method.
+ *
+ * <p>For desugaring we use invoke-direct instead. We need to do this as the Android Runtime will
+ * not allow invoke-virtual of a private method.
+ */
+public class InvokeToPrivateRewriter implements CfInstructionDesugaring {
+
+  @Override
+  public Collection<CfInstruction> desugarInstruction(
+      CfInstruction instruction,
+      FreshLocalProvider freshLocalProvider,
+      LocalStackAllocator localStackAllocator,
+      CfInstructionDesugaringEventConsumer eventConsumer,
+      ProgramMethod context,
+      MethodProcessingContext methodProcessingContext,
+      CfInstructionDesugaringCollection desugaringCollection,
+      DexItemFactory dexItemFactory) {
+    if (!instruction.isInvokeVirtual() && !instruction.isInvokeInterface()) {
+      return null;
+    }
+    CfInvoke invoke = instruction.asInvoke();
+    DexMethod method = invoke.getMethod();
+    DexEncodedMethod privateMethod = privateMethodInvokedOnSelf(invoke, context);
+    if (privateMethod == null) {
+      return null;
+    }
+    return ImmutableList.of(new CfInvoke(Opcodes.INVOKESPECIAL, method, invoke.isInterface()));
+  }
+
+  @Override
+  public boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) {
+    if (!instruction.isInvokeVirtual() && !instruction.isInvokeInterface()) {
+      return false;
+    }
+    return isInvokingPrivateMethodOnSelf(instruction.asInvoke(), context);
+  }
+
+  private DexEncodedMethod privateMethodInvokedOnSelf(CfInvoke invoke, ProgramMethod context) {
+    DexMethod method = invoke.getMethod();
+    if (method.getHolderType() != context.getHolderType()) {
+      return null;
+    }
+    DexEncodedMethod directTarget = context.getHolder().lookupDirectMethod(method);
+    if (directTarget != null && !directTarget.isStatic()) {
+      assert method.holder == directTarget.getHolderType();
+      return directTarget;
+    }
+    return null;
+  }
+
+  private boolean isInvokingPrivateMethodOnSelf(CfInvoke invoke, ProgramMethod context) {
+    return privateMethodInvokedOnSelf(invoke, context) != null;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
index 4ee8f70..aa8ca78 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
@@ -121,6 +121,9 @@
     desugarings.add(new LambdaInstructionDesugaring(appView));
     desugarings.add(new ConstantDynamicInstructionDesugaring(appView));
     desugarings.add(new InvokeSpecialToSelfDesugaring(appView));
+    if (appView.options().rewriteInvokeToPrivateInDesugar) {
+      desugarings.add(new InvokeToPrivateRewriter());
+    }
     desugarings.add(new StringConcatInstructionDesugaring(appView));
     desugarings.add(new BufferCovariantReturnTypeRewriter(appView));
     if (backportedMethodRewriter.hasBackports()) {
@@ -358,7 +361,8 @@
           //  identification is explicitly non-overlapping and remove the exceptions below.
           assert !alsoApplicable
                   || (appliedDesugaring instanceof InterfaceMethodRewriter
-                      && desugaring instanceof NestBasedAccessDesugaring)
+                      && (desugaring instanceof InvokeToPrivateRewriter
+                          || desugaring instanceof NestBasedAccessDesugaring))
                   || (appliedDesugaring instanceof TwrInstructionDesugaring
                       && desugaring instanceof InterfaceMethodRewriter)
               : "Desugaring of "
diff --git a/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java b/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java
index 5b0abaa..0b872f0 100644
--- a/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java
+++ b/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java
@@ -88,8 +88,11 @@
       throw new MappingComposeException(
           "Composition of mapping files supported from map version 2.2.");
     }
-    if (currentMapVersion == null || currentMapVersion.getMapVersion().isLessThan(newMapVersion)) {
+    if (currentMapVersion == null) {
       currentMapVersion = newMapVersionInfo;
+    } else {
+      currentMapVersion =
+          newMapVersionInfo.compose(currentMapVersion).asMapVersionMappingInformation();
     }
     for (ClassNamingForNameMapper classMapping : classNameMapper.getClassNameMappings().values()) {
       compose(classNameMapper, classMapping);
@@ -829,7 +832,8 @@
         List<MappedRange> existingMappedRanges,
         ComputedOutlineInformation computedOutlineInformation,
         int lastStartingMinifiedFrom,
-        int position) {
+        int position)
+        throws MappingComposeException {
       Range existingRange = existingMappedRanges.get(0).minifiedRange;
       assert existingMappedRanges.stream().allMatch(x -> x.minifiedRange.equals(existingRange));
       Range newMinifiedRange = new Range(lastStartingMinifiedFrom, position);
@@ -883,19 +887,33 @@
     }
 
     /***
-     * Populates newMappingInformation with existingMappingInformation, prioritizing new mapping
-     * information if new mapping information does not allow existing information.
+     * Populates newMappingInformation with existingMappingInformation.
      */
     private void composeMappingInformation(
         List<MappingInformation> newMappingInformation,
         List<MappingInformation> existingMappingInformation,
-        Consumer<MappingInformation> consumer) {
-      List<MappingInformation> existingMappingCopy = new ArrayList<>(existingMappingInformation);
-      for (MappingInformation newMappingInfo : newMappingInformation) {
-        existingMappingCopy.removeIf(otherInfo -> !newMappingInfo.allowOther(otherInfo));
+        Consumer<MappingInformation> consumer)
+        throws MappingComposeException {
+      Set<MappingInformation> nonCompasableNewInfos = Sets.newIdentityHashSet();
+      for (MappingInformation existingInfo : existingMappingInformation) {
+        boolean hasBeenComposed = false;
+        for (MappingInformation newInfo : newMappingInformation) {
+          if (newInfo.shouldCompose(existingInfo)) {
+            nonCompasableNewInfos.add(newInfo);
+            consumer.accept(newInfo.compose(existingInfo));
+            hasBeenComposed = true;
+          }
+        }
+        if (!hasBeenComposed) {
+          consumer.accept(existingInfo);
+        }
       }
-      existingMappingCopy.forEach(consumer);
-      newMappingInformation.forEach(consumer);
+      newMappingInformation.forEach(
+          info -> {
+            if (!nonCompasableNewInfos.contains(info)) {
+              consumer.accept(info);
+            }
+          });
     }
 
     private boolean isInlineMappedRange(List<MappedRange> mappedRanges, int index) {
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/CompilerSynthesizedMappingInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/CompilerSynthesizedMappingInformation.java
index 7f82be1..b1d1eff 100644
--- a/src/main/java/com/android/tools/r8/naming/mappinginformation/CompilerSynthesizedMappingInformation.java
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/CompilerSynthesizedMappingInformation.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.naming.mappinginformation;
 
 import com.android.tools.r8.naming.MapVersion;
+import com.android.tools.r8.naming.MappingComposeException;
 import com.google.gson.JsonObject;
 import com.google.gson.JsonPrimitive;
 import java.util.function.Consumer;
@@ -47,6 +48,12 @@
   }
 
   @Override
+  public MappingInformation compose(MappingInformation existing) throws MappingComposeException {
+    // There is no difference between taking the existing or new so we just do a consistent choice.
+    return existing;
+  }
+
+  @Override
   public boolean allowOther(MappingInformation information) {
     return !information.isCompilerSynthesizedMappingInformation();
   }
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/FileNameInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/FileNameInformation.java
index e4f3490..828b5d8 100644
--- a/src/main/java/com/android/tools/r8/naming/mappinginformation/FileNameInformation.java
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/FileNameInformation.java
@@ -6,6 +6,7 @@
 
 import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.naming.MapVersion;
+import com.android.tools.r8.naming.MappingComposeException;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
 import com.google.gson.JsonPrimitive;
@@ -42,6 +43,12 @@
   }
 
   @Override
+  public MappingInformation compose(MappingInformation existing) throws MappingComposeException {
+    // Always take the first mapping.
+    return existing;
+  }
+
+  @Override
   public boolean allowOther(MappingInformation information) {
     return !information.isFileNameInformation();
   }
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/MapVersionMappingInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/MapVersionMappingInformation.java
index 32f11f4..f061912 100644
--- a/src/main/java/com/android/tools/r8/naming/mappinginformation/MapVersionMappingInformation.java
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/MapVersionMappingInformation.java
@@ -7,6 +7,7 @@
 import static com.android.tools.r8.naming.mappinginformation.MappingInformationDiagnostics.noKeyForObjectWithId;
 
 import com.android.tools.r8.naming.MapVersion;
+import com.android.tools.r8.naming.MappingComposeException;
 import com.google.gson.JsonObject;
 import com.google.gson.JsonPrimitive;
 import java.util.function.Consumer;
@@ -40,6 +41,13 @@
   }
 
   @Override
+  public MappingInformation compose(MappingInformation existing) throws MappingComposeException {
+    assert existing.isMapVersionMappingInformation();
+    MapVersionMappingInformation existingMapVersionInfo = existing.asMapVersionMappingInformation();
+    return mapVersion.isLessThanOrEqualTo(existingMapVersionInfo.getMapVersion()) ? existing : this;
+  }
+
+  @Override
   public boolean allowOther(MappingInformation information) {
     return true;
   }
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java
index 25bb1f3..c62d4e6 100644
--- a/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java
@@ -6,6 +6,7 @@
 
 import com.android.tools.r8.DiagnosticsHandler;
 import com.android.tools.r8.naming.MapVersion;
+import com.android.tools.r8.naming.MappingComposeException;
 import com.android.tools.r8.naming.mappinginformation.ResidualSignatureMappingInformation.ResidualFieldSignatureMappingInformation;
 import com.android.tools.r8.naming.mappinginformation.ResidualSignatureMappingInformation.ResidualMethodSignatureMappingInformation;
 import com.google.gson.JsonElement;
@@ -96,6 +97,13 @@
     return null;
   }
 
+  public boolean shouldCompose(MappingInformation existing) {
+    return !allowOther(existing);
+  }
+
+  public abstract MappingInformation compose(MappingInformation existing)
+      throws MappingComposeException;
+
   public boolean isGlobalMappingInformation() {
     return false;
   }
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/OutlineCallsiteMappingInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/OutlineCallsiteMappingInformation.java
index 4bcfe95..b38d707 100644
--- a/src/main/java/com/android/tools/r8/naming/mappinginformation/OutlineCallsiteMappingInformation.java
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/OutlineCallsiteMappingInformation.java
@@ -6,6 +6,7 @@
 
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.naming.MapVersion;
+import com.android.tools.r8.naming.MappingComposeException;
 import com.android.tools.r8.references.MethodReference;
 import com.android.tools.r8.utils.MethodReferenceUtils;
 import com.google.gson.JsonElement;
@@ -68,6 +69,11 @@
     return this;
   }
 
+  @Override
+  public MappingInformation compose(MappingInformation existing) throws MappingComposeException {
+    throw new MappingComposeException("Unable to compose " + ID);
+  }
+
   public int rewritePosition(int originalPosition) {
     return positions.getOrDefault(originalPosition, originalPosition);
   }
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/OutlineMappingInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/OutlineMappingInformation.java
index 54c392b..09c60d9 100644
--- a/src/main/java/com/android/tools/r8/naming/mappinginformation/OutlineMappingInformation.java
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/OutlineMappingInformation.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.naming.mappinginformation;
 
 import com.android.tools.r8.naming.MapVersion;
+import com.android.tools.r8.naming.MappingComposeException;
 import com.google.gson.JsonObject;
 import com.google.gson.JsonPrimitive;
 import java.util.function.Consumer;
@@ -32,8 +33,14 @@
   }
 
   @Override
+  public MappingInformation compose(MappingInformation existing) throws MappingComposeException {
+    // It does not matter which one we take so just take the first one.
+    return existing;
+  }
+
+  @Override
   public boolean allowOther(MappingInformation information) {
-    return true;
+    return !information.isOutlineMappingInformation();
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/ResidualSignatureMappingInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/ResidualSignatureMappingInformation.java
index 9d6ac63..1c94da9 100644
--- a/src/main/java/com/android/tools/r8/naming/mappinginformation/ResidualSignatureMappingInformation.java
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/ResidualSignatureMappingInformation.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.naming.MapVersion;
+import com.android.tools.r8.naming.MappingComposeException;
 import com.android.tools.r8.utils.ArrayUtils;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.StringUtils;
@@ -114,6 +115,13 @@
     public ResidualMethodSignatureMappingInformation asResidualMethodSignatureMappingInformation() {
       return this;
     }
+
+    @Override
+    public MappingInformation compose(MappingInformation existing) throws MappingComposeException {
+      // Always take the newest residual mapping
+      assert existing.isResidualMethodSignatureMappingInformation();
+      return this;
+    }
   }
 
   public static class ResidualFieldSignatureMappingInformation
@@ -148,5 +156,12 @@
     public ResidualFieldSignatureMappingInformation asResidualFieldSignatureMappingInformation() {
       return this;
     }
+
+    @Override
+    public MappingInformation compose(MappingInformation existing) throws MappingComposeException {
+      // Always take the newest residual mapping.
+      assert existing.isResidualFieldSignatureMappingInformation();
+      return this;
+    }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/RewriteFrameMappingInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/RewriteFrameMappingInformation.java
index 00eb800..da0c1f3 100644
--- a/src/main/java/com/android/tools/r8/naming/mappinginformation/RewriteFrameMappingInformation.java
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/RewriteFrameMappingInformation.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.naming.MapVersion;
+import com.android.tools.r8.naming.MappingComposeException;
 import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.retrace.internal.RetraceStackTraceContextImpl;
@@ -105,6 +106,11 @@
     return this;
   }
 
+  @Override
+  public MappingInformation compose(MappingInformation existing) throws MappingComposeException {
+    throw new MappingComposeException("Unable to compose " + ID);
+  }
+
   public static RewriteFrameMappingInformation.Builder builder() {
     return new Builder();
   }
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/UnknownJsonMappingInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/UnknownJsonMappingInformation.java
index 848919b..3348063 100644
--- a/src/main/java/com/android/tools/r8/naming/mappinginformation/UnknownJsonMappingInformation.java
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/UnknownJsonMappingInformation.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.naming.mappinginformation;
 
 import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.naming.MappingComposeException;
 import com.google.gson.JsonObject;
 import java.util.function.Consumer;
 
@@ -47,6 +48,11 @@
     return this;
   }
 
+  @Override
+  public MappingInformation compose(MappingInformation existing) throws MappingComposeException {
+    throw new MappingComposeException("Unable to compose unknown json mapping information");
+  }
+
   public static void deserialize(
       String id, JsonObject object, Consumer<MappingInformation> onMappingInfo) {
     onMappingInfo.accept(new UnknownJsonMappingInformation(id, object.toString()));
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingLens.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingLens.java
index 64fe0eb..8b4ad76 100644
--- a/src/main/java/com/android/tools/r8/repackaging/RepackagingLens.java
+++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingLens.java
@@ -66,6 +66,11 @@
         this::isSimpleTypeRenamingOrEqual);
   }
 
+  @Override
+  public boolean isSimpleRenamingLens() {
+    return true;
+  }
+
   private boolean isSimpleTypeRenamingOrEqual(DexType from, DexType to) {
     return from == to || newTypes.get(from) == to;
   }
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceUtils.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceUtils.java
index 7c884e2..0180960 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceUtils.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceUtils.java
@@ -56,11 +56,6 @@
     return sb.toString();
   }
 
-  public static boolean hasPredictableSourceFileName(String originalClassName, String sourceFile) {
-    String synthesizedSourceFileName = getOuterClassSimpleName(originalClassName) + ".java";
-    return synthesizedSourceFileName.equals(sourceFile);
-  }
-
   private static String getOuterClassSimpleName(String clazz) {
     int lastIndexOfPeriod = clazz.lastIndexOf(DescriptorUtils.JAVA_PACKAGE_SEPARATOR);
     // Check if we can find a subclass separator.
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
index 0d767c6..54ef01b 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -101,6 +101,11 @@
     public boolean isSyntheticFinalizationGraphLens() {
       return true;
     }
+
+    @Override
+    public boolean isSimpleRenamingLens() {
+      return true;
+    }
   }
 
   private static class Builder {
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java b/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
index cbad6e6..cd7460b 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
@@ -77,6 +77,10 @@
     return DexVersion.getDexVersion(this);
   }
 
+  public boolean isPlatform() {
+    return this == ANDROID_PLATFORM;
+  }
+
   public static List<AndroidApiLevel> getAndroidApiLevelsSorted() {
     return Arrays.asList(AndroidApiLevel.values());
   }
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 06c4094..7369593 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
@@ -94,7 +94,7 @@
     // running with boot class path as min api and all definitions are accessible at runtime.
     if (!androidApiLevelCompute.isEnabled()) {
       assert !options.apiModelingOptions().enableLibraryApiModeling;
-      return options.isAndroidPlatformBuild();
+      return options.isAndroidPlatformBuildOrMinApiPlatform();
     }
     assert options.apiModelingOptions().enableLibraryApiModeling;
     ComputedApiLevel apiLevel =
@@ -113,7 +113,7 @@
   }
 
   public static boolean isApiSafeForReference(LibraryDefinition definition, AppView<?> appView) {
-    if (appView.options().isAndroidPlatformBuild()) {
+    if (appView.options().isAndroidPlatformBuildOrMinApiPlatform()) {
       assert definition != null;
       return true;
     }
@@ -177,7 +177,7 @@
     }
     if (!appView.options().apiModelingOptions().isApiCallerIdentificationEnabled()) {
       // Conservatively bail out if we don't have api modeling.
-      return false;
+      return appView.options().isAndroidPlatformBuildOrMinApiPlatform();
     }
     LibraryClass newBaseLibraryClass = newBaseClass.asLibraryClass();
     if (isApiSafeForReference(newBaseLibraryClass, appView)) {
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 41cc946..3939dd5 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -272,13 +272,15 @@
 
   public void configureAndroidPlatformBuild(boolean isAndroidPlatformBuild) {
     assert !androidPlatformBuild;
+    if (isAndroidPlatformBuildOrMinApiPlatform()) {
+      apiModelingOptions().disableApiModeling();
+    }
     if (!isAndroidPlatformBuild) {
       return;
     }
-    androidPlatformBuild = isAndroidPlatformBuild;
     // Configure options according to platform build assumptions.
     // See go/r8platformflag and b/232073181.
-    apiModelingOptions().disableApiModeling();
+    androidPlatformBuild = isAndroidPlatformBuild;
     enableBackportMethods = false;
   }
 
@@ -286,6 +288,10 @@
     return androidPlatformBuild;
   }
 
+  public boolean isAndroidPlatformBuildOrMinApiPlatform() {
+    return androidPlatformBuild || minApiLevel.isPlatform();
+  }
+
   public boolean printTimes = System.getProperty("com.android.tools.r8.printtimes") != null;
   // To print memory one also have to enable printtimes.
   public boolean printMemory = System.getProperty("com.android.tools.r8.printmemory") != null;
@@ -416,6 +422,11 @@
   public boolean createSingletonsForStatelessLambdas =
       System.getProperty("com.android.tools.r8.createSingletonsForStatelessLambdas") != null;
 
+  // Flag to control the representation of stateless lambdas.
+  // See b/222081665 for context.
+  public boolean rewriteInvokeToPrivateInDesugar =
+      System.getProperty("com.android.tools.r8.rewriteInvokeToPrivateInDesugar") != null;
+
   // Flag to allow record annotations in DEX. See b/231930852 for context.
   public boolean emitRecordAnnotationsInDex =
       System.getProperty("com.android.tools.r8.emitRecordAnnotationsInDex") != null;
@@ -1856,6 +1867,7 @@
     public boolean roundtripThroughLIR = false;
     private boolean hasReadCheckDeterminism = false;
     private DeterminismChecker determinismChecker = null;
+    public boolean usePcEncodingInCfForTesting = false;
 
     // Testing options to analyse locality of items in DEX files when they are generated.
     public boolean calculateItemUseCountInDex = false;
diff --git a/src/main/java/com/android/tools/r8/utils/SegmentTree.java b/src/main/java/com/android/tools/r8/utils/SegmentTree.java
index 9962b1c..c68a0c2 100644
--- a/src/main/java/com/android/tools/r8/utils/SegmentTree.java
+++ b/src/main/java/com/android/tools/r8/utils/SegmentTree.java
@@ -85,6 +85,9 @@
     if (internalTree.remove(start) != null) {
       size = size - 1;
     }
+    if (size == 0) {
+      internalTree.clear();
+    }
   }
 
   public void visitSegments(Consumer<V> consumer) {
diff --git a/src/main/java/com/android/tools/r8/utils/positions/ClassFilePositionToMappedRangeMapper.java b/src/main/java/com/android/tools/r8/utils/positions/ClassFilePositionToMappedRangeMapper.java
index b84e916..cd6efb7 100644
--- a/src/main/java/com/android/tools/r8/utils/positions/ClassFilePositionToMappedRangeMapper.java
+++ b/src/main/java/com/android/tools/r8/utils/positions/ClassFilePositionToMappedRangeMapper.java
@@ -7,10 +7,13 @@
 import static com.android.tools.r8.utils.positions.PositionUtils.remapAndAdd;
 
 import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfLabel;
 import com.android.tools.r8.cf.code.CfPosition;
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.CfCode;
 import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.utils.Pair;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -29,6 +32,18 @@
       boolean hasOverloads,
       boolean canUseDexPc,
       int pcEncodingCutoff) {
+    return appView.options().getTestingOptions().usePcEncodingInCfForTesting
+        ? getPcEncodedPositions(method, positionRemapper)
+        : getMappedPositionsRemapped(method, positionRemapper);
+  }
+
+  @Override
+  public void updateDebugInfoInCodeObjects() {
+    // Intentionally empty.
+  }
+
+  private List<MappedPosition> getMappedPositionsRemapped(
+      ProgramMethod method, PositionRemapper positionRemapper) {
     List<MappedPosition> mappedPositions = new ArrayList<>();
     // Do the actual processing for each method.
     CfCode oldCode = method.getDefinition().getCode().asCfCode();
@@ -36,8 +51,8 @@
     List<CfInstruction> newInstructions = new ArrayList<>(oldInstructions.size());
     for (CfInstruction oldInstruction : oldInstructions) {
       CfInstruction newInstruction;
-      if (oldInstruction instanceof CfPosition) {
-        CfPosition cfPosition = (CfPosition) oldInstruction;
+      if (oldInstruction.isPosition()) {
+        CfPosition cfPosition = oldInstruction.asPosition();
         newInstruction =
             new CfPosition(
                 cfPosition.getLabel(),
@@ -59,8 +74,64 @@
     return mappedPositions;
   }
 
-  @Override
-  public void updateDebugInfoInCodeObjects() {
-    // Intentionally empty.
+  private List<MappedPosition> getPcEncodedPositions(
+      ProgramMethod method, PositionRemapper positionRemapper) {
+    List<MappedPosition> mappedPositions = new ArrayList<>();
+    // Do the actual processing for each method.
+    CfCode oldCode = method.getDefinition().getCode().asCfCode();
+    List<CfInstruction> oldInstructions = oldCode.getInstructions();
+    List<CfInstruction> newInstructions = new ArrayList<>(oldInstructions.size() * 3);
+    Position currentPosition = null;
+    boolean isFirstEntry = false;
+    for (CfInstruction oldInstruction : oldInstructions) {
+      if (oldInstruction.isPosition()) {
+        CfPosition cfPosition = oldInstruction.asPosition();
+        currentPosition = cfPosition.getPosition();
+        isFirstEntry = true;
+      } else {
+        if (currentPosition != null) {
+          Pair<Position, Position> remappedPosition =
+              positionRemapper.createRemappedPosition(currentPosition);
+          Position oldPosition = remappedPosition.getFirst();
+          Position newPosition = remappedPosition.getSecond();
+          if (isFirstEntry) {
+            mappedPositions.add(
+                new MappedPosition(
+                    oldPosition.getMethod(),
+                    oldPosition.getLine(),
+                    oldPosition.getCallerPosition(),
+                    newPosition.getLine(),
+                    oldPosition.isOutline(),
+                    oldPosition.getOutlineCallee(),
+                    oldPosition.getOutlinePositions()));
+          } else {
+            mappedPositions.add(
+                new MappedPosition(
+                    oldPosition.getMethod(),
+                    oldPosition.getLine(),
+                    oldPosition.getCallerPosition(),
+                    newPosition.getLine(),
+                    false,
+                    null,
+                    null));
+          }
+          CfPosition position = new CfPosition(new CfLabel(), newPosition);
+          newInstructions.add(position);
+          newInstructions.add(position.getLabel());
+        }
+        isFirstEntry = false;
+        newInstructions.add(oldInstruction);
+      }
+    }
+    method.setCode(
+        new CfCode(
+            method.getHolderType(),
+            oldCode.getMaxStack(),
+            oldCode.getMaxLocals(),
+            newInstructions,
+            oldCode.getTryCatchRanges(),
+            oldCode.getLocalVariables()),
+        appView);
+    return mappedPositions;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java b/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java
index 5c23d90..2b51ed8 100644
--- a/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java
+++ b/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java
@@ -41,7 +41,6 @@
 import com.android.tools.r8.naming.mappinginformation.RewriteFrameMappingInformation.ThrowsCondition;
 import com.android.tools.r8.references.MethodReference;
 import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.retrace.internal.RetraceUtils;
 import com.android.tools.r8.synthesis.SyntheticItems;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
@@ -108,7 +107,6 @@
           assert appView.appInfo().definitionForWithoutExistenceAssert(holder) == null;
           String typeName = holder.toSourceString();
           String sourceFile = entry.getValue();
-          assert !RetraceUtils.hasPredictableSourceFileName(typeName, sourceFile);
           classNameMapperBuilder
               .classNamingBuilder(
                   typeName, typeName, com.android.tools.r8.position.Position.UNKNOWN)
@@ -150,11 +148,9 @@
       // Check if source file should be added to the map
       DexString originalSourceFile = originalSourceFiles.getOriginalSourceFile(clazz);
       if (originalSourceFile != null) {
-        String sourceFile = originalSourceFile.toString();
-        if (!RetraceUtils.hasPredictableSourceFileName(clazz.toSourceString(), sourceFile)) {
-          getBuilder()
-              .addMappingInformation(FileNameInformation.build(sourceFile), Unreachable::raise);
-        }
+        getBuilder()
+            .addMappingInformation(
+                FileNameInformation.build(originalSourceFile.toSourceString()), Unreachable::raise);
       }
       return this;
     }
@@ -219,13 +215,12 @@
             || appView.isCfByteCodePassThrough(definition);
         return this;
       }
-      // TODO(b/169953605): Ensure we emit the residual signature information.
       if (mapFileVersion.isGreaterThan(MapVersion.MAP_VERSION_2_1)
-          && originalMethod != method.getReference()) {
+          && originalMethod != method.getReference()
+          && !appView.graphLens().isSimpleRenaming(residualMethod)) {
         methodMappingInfo.add(
             ResidualMethodSignatureMappingInformation.fromDexMethod(residualMethod));
       }
-
       MethodSignature residualSignature = MethodSignature.fromDexMethod(residualMethod);
 
       MemberNaming memberNaming = new MemberNaming(originalSignature, residualSignature);
@@ -234,9 +229,7 @@
       // Add simple "a() -> b" mapping if we won't have any other with concrete line numbers
       if (mappedPositions.isEmpty()) {
         MappedRange range =
-            getBuilder()
-                .addMappedRange(
-                    null, originalSignature, null, residualSignature.getName().toString());
+            getBuilder().addMappedRange(null, originalSignature, null, residualSignature.getName());
         methodMappingInfo.forEach(info -> range.addMappingInformation(info, Unreachable::raise));
         return this;
       }
@@ -495,6 +488,10 @@
       return this == OUT_OF_RANGE;
     }
 
+    private boolean isSameDelta() {
+      return this == SAME_DELTA;
+    }
+
     public MappedPositionRange canAddNextMappingToRange(
         MappedPosition lastPosition, MappedPosition currentPosition) {
       if (isOutOfRange()) {
@@ -508,9 +505,12 @@
       boolean hasSameRightHandSide =
           lastPosition.getOriginalLine() == currentPosition.getOriginalLine();
       if (hasSameRightHandSide) {
+        if (isSameDelta()) {
+          return OUT_OF_RANGE;
+        }
         boolean hasSameLeftHandSide =
             lastPosition.getObfuscatedLine() == currentPosition.getObfuscatedLine();
-        return hasSameLeftHandSide ? SINGLE_LINE : RANGE_TO_SINGLE;
+        return (hasSameLeftHandSide && isSingleLine()) ? SINGLE_LINE : RANGE_TO_SINGLE;
       }
       if (isRangeToSingle()) {
         // We cannot recover a delta encoding if we have had range to single encoding.
diff --git a/src/test/java/com/android/tools/r8/desugar/InterfaceInvokePrivateTest.java b/src/test/java/com/android/tools/r8/desugar/InterfaceInvokePrivateTest.java
index 9cb8615..4055ea0 100644
--- a/src/test/java/com/android/tools/r8/desugar/InterfaceInvokePrivateTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/InterfaceInvokePrivateTest.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.TestRuntime.CfVm;
 import com.android.tools.r8.cf.CfVersion;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.BooleanUtils;
 import com.android.tools.r8.utils.StringUtils;
 import java.io.IOException;
 import org.junit.Test;
@@ -29,11 +30,16 @@
   @Parameter(1)
   public CfVersion inputCfVersion;
 
-  @Parameterized.Parameters(name = "{0}, Input CfVersion = {1}")
+  @Parameter(2)
+  public boolean rewriteInvokeToPrivateInDesugar;
+
+  @Parameterized.Parameters(
+      name = "{0}, Input CfVersion = {1}, rewriteInvokeToPrivateInDesugar = {2}")
   public static Iterable<?> data() {
     return buildParameters(
         getTestParameters().withCfRuntimes().withDexRuntimes().withAllApiLevelsAlsoForCf().build(),
-        CfVersion.rangeInclusive(CfVersion.V1_8, CfVersion.V15));
+        CfVersion.rangeInclusive(CfVersion.V1_8, CfVersion.V15),
+        BooleanUtils.values());
   }
 
   private static final String EXPECTED_OUTPUT = StringUtils.unixLines("Hello, world!", "21", "6");
@@ -47,6 +53,7 @@
   public void testReference() throws Exception {
     assumeTrue(parameters.getRuntime().isCf());
     assumeTrue(parameters.getApiLevel().isEqualTo(AndroidApiLevel.B));
+    assumeTrue(rewriteInvokeToPrivateInDesugar);
 
     testForJvm()
         .addProgramClassFileData(transformIToPrivate(inputCfVersion))
@@ -69,7 +76,9 @@
 
   @Test
   public void testDesugar() throws Exception {
-    testForDesugaring(parameters)
+    testForDesugaring(
+            parameters,
+            options -> options.rewriteInvokeToPrivateInDesugar = rewriteInvokeToPrivateInDesugar)
         .addProgramClassFileData(transformIToPrivate(inputCfVersion))
         .addProgramClasses(TestRunner.class)
         .run(parameters.getRuntime(), TestRunner.class)
@@ -91,7 +100,8 @@
                 parameters.getRuntime().isCf()
                     && parameters.getRuntime().asCf().isOlderThan(CfVm.JDK11)
                     && (DesugarTestConfiguration.isNotDesugared(c)
-                        || parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N)),
+                        || (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N)
+                            && !rewriteInvokeToPrivateInDesugar)),
             r -> r.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class),
             // All other conditions succeed.
             r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT));
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/R8WithCfPcEncodingTest.java b/src/test/java/com/android/tools/r8/mappingcompose/R8WithCfPcEncodingTest.java
new file mode 100644
index 0000000..99ea77c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/mappingcompose/R8WithCfPcEncodingTest.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.mappingcompose;
+
+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.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.mappingcompose.testclasses.MainWithHelloWorld;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.retrace.RetraceStackTraceContext;
+import com.android.tools.r8.retrace.RetracedMethodReference;
+import com.android.tools.r8.retrace.Retracer;
+import com.android.tools.r8.utils.codeinspector.LineNumberTable;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableSet;
+import java.util.HashSet;
+import java.util.OptionalInt;
+import java.util.Set;
+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 R8WithCfPcEncodingTest extends TestBase {
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withCfRuntimes().build();
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(MainWithHelloWorld.class)
+        .setMinApi(parameters.getApiLevel())
+        .addKeepMainRule(MainWithHelloWorld.class)
+        .addKeepAttributeLineNumberTable()
+        .addOptionsModification(
+            options -> options.getTestingOptions().usePcEncodingInCfForTesting = true)
+        .run(parameters.getRuntime(), MainWithHelloWorld.class)
+        .assertSuccessWithOutputLines("Hello World!")
+        .inspect(
+            inspector -> {
+              MethodSubject subject = inspector.clazz(MainWithHelloWorld.class).mainMethod();
+              assertThat(subject, isPresent());
+              LineNumberTable lineNumberTable = subject.getLineNumberTable();
+              Set<Integer> lines = new HashSet<>(lineNumberTable.getLines());
+              assertEquals(ImmutableSet.of(1, 2, 3, 4), lines);
+              Retracer retracer = inspector.getRetracer();
+              MethodReference methodReference = subject.getFinalReference();
+              Set<Integer> retracedLines = new HashSet<>();
+              lines.forEach(
+                  line -> {
+                    retracer
+                        .retraceFrame(
+                            RetraceStackTraceContext.empty(), OptionalInt.of(line), methodReference)
+                        .forEach(
+                            element -> {
+                              element.forEachRewritten(
+                                  singleFrame -> {
+                                    RetracedMethodReference retracedReference =
+                                        singleFrame.getMethodReference();
+                                    assertTrue(retracedReference.isKnown());
+                                    assertEquals(
+                                        methodReference,
+                                        retracedReference.asKnown().getMethodReference());
+                                    retracedLines.add(
+                                        retracedReference.getOriginalPositionOrDefault(-1));
+                                  });
+                            });
+                  });
+              assertEquals(ImmutableSet.of(10), retracedLines);
+            });
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/testclasses/MainWithHelloWorld.java b/src/test/java/com/android/tools/r8/mappingcompose/testclasses/MainWithHelloWorld.java
new file mode 100644
index 0000000..853dbee
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/mappingcompose/testclasses/MainWithHelloWorld.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.mappingcompose.testclasses;
+
+public class MainWithHelloWorld {
+
+  public static void main(String[] args) {
+    System.out.println("Hello World!");
+  }
+}
diff --git a/tools/compare_apk_sizes.py b/tools/compare_apk_sizes.py
index 1890283..ad75024 100755
--- a/tools/compare_apk_sizes.py
+++ b/tools/compare_apk_sizes.py
@@ -25,6 +25,10 @@
 
 def parse_options():
   result = optparse.OptionParser(usage=USAGE)
+  result.add_option('--no-build',
+                    help='Run without building first',
+                    default=False,
+                    action='store_true')
   result.add_option('--temp',
                     help='Temporary directory to store extracted classes in')
   result.add_option('--use_code_size',
@@ -47,12 +51,14 @@
     if not os.path.exists(f):
       raise Exception('%s does not exist' % f)
 
-def extract_classes(input, output):
+def extract_classes(input, output, options):
   if os.path.exists(output):
     shutil.rmtree(output)
   os.makedirs(output)
   args = ['--file-per-class',
           '--output', output]
+  if options.no_build:
+    args.extend(['--no-build'])
   args.extend(input)
   if toolhelper.run('d8', args) is not 0:
     raise Exception('Failed running d8')
@@ -183,8 +189,8 @@
     app1_classes_dir = os.path.join(output, 'app1_classes')
     app2_classes_dir = os.path.join(output, 'app2_classes')
 
-    extract_classes(app1_input, app1_classes_dir)
-    extract_classes(app2_input, app2_classes_dir)
+    extract_classes(app1_input, app1_classes_dir, options)
+    extract_classes(app2_input, app2_classes_dir, options)
     compare(app1_classes_dir, app2_classes_dir, app1, app2, options)
 
 if __name__ == '__main__':