Merge commit 'afc0a18eecdd0a7cf6e185e73f1ea364ac0d4585' into dev-release

Change-Id: Ic192f94dc9ecec55448e7d5024cef9e05e914407
diff --git a/.gitignore b/.gitignore
index 2fcc852..062fe33 100644
--- a/.gitignore
+++ b/.gitignore
@@ -93,6 +93,8 @@
 third_party/dependencies.tar.gz
 third_party/dependencies_new/
 third_party/dependencies_new.tar.gz
+third_party/dependencies_plugin/
+third_party/dependencies_plugin.tar.gz
 third_party/desugar/desugar_*.tar.gz
 third_party/desugar/desugar_*/
 third_party/examples
diff --git a/d8_r8/main/build.gradle.kts b/d8_r8/main/build.gradle.kts
index 7ce6cc7..47b5da0 100644
--- a/d8_r8/main/build.gradle.kts
+++ b/d8_r8/main/build.gradle.kts
@@ -2,14 +2,25 @@
 // 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.
 
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+import java.net.URI
+import java.nio.file.Path
+import java.nio.file.Paths
+import kotlin.io.path.exists
+import java.nio.file.Files.readString
 import net.ltgt.gradle.errorprone.errorprone
+import org.gradle.api.artifacts.ModuleVersionIdentifier
 import org.gradle.api.artifacts.component.ModuleComponentIdentifier
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+import org.spdx.sbom.gradle.SpdxSbomTask
+import org.spdx.sbom.gradle.extensions.DefaultSpdxSbomTaskExtension
+
+import com.google.gson.Gson
 
 plugins {
   `kotlin-dsl`
   id("dependencies-plugin")
   id("net.ltgt.errorprone") version "3.0.1"
+  id("org.spdx.sbom") version "0.2.0-r8-patch01"
 }
 
 java {
@@ -35,6 +46,33 @@
   errorprone(Deps.errorprone)
 }
 
+if (project.hasProperty("spdxVersion")) {
+  project.version = project.property("spdxVersion")
+}
+
+spdxSbom {
+  targets {
+    create("r8") {
+      // Use of both compileClasspath and runtimeClasspath due to how the
+      // dependencies jar is built and dependencies above therefore use
+      // compileOnly for actual runtime dependencies.
+      configurations.set(listOf("compileClasspath", "runtimeClasspath"))
+      scm {
+        uri.set("https://r8.googlesource.com/r8/")
+        if (project.hasProperty("spdxRevision")) {
+          revision.set(project.property("spdxRevision").toString())
+        }
+      }
+      document {
+        name.set("R8 Compiler Suite")
+        namespace.set("https://r8.googlesource.com/r8/-" + project.version + ".jar")
+        creator.set("Organization: Google LLC")
+        packageSupplier.set("Organization: Google LLC")
+      }
+    }
+  }
+}
+
 val keepAnnoJarTask = projectTask("keepanno", "jar")
 val resourceShrinkerJarTask = projectTask("resourceshrinker", "jar")
 val resourceShrinkerDepsTask = projectTask("resourceshrinker", "depsJar")
@@ -61,6 +99,50 @@
     dependsOn(gradle.includedBuild("shared").task(":downloadDeps"))
   }
 
+  withType<SpdxSbomTask> {
+    taskExtension.set(object : DefaultSpdxSbomTaskExtension() {
+      override fun mapRepoUri(input: URI?, moduleId: ModuleVersionIdentifier): URI? {
+
+        // Locate the file origin.json with URL for download location.
+        fun getOriginJson() : java.nio.file.Path {
+          var repositoryDir =
+              moduleId.group.replace('.', '/') + "/" + moduleId.name + "/" + moduleId.version
+          val path : Path =
+              Paths.get("third_party", "dependencies", repositoryDir, "origin.json");
+          val path_new : Path =
+              Paths.get("third_party", "dependencies_new", repositoryDir, "origin.json");
+          return if (path.exists()) path else path_new
+        }
+
+        // Simple data model of the content of origin.json generated by the tool to download
+        // and create a local repository. E.g.:
+        /*
+            {
+              "artifacts": [
+                {
+                  "file": "org/ow2/asm/asm/9.5/asm-9.5.pom",
+                  "repo": "https://repo1.maven.org/maven2/",
+                  "artifact": "org.ow2.asm:asm:pom:9.5"
+                },
+                {
+                  "file": "org/ow2/asm/asm/9.5/asm-9.5.jar",
+                  "repo": "https://repo1.maven.org/maven2/",
+                  "artifact": "org.ow2.asm:asm:jar:9.5"
+                }
+              ]
+            }
+        */
+        data class Artifact(val file: String, val repo: String, val artifact: String)
+        data class Artifacts(val artifacts: List<Artifact>)
+
+        // Read origin.json.
+        val json = readString(getOriginJson());
+        val artifacts = Gson().fromJson(json, Artifacts::class.java);
+        return URI.create(artifacts.artifacts.get(0).repo)
+      }
+    })
+  }
+
   val consolidatedLicense by registering {
     dependsOn(gradle.includedBuild("shared").task(":downloadDeps"))
     val root = getRoot()
diff --git a/d8_r8/main/settings.gradle.kts b/d8_r8/main/settings.gradle.kts
index 4e1d975..d4f7ab8 100644
--- a/d8_r8/main/settings.gradle.kts
+++ b/d8_r8/main/settings.gradle.kts
@@ -5,7 +5,7 @@
 pluginManagement {
   repositories {
     maven {
-      url = uri("file:../../third_party/dependencies")
+      url = uri("file:../../third_party/dependencies_plugin")
     }
     maven {
       url = uri("file:../../third_party/dependencies_new")
diff --git a/d8_r8/settings.gradle.kts b/d8_r8/settings.gradle.kts
index ac9216a..ad7577f 100644
--- a/d8_r8/settings.gradle.kts
+++ b/d8_r8/settings.gradle.kts
@@ -51,7 +51,7 @@
   process.waitFor()
   if (process.exitValue() != 0) {
     throw GradleException(
-      "Bootstrapping dependencies_new download failed:\n"
+      "Bootstrapping ${outputDir} download failed:\n"
         + "${String(process.getErrorStream().readAllBytes(),
                     java.nio.charset.StandardCharsets.UTF_8)}\n"
         + String(process.getInputStream().readAllBytes(),
@@ -68,6 +68,7 @@
 val thirdParty = getRepoRoot().resolve("third_party")
 downloadFromGoogleStorage(thirdParty.resolve("dependencies"))
 downloadFromGoogleStorage(thirdParty.resolve("dependencies_new"))
+downloadFromGoogleStorage(thirdParty.resolve("dependencies_plugin"))
 
 pluginManagement {
   repositories {
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 79623e1..9964d6e 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -17,10 +17,8 @@
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppServices;
 import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.Code;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
@@ -28,7 +26,6 @@
 import com.android.tools.r8.graph.GenericSignatureContextBuilder;
 import com.android.tools.r8.graph.GenericSignatureCorrectnessHelper;
 import com.android.tools.r8.graph.ProgramDefinition;
-import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.PrunedItems;
 import com.android.tools.r8.graph.SubtypingInfo;
 import com.android.tools.r8.graph.analysis.ClassInitializerAssertionEnablingAnalysis;
@@ -780,8 +777,6 @@
       }
       appView.appInfo().notifyMinifierFinished();
 
-      assert verifyMovedMethodsHaveOriginalMethodPosition(appView, getDirectApp(appView));
-
       // If a method filter is present don't produce output since the application is likely partial.
       if (options.hasMethodsFilter()) {
         System.out.println("Finished compilation with method filter: ");
@@ -841,6 +836,8 @@
             options.androidResourceProvider, options.androidResourceConsumer, appView.reporter());
       }
 
+      assert appView.verifyMovedMethodsHaveOriginalMethodPosition();
+
       // Generate the resulting application resources.
       writeApplication(appView, inputApp, executorService);
 
@@ -946,57 +943,6 @@
         executorService);
   }
 
-  @SuppressWarnings("ReferenceEquality")
-  private static boolean verifyMovedMethodsHaveOriginalMethodPosition(
-      AppView<?> appView, DirectMappedDexApplication application) {
-    application
-        .classesWithDeterministicOrder()
-        .forEach(
-            clazz ->
-                clazz.forEachProgramMethod(
-                    method -> {
-                      DexMethod originalMethod =
-                          appView.graphLens().getOriginalMethodSignature(method.getReference());
-                      if (originalMethod != method.getReference()) {
-                        DexEncodedMethod definition = method.getDefinition();
-                        Code code = definition.getCode();
-                        if (code == null) {
-                          return;
-                        }
-                        if (code.isCfCode() || code.isDexCode()) {
-                          assert verifyOriginalMethodInPosition(code, originalMethod, method);
-                        } else {
-                          assert code.isDefaultInstanceInitializerCode() || code.isThrowNullCode();
-                        }
-                      }
-                    }));
-    return true;
-  }
-
-  @SuppressWarnings({"ComplexBooleanConstant", "ReferenceEquality"})
-  private static boolean verifyOriginalMethodInPosition(
-      Code code, DexMethod originalMethod, ProgramMethod context) {
-    code.forEachPosition(
-        originalMethod,
-        position -> {
-          if (position.isOutlineCaller()) {
-            // Check the outlined positions for the original method
-            position
-                .getOutlinePositions()
-                .forEach(
-                    (ignored, outlinePosition) -> {
-                      assert outlinePosition.hasMethodInChain(originalMethod);
-                    });
-          } else if (context.getDefinition().isD8R8Synthesized()) {
-            // TODO(b/261971803): Enable assert.
-            assert true || position.hasMethodInChain(originalMethod);
-          } else {
-            assert position.getOutermostCaller().getMethod() == originalMethod;
-          }
-        });
-    return true;
-  }
-
   private AppView<AppInfoWithLiveness> runEnqueuer(
       AnnotationRemover.Builder annotationRemoverBuilder,
       ExecutorService executorService,
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 af882b2..6c86022 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -987,6 +987,7 @@
       throws ExecutionException {
     rewriteWithLensAndApplication(
         lens, application, executorService, timing, withClassHierarchy(), lens.getPrevious());
+    assert verifyMovedMethodsHaveOriginalMethodPosition();
   }
 
   private static void rewriteWithLensAndApplication(
@@ -1295,4 +1296,33 @@
   public String getPrunedClassSourceFileInfo(DexType dexType) {
     return sourceFileForPrunedTypes.get(dexType);
   }
+
+  public boolean verifyMovedMethodsHaveOriginalMethodPosition() {
+    DirectMappedDexApplication application = app().asDirect();
+    application
+        .classesWithDeterministicOrder()
+        .forEach(
+            clazz ->
+                clazz.forEachProgramMethod(
+                    method -> {
+                      assert verifyOriginalMethodInPosition(method);
+                    }));
+    return true;
+  }
+
+  private static boolean verifyOriginalMethodInPosition(ProgramMethod context) {
+    Code code = context.getDefinition().getCode();
+    if (code == null) {
+      return true;
+    }
+    DexMethod thisMethod = context.getReference();
+    code.forEachPosition(
+        context.getReference(),
+        context.getDefinition().isD8R8Synthesized(),
+        position -> {
+          DexMethod outerCaller = position.getOutermostCaller().getMethod();
+          assert thisMethod.isIdenticalTo(outerCaller);
+        });
+    return true;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index b636a5c..bc71dd6 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -684,7 +684,6 @@
             this,
             localVariables,
             method,
-            appView.graphLens().getOriginalMethodSignature(method.getReference()),
             callerPosition,
             origin,
             appView);
@@ -893,8 +892,17 @@
 
   @Override
   public Code getCodeAsInlining(
-      DexMethod caller, DexMethod callee, DexItemFactory factory, boolean isCalleeD8R8Synthesized) {
-    Position callerPosition = SyntheticPosition.builder().setLine(0).setMethod(caller).build();
+      DexMethod caller,
+      boolean isCallerD8R8Synthesized,
+      DexMethod callee,
+      boolean isCalleeD8R8Synthesized,
+      DexItemFactory factory) {
+    Position callerPosition =
+        SyntheticPosition.builder()
+            .setLine(0)
+            .setMethod(caller)
+            .setIsD8R8Synthesized(isCallerD8R8Synthesized)
+            .build();
     List<CfInstruction> newInstructions = new ArrayList<>(instructions.size() + 2);
     CfLabel firstLabel;
     if (instructions.get(0).isLabel()) {
@@ -968,7 +976,8 @@
   }
 
   @Override
-  public void forEachPosition(DexMethod method, Consumer<Position> positionConsumer) {
+  public void forEachPosition(
+      DexMethod method, boolean isD8R8Synthesized, Consumer<Position> positionConsumer) {
     for (CfInstruction instruction : getInstructions()) {
       if (instruction.isPosition()) {
         positionConsumer.accept(instruction.asPosition().getPosition());
diff --git a/src/main/java/com/android/tools/r8/graph/CfCodeStackMapValidatingException.java b/src/main/java/com/android/tools/r8/graph/CfCodeStackMapValidatingException.java
index 59788bc..0670aeb 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCodeStackMapValidatingException.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCodeStackMapValidatingException.java
@@ -16,10 +16,7 @@
     if (appView.enableWholeProgramOptimizations()) {
       sb.append(" In later version of R8, the method may be assumed not reachable.");
     }
-    return new CfCodeDiagnostics(
-        method.getOrigin(),
-        appView.graphLens().getOriginalMethodSignature(method.getReference()),
-        sb.toString());
+    return new CfCodeDiagnostics(method.getOrigin(), method.getReference(), sb.toString());
   }
 
   public static CfCodeDiagnostics multipleFramesForLabel(ProgramMethod method, AppView<?> appView) {
@@ -27,10 +24,7 @@
     if (appView.enableWholeProgramOptimizations()) {
       sb.append(" In later version of R8, the method may be assumed not reachable.");
     }
-    return new CfCodeDiagnostics(
-        method.getOrigin(),
-        appView.graphLens().getOriginalMethodSignature(method.getReference()),
-        sb.toString());
+    return new CfCodeDiagnostics(method.getOrigin(), method.getReference(), sb.toString());
   }
 
   public static CfCodeDiagnostics noFramesForMethodWithJumps(
@@ -40,10 +34,7 @@
     if (appView.enableWholeProgramOptimizations()) {
       sb.append(" In later version of R8, the method may be assumed not reachable.");
     }
-    return new CfCodeDiagnostics(
-        method.getOrigin(),
-        appView.graphLens().getOriginalMethodSignature(method.getReference()),
-        sb.toString());
+    return new CfCodeDiagnostics(method.getOrigin(), method.getReference(), sb.toString());
   }
 
   public static CfCodeDiagnostics invalidTryCatchRange(
@@ -57,10 +48,7 @@
     if (appView.enableWholeProgramOptimizations()) {
       sb.append(" In later version of R8, the method may be assumed not reachable.");
     }
-    return new CfCodeDiagnostics(
-        method.getOrigin(),
-        appView.graphLens().getOriginalMethodSignature(method.getReference()),
-        sb.toString());
+    return new CfCodeDiagnostics(method.getOrigin(), method.getReference(), sb.toString());
   }
 
   public static CfCodeDiagnostics invalidStackMapForInstruction(
@@ -80,9 +68,6 @@
     if (appView.enableWholeProgramOptimizations()) {
       sb.append(" In later version of R8, the method may be assumed not reachable.");
     }
-    return new CfCodeDiagnostics(
-        method.getOrigin(),
-        appView.graphLens().getOriginalMethodSignature(method.getReference()),
-        sb.toString());
+    return new CfCodeDiagnostics(method.getOrigin(), method.getReference(), sb.toString());
   }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/Code.java b/src/main/java/com/android/tools/r8/graph/Code.java
index 566f79e..36d8464 100644
--- a/src/main/java/com/android/tools/r8/graph/Code.java
+++ b/src/main/java/com/android/tools/r8/graph/Code.java
@@ -191,55 +191,53 @@
     return true;
   }
 
-  public Code getCodeAsInlining(DexMethod caller, DexEncodedMethod callee, DexItemFactory factory) {
-    return getCodeAsInlining(caller, callee.getReference(), factory, callee.isD8R8Synthesized());
-  }
-
   public Code getCodeAsInlining(
-      DexMethod caller, DexMethod callee, DexItemFactory factory, boolean isCalleeD8R8Synthesized) {
+      DexMethod caller,
+      boolean isCallerD8R8Synthesized,
+      DexMethod callee,
+      boolean isCalleeD8R8Synthesized,
+      DexItemFactory factory) {
     throw new Unreachable();
   }
 
   public static Position newInlineePosition(
-      Position callerPosition, Position oldPosition, boolean isCalleeD8R8Synthesized) {
-    Position outermostCaller = oldPosition.getOutermostCaller();
+      Position callerPosition, Position calleePosition, boolean isCalleeD8R8Synthesized) {
+    Position outermostCallee = calleePosition.getOutermostCaller();
+    // If the callee is not synthetic, then just append the frame.
+    assert outermostCallee.isD8R8Synthesized() == isCalleeD8R8Synthesized;
     if (!isCalleeD8R8Synthesized) {
-      return removeSameMethodAndLineZero(oldPosition, callerPosition);
+      assert !outermostCallee.isOutline();
+      return calleePosition.withOutermostCallerPosition(callerPosition);
     }
     // We can replace the position since the callee was synthesized by the compiler, however, if
     // the position carries special information we need to copy it.
-    if (!outermostCaller.isOutline() && !outermostCaller.isRemoveInnerFramesIfThrowingNpe()) {
-      return oldPosition.replacePosition(outermostCaller, callerPosition);
+    if (!outermostCallee.isOutline() && !outermostCallee.isRemoveInnerFramesIfThrowingNpe()) {
+      return calleePosition.replacePosition(outermostCallee, callerPosition);
     }
+
     assert !callerPosition.isOutline();
-    PositionBuilder<?, ?> positionBuilder =
-        outermostCaller
-            .builderWithCopy()
-            .setMethod(callerPosition.getMethod())
-            .setLine(callerPosition.getLine());
+    assert !callerPosition.hasCallerPosition();
+    // Copy the callee frame to ensure transfer of the outline key if present.
+    PositionBuilder<?, ?> newCallerBuilder =
+        outermostCallee.builderWithCopy().setMethod(callerPosition.getMethod());
+    // If the callee is an outline, the line must be that of the outline to maintain the positions.
+    if (outermostCallee.isOutline()) {
+      // This does not implement inlining an outline. The cases this hits should always be a full
+      // "move as inlining" to be correct.
+      assert callerPosition.isD8R8Synthesized();
+      assert callerPosition.getLine() == 0;
+    } else {
+      newCallerBuilder.setLine(outermostCallee.getLine());
+    }
+    // Transfer other info from the caller.
     if (callerPosition.isRemoveInnerFramesIfThrowingNpe()) {
-      positionBuilder.setRemoveInnerFramesIfThrowingNpe(true);
+      newCallerBuilder.setRemoveInnerFramesIfThrowingNpe(true);
     }
-    return oldPosition.replacePosition(outermostCaller, positionBuilder.build());
+    return calleePosition.replacePosition(outermostCallee, newCallerBuilder.build());
   }
 
-  @Deprecated()
-  @SuppressWarnings("ReferenceEquality")
-  // TODO(b/261971803): When having complete control over the positions we should not need this.
-  private static Position removeSameMethodAndLineZero(
-      Position calleePosition, Position callerPosition) {
-    Position outermostCaller = calleePosition.getOutermostCaller();
-    if (outermostCaller.getLine() == 0) {
-      while (callerPosition != null
-          && outermostCaller.getMethod() == callerPosition.getMethod()
-          && callerPosition.getLine() == 0) {
-        callerPosition = callerPosition.getCallerPosition();
-      }
-    }
-    return calleePosition.withOutermostCallerPosition(callerPosition);
-  }
-
-  public void forEachPosition(DexMethod method, Consumer<Position> positionConsumer) {
+  public void forEachPosition(
+      DexMethod method, boolean isD8R8Synthesized, Consumer<Position> positionConsumer) {
     // Intentionally empty. Override where we have fully build CF or DEX code.
   }
 }
diff --git a/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java b/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java
index b70ff11..906a0be 100644
--- a/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java
@@ -20,6 +20,7 @@
 import com.android.tools.r8.graph.lens.MethodLookupResult;
 import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InvokeDirect;
 import com.android.tools.r8.ir.code.NumberGenerator;
 import com.android.tools.r8.ir.code.Position;
 import com.android.tools.r8.ir.code.Position.SyntheticPosition;
@@ -87,6 +88,19 @@
     method.setCode(get().toCfCode(method, appView.dexItemFactory(), superType), appView);
   }
 
+  @Override
+  public Code getCodeAsInlining(
+      DexMethod caller,
+      boolean isCallerD8R8Synthesized,
+      DexMethod callee,
+      boolean isCalleeD8R8Synthesized,
+      DexItemFactory factory) {
+    // TODO(b/261971803): It is odd this code does not have an original position for the <init>.
+    //  See OverrideParentCollisionTest for a case hitting this inlining where callee is a
+    //  non-synthetic non-default <init> (it has argument String).
+    return this;
+  }
+
   @SuppressWarnings("ReferenceEquality")
   private static boolean hasDefaultInstanceInitializerCode(
       ProgramMethod method, AppView<?> appView) {
@@ -144,10 +158,9 @@
       AppView<?> appView,
       Origin origin,
       MutableMethodConversionOptions conversionOptions) {
-    DexMethod originalMethod =
-        appView.graphLens().getOriginalMethodSignature(method.getReference());
     DefaultInstanceInitializerSourceCode source =
-        new DefaultInstanceInitializerSourceCode(originalMethod);
+        new DefaultInstanceInitializerSourceCode(
+            method.getReference(), method.getDefinition().isD8R8Synthesized());
     return IRBuilder.create(method, appView, source, origin).build(method, conversionOptions);
   }
 
@@ -161,10 +174,9 @@
       Position callerPosition,
       Origin origin,
       RewrittenPrototypeDescription protoChanges) {
-    DexMethod originalMethod =
-        appView.graphLens().getOriginalMethodSignature(method.getReference());
     DefaultInstanceInitializerSourceCode source =
-        new DefaultInstanceInitializerSourceCode(originalMethod, callerPosition);
+        new DefaultInstanceInitializerSourceCode(
+            method.getReference(), method.getDefinition().isD8R8Synthesized(), callerPosition);
     return IRBuilder.createForInlining(
             method, appView, codeLens, source, origin, valueNumberGenerator, protoChanges)
         .build(context, MethodConversionOptions.nonConverting());
@@ -410,25 +422,33 @@
 
   static class DefaultInstanceInitializerSourceCode extends SyntheticStraightLineSourceCode {
 
-    DefaultInstanceInitializerSourceCode(DexMethod method) {
-      this(method, null);
+    DefaultInstanceInitializerSourceCode(DexMethod method, boolean isD8R8Synthesized) {
+      this(method, isD8R8Synthesized, null);
     }
 
-    DefaultInstanceInitializerSourceCode(DexMethod method, Position callerPosition) {
-      super(
-          getInstructionBuilders(),
+    DefaultInstanceInitializerSourceCode(
+        DexMethod method, boolean isD8R8Synthesized, Position callerPosition) {
+      super(getInstructionBuilders(), getPosition(method, isD8R8Synthesized, callerPosition));
+    }
+
+    private static Position getPosition(
+        DexMethod method, boolean isD8R8Synthesized, Position callerPosition) {
+      SyntheticPosition calleePosition =
           SyntheticPosition.builder()
               .setLine(0)
               .setMethod(method)
-              .setCallerPosition(callerPosition)
-              .build());
+              .setIsD8R8Synthesized(isD8R8Synthesized)
+              .build();
+      return callerPosition == null
+          ? calleePosition
+          : Code.newInlineePosition(callerPosition, calleePosition, isD8R8Synthesized);
     }
 
     private static List<Consumer<IRBuilder>> getInstructionBuilders() {
       return ImmutableList.of(
           builder ->
               builder.add(
-                  com.android.tools.r8.ir.code.InvokeDirect.builder()
+                  InvokeDirect.builder()
                       .setMethod(
                           getParentConstructor(
                               builder.getProgramMethod(), builder.dexItemFactory()))
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java
index f3fd961..e800b21 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -351,7 +351,11 @@
 
   @Override
   public Code getCodeAsInlining(
-      DexMethod caller, DexMethod callee, DexItemFactory factory, boolean isCalleeD8R8Synthesized) {
+      DexMethod caller,
+      boolean isCallerD8R8Synthesized,
+      DexMethod callee,
+      boolean isCalleeD8R8Synthesized,
+      DexItemFactory factory) {
     return new DexCode(
         registerSize,
         incomingRegisterSize,
@@ -396,7 +400,8 @@
     //    => Insert a default event and potentially advance line.
     // 4) There is no SET_POSITION_FRAME and no default event setting a position for PC 0..
     //    => Insert a SET_POSITION_FRAME and a default event and potentially advance line.
-    PositionInfo positionInfo = computePreamblePosition(callee, eventBasedInfo);
+    PositionInfo positionInfo =
+        computePreamblePosition(callee, isCalleeD8R8Synthesized, eventBasedInfo);
     DexDebugEvent[] oldEvents = eventBasedInfo.events;
     boolean adjustStartPosition =
         !positionInfo.hasLinePositionAtPcZero() && debugInfo.getStartLine() > 0;
@@ -537,7 +542,6 @@
         new DexSourceCode(
             this,
             method,
-            appView.graphLens().getOriginalMethodSignature(method.getReference()),
             null,
             appView.dexItemFactory());
     return IRBuilder.create(method, appView, source, origin).build(method, conversionOptions);
@@ -558,7 +562,6 @@
         new DexSourceCode(
             this,
             method,
-            appView.graphLens().getOriginalMethodSignature(method.getReference()),
             callerPosition,
             appView.dexItemFactory());
     return IRBuilder.createForInlining(
@@ -875,7 +878,8 @@
   }
 
   @Override
-  public void forEachPosition(DexMethod method, Consumer<Position> positionConsumer) {
+  public void forEachPosition(
+      DexMethod method, boolean isD8R8Synthesized, Consumer<Position> positionConsumer) {
     if (getDebugInfo() == null || getDebugInfo().isPcBasedInfo()) {
       return;
     }
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java b/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
index e323ab8..e5ca60d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
@@ -57,9 +57,9 @@
   private List<DexDebugEntry> entries = new ArrayList<>();
   private final DexDebugPositionState positionState;
 
-  public DexDebugEntryBuilder(int startLine, DexMethod method) {
+  public DexDebugEntryBuilder(DexMethod method, boolean isD8R8Synthesized, int startLine) {
     assert method != null;
-    positionState = new DexDebugPositionState(startLine, method);
+    positionState = new DexDebugPositionState(startLine, method, isD8R8Synthesized);
   }
 
   public DexDebugEntryBuilder(DexEncodedMethod method, DexItemFactory factory) {
@@ -68,7 +68,9 @@
     EventBasedDebugInfo info = code.getDebugInfo().asEventBasedInfo();
     // Only event based debug info supports conversion to entries.
     assert info != null;
-    positionState = new DexDebugPositionState(info.startLine, method.getReference());
+    positionState =
+        new DexDebugPositionState(
+            info.startLine, method.getReference(), method.isD8R8Synthesized());
     int argumentRegister = code.registerSize - code.incomingRegisterSize;
     if (!method.accessFlags.isStatic()) {
       DexString name = factory.thisName;
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
index b77b311..557bb44 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
@@ -202,10 +202,12 @@
         return;
       }
       startLine = position.getLine();
+      Position outermostCaller = position.getOutermostCaller();
       emittedPosition =
           SourcePosition.builder()
               .setLine(position.getLine())
-              .setMethod(position.getOutermostCaller().getMethod())
+              .setMethod(outermostCaller.getMethod())
+              .setIsD8R8Synthesized(outermostCaller.isD8R8Synthesized())
               .build();
     }
     assert emittedPc != pc;
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java b/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
index 1a0023a..3707fdf 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
@@ -239,8 +239,8 @@
       return parameters.length;
     }
 
-    public List<DexDebugEntry> computeEntries(DexMethod method) {
-      DexDebugEntryBuilder builder = new DexDebugEntryBuilder(startLine, method);
+    public List<DexDebugEntry> computeEntries(DexMethod method, boolean isD8R8Synthesized) {
+      DexDebugEntryBuilder builder = new DexDebugEntryBuilder(method, isD8R8Synthesized, startLine);
       for (DexDebugEvent event : events) {
         event.accept(builder);
       }
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugPositionState.java b/src/main/java/com/android/tools/r8/graph/DexDebugPositionState.java
index 96eb206..26f1577 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugPositionState.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugPositionState.java
@@ -27,11 +27,13 @@
   private int currentPc = 0;
   private int currentLine;
   protected DexMethod currentMethod;
+  protected boolean isCurrentMethodD8R8Synthesized;
   protected Position currentPosition;
 
-  public DexDebugPositionState(int startLine, DexMethod method) {
+  public DexDebugPositionState(int startLine, DexMethod method, boolean isD8R8Synthesized) {
     currentLine = startLine;
     currentMethod = method;
+    isCurrentMethodD8R8Synthesized = isD8R8Synthesized;
   }
 
   @Override
@@ -50,6 +52,7 @@
     assert setPositionFrame.getPosition() != null;
     Position position = setPositionFrame.getPosition();
     currentMethod = position.getMethod();
+    isCurrentMethodD8R8Synthesized = position.isD8R8Synthesized();
     currentPosition = position;
   }
 
@@ -103,6 +106,7 @@
       return (getCurrentLine() > 0 ? SourcePosition.builder() : SyntheticPosition.builder())
           .setLine(getCurrentLine())
           .setMethod(currentMethod)
+          .setIsD8R8Synthesized(isCurrentMethodD8R8Synthesized)
           .build();
     } else {
       return currentPosition.builderWithCopy().setLine(getCurrentLine()).build();
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 87740a8..c1f8ac7 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -213,11 +213,12 @@
   /**
    * Flags this method as no longer being obsolete.
    *
-   * Example use case: The vertical class merger optimistically merges two classes before it is
+   * <p>Example use case: The vertical class merger optimistically merges two classes before it is
    * guaranteed that the two classes can be merged. In this process, methods are moved from the
-   * source class to the target class using {@link #toTypeSubstitutedMethod(DexMethod)}, which
-   * causes the original methods of the source class to become obsolete. If vertical class merging
-   * is aborted, the original methods of the source class needs to be marked as not being obsolete.
+   * source class to the target class using {@link #toTypeSubstitutedMethodAsInlining(DexMethod,
+   * DexItemFactory)}, which causes the original methods of the source class to become obsolete. If
+   * vertical class merging is aborted, the original methods of the source class needs to be marked
+   * as not being obsolete.
    */
   public void unsetObsolete() {
     obsolete = false;
@@ -1124,16 +1125,6 @@
     return new CfCode(getReference().holder, 3, locals, instructionBuilder.build());
   }
 
-  public DexEncodedMethod toTypeSubstitutedMethod(DexMethod method) {
-    checkIfObsolete();
-    return toTypeSubstitutedMethodHelper(method, isD8R8Synthesized(), null);
-  }
-
-  public DexEncodedMethod toTypeSubstitutedMethod(DexMethod method, Consumer<Builder> consumer) {
-    checkIfObsolete();
-    return toTypeSubstitutedMethodHelper(method, isD8R8Synthesized(), consumer);
-  }
-
   public DexEncodedMethod toTypeSubstitutedMethodAsInlining(
       DexMethod method, DexItemFactory factory) {
     checkIfObsolete();
@@ -1142,12 +1133,20 @@
 
   public DexEncodedMethod toTypeSubstitutedMethodAsInlining(
       DexMethod method, DexItemFactory factory, Consumer<Builder> consumer) {
+    boolean isCallerD8R8Synthesized = true;
     return toTypeSubstitutedMethodHelper(
         method,
-        true,
+        isCallerD8R8Synthesized,
         builder -> {
           if (code != null) {
-            builder.setCode(getCode().getCodeAsInlining(method, this, factory));
+            builder.setCode(
+                getCode()
+                    .getCodeAsInlining(
+                        method,
+                        isCallerD8R8Synthesized,
+                        getReference(),
+                        isD8R8Synthesized(),
+                        factory));
           }
           if (consumer != null) {
             consumer.accept(builder);
@@ -1181,24 +1180,26 @@
     return builder.build();
   }
 
-  @SuppressWarnings("ReferenceEquality")
-  public ProgramMethod toPrivateSyntheticMethod(DexProgramClass holder, DexMethod method) {
+  public ProgramMethod toPrivateSyntheticMethod(
+      DexProgramClass holder, DexMethod method, DexItemFactory factory) {
     assert !isStatic();
     assert !isPrivate();
-    assert getHolderType() == method.getHolderType();
+    assert getHolderType().isIdenticalTo(method.getHolderType());
     checkIfObsolete();
-    return new ProgramMethod(
-        holder,
-        syntheticBuilder(this)
-            .setMethod(method)
-            .modifyAccessFlags(
-                accessFlags -> {
-                  accessFlags.setSynthetic();
-                  accessFlags.unsetProtected();
-                  accessFlags.unsetPublic();
-                  accessFlags.setPrivate();
-                })
-            .build());
+    DexEncodedMethod newMethod =
+        toTypeSubstitutedMethodAsInlining(
+            method,
+            factory,
+            builder -> {
+              builder.modifyAccessFlags(
+                  accessFlags -> {
+                    accessFlags.setSynthetic();
+                    accessFlags.unsetProtected();
+                    accessFlags.unsetPublic();
+                    accessFlags.setPrivate();
+                  });
+            });
+    return new ProgramMethod(holder, newMethod);
   }
 
   public DexEncodedMethod toForwardingMethod(DexClass newHolder, AppView<?> definitions) {
diff --git a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
index 5676a96..c7fe5c9 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
@@ -181,8 +181,15 @@
   }
 
   @Override
-  public Code getCodeAsInlining(DexMethod caller, DexEncodedMethod callee, DexItemFactory factory) {
-    return asCfCode().getCodeAsInlining(caller, callee, factory);
+  public Code getCodeAsInlining(
+      DexMethod caller,
+      boolean isCallerD8R8Synthesized,
+      DexMethod callee,
+      boolean isCalleeD8R8Synthesized,
+      DexItemFactory factory) {
+    return asCfCode()
+        .getCodeAsInlining(
+            caller, isCallerD8R8Synthesized, callee, isCalleeD8R8Synthesized, factory);
   }
 
   public static class DebugParsingOptions {
diff --git a/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java b/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java
index 735be28..a58f744 100644
--- a/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java
+++ b/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java
@@ -55,6 +55,18 @@
   }
 
   @Override
+  public Code getCodeAsInlining(
+      DexMethod caller,
+      boolean isCallerD8R8Synthesized,
+      DexMethod callee,
+      boolean isCalleeD8R8Synthesized,
+      DexItemFactory factory) {
+    // We don't maintain a position on the throwing stub. We may want to reconsider this as it
+    // would allow retracing to recover inlinings of this stub.
+    return this;
+  }
+
+  @Override
   public IRCode buildIR(
       ProgramMethod method,
       AppView<?> appView,
@@ -131,7 +143,7 @@
 
   @Override
   public TryHandler[] getHandlers() {
-    return new TryHandler[0];
+    return TryHandler.EMPTY_ARRAY;
   }
 
   @Override
@@ -164,7 +176,7 @@
 
   @Override
   public Try[] getTries() {
-    return new Try[0];
+    return Try.EMPTY_ARRAY;
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/graph/fixup/TreeFixerBase.java b/src/main/java/com/android/tools/r8/graph/fixup/TreeFixerBase.java
index 908749c..4810b23 100644
--- a/src/main/java/com/android/tools/r8/graph/fixup/TreeFixerBase.java
+++ b/src/main/java/com/android/tools/r8/graph/fixup/TreeFixerBase.java
@@ -258,12 +258,12 @@
   }
 
   /** Fixup a method definition. */
-  @SuppressWarnings("ReferenceEquality")
   public DexEncodedMethod fixupMethod(DexEncodedMethod method) {
     DexMethod methodReference = method.getReference();
     DexMethod newMethodReference = fixupMethodReference(methodReference);
-    if (newMethodReference != methodReference) {
-      return recordMethodChange(method, method.toTypeSubstitutedMethod(newMethodReference));
+    if (!methodReference.isIdenticalTo(newMethodReference)) {
+      return recordMethodChange(
+          method, method.toTypeSubstitutedMethodAsInlining(newMethodReference, dexItemFactory));
     }
     return method;
   }
diff --git a/src/main/java/com/android/tools/r8/graph/lens/GraphLens.java b/src/main/java/com/android/tools/r8/graph/lens/GraphLens.java
index 4d339e8..ef55a1f 100644
--- a/src/main/java/com/android/tools/r8/graph/lens/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/lens/GraphLens.java
@@ -678,16 +678,8 @@
                 + "` back to original program";
       }
       for (DexEncodedMethod method : clazz.methods()) {
-        if (method.isD8R8Synthesized()) {
-          // Methods synthesized by D8/R8 may not be mapped.
-          continue;
-        }
-        DexMethod originalMethod = getOriginalMethodSignature(method.getReference());
-        assert originalMethods.contains(originalMethod)
-            : "Method could not be mapped back: "
-                + method.toSourceString()
-                + ", originalMethod: "
-                + originalMethod.toSourceString();
+        // Methods synthesized by D8/R8 are not mapped, but all non-synthesized must be originals.
+        assert method.isD8R8Synthesized() || originalMethods.contains(method.getReference());
       }
     }
 
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
index 06117ec..d6b92e0 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -142,7 +142,7 @@
         DexEncodedMethod.syntheticBuilder()
             .setMethod(newMethodReference)
             .setAccessFlags(MethodAccessFlags.createForClassInitializer())
-            .setCode(classInitializerMerger.getCode(syntheticMethodReference))
+            .setCode(classInitializerMerger.getCode(newMethodReference))
             .setClassFileVersion(classInitializerMerger.getCfVersion())
             .setApiLevelForDefinition(apiReferenceLevel)
             .setApiLevelForCode(apiReferenceLevel)
@@ -176,7 +176,7 @@
             }
             classMethodsBuilder.addDirectMethod(
                 newMethod != method.getReference()
-                    ? definition.toTypeSubstitutedMethod(newMethod)
+                    ? definition.toTypeSubstitutedMethodAsInlining(newMethod, dexItemFactory)
                     : method.getDefinition());
             if (definition.getReference() != newMethod) {
               lensBuilder.moveMethod(definition.getReference(), newMethod);
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorEntryPoint.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorEntryPoint.java
index d4cb598..49f51d8 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorEntryPoint.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorEntryPoint.java
@@ -41,9 +41,8 @@
       Int2ReferenceSortedMap<DexMethod> typeConstructors,
       DexMethod newConstructor,
       DexField classIdField,
-      Position callerPosition,
-      DexMethod originalMethod) {
-    super(newConstructor.holder, newConstructor, callerPosition, originalMethod);
+      Position position) {
+    super(newConstructor.holder, newConstructor, position);
 
     this.typeConstructors = typeConstructors;
     this.classIdField = classIdField;
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteHorizontalClassMergerCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteHorizontalClassMergerCode.java
index abb1fbf..7e37921 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteHorizontalClassMergerCode.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteHorizontalClassMergerCode.java
@@ -11,6 +11,8 @@
 import com.android.tools.r8.graph.ClasspathMethod;
 import com.android.tools.r8.graph.Code;
 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.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.code.IRCode;
@@ -35,6 +37,18 @@
       ProgramMethod method,
       HorizontalClassMergerGraphLens lens);
 
+  @Override
+  public final Code getCodeAsInlining(
+      DexMethod caller,
+      boolean isCallerD8R8Synthesized,
+      DexMethod callee,
+      boolean isCalleeD8R8Synthesized,
+      DexItemFactory factory) {
+    // This code object is synthesized so "inlining" just "strips" the callee position.
+    assert isCalleeD8R8Synthesized;
+    return this;
+  }
+
   // Implement Code.
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteMergedInstanceInitializerCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteMergedInstanceInitializerCode.java
index a77873f..837157e 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteMergedInstanceInitializerCode.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/IncompleteMergedInstanceInitializerCode.java
@@ -48,7 +48,6 @@
   private final DexField classIdField;
   private final int extraNulls;
   private final DexMethod originalMethodReference;
-  private final DexMethod syntheticMethodReference;
 
   private final Map<DexField, InstanceFieldInitializationInfo> instanceFieldAssignmentsPre;
   private final Map<DexField, InstanceFieldInitializationInfo> instanceFieldAssignmentsPost;
@@ -60,7 +59,6 @@
       DexField classIdField,
       int extraNulls,
       DexMethod originalMethodReference,
-      DexMethod syntheticMethodReference,
       Map<DexField, InstanceFieldInitializationInfo> instanceFieldAssignmentsPre,
       Map<DexField, InstanceFieldInitializationInfo> instanceFieldAssignmentsPost,
       DexMethod parentConstructor,
@@ -68,7 +66,6 @@
     this.classIdField = classIdField;
     this.extraNulls = extraNulls;
     this.originalMethodReference = originalMethodReference;
-    this.syntheticMethodReference = syntheticMethodReference;
     this.instanceFieldAssignmentsPre = instanceFieldAssignmentsPre;
     this.instanceFieldAssignmentsPost = instanceFieldAssignmentsPost;
     this.parentConstructor = parentConstructor;
@@ -90,16 +87,13 @@
     IntBox maxStack = new IntBox();
     ImmutableList.Builder<CfInstruction> instructionBuilder = ImmutableList.builder();
 
-    // Set position.
-    Position callerPosition =
-        SyntheticPosition.builder().setLine(0).setMethod(syntheticMethodReference).build();
-    Position calleePosition =
+    Position preamblePosition =
         SyntheticPosition.builder()
             .setLine(0)
-            .setMethod(originalMethodReference)
-            .setCallerPosition(callerPosition)
+            .setMethod(method.getReference())
+            .setIsD8R8Synthesized(method.getDefinition().isD8R8Synthesized())
             .build();
-    CfPosition position = new CfPosition(new CfLabel(), calleePosition);
+    CfPosition position = new CfPosition(new CfLabel(), preamblePosition);
     instructionBuilder.add(position);
     instructionBuilder.add(position.getLabel());
 
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java
index b2421bb..2360c36 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerDescription.java
@@ -71,12 +71,9 @@
    * Transform this description into actual CF code.
    *
    * @param originalMethodReference the original reference of the representative method
-   * @param syntheticMethodReference the original, synthetic reference of the new method reference
-   *     ($r8$init$synthetic)
    */
   public IncompleteMergedInstanceInitializerCode createCfCode(
       DexMethod originalMethodReference,
-      DexMethod syntheticMethodReference,
       MergeGroup group,
       boolean hasClassId,
       int extraNulls) {
@@ -84,7 +81,6 @@
         hasClassId ? group.getClassIdField() : null,
         extraNulls,
         originalMethodReference,
-        syntheticMethodReference,
         instanceFieldAssignmentsPre,
         instanceFieldAssignmentsPost,
         parentConstructor,
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
index 91bfef0..38b47fc 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
@@ -253,7 +253,9 @@
             classMethodsBuilder::isFresh);
 
     DexEncodedMethod encodedMethod =
-        instanceInitializer.getDefinition().toTypeSubstitutedMethod(method);
+        instanceInitializer
+            .getDefinition()
+            .toTypeSubstitutedMethodAsInlining(method, dexItemFactory);
     encodedMethod.getMutableOptimizationInfo().markForceInline();
     encodedMethod.getAccessFlags().unsetConstructor();
     encodedMethod.getAccessFlags().unsetPublic();
@@ -271,25 +273,20 @@
 
   private Code getNewCode(
       DexMethod newMethodReference,
-      DexMethod syntheticMethodReference,
       boolean needsClassId,
       int extraNulls) {
     if (hasInstanceInitializerDescription()) {
       return instanceInitializerDescription.createCfCode(
           getOriginalMethodReference(),
-          syntheticMethodReference,
           group,
           needsClassId,
           extraNulls);
     }
-    if (!useSyntheticMethod()) {
-      return getRepresentative().getDefinition().getCode();
-    }
+    assert useSyntheticMethod();
     return new ConstructorEntryPointSynthesizedCode(
         createClassIdToInstanceInitializerMap(),
         newMethodReference,
-        group.hasClassIdField() ? group.getClassIdField() : null,
-        syntheticMethodReference);
+        group.hasClassIdField() ? group.getClassIdField() : null);
   }
 
   private boolean isSingleton() {
@@ -381,24 +378,24 @@
     }
 
     DexEncodedMethod representativeMethod = representative.getDefinition();
-    boolean useSynthethicBuilder = useSyntheticMethod() || representativeMethod.isD8R8Synthesized();
-    DexEncodedMethod newInstanceInitializer =
-        (useSynthethicBuilder ? DexEncodedMethod.syntheticBuilder() : DexEncodedMethod.builder())
-            .setMethod(newMethodReference)
-            .setAccessFlags(
-                useSynthethicBuilder
-                    ? getNewAccessFlags()
-                    : representative.getAccessFlags().withPublic())
-            .setCode(
-                getNewCode(
-                    newMethodReference,
-                    syntheticMethodReference,
-                    needsClassId,
-                    extraUnusedNullParameters.size()))
-            .setClassFileVersion(getNewClassFileVersion())
-            .setApiLevelForDefinition(representativeMethod.getApiLevelForDefinition())
-            .setApiLevelForCode(representativeMethod.getApiLevelForCode())
-            .build();
+
+    DexEncodedMethod newInstanceInitializer;
+    if (!hasInstanceInitializerDescription() && !useSyntheticMethod()) {
+      newInstanceInitializer =
+          representativeMethod.toTypeSubstitutedMethodAsInlining(
+              newMethodReference, dexItemFactory);
+    } else {
+      newInstanceInitializer =
+          DexEncodedMethod.syntheticBuilder()
+              .setMethod(newMethodReference)
+              .setAccessFlags(getNewAccessFlags())
+              .setCode(
+                  getNewCode(newMethodReference, needsClassId, extraUnusedNullParameters.size()))
+              .setClassFileVersion(getNewClassFileVersion())
+              .setApiLevelForDefinition(representativeMethod.getApiLevelForDefinition())
+              .setApiLevelForCode(representativeMethod.getApiLevelForCode())
+              .build();
+    }
     classMethodsBuilder.addDirectMethod(newInstanceInitializer);
 
     if (mode.isFinal()) {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
index 8cca85f..538c0d3 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
@@ -237,7 +237,7 @@
         newMethodSignature.withHolder(originalMethodReference, dexItemFactory);
     lensBuilder.fixupMethod(originalMethodReference, newMethodReference);
     return newMethodReference != originalMethodReference
-        ? method.toTypeSubstitutedMethod(newMethodReference)
+        ? method.toTypeSubstitutedMethodAsInlining(newMethodReference, dexItemFactory)
         : method;
   }
 
@@ -278,7 +278,8 @@
 
     lensBuilder.fixupMethod(originalMethodReference, newMethodReference);
 
-    DexEncodedMethod newMethod = method.toTypeSubstitutedMethod(newMethodReference);
+    DexEncodedMethod newMethod =
+        method.toTypeSubstitutedMethodAsInlining(newMethodReference, dexItemFactory);
     if (newMethod.isNonPrivateVirtualMethod()) {
       // Since we changed the return type or one of the parameters, this method cannot be a
       // classpath or library method override, since we only class merge program classes.
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
index eca0a6f..5b1ee78 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
@@ -124,7 +124,8 @@
             group.getTarget().getType(),
             classMethodsBuilder::isFresh);
 
-    DexEncodedMethod encodedMethod = oldMethod.getDefinition().toTypeSubstitutedMethod(method);
+    DexEncodedMethod encodedMethod =
+        oldMethod.getDefinition().toTypeSubstitutedMethodAsInlining(method, dexItemFactory);
     MethodAccessFlags flags = encodedMethod.getAccessFlags();
     flags.unsetProtected();
     flags.unsetPublic();
@@ -235,8 +236,9 @@
       newMethod =
           representative
               .getDefinition()
-              .toTypeSubstitutedMethod(
+              .toTypeSubstitutedMethodAsInlining(
                   newMethodReference,
+                  dexItemFactory,
                   builder -> builder.setIsLibraryMethodOverrideIfKnown(isLibraryMethodOverride));
     }
 
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ClassInitializerMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ClassInitializerMerger.java
index b4d26cd..a0b8b9b 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ClassInitializerMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ClassInitializerMerger.java
@@ -82,7 +82,7 @@
           classInitializer -> classInitializer.getDefinition().getCode().isCfCode());
       return new CfCodeBuilder().build(syntheticMethodReference);
     }
-    return new IRProvider(classInitializers, syntheticMethodReference);
+    return new IRProvider(classInitializers);
   }
 
   public CfVersion getCfVersion() {
@@ -141,7 +141,11 @@
       // Building the instructions will adjust maxStack and maxLocals. Build it here before invoking
       // the CfCode constructor to ensure that the value passed in is the updated values.
       Position callerPosition =
-          SyntheticPosition.builder().setLine(0).setMethod(syntheticMethodReference).build();
+          SyntheticPosition.builder()
+              .setLine(0)
+              .setMethod(syntheticMethodReference)
+              .setIsD8R8Synthesized(true)
+              .build();
       List<CfInstruction> instructions = buildInstructions(callerPosition);
       return new CfCode(
           syntheticMethodReference.getHolderType(), maxStack, maxLocals, instructions);
@@ -163,13 +167,15 @@
       CfLabel endLabel = new CfLabel();
       boolean requiresLabel = false;
       int index = 1;
+      boolean calleeD8R8Synthesized = method.getDefinition().isD8R8Synthesized();
       for (CfInstruction instruction : code.getInstructions()) {
         if (instruction.isPosition()) {
-          CfPosition cfPosition = instruction.asPosition();
+          CfPosition calleePosition = instruction.asPosition();
           newInstructions.add(
               new CfPosition(
-                  cfPosition.getLabel(),
-                  cfPosition.getPosition().withOutermostCallerPosition(callerPosition)));
+                  calleePosition.getLabel(),
+                  Code.newInlineePosition(
+                      callerPosition, calleePosition.getPosition(), calleeD8R8Synthesized)));
         } else if (instruction.isReturn()) {
           if (code.getInstructions().size() != index) {
             newInstructions.add(new CfGoto(endLabel));
@@ -193,12 +199,9 @@
   static class IRProvider extends Code {
 
     private final ImmutableList<ProgramMethod> classInitializers;
-    private final DexMethod syntheticMethodReference;
 
-    private IRProvider(
-        ImmutableList<ProgramMethod> classInitializers, DexMethod syntheticMethodReference) {
+    private IRProvider(ImmutableList<ProgramMethod> classInitializers) {
       this.classInitializers = classInitializers;
-      this.syntheticMethodReference = syntheticMethodReference;
     }
 
     @Override
@@ -209,8 +212,13 @@
         MutableMethodConversionOptions conversionOptions) {
       assert !classInitializers.isEmpty();
 
-      Position callerPosition =
-          SyntheticPosition.builder().setLine(0).setMethod(syntheticMethodReference).build();
+      Position preamblePosition =
+          SyntheticPosition.builder()
+              .setLine(0)
+              .setMethod(method.getReference())
+              .setIsD8R8Synthesized(method.getDefinition().isD8R8Synthesized())
+              .build();
+
       IRMetadata metadata = new IRMetadata();
       NumberGenerator blockNumberGenerator = new NumberGenerator();
       NumberGenerator valueNumberGenerator = new NumberGenerator();
@@ -223,7 +231,7 @@
         block.add(
             InvokeStatic.builder()
                 .setMethod(classInitializer.getReference())
-                .setPosition(callerPosition)
+                .setPosition(preamblePosition)
                 .build(),
             metadata);
       }
@@ -236,7 +244,7 @@
           new IRCode(
               appView.options(),
               method,
-              callerPosition,
+              preamblePosition,
               ListUtils.newLinkedList(block),
               valueNumberGenerator,
               blockNumberGenerator,
@@ -266,7 +274,7 @@
                     appView,
                     appView.codeLens(),
                     valueNumberGenerator,
-                    callerPosition,
+                    preamblePosition,
                     classInitializer.getOrigin(),
                     RewrittenPrototypeDescription.none());
         classInitializer.getDefinition().setObsolete();
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ConstructorEntryPointSynthesizedCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ConstructorEntryPointSynthesizedCode.java
index 60903cc..a8e46f9 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ConstructorEntryPointSynthesizedCode.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/ConstructorEntryPointSynthesizedCode.java
@@ -15,26 +15,22 @@
 
 public class ConstructorEntryPointSynthesizedCode extends AbstractSynthesizedCode {
   private final DexMethod newConstructor;
-  private final DexMethod originalMethod;
   private final DexField classIdField;
   private final Int2ReferenceSortedMap<DexMethod> typeConstructors;
 
   public ConstructorEntryPointSynthesizedCode(
       Int2ReferenceSortedMap<DexMethod> typeConstructors,
       DexMethod newConstructor,
-      DexField classIdField,
-      DexMethod originalMethod) {
+      DexField classIdField) {
     this.typeConstructors = typeConstructors;
     this.newConstructor = newConstructor;
     this.classIdField = classIdField;
-    this.originalMethod = originalMethod;
   }
 
   @Override
   public SourceCodeProvider getSourceCodeProvider() {
-    return (ignored, callerPosition) ->
-        new ConstructorEntryPoint(
-            typeConstructors, newConstructor, classIdField, callerPosition, originalMethod);
+    return (ignored, position) ->
+        new ConstructorEntryPoint(typeConstructors, newConstructor, classIdField, position);
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/CanonicalPositions.java b/src/main/java/com/android/tools/r8/ir/code/CanonicalPositions.java
index 1a07f41..89e7c36 100644
--- a/src/main/java/com/android/tools/r8/ir/code/CanonicalPositions.java
+++ b/src/main/java/com/android/tools/r8/ir/code/CanonicalPositions.java
@@ -34,7 +34,12 @@
     canonicalPositions =
         new HashMap<>(1 + (callerPosition == null ? 0 : 1) + expectedPositionsCount);
     if (preamblePosition == null) {
-      preamblePosition = SyntheticPosition.builder().setLine(0).setMethod(method).build();
+      preamblePosition =
+          SyntheticPosition.builder()
+              .setLine(0)
+              .setMethod(method)
+              .setIsD8R8Synthesized(methodIsSynthesized)
+              .build();
     }
     if (callerPosition != null) {
       this.callerPosition = getCanonical(callerPosition);
@@ -62,6 +67,18 @@
     return canonical != null ? canonical : position;
   }
 
+  public Position canonicalizePositionWithCaller(Position position) {
+    if (position.isD8R8Synthesized() && callerPosition != null) {
+      assert !position.hasCallerPosition();
+      return getCanonical(Code.newInlineePosition(callerPosition, position, true));
+    }
+    return getCanonical(
+        position
+            .builderWithCopy()
+            .setCallerPosition(canonicalizeCallerPosition(position.getCallerPosition()))
+            .build());
+  }
+
   /**
    * Append callerPosition (supplied in constructor) to the end of caller's caller chain and return
    * the canonical instance.
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeType.java b/src/main/java/com/android/tools/r8/ir/code/InvokeType.java
index de333be..9d9fad1 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeType.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeType.java
@@ -96,7 +96,14 @@
         if (holderType != null
             && holderType.isInANest()
             && holderType.isInSameNest(context.getHolder())) {
-          return InvokeType.DIRECT;
+          // Invoking a private super method within a nest must use invoke-direct. Invoking a
+          // non-private super method within a nest must use invoke-super.
+          MethodLookupResult lookupResult =
+              graphLens.lookupMethod(invokedMethod, context.getReference(), InvokeType.DIRECT);
+          DexEncodedMethod definition = holderType.lookupMethod(lookupResult.getReference());
+          return definition != null && definition.isPrivate()
+              ? InvokeType.DIRECT
+              : InvokeType.SUPER;
         }
       }
       return InvokeType.SUPER;
diff --git a/src/main/java/com/android/tools/r8/ir/code/Position.java b/src/main/java/com/android/tools/r8/ir/code/Position.java
index 187fe6a..48432c4 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Position.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Position.java
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.code;
 
-import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.ProgramMethod;
@@ -41,6 +40,8 @@
       Position callerPosition,
       boolean removeInnerFramesIfThrowingNpe,
       boolean isD8R8Synthesized) {
+    assert callerPosition == null || !isD8R8Synthesized
+        : "Synthetic positions should always be outermost";
     this.line = line;
     this.method = method;
     this.callerPosition = callerPosition;
@@ -128,30 +129,20 @@
         .withBool(Position::isD8R8Synthesized);
   }
 
-  @SuppressWarnings("ReferenceEquality")
   public static Position syntheticNone() {
     return SyntheticPosition.NO_POSITION_SYNTHETIC;
   }
 
-  @SuppressWarnings("ReferenceEquality")
-  public static Position getPositionForInlining(
-      AppView<?> appView, InvokeMethod invoke, ProgramMethod context) {
+  public static Position getPositionForInlining(InvokeMethod invoke, ProgramMethod context) {
     Position position = invoke.getPosition();
     if (position.method == null) {
       assert position.isNone();
       position = SourcePosition.builder().setMethod(context.getReference()).build();
     }
-    if (context.getDefinition().isD8R8Synthesized()) {
-      // Some rewritings map a synthetic method back to an original in the program. To ensure we
-      // have correct line information we have to rewrite the positions as inline position
-      // therefore we only check if the original method is present.
-      DexMethod originalMethodSignature =
-          appView.graphLens().getOriginalMethodSignature(context.getReference());
-      assert position.hasMethodInChain(originalMethodSignature);
-    } else {
-      assert position.getOutermostCaller().method
-          == appView.graphLens().getOriginalMethodSignature(context.getReference());
-    }
+    // If this assert is hit, then the method has changed/moved and the position info has not been
+    // updated. The code changing/moving the method needs to use the "AsInlining" helpers to
+    // maintain correct mapping information.
+    assert context.getReference().isIdenticalTo(position.getOutermostCaller().getMethod());
     return position;
   }
 
@@ -198,22 +189,6 @@
     return null;
   }
 
-  public boolean hasPositionMatching(Predicate<Position> positionPredicate) {
-    Position lastPosition = this;
-    while (lastPosition != null) {
-      if (positionPredicate.test(lastPosition)) {
-        return true;
-      }
-      lastPosition = lastPosition.getCallerPosition();
-    }
-    return false;
-  }
-
-  @SuppressWarnings("ReferenceEquality")
-  public boolean hasMethodInChain(DexMethod method) {
-    return hasPositionMatching(position -> position.getMethod() == method);
-  }
-
   public Position withOutermostCallerPosition(Position newOutermostCallerPosition) {
     return builderWithCopy()
         .setCallerPosition(
@@ -497,13 +472,23 @@
 
   public static class OutlinePosition extends Position {
 
+    // This is the method that the outline is created at. This needs to stay stable as it is used
+    // to identify the mapping table between callers and this outline definition.
+    private final DexMethod outlineMethodKey;
+
     private OutlinePosition(
         int line,
         DexMethod method,
         Position callerPosition,
         boolean removeInnerFramesIfThrowingNpe,
-        boolean isD8R8Synthesized) {
-      super(line, method, callerPosition, removeInnerFramesIfThrowingNpe, isD8R8Synthesized);
+        DexMethod outlineMethodKey) {
+      super(line, method, callerPosition, removeInnerFramesIfThrowingNpe, true);
+      this.outlineMethodKey = outlineMethodKey;
+      assert outlineMethodKey != null;
+    }
+
+    public DexMethod getOutlineMethodKey() {
+      return outlineMethodKey;
     }
 
     @Override
@@ -518,7 +503,7 @@
 
     @Override
     public PositionBuilder<?, ?> builderWithCopy() {
-      return builder()
+      return builder(outlineMethodKey)
           .setLine(line)
           .setMethod(method)
           .setCallerPosition(callerPosition)
@@ -531,13 +516,15 @@
       return Position::specifyBasePosition;
     }
 
-    public static OutlinePositionBuilder builder() {
-      return new OutlinePositionBuilder();
+    public static OutlinePositionBuilder builder(DexMethod outlineMethodKey) {
+      return new OutlinePositionBuilder().setOutlineMethodKey(outlineMethodKey);
     }
 
     public static class OutlinePositionBuilder
         extends PositionBuilder<OutlinePosition, OutlinePositionBuilder> {
 
+      private DexMethod outlineMethodKey;
+
       private OutlinePositionBuilder() {}
 
       @Override
@@ -545,10 +532,17 @@
         return this;
       }
 
+      // Intentionally hidden without external setter.
+      private OutlinePositionBuilder setOutlineMethodKey(DexMethod outlineMethodKey) {
+        this.outlineMethodKey = outlineMethodKey;
+        return this;
+      }
+
       @Override
       public OutlinePosition build() {
+        assert isD8R8Synthesized;
         return new OutlinePosition(
-            line, method, callerPosition, removeInnerFramesIfThrowingNpe, isD8R8Synthesized);
+            line, method, callerPosition, removeInnerFramesIfThrowingNpe, outlineMethodKey);
       }
     }
   }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
index 424108f..de8a584 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
@@ -27,7 +27,6 @@
 import com.android.tools.r8.graph.DebugLocalInfo.PrintLevel;
 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.DexType;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.CanonicalPositions;
@@ -210,7 +209,6 @@
       CfCode code,
       List<CfCode.LocalVariableInfo> localVariables,
       ProgramMethod method,
-      DexMethod originalMethod,
       Position callerPosition,
       Origin origin,
       AppView<?> appView) {
@@ -234,7 +232,7 @@
         new CanonicalPositions(
             callerPosition,
             cfPositionCount,
-            originalMethod,
+            method.getReference(),
             method.getDefinition().isD8R8Synthesized(),
             code.getPreamblePosition());
     internalOutputMode = appView.options().getInternalOutputMode();
@@ -904,11 +902,6 @@
   }
 
   public Position getCanonicalPosition(Position position) {
-    return canonicalPositions.getCanonical(
-        position
-            .builderWithCopy()
-            .setCallerPosition(
-                canonicalPositions.canonicalizeCallerPosition(position.getCallerPosition()))
-            .build());
+    return canonicalPositions.canonicalizePositionWithCaller(position);
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
index 2edb76d..b830c14 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
@@ -74,29 +74,28 @@
   private final CanonicalPositions canonicalPositions;
 
   private List<DexDebugEntry> debugEntries = null;
-  // In case of inlining the position of the invoke in the caller.
-  private final DexMethod originalMethod;
 
   public DexSourceCode(
       DexCode code,
       ProgramMethod method,
-      DexMethod originalMethod,
       Position callerPosition,
       DexItemFactory factory) {
     this.code = code;
     this.method = method;
-    this.originalMethod = originalMethod;
+    DexMethod reference = method.getReference();
+    boolean d8R8Synthesized = method.getDefinition().isD8R8Synthesized();
     EventBasedDebugInfo info = DexDebugInfo.convertToEventBased(code, factory);
     if (info != null) {
-      debugEntries = info.computeEntries(originalMethod);
+      debugEntries = info.computeEntries(reference, d8R8Synthesized);
     }
     canonicalPositions =
         new CanonicalPositions(
             callerPosition,
             debugEntries == null ? 0 : debugEntries.size(),
-            originalMethod,
-            method.getDefinition().isD8R8Synthesized(),
-            DexDebugUtils.computePreamblePosition(originalMethod, info).getFramePosition());
+            reference,
+            d8R8Synthesized,
+            DexDebugUtils.computePreamblePosition(reference, d8R8Synthesized, info)
+                .getFramePosition());
   }
 
   @Override
@@ -261,17 +260,7 @@
   }
 
   private Position getCanonicalPositionAppendCaller(DexDebugEntry entry) {
-    // If this instruction has already been inlined then the original method must be in the caller
-    // chain.
-    Position position = entry.getPosition();
-    // TODO(b/261971803): The original method should probably always be in the chain.
-    assert !position.hasCallerPosition() || position.hasMethodInChain(originalMethod);
-    return canonicalPositions.getCanonical(
-        position
-            .builderWithCopy()
-            .setCallerPosition(
-                canonicalPositions.canonicalizeCallerPosition(position.getCallerPosition()))
-            .build());
+    return canonicalPositions.canonicalizePositionWithCaller(entry.getPosition());
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index d7dee5a..73e94aa 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -1057,7 +1057,6 @@
             appView,
             null,
             RewrittenPrototypeDescription.none(),
-            appView.graphLens().getOriginalMethodSignature(code.context().getReference()),
             (MutableMethodConversionOptions) code.getConversionOptions());
     timing.end();
     return irCode;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index 44f9afd..46988a7 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.Code;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexEncodedMethod;
 import com.android.tools.r8.graph.DexField;
@@ -675,6 +676,8 @@
                     newAccessFlags.unsetPrivate();
                     // Always make the method public to provide access.
                     newAccessFlags.setPublic();
+                    Code code = encodedMethod.getCode();
+                    DexItemFactory factory = appView.dexItemFactory();
                     DexEncodedMethod newMethod =
                         DexEncodedMethod.syntheticBuilder()
                             .setMethod(callTarget)
@@ -683,10 +686,12 @@
                             .setAnnotations(encodedMethod.annotations())
                             .setParameterAnnotations(encodedMethod.parameterAnnotationsList)
                             .setCode(
-                                encodedMethod
-                                    .getCode()
-                                    .getCodeAsInlining(
-                                        callTarget, encodedMethod, appView.dexItemFactory()))
+                                code.getCodeAsInlining(
+                                    callTarget,
+                                    true,
+                                    encodedMethod.getReference(),
+                                    encodedMethod.isD8R8Synthesized(),
+                                    factory))
                             .setApiLevelForDefinition(encodedMethod.getApiLevelForDefinition())
                             .setApiLevelForCode(encodedMethod.getApiLevelForCode())
                             .build();
@@ -770,6 +775,8 @@
                     // its accessibility and make it virtual.
                     MethodAccessFlags newAccessFlags = encodedMethod.accessFlags.copy();
                     newAccessFlags.unsetPrivate();
+                    Code code = encodedMethod.getCode();
+                    DexItemFactory factory = appView.dexItemFactory();
                     DexEncodedMethod newMethod =
                         DexEncodedMethod.syntheticBuilder()
                             .setMethod(callTarget)
@@ -778,10 +785,12 @@
                             .setAnnotations(encodedMethod.annotations())
                             .setParameterAnnotations(encodedMethod.parameterAnnotationsList)
                             .setCode(
-                                encodedMethod
-                                    .getCode()
-                                    .getCodeAsInlining(
-                                        callTarget, encodedMethod, appView.dexItemFactory()))
+                                code.getCodeAsInlining(
+                                    callTarget,
+                                    true,
+                                    encodedMethod.getReference(),
+                                    encodedMethod.isD8R8Synthesized(),
+                                    factory))
                             .setApiLevelForDefinition(encodedMethod.getApiLevelForDefinition())
                             .setApiLevelForCode(encodedMethod.getApiLevelForCode())
                             .build();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/invokespecial/InvokeSpecialToSelfDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/invokespecial/InvokeSpecialToSelfDesugaring.java
index 092423e..0dd9fec 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/invokespecial/InvokeSpecialToSelfDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/invokespecial/InvokeSpecialToSelfDesugaring.java
@@ -108,7 +108,7 @@
       if (clazz.lookupProgramMethod(bridgeReference) == null) {
         // Create a new private method holding the code of the virtual method.
         ProgramMethod newDirectMethod =
-            method.getDefinition().toPrivateSyntheticMethod(clazz, bridgeReference);
+            method.getDefinition().toPrivateSyntheticMethod(clazz, bridgeReference, dexItemFactory);
 
         // Create the new cf code object for the virtual method.
         CfCode virtualMethodCode =
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
index 21771de..4fff3f5 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
@@ -457,10 +457,12 @@
 
   // Represent a static interface method as a method of companion class.
   private DexMethod staticAsMethodOfCompanionClass(DexClassAndMethod method) {
-    DexItemFactory dexItemFactory = appView.dexItemFactory();
-    DexType companionClassType = getCompanionClassType(method.getHolderType(), dexItemFactory);
-    DexMethod rewritten = method.getReference().withHolder(companionClassType, dexItemFactory);
-    return rewritten;
+    return staticAsMethodOfCompanionClass(method.getReference(), appView.dexItemFactory());
+  }
+
+  public static DexMethod staticAsMethodOfCompanionClass(DexMethod method, DexItemFactory factory) {
+    DexType companionClassType = getCompanionClassType(method.getHolderType(), factory);
+    return method.withHolder(companionClassType, factory);
   }
 
   private static DexMethod instanceAsMethodOfCompanionClass(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
index 7ac6713..1e53aa6 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
@@ -204,7 +204,11 @@
         definition
             .getCode()
             .getCodeAsInlining(
-                companion.getReference(), method.getDefinition(), appView.dexItemFactory());
+                companion.getReference(),
+                companion.getDefinition().isD8R8Synthesized(),
+                method.getReference(),
+                method.getDefinition().isD8R8Synthesized(),
+                appView.dexItemFactory());
     if (!definition.isStatic()) {
       DexEncodedMethod.setDebugInfoWithFakeThisParameter(
           code, companion.getReference().getArity(), appView);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
index 5888f9e..32e5a1a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
@@ -932,8 +932,9 @@
             method, localUtilityClass, availableMethodSignatures, method.getReference());
     return method
         .getDefinition()
-        .toTypeSubstitutedMethod(
+        .toTypeSubstitutedMethodAsInlining(
             newMethod,
+            factory,
             builder ->
                 transformMethodForLocalUtility(builder, method)
                     .setCompilationState(method.getDefinition().getCompilationState()));
@@ -950,8 +951,9 @@
     DexEncodedMethod dexEncodedMethod =
         method
             .getDefinition()
-            .toTypeSubstitutedMethod(
+            .toTypeSubstitutedMethodAsInlining(
                 newMethod,
+                factory,
                 builder ->
                     transformMethodForLocalUtility(builder, method)
                         .modifyAccessFlags(MethodAccessFlags::unsetAbstract)
@@ -1032,8 +1034,9 @@
     RewrittenPrototypeDescription prototypeChanges =
         lensBuilder.moveAndMap(
             method.getReference(), newMethod, isStatic, isStatic, extraUnusedNullParameters);
-    return method.toTypeSubstitutedMethod(
+    return method.toTypeSubstitutedMethodAsInlining(
         newMethod,
+        factory,
         builder ->
             builder
                 .fixupOptimizationInfo(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/code/CheckNotZeroCode.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/code/CheckNotZeroCode.java
index 4e7801c..b17f4b6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/code/CheckNotZeroCode.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/code/CheckNotZeroCode.java
@@ -16,6 +16,9 @@
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.Instruction;
 import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.NumberGenerator;
+import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.ir.code.Position.SyntheticPosition;
 import com.android.tools.r8.ir.code.Return;
 import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
 import com.android.tools.r8.ir.optimize.enums.EnumUnboxerImpl;
@@ -48,7 +51,28 @@
       Origin origin,
       MutableMethodConversionOptions conversionOptions) {
     // Build IR from the checkNotNull() method.
-    IRCode code = checkNotNullMethod.buildIR(appView);
+    Position callerPosition =
+        SyntheticPosition.builder()
+            .setMethod(checkNotZeroMethod.getReference())
+            .setLine(0)
+            .setIsD8R8Synthesized(checkNotZeroMethod.getDefinition().isD8R8Synthesized())
+            .build();
+    NumberGenerator valueNumberGenerator = new NumberGenerator();
+    IRCode code =
+        checkNotNullMethod
+            .getDefinition()
+            .getCode()
+            .buildInliningIR(
+                checkNotZeroMethod,
+                checkNotNullMethod,
+                appView,
+                appView.graphLens(),
+                valueNumberGenerator,
+                callerPosition,
+                checkNotZeroMethod.getOrigin(),
+                appView
+                    .graphLens()
+                    .lookupPrototypeChangesForMethodDefinition(checkNotNullMethod.getReference()));
     InstructionListIterator instructionIterator = code.instructionListIterator();
 
     // Start iterating at the argument instruction for the checked argument.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/InliningIRProvider.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/InliningIRProvider.java
index 958b02d..b1e76f3 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/InliningIRProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/InliningIRProvider.java
@@ -84,7 +84,7 @@
         context,
         appView,
         valueNumberGenerator,
-        Position.getPositionForInlining(appView, invoke, context),
+        Position.getPositionForInlining(invoke, context),
         origin,
         methodProcessor);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlinerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlinerImpl.java
index 6c0e779..6873f86 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlinerImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlinerImpl.java
@@ -1268,8 +1268,9 @@
         argumentsMapIndex = 0;
         OutlineCallerPositionBuilder positionBuilder =
             OutlineCallerPosition.builder()
-                .setMethod(appView.graphLens().getOriginalMethodSignature(method.getReference()))
+                .setMethod(method.getReference())
                 .setOutlineCallee(outlineMethod)
+                .setIsD8R8Synthesized(method.getDefinition().isD8R8Synthesized())
                 // We set the line number to 0 here and rely on the LineNumberOptimizer to
                 // set a new disjoint line.
                 .setLine(0);
@@ -1752,11 +1753,10 @@
     }
 
     @Override
-    @SuppressWarnings("ReferenceEquality")
     public void buildInstruction(
         IRBuilder builder, int instructionIndex, boolean firstBlockInstruction) {
       if (instructionIndex == outline.templateInstructions.size()) {
-        if (outline.returnType == dexItemFactory.voidType) {
+        if (outline.returnType.isVoidType()) {
           builder.addReturn();
         } else {
           builder.addReturn(outline.argumentCount());
@@ -1803,7 +1803,11 @@
     public Position getCurrentPosition() {
       // Always build positions for outlinee - each callsite will only build a position map for
       // instructions that are actually throwing.
-      return OutlinePosition.builder().setLine(position).setMethod(method).build();
+      return OutlinePosition.builder(method)
+          .setLine(position)
+          .setMethod(method)
+          .setIsD8R8Synthesized(true)
+          .build();
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/AbstractSynthesizedCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/AbstractSynthesizedCode.java
index f726dda..7a9429c 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/AbstractSynthesizedCode.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/AbstractSynthesizedCode.java
@@ -10,6 +10,8 @@
 import com.android.tools.r8.graph.Code;
 import com.android.tools.r8.graph.DexClassAndMethod;
 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.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.graph.lens.GraphLens;
@@ -17,6 +19,7 @@
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.NumberGenerator;
 import com.android.tools.r8.ir.code.Position;
+import com.android.tools.r8.ir.code.Position.SyntheticPosition;
 import com.android.tools.r8.ir.conversion.IRBuilder;
 import com.android.tools.r8.ir.conversion.MethodConversionOptions;
 import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
@@ -46,12 +49,18 @@
       AppView<?> appView,
       Origin origin,
       MutableMethodConversionOptions conversionOptions) {
-    return IRBuilder.create(method, appView, getSourceCodeProvider().get(method, null), origin)
+    SyntheticPosition position =
+        SyntheticPosition.builder()
+            .setLine(0)
+            .setMethod(method.getReference())
+            .setIsD8R8Synthesized(true)
+            .build();
+    return IRBuilder.create(method, appView, getSourceCodeProvider().get(method, position), origin)
         .build(method, conversionOptions);
   }
 
   @Override
-  public IRCode buildInliningIR(
+  public final IRCode buildInliningIR(
       ProgramMethod context,
       ProgramMethod method,
       AppView<?> appView,
@@ -72,6 +81,18 @@
   }
 
   @Override
+  public final Code getCodeAsInlining(
+      DexMethod caller,
+      boolean isCallerD8R8Synthesized,
+      DexMethod callee,
+      boolean isCalleeD8R8Synthesized,
+      DexItemFactory factory) {
+    // This code object is synthesized so "inlining" just "strips" the callee position.
+    assert isCalleeD8R8Synthesized;
+    return this;
+  }
+
+  @Override
   public final String toString() {
     return toString(null, RetracerForCodePrinting.empty());
   }
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodSourceCode.java
index 24e8151..c1fbd37 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodSourceCode.java
@@ -28,7 +28,6 @@
 
     private DexType receiver;
     private DexMethod method;
-    private DexMethod originalMethod;
     private DexType targetReceiver;
     private DexMethod target;
     private InvokeType invokeType;
@@ -38,7 +37,6 @@
 
     public Builder(DexMethod method) {
       this.method = method;
-      this.originalMethod = method;
     }
 
     public Builder setReceiver(DexType receiver) {
@@ -51,11 +49,6 @@
       return this;
     }
 
-    public Builder setOriginalMethod(DexMethod originalMethod) {
-      this.originalMethod = originalMethod;
-      return this;
-    }
-
     public Builder setTargetReceiver(DexType targetReceiver) {
       this.targetReceiver = targetReceiver;
       return this;
@@ -86,15 +79,14 @@
       return this;
     }
 
-    public ForwardMethodSourceCode build(ProgramMethod context, Position callerPosition) {
+    public ForwardMethodSourceCode build(ProgramMethod context, Position position) {
       return new ForwardMethodSourceCode(
           receiver,
           method,
-          originalMethod,
           targetReceiver,
           target,
           invokeType,
-          callerPosition,
+          position,
           isInterface,
           castResult,
           extraNullParameter);
@@ -111,15 +103,14 @@
   ForwardMethodSourceCode(
       DexType receiver,
       DexMethod method,
-      DexMethod originalMethod,
       DexType targetReceiver,
       DexMethod target,
       InvokeType invokeType,
-      Position callerPosition,
+      Position position,
       boolean isInterface,
       boolean castResult,
       boolean extraNullParameter) {
-    super(receiver, method, callerPosition, originalMethod);
+    super(receiver, method, position);
     assert (targetReceiver == null) == (invokeType == InvokeType.STATIC);
 
     this.target = target;
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java
index f9c13e9..4e3bf9c 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/SyntheticSourceCode.java
@@ -11,7 +11,6 @@
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.ir.code.CatchHandlers;
 import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.code.Position.SyntheticPosition;
 import com.android.tools.r8.ir.code.ValueType;
 import com.android.tools.r8.ir.conversion.DexSourceCode;
 import com.android.tools.r8.ir.conversion.IRBuilder;
@@ -44,16 +43,12 @@
 
   private final Position position;
 
-  protected SyntheticSourceCode(DexType receiver, DexMethod method, Position callerPosition) {
-    this(receiver, method, callerPosition, method);
-  }
-
-  protected SyntheticSourceCode(
-      DexType receiver, DexMethod method, Position callerPosition, DexMethod originalMethod) {
+  protected SyntheticSourceCode(DexType receiver, DexMethod method, Position position) {
     assert method != null;
     this.receiver = receiver;
     this.method = method;
     this.proto = method.proto;
+    this.position = position;
 
     // Initialize register values for receiver and arguments
     this.receiverRegister = receiver != null ? nextRegister(ValueType.OBJECT) : -1;
@@ -64,14 +59,6 @@
     for (int i = 0; i < paramCount; i++) {
       this.paramRegisters[i] = nextRegister(ValueType.fromDexType(params[i]));
     }
-
-    position =
-        SyntheticPosition.builder()
-            .setLine(0)
-            .setMethod(originalMethod)
-            .setCallerPosition(callerPosition)
-            .setIsD8R8Synthesized(true)
-            .build();
   }
 
   protected final void add(Consumer<IRBuilder> constructor) {
diff --git a/src/main/java/com/android/tools/r8/lightir/IR2LirConverter.java b/src/main/java/com/android/tools/r8/lightir/IR2LirConverter.java
index 2e191a7..4c78283 100644
--- a/src/main/java/com/android/tools/r8/lightir/IR2LirConverter.java
+++ b/src/main/java/com/android/tools/r8/lightir/IR2LirConverter.java
@@ -39,7 +39,11 @@
     this.strategy = strategy;
     this.bytecodeMetadataProvider = bytecodeMetadataProvider;
     this.builder =
-        new LirBuilder<>(irCode.context().getReference(), strategy, options)
+        new LirBuilder<>(
+                irCode.context().getReference(),
+                irCode.context().getDefinition().isD8R8Synthesized(),
+                strategy,
+                options)
             .setMetadata(irCode.metadata())
             .prepareForBytecodeInstructionMetadata(bytecodeMetadataProvider.size());
   }
diff --git a/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java b/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
index 877794a..1bd59ff 100644
--- a/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
+++ b/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
@@ -31,7 +31,6 @@
 import com.android.tools.r8.ir.code.ArrayLength;
 import com.android.tools.r8.ir.code.ArrayPut;
 import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.CanonicalPositions;
 import com.android.tools.r8.ir.code.CatchHandlers;
 import com.android.tools.r8.ir.code.CheckCast;
 import com.android.tools.r8.ir.code.Cmp;
@@ -82,7 +81,6 @@
 import com.android.tools.r8.ir.code.Or;
 import com.android.tools.r8.ir.code.Phi;
 import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.code.Position.SyntheticPosition;
 import com.android.tools.r8.ir.code.RecordFieldValues;
 import com.android.tools.r8.ir.code.Rem;
 import com.android.tools.r8.ir.code.Return;
@@ -104,7 +102,6 @@
 import com.android.tools.r8.lightir.LirBuilder.IntSwitchPayload;
 import com.android.tools.r8.lightir.LirBuilder.StringSwitchPayload;
 import com.android.tools.r8.lightir.LirCode.PositionEntry;
-import com.android.tools.r8.lightir.LirCode.StructuredPositionEntry;
 import com.android.tools.r8.lightir.LirCode.TryCatchTable;
 import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo;
 import com.android.tools.r8.utils.ListUtils;
@@ -131,12 +128,11 @@
       AppView<?> appView,
       Position callerPosition,
       RewrittenPrototypeDescription protoChanges,
-      DexMethod originalMethod,
       MutableMethodConversionOptions conversionOptions) {
     Parser<EV> parser =
         new Parser<>(
             lirCode,
-            originalMethod,
+            method.getReference(),
             method.getDefinition().isD8R8Synthesized(),
             appView,
             strategy,
@@ -168,7 +164,7 @@
 
     private final AppView<?> appView;
     private final LirCode<EV> code;
-    private final DexMethod originalMethod;
+    private final DexMethod method;
     private final LirDecodingStrategy<Value, EV> strategy;
     private final NumberGenerator basicBlockNumberGenerator = new NumberGenerator();
     private final RewrittenPrototypeDescription protoChanges;
@@ -188,7 +184,7 @@
 
     public Parser(
         LirCode<EV> code,
-        DexMethod originalMethod,
+        DexMethod method,
         boolean isD8R8Synthesized,
         AppView<?> appView,
         LirDecodingStrategy<Value, EV> strategy,
@@ -197,7 +193,7 @@
       super(code);
       this.appView = appView;
       this.code = code;
-      this.originalMethod = originalMethod;
+      this.method = method;
       this.strategy = strategy;
       this.protoChanges = protoChanges;
       assert protoChanges != null;
@@ -205,43 +201,14 @@
         buildForInlining = false;
         positionTable = code.getPositionTable();
         // Recreate the preamble position. This is active for arguments and code with no positions.
-        currentPosition = SyntheticPosition.builder().setLine(0).setMethod(originalMethod).build();
+        currentPosition = code.getPreamblePosition(method, isD8R8Synthesized);
       } else {
         buildForInlining = true;
-        PositionEntry[] inlineePositions = code.getPositionTable();
-        Position inlineePreamble = null;
-        if (inlineePositions.length > 0 && inlineePositions[0].getFromInstructionIndex() == 0) {
-          inlineePreamble = inlineePositions[0].getPosition(originalMethod);
-        }
-        CanonicalPositions canonicalPositions =
-            new CanonicalPositions(
-                callerPosition,
-                inlineePositions.length,
-                originalMethod,
-                isD8R8Synthesized,
-                inlineePreamble);
-        currentPosition = canonicalPositions.getPreamblePosition();
-        positionTable = new PositionEntry[inlineePositions.length];
-        for (int i = 0; i < inlineePositions.length; i++) {
-          PositionEntry inlineeEntry = inlineePositions[i];
-          Position inlineePosition = inlineeEntry.getPosition(originalMethod);
-          positionTable[i] =
-              new StructuredPositionEntry(
-                  inlineeEntry.getFromInstructionIndex(),
-                  canonicalPositions.getCanonical(
-                      inlineePosition
-                          .builderWithCopy()
-                          .setCallerPosition(
-                              canonicalPositions.canonicalizeCallerPosition(
-                                  inlineePosition.getCallerPosition()))
-                          .build()));
-        }
+        positionTable =
+            code.getPositionTableAsInlining(
+                callerPosition, method, isD8R8Synthesized, preamble -> currentPosition = preamble);
       }
-      if (positionTable.length > 0 && positionTable[0].getFromInstructionIndex() == 0) {
-        entryPosition = positionTable[0].getPosition(originalMethod);
-      } else {
-        entryPosition = currentPosition;
-      }
+      entryPosition = currentPosition;
     }
 
     @Override
@@ -275,7 +242,9 @@
     private void ensureCurrentPosition() {
       if (nextPositionEntry != null
           && nextPositionEntry.getFromInstructionIndex() <= nextInstructionIndex) {
-        currentPosition = nextPositionEntry.getPosition(originalMethod);
+        currentPosition =
+            nextPositionEntry.getPosition(
+                method, entryPosition.getOutermostCaller().isD8R8Synthesized());
         advanceNextPositionEntry();
       }
     }
@@ -501,7 +470,6 @@
 
     private Argument internalAddArgument(Value dest, boolean isBooleanType) {
       assert currentBlock != null;
-      assert currentPosition.isSyntheticPosition() || buildForInlining;
       // Arguments are not included in the "instructions" so this does not call "addInstruction"
       // which would otherwise advance the state.
       Argument argument = new Argument(dest, currentBlock.size(), isBooleanType);
diff --git a/src/main/java/com/android/tools/r8/lightir/LirBuilder.java b/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
index d222f5f..5d988d8 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
@@ -276,13 +276,21 @@
   }
 
   public LirBuilder(
-      DexMethod method, LirEncodingStrategy<V, EV> strategy, InternalOptions options) {
+      DexMethod method,
+      boolean isD8R8Synthesized,
+      LirEncodingStrategy<V, EV> strategy,
+      InternalOptions options) {
     useDexEstimationStrategy = options.isGeneratingDex();
     factory = options.dexItemFactory();
     constants = new Reference2IntOpenHashMap<>();
     positionTable = new ArrayList<>();
     this.strategy = strategy;
-    currentPosition = SyntheticPosition.builder().setLine(0).setMethod(method).build();
+    currentPosition =
+        SyntheticPosition.builder()
+            .setLine(0)
+            .setMethod(method)
+            .setIsD8R8Synthesized(isD8R8Synthesized)
+            .build();
     flushedPosition = currentPosition;
   }
 
diff --git a/src/main/java/com/android/tools/r8/lightir/LirCode.java b/src/main/java/com/android/tools/r8/lightir/LirCode.java
index f79a18f..8270d8a 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirCode.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirCode.java
@@ -21,12 +21,14 @@
 import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadata;
 import com.android.tools.r8.graph.lens.GraphLens;
 import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
+import com.android.tools.r8.ir.code.CanonicalPositions;
 import com.android.tools.r8.ir.code.CatchHandlers;
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.IRMetadata;
 import com.android.tools.r8.ir.code.NumberGenerator;
 import com.android.tools.r8.ir.code.Position;
 import com.android.tools.r8.ir.code.Position.SourcePosition;
+import com.android.tools.r8.ir.code.Position.SyntheticPosition;
 import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
 import com.android.tools.r8.ir.conversion.MethodConversionOptions;
 import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
@@ -59,6 +61,8 @@
 
   public abstract static class PositionEntry implements StructuralItem<PositionEntry> {
 
+    public static final PositionEntry[] EMPTY_ARRAY = new PositionEntry[0];
+
     private final int fromInstructionIndex;
 
     PositionEntry(int fromInstructionIndex) {
@@ -69,7 +73,7 @@
       return fromInstructionIndex;
     }
 
-    public abstract Position getPosition(DexMethod method);
+    public abstract Position getPosition(DexMethod method, boolean isD8R8Synthesized);
 
     abstract int getOrder();
 
@@ -116,8 +120,12 @@
     }
 
     @Override
-    public Position getPosition(DexMethod method) {
-      return SourcePosition.builder().setMethod(method).setLine(line).build();
+    public Position getPosition(DexMethod method, boolean isD8R8Synthesized) {
+      return (isD8R8Synthesized ? SyntheticPosition.builder() : SourcePosition.builder())
+          .setMethod(method)
+          .setIsD8R8Synthesized(isD8R8Synthesized)
+          .setLine(line)
+          .build();
     }
 
     @Override
@@ -145,7 +153,7 @@
     }
 
     @Override
-    public Position getPosition(DexMethod method) {
+    public Position getPosition(DexMethod method, boolean isD8R8Synthesized) {
       return position;
     }
 
@@ -344,8 +352,11 @@
   private Int2ReferenceMap<BytecodeInstructionMetadata> metadataMap;
 
   public static <V, EV> LirBuilder<V, EV> builder(
-      DexMethod method, LirEncodingStrategy<V, EV> strategy, InternalOptions options) {
-    return new LirBuilder<>(method, strategy, options);
+      DexMethod method,
+      boolean isD8R8Synthesized,
+      LirEncodingStrategy<V, EV> strategy,
+      InternalOptions options) {
+    return new LirBuilder<>(method, isD8R8Synthesized, strategy, options);
   }
 
   private static <EV> void specify(StructuralSpecification<LirCode<EV>, ?> spec) {
@@ -366,7 +377,7 @@
   LirCode(
       IRMetadata irMetadata,
       LirConstant[] constants,
-      PositionEntry[] positions,
+      PositionEntry[] positionTable,
       int argumentCount,
       byte[] instructions,
       int instructionCount,
@@ -375,9 +386,10 @@
       LirStrategyInfo<EV> strategyInfo,
       boolean useDexEstimationStrategy,
       Int2ReferenceMap<BytecodeInstructionMetadata> metadataMap) {
+    assert positionTable != null;
     this.irMetadata = irMetadata;
     this.constants = constants;
-    this.positionTable = positions;
+    this.positionTable = positionTable;
     this.argumentCount = argumentCount;
     this.instructions = instructions;
     this.instructionCount = instructionCount;
@@ -540,7 +552,6 @@
         appView,
         callerPosition,
         protoChanges,
-        appView.graphLens().getOriginalMethodSignature(method.getReference()),
         conversionOptions);
   }
 
@@ -634,9 +645,78 @@
     }
   }
 
+  public Position getPreamblePosition(DexMethod method, boolean isD8R8Synthesized) {
+    if (positionTable.length > 0 && positionTable[0].fromInstructionIndex == 0) {
+      return positionTable[0].getPosition(method, isD8R8Synthesized);
+    }
+    return SyntheticPosition.builder()
+        .setLine(0)
+        .setMethod(method)
+        .setIsD8R8Synthesized(isD8R8Synthesized)
+        .build();
+  }
+
+  public PositionEntry[] getPositionTableAsInlining(
+      Position callerPosition,
+      DexMethod callee,
+      boolean isCalleeD8R8Synthesized,
+      Consumer<Position> preamblePositionConsumer) {
+    // Fast path for moving a synthetic method with no actual line info.
+    if (isCalleeD8R8Synthesized && positionTable.length == 0) {
+      preamblePositionConsumer.accept(callerPosition);
+      return PositionEntry.EMPTY_ARRAY;
+    }
+    Position calleePreamble = getPreamblePosition(callee, isCalleeD8R8Synthesized);
+    CanonicalPositions canonicalPositions =
+        new CanonicalPositions(
+            callerPosition, positionTable.length, callee, isCalleeD8R8Synthesized, calleePreamble);
+    PositionEntry[] newPositionTable;
+    if (positionTable.length == 0) {
+      newPositionTable =
+          new PositionEntry[] {
+            new StructuredPositionEntry(0, canonicalPositions.getPreamblePosition())
+          };
+    } else {
+      newPositionTable = new PositionEntry[positionTable.length];
+      for (int i = 0; i < positionTable.length; i++) {
+        PositionEntry inlineeEntry = positionTable[i];
+        Position inlineePosition = inlineeEntry.getPosition(callee, isCalleeD8R8Synthesized);
+        newPositionTable[i] =
+            new StructuredPositionEntry(
+                inlineeEntry.getFromInstructionIndex(),
+                canonicalPositions.canonicalizePositionWithCaller(inlineePosition));
+      }
+    }
+    preamblePositionConsumer.accept(canonicalPositions.getPreamblePosition());
+    return newPositionTable;
+  }
+
   @Override
-  public Code getCodeAsInlining(DexMethod caller, DexEncodedMethod callee, DexItemFactory factory) {
-    throw new Unimplemented();
+  public Code getCodeAsInlining(
+      DexMethod caller,
+      boolean isCallerD8R8Synthesized,
+      DexMethod callee,
+      boolean isCalleeD8R8Synthesized,
+      DexItemFactory factory) {
+    Position callerPosition =
+        SyntheticPosition.builder().setLine(0).setMethod(caller).setIsD8R8Synthesized(true).build();
+    PositionEntry[] newPositionTable =
+        getPositionTableAsInlining(callerPosition, callee, isCalleeD8R8Synthesized, unused -> {});
+    if (Arrays.equals(positionTable, newPositionTable)) {
+      return this;
+    }
+    return new LirCode<>(
+        irMetadata,
+        constants,
+        newPositionTable,
+        argumentCount,
+        instructions,
+        instructionCount,
+        tryCatchTable,
+        debugLocalInfoTable,
+        strategyInfo,
+        useDexEstimationStrategy,
+        metadataMap);
   }
 
   @Override
@@ -662,9 +742,10 @@
   }
 
   @Override
-  public void forEachPosition(DexMethod method, Consumer<Position> positionConsumer) {
+  public void forEachPosition(
+      DexMethod method, boolean isD8R8Synthesized, Consumer<Position> positionConsumer) {
     for (PositionEntry entry : positionTable) {
-      positionConsumer.accept(entry.getPosition(method));
+      positionConsumer.accept(entry.getPosition(method, isD8R8Synthesized));
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
index 24fb83a..588666a 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
@@ -8,6 +8,7 @@
 import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.defaultAsMethodOfCompanionClass;
 import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.getInterfaceClassType;
 import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.isCompanionClassType;
+import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper.staticAsMethodOfCompanionClass;
 
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
@@ -77,7 +78,7 @@
   // To keep the order deterministic, we sort the classes by their type, which is a unique key.
   private final Set<ProgramOrClasspathClass> mappedClasses = Sets.newIdentityHashSet();
   private final Map<DexReference, MemberNaming> memberNames = Maps.newIdentityHashMap();
-  private final Map<DexMethod, DexString> defaultInterfaceMethodImplementationNames =
+  private final Map<DexMethod, DexString> companionClassInterfaceMethodImplementationNames =
       Maps.newIdentityHashMap();
   private final Map<DexMethod, DexString> additionalMethodNamings = Maps.newIdentityHashMap();
   private final Map<DexField, DexString> additionalFieldNamings = Maps.newIdentityHashMap();
@@ -135,7 +136,7 @@
         new MethodNameMinifier(appView, nameStrategy)
             .computeRenaming(interfaces, subtypingInfo, executorService, timing);
     // Amend the method renamings with the default interface methods.
-    methodRenaming.renaming.putAll(defaultInterfaceMethodImplementationNames);
+    methodRenaming.renaming.putAll(companionClassInterfaceMethodImplementationNames);
     methodRenaming.renaming.putAll(additionalMethodNamings);
     timing.end();
 
@@ -329,19 +330,16 @@
       // TODO(b/150736225): Is this sound? What if the type is a library type that has been pruned?
       DexClass dexClass = appView.appInfo().definitionForWithoutExistenceAssert(type);
       if (dexClass == null || dexClass.isClasspathClass()) {
-        computeDefaultInterfaceMethodMappingsForType(
-            type,
-            classNaming,
-            defaultInterfaceMethodImplementationNames);
+        computeCompanionClassInterfaceMethodMappingsForType(
+            type, classNaming, companionClassInterfaceMethodImplementationNames);
       }
     }
   }
 
-  @SuppressWarnings("ReferenceEquality")
-  private void computeDefaultInterfaceMethodMappingsForType(
+  private void computeCompanionClassInterfaceMethodMappingsForType(
       DexType type,
       ClassNamingForMapApplier classNaming,
-      Map<DexMethod, DexString> defaultInterfaceMethodImplementationNames) {
+      Map<DexMethod, DexString> companionClassInterfaceMethodImplementationNames) {
     // If the class does not resolve, then check if it is a companion class for an interface on
     // the class path.
     if (!isCompanionClassType(type)) {
@@ -360,12 +358,26 @@
       MemberNaming naming = namings.get(0);
       MethodSignature signature = (MethodSignature) naming.getOriginalSignature();
       if (signature.name.startsWith(interfaceType.type.toSourceString())) {
-        DexMethod defaultMethod =
-            defaultAsMethodOfCompanionClass(
-                signature.toUnqualified().toDexMethod(factory, interfaceType.type), factory);
-        assert defaultMethod.holder == type;
-        defaultInterfaceMethodImplementationNames.put(
-            defaultMethod, factory.createString(naming.getRenamedName()));
+        DexMethod originalReference =
+            signature.toUnqualified().toDexMethod(factory, interfaceType.type);
+        DexEncodedMethod originalDefinition = interfaceType.lookupMethod(originalReference);
+        if (originalDefinition == null) {
+          assert false;
+          continue;
+        }
+        DexMethod originalDesugaredMethod;
+        if (originalDefinition.isStatic()) {
+          originalDesugaredMethod = staticAsMethodOfCompanionClass(originalReference, factory);
+        } else if (originalDefinition.isNonAbstractVirtualMethod()) {
+          originalDesugaredMethod = defaultAsMethodOfCompanionClass(originalReference, factory);
+        } else {
+          // There is no implementation for private interface methods as those are not accessible
+          // outside the interface itself and should never need to be represented for apply mapping.
+          continue;
+        }
+        assert type.isIdenticalTo(originalDesugaredMethod.holder);
+        DexString renamed = factory.createString(naming.getRenamedName());
+        companionClassInterfaceMethodImplementationNames.put(originalDesugaredMethod, renamed);
       }
     }
   }
diff --git a/src/main/java/com/android/tools/r8/optimize/accessmodification/AccessModifier.java b/src/main/java/com/android/tools/r8/optimize/accessmodification/AccessModifier.java
index fba2f6f..c97f10b 100644
--- a/src/main/java/com/android/tools/r8/optimize/accessmodification/AccessModifier.java
+++ b/src/main/java/com/android/tools/r8/optimize/accessmodification/AccessModifier.java
@@ -317,7 +317,10 @@
       lensBuilder.recordMove(method.getReference(), newMethodReference);
       method =
           new ProgramMethod(
-              holder, method.getDefinition().toTypeSubstitutedMethod(newMethodReference));
+              holder,
+              method
+                  .getDefinition()
+                  .toTypeSubstitutedMethodAsInlining(newMethodReference, appView.dexItemFactory()));
     }
     if (method.getAccessFlags().isPromotedFromPrivateToPublic()
         && method.getAccessFlags().belongsToVirtualPool()) {
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
index 12a71ce..3d7c4e6 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
@@ -102,8 +102,9 @@
           }
 
           DexEncodedMethod replacement =
-              method.toTypeSubstitutedMethod(
+              method.toTypeSubstitutedMethodAsInlining(
                   methodReferenceAfterParameterRemoval,
+                  appView.dexItemFactory(),
                   builder -> {
                     if (graphLens.hasPrototypeChanges(methodReferenceAfterParameterRemoval)) {
                       RewrittenPrototypeDescription prototypeChanges =
diff --git a/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoisting.java b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoisting.java
index d8149d7..387d67d 100644
--- a/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoisting.java
+++ b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoisting.java
@@ -263,7 +263,9 @@
     DexMethod newMethodReference =
         appView.dexItemFactory().createMethod(clazz.type, method.proto, method.name);
     DexEncodedMethod newMethod =
-        representative.getDefinition().toTypeSubstitutedMethod(newMethodReference);
+        representative
+            .getDefinition()
+            .toTypeSubstitutedMethodAsInlining(newMethodReference, appView.dexItemFactory());
     if (newMethod.getAccessFlags().isFinal()) {
       newMethod.getAccessFlags().demoteFromFinal();
     }
diff --git a/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizer.java b/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizer.java
index 3cac2b3..902fb45 100644
--- a/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/proto/ProtoNormalizer.java
@@ -136,8 +136,9 @@
                     // this should simply clear the optimization info, or replace it by a
                     // ThrowingMethodOptimizationInfo since we should never use the optimization
                     // info after this point.
-                    return method.toTypeSubstitutedMethod(
+                    return method.toTypeSubstitutedMethodAsInlining(
                         newMethodReference,
+                        dexItemFactory,
                         builder -> {
                           if (!prototypeChanges.isEmpty()) {
                             builder
diff --git a/src/main/java/com/android/tools/r8/repackaging/Repackaging.java b/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
index 1b9b6c3..55501d8 100644
--- a/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
+++ b/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexReference;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.DirectMappedDexApplication;
 import com.android.tools.r8.graph.InnerClassAttribute;
@@ -115,6 +116,11 @@
           protected boolean isLegitimateToHaveEmptyMappings() {
             return true;
           }
+
+          @Override
+          public <T extends DexReference> boolean isSimpleRenaming(T from, T to) {
+            return getPrevious().isSimpleRenaming(from, to);
+          }
         };
     DirectMappedDexApplication newApplication =
         appView
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 0f3b284..f96b242 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -67,7 +67,6 @@
 import com.android.tools.r8.graph.lens.NonIdentityGraphLens;
 import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
 import com.android.tools.r8.ir.code.InvokeType;
-import com.android.tools.r8.ir.code.Position.SyntheticPosition;
 import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
 import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
@@ -1014,7 +1013,9 @@
                   virtualMethod.getProto().prependParameter(source.getType(), dexItemFactory),
                   dexItemFactory.createGloballyFreshMemberString(resultingMethodBaseName));
           assert availableMethodSignatures.test(resultingMethodReference);
-          resultingMethod = virtualMethod.toTypeSubstitutedMethod(resultingMethodReference);
+          resultingMethod =
+              virtualMethod.toTypeSubstitutedMethodAsInlining(
+                  resultingMethodReference, dexItemFactory);
           makeStatic(resultingMethod);
         } else {
           // This virtual method could be called directly from a sub class via an invoke-super in-
@@ -1443,7 +1444,6 @@
       SynthesizedBridgeCode code =
           new SynthesizedBridgeCode(
               newMethod,
-              appView.graphLens().getOriginalMethodSignature(method.getReference()),
               invocationTarget.getReference(),
               invocationTarget.isStatic()
                   ? STATIC
@@ -1562,7 +1562,8 @@
         count++;
       } while (!availableMethodSignatures.test(newSignature));
 
-      DexEncodedMethod result = method.toTypeSubstitutedMethod(newSignature);
+      DexEncodedMethod result =
+          method.toTypeSubstitutedMethodAsInlining(newSignature, appView.dexItemFactory());
       result.getMutableOptimizationInfo().markForceInline();
       deferredRenamings.map(method.getReference(), result.getReference());
       deferredRenamings.recordMove(method.getReference(), result.getReference());
@@ -1616,7 +1617,7 @@
           throw new Unreachable();
       }
 
-      return method.toTypeSubstitutedMethod(newSignature);
+      return method.toTypeSubstitutedMethodAsInlining(newSignature, appView.dexItemFactory());
     }
 
     private DexEncodedField renameFieldIfNeeded(
@@ -2242,22 +2243,19 @@
     public void registerTypeReference(DexType type) {}
   }
 
-  protected static class SynthesizedBridgeCode extends AbstractSynthesizedCode {
+  public static class SynthesizedBridgeCode extends AbstractSynthesizedCode {
 
     private DexMethod method;
-    private DexMethod originalMethod;
     private DexMethod invocationTarget;
     private InvokeType type;
     private final boolean isInterface;
 
     public SynthesizedBridgeCode(
         DexMethod method,
-        DexMethod originalMethod,
         DexMethod invocationTarget,
         InvokeType type,
         boolean isInterface) {
       this.method = method;
-      this.originalMethod = originalMethod;
       this.invocationTarget = invocationTarget;
       this.type = type;
       this.isInterface = isInterface;
@@ -2285,21 +2283,11 @@
           ForwardMethodSourceCode.builder(method);
       forwardSourceCodeBuilder
           .setReceiver(method.holder)
-          .setOriginalMethod(originalMethod)
           .setTargetReceiver(type.isStatic() ? null : method.holder)
           .setTarget(invocationTarget)
           .setInvokeType(type)
           .setIsInterface(isInterface);
-      return (context, callerPosition) -> {
-        SyntheticPosition caller =
-            SyntheticPosition.builder()
-                .setLine(0)
-                .setMethod(method)
-                .setIsD8R8Synthesized(true)
-                .setCallerPosition(callerPosition)
-                .build();
-        return forwardSourceCodeBuilder.build(context, caller);
-      };
+      return forwardSourceCodeBuilder::build;
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/utils/DexDebugUtils.java b/src/main/java/com/android/tools/r8/utils/DexDebugUtils.java
index adfda77..54b8da6 100644
--- a/src/main/java/com/android/tools/r8/utils/DexDebugUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/DexDebugUtils.java
@@ -37,13 +37,13 @@
   }
 
   public static PositionInfo computePreamblePosition(
-      DexMethod method, EventBasedDebugInfo debugInfo) {
+      DexMethod method, boolean isD8R8Synthesized, EventBasedDebugInfo debugInfo) {
     if (debugInfo == null) {
       return PositionInfo.builder().build();
     }
     Box<Position> existingPositionFrame = new Box<>();
     DexDebugPositionState visitor =
-        new DexDebugPositionState(debugInfo.startLine, method) {
+        new DexDebugPositionState(debugInfo.startLine, method, isD8R8Synthesized) {
           @Override
           public void visit(SetPositionFrame setPositionFrame) {
             super.visit(setPositionFrame);
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 787dd32..08d30ac 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
@@ -70,17 +70,20 @@
       // If a method with overloads does not have an actual position then map it to the implicit
       // preamble position.
       DexMethod reference = method.getReference();
-      DexMethod original = appView.graphLens().getOriginalMethodSignature(reference);
       CfPosition preamblePositionForOverload =
           new CfPosition(
               new CfLabel(),
               remapAndAdd(
-                  SyntheticPosition.builder().setMethod(original).setLine(0).build(),
+                  SyntheticPosition.builder()
+                      .setMethod(reference)
+                      .setLine(0)
+                      .setIsD8R8Synthesized(method.getDefinition().isD8R8Synthesized())
+                      .build(),
                   positionRemapper,
                   mappedPositions));
       List<CfInstruction> shiftedPositions = new ArrayList<>(oldInstructions.size() + 2);
-      shiftedPositions.add(preamblePositionForOverload);
       shiftedPositions.add(preamblePositionForOverload.getLabel());
+      shiftedPositions.add(preamblePositionForOverload);
       shiftedPositions.addAll(newInstructions);
       newInstructions = shiftedPositions;
     }
diff --git a/src/main/java/com/android/tools/r8/utils/positions/DexPositionToNoPcMappedRangeMapper.java b/src/main/java/com/android/tools/r8/utils/positions/DexPositionToNoPcMappedRangeMapper.java
index 8e0fcbb..099e233 100644
--- a/src/main/java/com/android/tools/r8/utils/positions/DexPositionToNoPcMappedRangeMapper.java
+++ b/src/main/java/com/android/tools/r8/utils/positions/DexPositionToNoPcMappedRangeMapper.java
@@ -99,8 +99,9 @@
         List<DexDebugEvent> processedEvents,
         DexItemFactory factory,
         int startLine,
-        DexMethod method) {
-      super(startLine, method);
+        DexMethod method,
+        boolean isD8R8Synthesized) {
+      super(startLine, method, isD8R8Synthesized);
       this.positionEventEmitter = positionEventEmitter;
       this.mappedPositions = mappedPositions;
       this.positionRemapper = positionRemapper;
@@ -189,12 +190,9 @@
         getEventBasedDebugInfo(method.getDefinition(), dexCode, appView);
 
     List<DexDebugEvent> processedEvents = new ArrayList<>();
-
     PositionEventEmitter positionEventEmitter =
         new PositionEventEmitter(
-            application.dexItemFactory,
-            appView.graphLens().getOriginalMethodSignature(method.getReference()),
-            processedEvents);
+            application.dexItemFactory, method.getReference(), processedEvents);
 
     DexDebugPositionStateVisitor visitor =
         new DexDebugPositionStateVisitor(
@@ -204,7 +202,8 @@
             processedEvents,
             appView.dexItemFactory(),
             debugInfo.startLine,
-            appView.graphLens().getOriginalMethodSignature(method.getReference()));
+            method.getReference(),
+            method.getDefinition().isD8R8Synthesized());
 
     DexDebugEvent[] events = debugInfo.events;
     for (DexDebugEvent event : events) {
@@ -219,7 +218,7 @@
 
     assert !isIdentityMapping
         || visitor.inlinedOriginalPosition
-        || verifyIdentityMapping(debugInfo, optimizedDebugInfo);
+        || verifyIdentityMapping(method, debugInfo, optimizedDebugInfo);
 
     dexCode.setDebugInfo(optimizedDebugInfo);
     return mappedPositions;
@@ -242,7 +241,12 @@
   }
 
   private static boolean verifyIdentityMapping(
-      EventBasedDebugInfo originalDebugInfo, EventBasedDebugInfo optimizedDebugInfo) {
+      ProgramMethod method,
+      EventBasedDebugInfo originalDebugInfo,
+      EventBasedDebugInfo optimizedDebugInfo) {
+    if (isTrivialSyntheticMethod(method, originalDebugInfo)) {
+      return true;
+    }
     assert optimizedDebugInfo.startLine == originalDebugInfo.startLine;
     assert optimizedDebugInfo.events.length == originalDebugInfo.events.length;
     for (int i = 0; i < originalDebugInfo.events.length; ++i) {
@@ -250,4 +254,27 @@
     }
     return true;
   }
+
+  private static boolean isTrivialSyntheticMethod(
+      ProgramMethod method, EventBasedDebugInfo originalDebugInfo) {
+    if (!method.getDefinition().isD8R8Synthesized()) {
+      return false;
+    }
+    // A synthetic method may have different debug info but still be trivial if it holds that all
+    // the frames are just trivial frames for the synthetic method itself.
+    for (DexDebugEvent event : originalDebugInfo.events) {
+      if (event.isPositionFrame()) {
+        Position position = event.asSetPositionFrame().getPosition();
+        if (!method.getReference().isIdenticalTo(position.getMethod())
+            || !position.isD8R8Synthesized()
+            || position.hasCallerPosition()
+            || position.isOutline()
+            || position.isRemoveInnerFramesIfThrowingNpe()) {
+          // If the frame is not trivial then break and assert the usual identity property.
+          return false;
+        }
+      }
+    }
+    return true;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/positions/DexPositionToPcMappedRangeMapper.java b/src/main/java/com/android/tools/r8/utils/positions/DexPositionToPcMappedRangeMapper.java
index ecc6468..a1bb8a5 100644
--- a/src/main/java/com/android/tools/r8/utils/positions/DexPositionToPcMappedRangeMapper.java
+++ b/src/main/java/com/android/tools/r8/utils/positions/DexPositionToPcMappedRangeMapper.java
@@ -45,7 +45,8 @@
     DexDebugEventVisitor visitor =
         new DexDebugPositionState(
             debugInfo.startLine,
-            appView.graphLens().getOriginalMethodSignature(method.getReference())) {
+            method.getReference(),
+            method.getDefinition().isD8R8Synthesized()) {
           @Override
           public void visit(Default defaultEvent) {
             super.visit(defaultEvent);
diff --git a/src/main/java/com/android/tools/r8/utils/positions/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/positions/LineNumberOptimizer.java
index c91839c..12a6f21 100644
--- a/src/main/java/com/android/tools/r8/utils/positions/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/positions/LineNumberOptimizer.java
@@ -147,9 +147,7 @@
 
         for (ProgramMethod method : methods) {
           DexEncodedMethod definition = method.getDefinition();
-          DexMethod methodReference = method.getReference();
           if (methodName == method.getName()
-              && appView.graphLens().getOriginalMethodSignature(methodReference) == methodReference
               && !mustHaveResidualDebugInfo(appView.options(), definition)
               && !definition.isD8R8Synthesized()
               && methods.size() <= 1) {
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 41a3ecc..80e22b5 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
@@ -18,6 +18,7 @@
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.ir.code.Position;
 import com.android.tools.r8.ir.code.Position.OutlineCallerPosition;
+import com.android.tools.r8.ir.code.Position.OutlinePosition;
 import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.naming.ClassNaming;
 import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRange;
@@ -224,10 +225,6 @@
         PositionRemapper positionRemapper,
         boolean canUseDexPc) {
       DexEncodedMethod definition = method.getDefinition();
-      DexMethod originalMethod =
-          appView.graphLens().getOriginalMethodSignatureForMapping(method.getReference());
-      MethodSignature originalSignature =
-          MethodSignature.fromDexMethod(originalMethod, originalMethod.holder != originalType);
 
       OneShotCollectionConsumer<MappingInformation> methodSpecificMappingInformation =
           OneShotCollectionConsumer.wrap(new ArrayList<>());
@@ -236,13 +233,25 @@
       // classes in the library. Additionally, this is one place where it is helpful for developers
       // to also get reported synthesized frames since stubbing can change control-flow and
       // exceptions.
-      if (isD8R8Synthesized(method, mappedPositions)
-          && !appView.getSyntheticItems().isGlobalSyntheticClass(method.getHolder())) {
+      boolean canStripOuterFrame = canStripOuterFrame(method, mappedPositions);
+      boolean residualIsD8R8Synthesized =
+          method.getDefinition().isD8R8Synthesized()
+              && !appView.getSyntheticItems().isGlobalSyntheticClass(method.getHolder())
+              // TODO(b/302509457): Currently we can only represent moves on methods that have code
+              //  and thus positions. For methods with no code, use the lens to find the original.
+              && method.getDefinition().hasCode();
+      if (residualIsD8R8Synthesized && !canStripOuterFrame) {
         methodSpecificMappingInformation.add(CompilerSynthesizedMappingInformation.getInstance());
       }
 
       DexMethod residualMethod =
           appView.getNamingLens().lookupMethod(method.getReference(), appView.dexItemFactory());
+      MethodSignature residualSignature = MethodSignature.fromDexMethod(residualMethod);
+
+      DexMethod originalMethod =
+          appView.graphLens().getOriginalMethodSignatureForMapping(method.getReference());
+      MethodSignature originalSignature =
+          MethodSignature.fromDexMethod(originalMethod, originalMethod.holder != originalType);
 
       MapVersion mapFileVersion = appView.options().getMapFileVersion();
       if (isIdentityMapping(
@@ -257,13 +266,32 @@
             || appView.isCfByteCodePassThrough(definition);
         return this;
       }
-      MethodSignature residualSignature = MethodSignature.fromDexMethod(residualMethod);
 
-      if (ResidualSignatureMappingInformation.isSupported(mapFileVersion)
-          && (!originalSignature.type.equals(residualSignature.type)
-              || !Arrays.equals(originalSignature.parameters, residualSignature.parameters))) {
-        methodSpecificMappingInformation.add(
-            ResidualMethodSignatureMappingInformation.fromDexMethod(residualMethod));
+      if (ResidualSignatureMappingInformation.isSupported(mapFileVersion)) {
+        boolean isSame = true;
+        if (canStripOuterFrame) {
+          for (MappedPosition mappedPosition : mappedPositions) {
+            Position outerMostAfterStrip = mappedPosition.getPosition();
+            while (outerMostAfterStrip.getCallerPosition().hasCallerPosition()) {
+              outerMostAfterStrip = outerMostAfterStrip.getCallerPosition();
+            }
+            MethodSignature positionSignature =
+                MethodSignature.fromDexMethod(outerMostAfterStrip.getMethod());
+            if (!positionSignature.type.equals(residualSignature.type)
+                || !Arrays.equals(positionSignature.parameters, residualSignature.parameters)) {
+              isSame = false;
+              break;
+            }
+          }
+        } else {
+          isSame =
+              originalSignature.type.equals(residualSignature.type)
+                  && Arrays.equals(originalSignature.parameters, residualSignature.parameters);
+        }
+        if (!isSame) {
+          methodSpecificMappingInformation.add(
+              ResidualMethodSignatureMappingInformation.fromDexMethod(residualMethod));
+        }
       }
 
       MemberNaming memberNaming = new MemberNaming(originalSignature, residualSignature);
@@ -293,11 +321,11 @@
                   });
 
       // Check if mapped position is an outline
-      DexMethod outlineMethod = getOutlineMethod(mappedPositions.get(0).getPosition());
-      if (outlineMethod != null) {
+      DexMethod outlineMethodKey = getOutlineMethodKey(mappedPositions);
+      if (outlineMethodKey != null) {
         outlinesToFix
             .computeIfAbsent(
-                outlineMethod,
+                outlineMethodKey,
                 outline -> new OutlineFixupBuilder(computeMappedMethod(outline, appView)))
             .setMappedPositionsOutline(mappedPositions);
         methodSpecificMappingInformation.add(OutlineMappingInformation.builder().build());
@@ -348,6 +376,11 @@
         Range originalRange =
             nonCardinalRangeCache.get(firstPosition.getLine(), lastPosition.getLine());
 
+        boolean hasSyntheticOuterFrameAndNonSyntheticInner =
+            residualIsD8R8Synthesized && firstPosition.hasCallerPosition();
+        assert !hasSyntheticOuterFrameAndNonSyntheticInner
+            || firstPosition.getOutermostCaller().isD8R8Synthesized();
+
         MappedRange lastMappedRange =
             getMappedRangesForPosition(
                 appView,
@@ -358,7 +391,8 @@
                 obfuscatedRange,
                 originalRange,
                 prunedInlinedClasses,
-                cardinalRangeCache);
+                cardinalRangeCache,
+                canStripOuterFrame);
         // firstPosition will contain a potential outline caller.
         if (firstPosition.isOutlineCaller()) {
           outlineCallerPositions.putIfAbsent(firstPosition.asOutlineCaller(), lastMappedRange);
@@ -395,7 +429,8 @@
                               placeHolderLineToBeFixed, placeHolderLineToBeFixed),
                           nonCardinalRangeCache.get(position.getLine(), position.getLine()),
                           prunedInlinedClasses,
-                          cardinalRangeCache);
+                          cardinalRangeCache,
+                          canStripOuterFrame);
                   maxPc.set(lastRange.minifiedRange.to);
                 });
         outlinesToFix
@@ -409,10 +444,40 @@
       return this;
     }
 
-    private boolean isD8R8Synthesized(ProgramMethod method, List<MappedPosition> mappedPositions) {
-      return method.getDefinition().isD8R8Synthesized()
-          || (!mappedPositions.isEmpty()
-              && mappedPositions.get(0).getPosition().isD8R8Synthesized());
+    private boolean canStripOuterFrame(ProgramMethod method, List<MappedPosition> mappedPositions) {
+      assert verifySyntheticPositions(method, mappedPositions);
+      if (!method.getDefinition().isD8R8Synthesized() || mappedPositions.isEmpty()) {
+        return false;
+      }
+      for (MappedPosition mappedPosition : mappedPositions) {
+        Position position = mappedPosition.getPosition();
+        if (!position.hasCallerPosition()) {
+          // At least one position only has the synthetic method as its frame, so we can't strip it.
+          return false;
+        }
+        if (position.isOutline() || position.isOutlineCaller()) {
+          return false;
+        }
+      }
+      return true;
+    }
+
+    private boolean verifySyntheticPositions(
+        ProgramMethod method, List<MappedPosition> mappedPositions) {
+      DexMethod thisMethod = method.getReference();
+      boolean d8R8Synthesized = method.getDefinition().isD8R8Synthesized();
+      for (MappedPosition mappedPosition : mappedPositions) {
+        Position position = mappedPosition.getPosition();
+        while (position.hasCallerPosition()) {
+          assert !position.isOutline();
+          assert !position.isD8R8Synthesized();
+          position = position.getCallerPosition();
+        }
+        DexMethod outerCaller = position.getMethod();
+        assert thisMethod.isIdenticalTo(outerCaller);
+        assert d8R8Synthesized == position.isD8R8Synthesized();
+      }
+      return true;
     }
 
     private MethodReference computeMappedMethod(DexMethod current, AppView<?> appView) {
@@ -432,13 +497,15 @@
         Range obfuscatedRange,
         Range originalLine,
         Map<DexType, String> prunedInlineHolder,
-        CardinalPositionRangeAllocator cardinalRangeCache) {
+        CardinalPositionRangeAllocator cardinalRangeCache,
+        boolean canStripOuterFrame) {
       MappedRange lastMappedRange = null;
       int inlineFramesCount = -1;
       do {
-        if (position.isD8R8Synthesized() && position.hasCallerPosition()) {
-          position = position.getCallerPosition();
-          continue;
+        if (canStripOuterFrame && !position.hasCallerPosition()) {
+          assert position.isD8R8Synthesized();
+          assert lastMappedRange != null;
+          break;
         }
         inlineFramesCount += 1;
         DexType holderType = position.getMethod().getHolderType();
@@ -473,16 +540,15 @@
       return lastMappedRange;
     }
 
-    private DexMethod getOutlineMethod(Position mappedPosition) {
-      if (mappedPosition.isOutline()) {
-        return mappedPosition.getMethod();
+    private DexMethod getOutlineMethodKey(List<MappedPosition> mappedPositions) {
+      for (MappedPosition mappedPosition : mappedPositions) {
+        Position position = mappedPosition.getPosition().getOutermostCaller();
+        if (position.isOutline()) {
+          OutlinePosition outline = (OutlinePosition) position;
+          return outline.getOutlineMethodKey();
+        }
       }
-      Position caller = mappedPosition.getCallerPosition();
-      if (caller == null) {
-        return null;
-      }
-      Position outermostCaller = caller.getOutermostCaller();
-      return outermostCaller.isOutline() ? outermostCaller.getMethod() : null;
+      return null;
     }
 
     @SuppressWarnings("ReferenceEquality")
diff --git a/src/main/java/com/android/tools/r8/utils/positions/PositionRemapper.java b/src/main/java/com/android/tools/r8/utils/positions/PositionRemapper.java
index 1d7e99f..4026dc3 100644
--- a/src/main/java/com/android/tools/r8/utils/positions/PositionRemapper.java
+++ b/src/main/java/com/android/tools/r8/utils/positions/PositionRemapper.java
@@ -77,10 +77,9 @@
     }
 
     @Override
-    @SuppressWarnings("ReferenceEquality")
     public Pair<Position, Position> createRemappedPosition(Position position) {
       assert position.getMethod() != null;
-      if (previousMethod == position.getMethod()) {
+      if (position.getMethod().isIdenticalTo(previousMethod)) {
         assert previousSourceLine >= 0;
         if (position.getLine() > previousSourceLine
             && position.getLine() - previousSourceLine <= maxLineDelta) {
diff --git a/src/test/examples/shaking1/print-mapping-cf.ref b/src/test/examples/shaking1/print-mapping-cf.ref
deleted file mode 100644
index 8b38084..0000000
--- a/src/test/examples/shaking1/print-mapping-cf.ref
+++ /dev/null
@@ -1,7 +0,0 @@
-shaking1.Shaking -> shaking1.Shaking:
-shaking1.Used -> a.a:
-    1:2:void <init>(java.lang.String):12:13 -> <init>
-    1:1:java.lang.String aMethodThatIsNotUsedButKept():21:21 -> aMethodThatIsNotUsedButKept
-    1:2:void main(java.lang.String[]):8:9 -> main
-    1:1:java.lang.String method():17:17 -> a
-    java.lang.String name -> a
\ No newline at end of file
diff --git a/src/test/examples/shaking1/print-mapping-dex.ref b/src/test/examples/shaking1/print-mapping-dex.ref
deleted file mode 100644
index cd0d3a3..0000000
--- a/src/test/examples/shaking1/print-mapping-dex.ref
+++ /dev/null
@@ -1,8 +0,0 @@
-shaking1.Shaking -> shaking1.Shaking:
-    0:8:void main(java.lang.String[]):8:8 -> main
-    9:21:void main(java.lang.String[]):9:9 -> main
-shaking1.Used -> a.a:
-    java.lang.String name -> a
-    0:5:void <init>(java.lang.String):12:12 -> <init>
-    0:16:java.lang.String method():17:17 -> a
-    0:2:java.lang.String aMethodThatIsNotUsedButKept():0:0 -> aMethodThatIsNotUsedButKept
diff --git a/src/test/examplesJava11/nesthostexample/NestHierachy.java b/src/test/examplesJava11/nesthostexample/NestHierachy.java
new file mode 100644
index 0000000..ba971d2
--- /dev/null
+++ b/src/test/examplesJava11/nesthostexample/NestHierachy.java
@@ -0,0 +1,76 @@
+// Copyright (c) 2023, 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 nesthostexample;
+
+public class NestHierachy {
+  abstract static class InnerSuper {
+    public void m1() {
+      System.out.println("m1");
+    }
+
+    private void m2() {
+      System.out.println("m2");
+    }
+
+    private void m3() {
+      System.out.println("m3");
+    }
+
+    public static void s1() {
+      System.out.println("s1");
+    }
+
+    private static void s2() {
+      System.out.println("s2");
+    }
+  }
+
+  static class InnerSub extends InnerSuper {
+    public void m1() {
+      super.m1();
+    }
+
+    public void m2() {
+      super.m2();
+    }
+
+    private void m3() {
+      super.m3();
+    }
+
+    public static void s1() {
+      InnerSuper.s1();
+    }
+
+    private static void s2() {
+      InnerSuper.s2();
+    }
+  }
+
+  public static void callOnInnerSuper(InnerSuper innerSuper) {
+    innerSuper.m1();
+    innerSuper.m2();
+    innerSuper.m3();
+    innerSuper.s1();
+    innerSuper.s2();
+  }
+
+  public static void callOnInnerSub(InnerSub innerSub) {
+    innerSub.m1();
+    innerSub.m2();
+    innerSub.m3();
+    innerSub.s1();
+    innerSub.s2();
+  }
+
+  public static void main(String[] args) {
+    callOnInnerSuper(new InnerSub());
+    callOnInnerSub(new InnerSub());
+    InnerSuper.s1();
+    InnerSuper.s2();
+    InnerSub.s1();
+    InnerSub.s2();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/R8EntryPointTests.java b/src/test/java/com/android/tools/r8/R8EntryPointTests.java
index c0b143c..72438ac 100644
--- a/src/test/java/com/android/tools/r8/R8EntryPointTests.java
+++ b/src/test/java/com/android/tools/r8/R8EntryPointTests.java
@@ -19,7 +19,11 @@
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
 
+@RunWith(Parameterized.class)
 public class R8EntryPointTests extends TestBase {
 
   private static final String MAPPING = "mapping.txt";
@@ -31,6 +35,15 @@
 
   private Path testFlags;
 
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return TestParameters.builder().withNoneRuntime().build();
+  }
+
+  public R8EntryPointTests(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
+
   @Before
   public void setup() throws IOException {
     testFlags = temp.newFile("local.flags").toPath();
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithOverlappingVisibilitiesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithOverlappingVisibilitiesTest.java
index 57e100b..e88c86e 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithOverlappingVisibilitiesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithOverlappingVisibilitiesTest.java
@@ -43,14 +43,14 @@
 
               ClassSubject bClassSubject = codeInspector.clazz(B.class);
               assertThat(bClassSubject, isPresent());
-              methodSubject = bClassSubject.method("void", "foo$bridge");
+              methodSubject = getUniqueDispatchBridgeMethod(bClassSubject);
               assertThat(methodSubject, isPackagePrivate());
 
               assertThat(codeInspector.clazz(C.class), isAbsent());
 
               ClassSubject dClassSubject = codeInspector.clazz(D.class);
               assertThat(dClassSubject, isPresent());
-              methodSubject = dClassSubject.method("void", "foo$bridge");
+              methodSubject = getUniqueDispatchBridgeMethod(dClassSubject);
               assertThat(methodSubject, isPublic());
 
               ClassSubject eClassSubject = codeInspector.clazz(E.class);
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingOverlapTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingOverlapTest.java
index e7723fd..7dee4de 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingOverlapTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingOverlapTest.java
@@ -49,7 +49,7 @@
               assertThat(otherInitSubject, isPresent());
               assertThat(otherInitSubject, writesInstanceField(classIdFieldSubject.getDexField()));
 
-              MethodSubject printSubject = aClassSubject.method("void", "print$bridge");
+              MethodSubject printSubject = getUniqueDispatchBridgeMethod(aClassSubject);
               assertThat(printSubject, isPresent());
               assertThat(printSubject, readsInstanceField(classIdFieldSubject.getDexField()));
 
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
index 9288d9b..ac23576 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
@@ -61,7 +61,7 @@
               assertThat(otherInitSubject, isPresent());
               assertThat(otherInitSubject, writesInstanceField(classIdFieldSubject.getDexField()));
 
-              MethodSubject printSubject = aClassSubject.method("void", "print$bridge");
+              MethodSubject printSubject = getUniqueDispatchBridgeMethod(aClassSubject);
               assertThat(printSubject, isPresent());
               assertThat(printSubject, readsInstanceField(classIdFieldSubject.getDexField()));
 
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTrivialOverlapTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTrivialOverlapTest.java
index ba27f46..2be25d3 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTrivialOverlapTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTrivialOverlapTest.java
@@ -49,7 +49,7 @@
               assertThat(otherInitSubject, isPresent());
               assertThat(otherInitSubject, writesInstanceField(classIdFieldSubject.getDexField()));
 
-              MethodSubject printSubject = aClassSubject.method("void", "print$bridge");
+              MethodSubject printSubject = getUniqueDispatchBridgeMethod(aClassSubject);
               assertThat(printSubject, isPresent());
               assertThat(printSubject, readsInstanceField(classIdFieldSubject.getDexField()));
 
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingTestBase.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingTestBase.java
index a24aca6..0c564bc 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingTestBase.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingTestBase.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.horizontalclassmerging.ClassMerger;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.FieldSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
@@ -29,6 +30,10 @@
     return getTestParameters().withAllRuntimesAndApiLevels().build();
   }
 
+  public static MethodSubject getUniqueDispatchBridgeMethod(ClassSubject clazz) {
+    return clazz.uniqueMethodThatMatches(m -> m.isVirtual() && m.isCompilerSynthesized());
+  }
+
   protected FieldSubject classMergerClassIdField(ClassSubject classSubject) {
     assertTrue(classSubject.isPresent());
     FieldSubject[] classIds =
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorForwardingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorForwardingTest.java
index 915e10b..4f7faad 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorForwardingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorForwardingTest.java
@@ -51,7 +51,7 @@
               assertThat(otherInitSubject, isPresent());
               assertThat(otherInitSubject, writesInstanceField(classIdFieldSubject.getDexField()));
 
-              MethodSubject printSubject = aClassSubject.method("void", "print$bridge");
+              MethodSubject printSubject = getUniqueDispatchBridgeMethod(aClassSubject);
               assertThat(printSubject, isPresent());
               assertThat(printSubject, readsInstanceField(classIdFieldSubject.getDexField()));
 
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorWithEquivalenceStackTraceTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorWithEquivalenceStackTraceTest.java
index 655e6a8..3126419 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorWithEquivalenceStackTraceTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorWithEquivalenceStackTraceTest.java
@@ -4,36 +4,87 @@
 
 package com.android.tools.r8.classmerging.horizontal;
 
-import static com.android.tools.r8.naming.retrace.StackTrace.isSame;
+import static com.android.tools.r8.naming.retrace.StackTrace.isSameExceptForLineNumbers;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static junit.framework.TestCase.assertTrue;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
 
 import com.android.tools.r8.NeverClassInline;
 import com.android.tools.r8.NoVerticalClassMerging;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestRuntime.CfRuntime;
+import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.naming.retrace.StackTrace;
-import org.junit.BeforeClass;
+import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine;
 import org.junit.Test;
 
 public class MergedConstructorWithEquivalenceStackTraceTest extends HorizontalClassMergingTestBase {
 
-  private static StackTrace expectedStackTrace;
-
   public MergedConstructorWithEquivalenceStackTraceTest(TestParameters parameters) {
     super(parameters);
   }
 
-  @BeforeClass
-  public static void setup() throws Exception {
+  private final String FILE_NAME =
+      ToolHelper.getSourceFileForTestClass(getClass()).getFileName().toString();
+
+  private StackTrace getExpectedStackTrace() {
+    return StackTrace.builder()
+        .add(
+            StackTraceLine.builder()
+                .setFileName(FILE_NAME)
+                .setClassName(typeName(Parent.class))
+                .setMethodName("<init>")
+                .build())
+        .add(
+            StackTraceLine.builder()
+                .setFileName(FILE_NAME)
+                .setClassName(typeName(A.class))
+                .setMethodName("<init>")
+                .build())
+        .add(
+            StackTraceLine.builder()
+                .setFileName(FILE_NAME)
+                .setClassName(typeName(Main.class))
+                .setMethodName("main")
+                .build())
+        .build();
+  }
+
+  // TODO(b/301920457): The constructors should be merged in such a way that the original stack can
+  //  be recovered.
+  private StackTrace getUnxpectedStackTrace() {
+    return StackTrace.builder()
+        .add(
+            StackTraceLine.builder()
+                .setFileName(FILE_NAME)
+                .setClassName(typeName(Parent.class))
+                .setMethodName("<init>")
+                .build())
+        .add(
+            StackTraceLine.builder()
+                .setFileName(FILE_NAME)
+                .setClassName(typeName(Main.class))
+                .setMethodName("main")
+                .build())
+        .build();
+  }
+
+  private void checkRetracedStackTrace(StackTrace expectedStackTrace, StackTrace stackTrace) {
+    assertThat(stackTrace, isSameExceptForLineNumbers(expectedStackTrace));
+    for (StackTraceLine line : stackTrace.getStackTraceLines()) {
+      assertTrue(line.lineNumber > 0);
+    }
+  }
+
+  @Test
+  public void testReference() throws Exception {
+    parameters.assumeJvmTestParameters();
     // Get the expected stack trace by running on the JVM.
-    expectedStackTrace =
-        testForJvm(getStaticTemp())
-            .addTestClasspath()
-            .run(CfRuntime.getSystemRuntime(), Main.class)
-            .assertFailure()
-            .map(StackTrace::extractFromJvm);
+    testForJvm(parameters)
+        .addTestClasspath()
+        .run(parameters.getRuntime(), Main.class)
+        .assertFailure()
+        .inspectStackTrace(actual -> checkRetracedStackTrace(getExpectedStackTrace(), actual));
   }
 
   @Test
@@ -54,17 +105,8 @@
         .inspectStackTrace(
             (stackTrace, codeInspector) -> {
               assertThat(codeInspector.clazz(A.class), isPresent());
-              StackTrace expectedStackTraceWithMergedConstructor =
-                  StackTrace.builder()
-                      .add(expectedStackTrace)
-                      // TODO(b/124483578): Stack trace lines from the merging of equivalent
-                      //  constructors should retrace to the set of lines from each of the
-                      //  individual source constructors.
-                      .map(
-                          1, stackTraceLine -> stackTraceLine.builderOf().setLineNumber(-1).build())
-                      .build();
-              assertThat(stackTrace, isSame(expectedStackTraceWithMergedConstructor));
               assertThat(codeInspector.clazz(B.class), not(isPresent()));
+              checkRetracedStackTrace(getUnxpectedStackTrace(), stackTrace);
             });
   }
 
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonFinalOverrideOfFinalMethodNonTrivialMergeTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonFinalOverrideOfFinalMethodNonTrivialMergeTest.java
index 478958e..625af3f 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonFinalOverrideOfFinalMethodNonTrivialMergeTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonFinalOverrideOfFinalMethodNonTrivialMergeTest.java
@@ -47,7 +47,7 @@
               ClassSubject classSubject = inspector.clazz(A.class);
               assertThat(classSubject, isPresent());
 
-              MethodSubject methodSubject = classSubject.uniqueMethodWithOriginalName("foo$bridge");
+              MethodSubject methodSubject = getUniqueDispatchBridgeMethod(classSubject);
               assertThat(methodSubject, isPresent());
               assertFalse(methodSubject.isFinal());
             })
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonPublicOverrideOfPublicMethodAfterClassMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonPublicOverrideOfPublicMethodAfterClassMergingTest.java
index 1edbf47..33a04c3 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonPublicOverrideOfPublicMethodAfterClassMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonPublicOverrideOfPublicMethodAfterClassMergingTest.java
@@ -59,7 +59,8 @@
               ClassSubject iClassSubject = inspector.clazz(I.class);
               assertThat(iClassSubject, isPresent());
               assertThat(
-                  iClassSubject.uniqueMethodWithOriginalName("m"), allOf(isPresent(), isPublic()));
+                  iClassSubject.uniqueMethodThatMatches(m -> !m.isInstanceInitializer()),
+                  allOf(isPresent(), isPublic()));
 
               ClassSubject aClassSubject = inspector.clazz(A.class);
               assertThat(aClassSubject, isPresent());
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/StrictMethodMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/StrictMethodMergingTest.java
index 0b644b8..834f76d 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/StrictMethodMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/StrictMethodMergingTest.java
@@ -47,7 +47,7 @@
               assertThat(aClassSubject, isPresent());
 
               MethodSubject synchronizedMethodSubject =
-                  aClassSubject.uniqueMethodWithOriginalName("m$bridge");
+                  getUniqueDispatchBridgeMethod(aClassSubject);
               assertThat(synchronizedMethodSubject, isPresent());
               assertTrue(synchronizedMethodSubject.getAccessFlags().isStrict());
 
@@ -55,7 +55,7 @@
               assertThat(cClassSubject, isPresent());
 
               MethodSubject unsynchronizedMethodSubject =
-                  cClassSubject.uniqueMethodWithOriginalName("m$bridge");
+                  getUniqueDispatchBridgeMethod(cClassSubject);
               assertThat(unsynchronizedMethodSubject, isPresent());
               assertFalse(unsynchronizedMethodSubject.getAccessFlags().isStrict());
             })
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/SynchronizedMethodMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/SynchronizedMethodMergingTest.java
index dd93d69..9eeea86 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/SynchronizedMethodMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/SynchronizedMethodMergingTest.java
@@ -47,7 +47,7 @@
               assertThat(aClassSubject, isPresent());
 
               MethodSubject synchronizedMethodSubject =
-                  aClassSubject.uniqueMethodWithOriginalName("m$bridge");
+                  getUniqueDispatchBridgeMethod(aClassSubject);
               assertThat(synchronizedMethodSubject, isPresent());
               assertTrue(synchronizedMethodSubject.isSynchronized());
 
@@ -55,7 +55,7 @@
               assertThat(cClassSubject, isPresent());
 
               MethodSubject unsynchronizedMethodSubject =
-                  cClassSubject.uniqueMethodWithOriginalName("m$bridge");
+                  getUniqueDispatchBridgeMethod(cClassSubject);
               assertThat(unsynchronizedMethodSubject, isPresent());
               assertFalse(unsynchronizedMethodSubject.isSynchronized());
             })
diff --git a/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaInStacktraceTest.java b/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaInStacktraceTest.java
index 851d489..29d8d11 100644
--- a/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaInStacktraceTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaInStacktraceTest.java
@@ -32,7 +32,6 @@
           "lambda$main$1(" + fileName + ")",
           "main(" + fileName + ")");
 
-  // TODO(b/187491007): The "call" frame should have a file name.
   static final String EXPECTED_D8 =
       StringUtils.lines(
           "getStacktraceWithFileNames(" + fileName + ")",
@@ -44,17 +43,6 @@
           "call(D8$$SyntheticClass)",
           "main(" + fileName + ")");
 
-  static final String EXPECTED_D8_ANDROID_O =
-      StringUtils.lines(
-          "getStacktraceWithFileNames(" + fileName + ")",
-          "lambda$main$0(" + fileName + ")",
-          "call(NULL)",
-          "main(" + fileName + ")",
-          "getStacktraceWithFileNames(" + fileName + ")",
-          "lambda$main$1(" + fileName + ")",
-          "call(NULL)",
-          "main(" + fileName + ")");
-
   private final TestParameters parameters;
   private final boolean isAndroidOOrLater;
   private final boolean isDalvik;
@@ -88,7 +76,7 @@
         .addInnerClasses(LambdaInStacktraceTest.class)
         .setMinApi(parameters)
         .run(parameters.getRuntime(), TestRunner.class, Boolean.toString(isDalvik))
-        .assertSuccessWithOutput(isAndroidOOrLater ? EXPECTED_D8_ANDROID_O : EXPECTED_D8);
+        .assertSuccessWithOutput(EXPECTED_D8);
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesInDexRewriteInvokeSpecialTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesInDexRewriteInvokeSpecialTest.java
new file mode 100644
index 0000000..b50b2f9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesInDexRewriteInvokeSpecialTest.java
@@ -0,0 +1,222 @@
+// Copyright (c) 2023, 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.nestaccesscontrol;
+
+import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.google.common.base.Predicates.alwaysTrue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.function.Predicate;
+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 NestAttributesInDexRewriteInvokeSpecialTest extends NestAttributesInDexTestBase {
+
+  private static final Path JDK17_JAR =
+      Paths.get(ToolHelper.TESTS_BUILD_DIR, "examplesJava11")
+          .resolve("nesthostexample" + JAR_EXTENSION);
+  private static final String MAIN = "nesthostexample.NestHierachy";
+
+  @Parameter() public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+  }
+
+  private static final String EXPECTED_OUTPUT =
+      StringUtils.lines(
+          "m1", "m2", "m3", "s1", "s2", "m1", "m2", "m3", "s1", "s2", "s1", "s2", "s1", "s2");
+
+  @Test
+  public void testRuntime() throws Exception {
+    assumeTrue(parameters.isCfRuntime() && isRuntimeWithNestSupport(parameters.asCfRuntime()));
+    testForJvm(parameters)
+        .addProgramFiles(JDK17_JAR)
+        .run(parameters.getRuntime(), MAIN)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    parameters.assumeDexRuntime();
+    testForD8()
+        .addProgramFiles(JDK17_JAR)
+        .setMinApi(parameters)
+        .compile()
+        .run(parameters.getRuntime(), MAIN)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  private void assertSingleInvokeSuper(MethodSubject method, Predicate<String> methodNameFilter) {
+    long invokeCount =
+        method
+            .streamInstructions()
+            .filter(InstructionSubject::isInvoke)
+            .filter(
+                instruction -> methodNameFilter.test(instruction.getMethod().getName().toString()))
+            .count();
+    long invokeSuperCount =
+        method
+            .streamInstructions()
+            .filter(instruction -> instruction.asDexInstruction().isInvokeSuper())
+            .filter(
+                instruction -> methodNameFilter.test(instruction.getMethod().getName().toString()))
+            .count();
+    assertEquals(1, invokeCount);
+    assertEquals(1, invokeSuperCount);
+  }
+
+  private void assertSingleInvokeDirect(MethodSubject method, Predicate<String> methodNameFilter) {
+    long invokeCount =
+        method
+            .streamInstructions()
+            .filter(InstructionSubject::isInvoke)
+            .filter(
+                instruction -> methodNameFilter.test(instruction.getMethod().getName().toString()))
+            .count();
+    long invokeSuperCount =
+        method
+            .streamInstructions()
+            .filter(instruction -> instruction.asDexInstruction().isInvokeDirect())
+            .filter(
+                instruction -> methodNameFilter.test(instruction.getMethod().getName().toString()))
+            .count();
+    assertEquals(1, invokeCount);
+    assertEquals(1, invokeSuperCount);
+  }
+
+  private void assertSingleInvokeVirtual(MethodSubject method, Predicate<String> methodNameFilter) {
+    long invokeCount =
+        method
+            .streamInstructions()
+            .filter(InstructionSubject::isInvoke)
+            .filter(
+                instruction -> methodNameFilter.test(instruction.getMethod().getName().toString()))
+            .count();
+    long invokeVirtualCount =
+        method
+            .streamInstructions()
+            .filter(InstructionSubject::isInvokeVirtual)
+            .filter(
+                instruction -> methodNameFilter.test(instruction.getMethod().getName().toString()))
+            .count();
+    assertEquals(1, invokeCount);
+    assertEquals(1, invokeVirtualCount);
+  }
+
+  private void assertSingleInvokeStatic(MethodSubject method, Predicate<String> methodNameFilter) {
+    long invokeCount =
+        method
+            .streamInstructions()
+            .filter(InstructionSubject::isInvoke)
+            .filter(
+                instruction -> methodNameFilter.test(instruction.getMethod().getName().toString()))
+            .count();
+    long invokeVirtualCount =
+        method
+            .streamInstructions()
+            .filter(InstructionSubject::isInvokeStatic)
+            .filter(
+                instruction -> methodNameFilter.test(instruction.getMethod().getName().toString()))
+            .count();
+    assertEquals(1, invokeCount);
+    assertEquals(1, invokeVirtualCount);
+  }
+
+  private void assertSingleInvokeSuper(MethodSubject method) {
+    assertSingleInvokeSuper(method, alwaysTrue());
+  }
+
+  private void assertSingleInvokeDirect(MethodSubject method) {
+    assertSingleInvokeDirect(method, alwaysTrue());
+  }
+
+  private void assertSingleInvokeStatic(MethodSubject method) {
+    assertSingleInvokeStatic(method, alwaysTrue());
+  }
+
+  private void assertSingleInvokeDirect(MethodSubject method, String invokedMethodName) {
+    assertSingleInvokeDirect(method, name -> name.equals(invokedMethodName));
+  }
+
+  private void assertSingleInvokeVirtual(MethodSubject method, String invokedMethodName) {
+    assertSingleInvokeVirtual(method, name -> name.equals(invokedMethodName));
+  }
+
+  private void assertSingleInvokeStatic(MethodSubject method, String invokedMethodName) {
+    assertSingleInvokeStatic(method, name -> name.equals(invokedMethodName));
+  }
+
+  @Test
+  public void testD8DexWithNestSupport() throws Exception {
+    parameters.assumeDexRuntime();
+    assumeTrue(parameters.getApiLevel().getLevel() >= 34);
+    // TODO(b/247047415): Update test when a DEX VM natively supporting nests is added.
+    assertFalse(parameters.getApiLevel().getLevel() > 34);
+    testForD8()
+        .addProgramFiles(JDK17_JAR)
+        .setMinApi(AndroidApiLevel.U)
+        .addOptionsModification(options -> options.emitNestAnnotationsInDex = true)
+        .compile()
+        .inspect(
+            inspector -> {
+              ClassSubject innerSub = inspector.clazz("nesthostexample.NestHierachy$InnerSub");
+              assertThat(innerSub, isPresent());
+              // invokespecial on public super.
+              assertSingleInvokeSuper(innerSub.uniqueMethodWithOriginalName("m1"));
+              // invokespecial on private super.
+              assertSingleInvokeDirect(innerSub.uniqueMethodWithOriginalName("m2"));
+              // invokespecial on private super.
+              assertSingleInvokeDirect(innerSub.uniqueMethodWithOriginalName("m3"));
+
+              assertSingleInvokeStatic(innerSub.uniqueMethodWithOriginalName("s1"));
+              assertSingleInvokeStatic(innerSub.uniqueMethodWithOriginalName("s2"));
+
+              // invoke-virtual on public nest members, invoke-direct on private nest members
+              ClassSubject outer = inspector.clazz("nesthostexample.NestHierachy");
+              assertThat(outer, isPresent());
+              MethodSubject callOnInnerSuper =
+                  outer.uniqueMethodWithOriginalName("callOnInnerSuper");
+              // invokevirtual on public in nest.
+              assertSingleInvokeVirtual(callOnInnerSuper, "m1");
+              // invokevirtual on private in nest.
+              assertSingleInvokeDirect(callOnInnerSuper, "m2");
+              // invokevirtual on private in nest.
+              assertSingleInvokeDirect(callOnInnerSuper, "m3");
+
+              assertSingleInvokeStatic(callOnInnerSuper, "s1");
+              assertSingleInvokeStatic(callOnInnerSuper, "s2");
+
+              MethodSubject callOnInnerSub = outer.uniqueMethodWithOriginalName("callOnInnerSub");
+              // invokevirtual on public in nest.
+              assertSingleInvokeVirtual(callOnInnerSub, "m1");
+              // invokevirtual on public in nest.
+              assertSingleInvokeVirtual(callOnInnerSub, "m2");
+              // invokevirtual on private in nest.
+              assertSingleInvokeDirect(callOnInnerSub, "m3");
+
+              assertSingleInvokeStatic(callOnInnerSub, "s1");
+              assertSingleInvokeStatic(callOnInnerSub, "s2");
+            });
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/lightir/LirBasicCallbackTest.java b/src/test/java/com/android/tools/r8/lightir/LirBasicCallbackTest.java
index 909c083..a6cc50b 100644
--- a/src/test/java/com/android/tools/r8/lightir/LirBasicCallbackTest.java
+++ b/src/test/java/com/android/tools/r8/lightir/LirBasicCallbackTest.java
@@ -80,7 +80,7 @@
             .dexItemFactory()
             .createMethod(Reference.methodFromDescriptor("LFoo;", "bar", "()V"));
     LirCode<?> code =
-        LirCode.builder(method, new ThrowingStrategy(), options)
+        LirCode.builder(method, false, new ThrowingStrategy(), options)
             .setMetadata(IRMetadata.unknown())
             .addConstNull()
             .addConstInt(42)
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/InvokeSpecialToVirtualMethodProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/InvokeSpecialToVirtualMethodProfileRewritingTest.java
index 1f7da5f..a1c6307 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/InvokeSpecialToVirtualMethodProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/InvokeSpecialToVirtualMethodProfileRewritingTest.java
@@ -4,10 +4,11 @@
 
 package com.android.tools.r8.profile.art.completeness;
 
+import static com.android.tools.r8.synthesis.SyntheticItemsTestUtils.syntheticInvokeSpecialMethod;
+import static com.android.tools.r8.utils.codeinspector.CodeMatchers.isInvokeWithTarget;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
 
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
@@ -15,13 +16,15 @@
 import com.android.tools.r8.profile.art.model.ExternalArtProfile;
 import com.android.tools.r8.profile.art.utils.ArtProfileInspector;
 import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
 import com.android.tools.r8.utils.BooleanBox;
 import com.android.tools.r8.utils.InternalOptions.InlinerOptions;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.android.tools.r8.utils.codeinspector.CodeMatchers;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
 import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -104,20 +107,28 @@
 
   private void inspect(ArtProfileInspector profileInspector, CodeInspector inspector)
       throws Exception {
+    Method mMethod = Main.class.getDeclaredMethod("m");
     ClassSubject mainClassSubject = inspector.clazz(Main.class);
     assertThat(mainClassSubject, isPresent());
 
-    MethodSubject mMethodSubject = mainClassSubject.uniqueMethodWithOriginalName("m");
-    assertThat(mMethodSubject, isPresent());
-
-    MethodSubject mMovedMethodSubject =
-        mainClassSubject.method(
-            SyntheticItemsTestUtils.syntheticInvokeSpecialMethod(
-                Main.class.getDeclaredMethod("m")));
-    assertThat(mMovedMethodSubject, isPresent());
-      assertNotEquals(
-          mMethodSubject.getProgramMethod().getName(),
-          mMovedMethodSubject.getProgramMethod().getName());
+    // Find the two methods named 'm'. Avoid encoding the order and inspect which is the caller.
+    List<FoundMethodSubject> methods =
+        mainClassSubject.allMethods(
+            m -> {
+              String originalName = m.getOriginalName();
+              return originalName.equals("m")
+                  || originalName.equals(syntheticInvokeSpecialMethod(mMethod).getMethodName());
+            });
+    assertEquals(2, methods.size());
+    FoundMethodSubject mMethodSubject = methods.get(0);
+    FoundMethodSubject mMovedMethodSubject = methods.get(1);
+    if (!mMethodSubject
+        .streamInstructions()
+        .anyMatch(isInvokeWithTarget(mMovedMethodSubject.getFinalReference()))) {
+      mMethodSubject = methods.get(1);
+      mMovedMethodSubject = methods.get(0);
+    }
+    assertThat(mMethodSubject, CodeMatchers.invokesMethod(mMovedMethodSubject));
 
     // Verify residual profile contains private synthetic method when present.
     profileInspector
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/MovedPrivateInterfaceMethodProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/MovedPrivateInterfaceMethodProfileRewritingTest.java
index 9d36b97..82edb64 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/MovedPrivateInterfaceMethodProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/MovedPrivateInterfaceMethodProfileRewritingTest.java
@@ -132,11 +132,17 @@
           inspector.clazz(SyntheticItemsTestUtils.syntheticCompanionClass(I.class));
       assertThat(companionClassSubject, isPresent());
 
+      String mMethodOnI = typeName(I.class) + ".m";
+      String mMethodNameOnICC =
+          SyntheticItemsTestUtils.syntheticPrivateInterfaceMethodAsCompanionMethod(
+                  I.class.getDeclaredMethod("m"))
+              .getMethodName();
       MethodSubject privateInterfaceMethodSubject =
-          companionClassSubject.uniqueMethodWithOriginalName(
-              SyntheticItemsTestUtils.syntheticPrivateInterfaceMethodAsCompanionMethod(
-                      I.class.getDeclaredMethod("m"))
-                  .getMethodName());
+          companionClassSubject.uniqueMethodThatMatches(
+              m -> {
+                String originalName = m.getOriginalName();
+                return originalName.equals(mMethodOnI) || originalName.equals(mMethodNameOnICC);
+              });
       assertThat(privateInterfaceMethodSubject, isPresent());
 
       profileInspector
diff --git a/src/test/java/com/android/tools/r8/retrace/ProxyRetraceAmbiguousTest.java b/src/test/java/com/android/tools/r8/retrace/ProxyRetraceAmbiguousTest.java
new file mode 100644
index 0000000..b406d4e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/ProxyRetraceAmbiguousTest.java
@@ -0,0 +1,181 @@
+// Copyright (c) 2023, 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.retrace;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.retrace.internal.StackTraceElementStringProxy;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ProxyRetraceAmbiguousTest extends TestBase {
+
+  private static final String MAPPING =
+      StringUtils.unixLines(
+          "# {'id':'com.android.tools.r8.mapping','version':'2.2'}",
+          "package.Class$$ExternalSyntheticOutline0 -> package.internal.X:",
+          "# {'id':'sourceFile','fileName':'R8$$SyntheticClass'}",
+          "# {'id':'com.android.tools.r8.synthesized'}",
+          "    1:2:long package.$HASH$0.m(long,long,long):0:1 -> a",
+          "    # {'id':'com.android.tools.r8.synthesized'}",
+          "package.Class -> package.internal.Y:",
+          "# {'id':'sourceFile','fileName':'FieldDefinition.java'}",
+          "    1:10:void foo():0:0 -> a",
+          "    11:20:void bar():0:0 -> a");
+
+  private static final List<String> STACKTRACE =
+      ImmutableList.of(
+          "Error in something",
+          "  at package.internal.X.a(SourceFile:1)",
+          "  at package.internal.Y.a(SourceFile)");
+
+  // TODO(b/305292991): The result should be ambiguous with 'foo' and 'bar' frames.
+  private static final List<String> UNEXPECTED_UNAMBIGUOUS =
+      ImmutableList.of("Error in something", "  at package.Class.foo(FieldDefinition.java)");
+
+  // TODO(b/305292991): The result should be ambiguous with 'foo' and 'bar' frames.
+  private static final List<String> UNEXPECTED_AMBIGUOUS =
+      ImmutableList.of(
+          "Error in something",
+          "alternative 0:\n  at package.Class.foo(FieldDefinition.java)",
+          "alternative 1:\n  at package.Class.foo(FieldDefinition.java)");
+
+  // TODO(b/305292991): The result should be ambiguous with 'foo' and 'bar' frames.
+  private static final List<String> UNEXPECTED_AMBIGUOUS_JOINED =
+      ImmutableList.of(
+          "Error in something",
+          "  at package.Class.foo(FieldDefinition.java)",
+          "  at package.Class.foo(FieldDefinition.java)");
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  public ProxyRetraceAmbiguousTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
+
+  private List<String> joinResult(List<RetraceStackFrameAmbiguousResult<String>> result) {
+    List<String> lines = new ArrayList<>();
+    for (RetraceStackFrameAmbiguousResult<String> potentiallyAmbiguousFrame : result) {
+      if (potentiallyAmbiguousFrame.isAmbiguous()) {
+        for (int i = 0; i < potentiallyAmbiguousFrame.size(); i++) {
+          StringBuilder builder = new StringBuilder("alternative ").append(i).append(":\n");
+          potentiallyAmbiguousFrame.get(i).forEach(s -> builder.append(s).append('\n'));
+          // Remove the last newline to stay consistent with "list of lines".
+          String line = builder.toString();
+          lines.add(line.substring(0, line.length() - 1));
+        }
+      } else if (!potentiallyAmbiguousFrame.isEmpty()) {
+        potentiallyAmbiguousFrame.get(0).forEach(lines::add);
+      }
+    }
+    return lines;
+  }
+
+  @Test
+  public void testRetraceFullStack() {
+    Retrace<String, StackTraceElementStringProxy> retrace = getRetraceProxy();
+    RetraceStackTraceResult<String> result =
+        retrace.retraceStackTrace(STACKTRACE, RetraceStackTraceContext.empty());
+    assertEquals(UNEXPECTED_UNAMBIGUOUS, joinResult(result.getResult()));
+  }
+
+  @Test
+  public void testRetraceSingletonStack() {
+    Retrace<String, StackTraceElementStringProxy> retrace = getRetraceProxy();
+    RetraceStackTraceContext context = RetraceStackTraceContext.empty();
+    List<RetraceStackFrameAmbiguousResult<String>> retraced = new ArrayList<>();
+    for (String line : STACKTRACE) {
+      RetraceStackTraceResult<String> result =
+          retrace.retraceStackTrace(Collections.singletonList(line), context);
+      context = result.getContext();
+      result.forEach(retraced::add);
+    }
+    assertEquals(UNEXPECTED_UNAMBIGUOUS, joinResult(retraced));
+  }
+
+  @Test
+  public void testRetraceByLine() {
+    Retrace<String, StackTraceElementStringProxy> retrace = getRetraceProxy();
+    RetraceStackTraceContext context = RetraceStackTraceContext.empty();
+    List<String> retraced = new ArrayList<>();
+    for (String line : STACKTRACE) {
+      RetraceStackFrameResultWithContext<String> result = retrace.retraceLine(line, context);
+      context = result.getContext();
+      result.forEach(retraced::add);
+    }
+    assertEquals(UNEXPECTED_AMBIGUOUS_JOINED, retraced);
+  }
+
+  @Test
+  public void testRetraceByFrame() {
+    Retrace<String, StackTraceElementStringProxy> retrace = getRetraceProxy();
+    RetraceStackTraceContext context = RetraceStackTraceContext.empty();
+    List<RetraceStackFrameAmbiguousResult<String>> retraced = new ArrayList<>();
+    for (String line : STACKTRACE) {
+      RetraceStackFrameAmbiguousResultWithContext<String> result =
+          retrace.retraceFrame(line, context);
+      context = result.getContext();
+      retraced.add(result);
+    }
+    assertEquals(UNEXPECTED_AMBIGUOUS, joinResult(retraced));
+  }
+
+  @Test
+  public void testRetraceFullStackParsed() {
+    Retrace<String, StackTraceElementStringProxy> retrace = getRetraceProxy();
+    RetraceStackTraceResult<String> result =
+        retrace.retraceStackTraceParsed(getParsedStacktrace(), RetraceStackTraceContext.empty());
+    assertEquals(UNEXPECTED_UNAMBIGUOUS, joinResult(result.getResult()));
+  }
+
+  @Test
+  public void testRetraceSingletonStackParsed() {
+    Retrace<String, StackTraceElementStringProxy> retrace = getRetraceProxy();
+    RetraceStackTraceContext context = RetraceStackTraceContext.empty();
+    List<RetraceStackFrameAmbiguousResult<String>> retraced = new ArrayList<>();
+    for (StackTraceElementStringProxy line : getParsedStacktrace()) {
+      RetraceStackTraceResult<String> result =
+          retrace.retraceStackTraceParsed(Collections.singletonList(line), context);
+      context = result.getContext();
+      result.forEach(retraced::add);
+    }
+    assertEquals(UNEXPECTED_UNAMBIGUOUS, joinResult(retraced));
+  }
+
+  private static List<StackTraceElementStringProxy> getParsedStacktrace() {
+    StackTraceLineParser<String, StackTraceElementStringProxy> parser = getParser();
+    return ListUtils.map(STACKTRACE, parser::parse);
+  }
+
+  private static StackTraceLineParser<String, StackTraceElementStringProxy> getParser() {
+    return StackTraceLineParser.createRegularExpressionParser(
+        RetraceOptions.defaultRegularExpression());
+  }
+
+  private static Retrace<String, StackTraceElementStringProxy> getRetraceProxy() {
+    ProguardMappingSupplier mappingSupplier =
+        ProguardMappingSupplier.builder()
+            .setProguardMapProducer(ProguardMapProducer.fromString(MAPPING))
+            .build();
+    Retrace<String, StackTraceElementStringProxy> retracer =
+        new Retrace<>(getParser(), mappingSupplier, new DiagnosticsHandler() {}, false);
+    return retracer;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/ProxyRetraceOutlineTest.java b/src/test/java/com/android/tools/r8/retrace/ProxyRetraceOutlineTest.java
new file mode 100644
index 0000000..85d0ae0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/ProxyRetraceOutlineTest.java
@@ -0,0 +1,175 @@
+// Copyright (c) 2023, 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.retrace;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.retrace.internal.StackTraceElementStringProxy;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ProxyRetraceOutlineTest extends TestBase {
+
+  private static final String MAPPING =
+      StringUtils.unixLines(
+          "# {'id':'com.android.tools.r8.mapping','version':'2.2'}",
+          "package.Class$$ExternalSyntheticOutline0 -> package.internal.X:",
+          "# {'id':'sourceFile','fileName':'R8$$SyntheticClass'}",
+          "# {'id':'com.android.tools.r8.synthesized'}",
+          "    1:2:long package.$HASH$0.m(long,long,long):0:1 -> a",
+          "    # {'id':'com.android.tools.r8.outline'}",
+          "package.Class -> package.internal.Y:",
+          "# {'id':'sourceFile','fileName':'FieldDefinition.java'}",
+          "    1:6:void foo():21:26 -> a",
+          "    7:7:void foo():0:0 -> a",
+          "    # {'id':'com.android.tools.r8.outlineCallsite',"
+              + "'positions':{'1':10,'2':11},"
+              + "'outline':'Lpackage/internal/X;a(JJJ)J'}",
+          "    8:9:void foo():38:39 -> a",
+          "    10:10:void inlineeInOutline():1337:1337 -> a",
+          "    10:10:void foo():42 -> a",
+          "    11:11:void foo():44:44 -> a");
+
+  private static final List<String> STACKTRACE =
+      ImmutableList.of(
+          "Error in something",
+          "  at package.internal.X.a(SourceFile:1)",
+          "  at package.internal.Y.a(SourceFile:7)");
+
+  private static final List<String> EXPECTED =
+      ImmutableList.of(
+          "Error in something",
+          "  at package.Class.inlineeInOutline(FieldDefinition.java:1337)",
+          "  at package.Class.foo(FieldDefinition.java:42)");
+
+  // TODO(b/305292991): These should all report EXPECTED.
+  private static final List<String> UNEXPECTED =
+      ImmutableList.of("Error in something", "  at package.Class.foo(FieldDefinition.java)");
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  public ProxyRetraceOutlineTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
+
+  private List<String> joinResult(List<RetraceStackFrameAmbiguousResult<String>> result) {
+    List<String> lines = new ArrayList<>();
+    for (RetraceStackFrameAmbiguousResult<String> potentiallyAmbiguousFrame : result) {
+      assertFalse(potentiallyAmbiguousFrame.isAmbiguous());
+      potentiallyAmbiguousFrame.forEach(
+          frameResult -> {
+            frameResult.forEach(lines::add);
+          });
+    }
+    return lines;
+  }
+
+  @Test
+  public void testRetraceFullStack() {
+    Retrace<String, StackTraceElementStringProxy> retrace = getRetraceProxy();
+    RetraceStackTraceResult<String> result =
+        retrace.retraceStackTrace(STACKTRACE, RetraceStackTraceContext.empty());
+    assertEquals(EXPECTED, joinResult(result.getResult()));
+  }
+
+  @Test
+  public void testRetraceSingletonStack() {
+    Retrace<String, StackTraceElementStringProxy> retrace = getRetraceProxy();
+    RetraceStackTraceContext context = RetraceStackTraceContext.empty();
+    List<RetraceStackFrameAmbiguousResult<String>> retraced = new ArrayList<>();
+    for (String line : STACKTRACE) {
+      RetraceStackTraceResult<String> result =
+          retrace.retraceStackTrace(Collections.singletonList(line), context);
+      context = result.getContext();
+      result.forEach(retraced::add);
+    }
+    assertEquals(EXPECTED, joinResult(retraced));
+  }
+
+  @Test
+  public void testRetraceByLine() {
+    Retrace<String, StackTraceElementStringProxy> retrace = getRetraceProxy();
+    RetraceStackTraceContext context = RetraceStackTraceContext.empty();
+    List<String> retraced = new ArrayList<>();
+    for (String line : STACKTRACE) {
+      RetraceStackFrameResultWithContext<String> result = retrace.retraceLine(line, context);
+      context = result.getContext();
+      result.forEach(retraced::add);
+    }
+    assertEquals(UNEXPECTED, retraced);
+  }
+
+  @Test
+  public void testRetraceByFrame() {
+    Retrace<String, StackTraceElementStringProxy> retrace = getRetraceProxy();
+    RetraceStackTraceContext context = RetraceStackTraceContext.empty();
+    List<RetraceStackFrameAmbiguousResult<String>> retraced = new ArrayList<>();
+    for (String line : STACKTRACE) {
+      RetraceStackFrameAmbiguousResultWithContext<String> result =
+          retrace.retraceFrame(line, context);
+      context = result.getContext();
+      retraced.add(result);
+    }
+    assertEquals(UNEXPECTED, joinResult(retraced));
+  }
+
+  @Test
+  public void testRetraceFullStackParsed() {
+    Retrace<String, StackTraceElementStringProxy> retrace = getRetraceProxy();
+    RetraceStackTraceResult<String> result =
+        retrace.retraceStackTraceParsed(getParsedStacktrace(), RetraceStackTraceContext.empty());
+    assertEquals(EXPECTED, joinResult(result.getResult()));
+  }
+
+  @Test
+  public void testRetraceSingletonStackParsed() {
+    Retrace<String, StackTraceElementStringProxy> retrace = getRetraceProxy();
+    RetraceStackTraceContext context = RetraceStackTraceContext.empty();
+    List<RetraceStackFrameAmbiguousResult<String>> retraced = new ArrayList<>();
+    for (StackTraceElementStringProxy line : getParsedStacktrace()) {
+      RetraceStackTraceResult<String> result =
+          retrace.retraceStackTraceParsed(Collections.singletonList(line), context);
+      context = result.getContext();
+      result.forEach(retraced::add);
+    }
+    assertEquals(EXPECTED, joinResult(retraced));
+  }
+
+  private static List<StackTraceElementStringProxy> getParsedStacktrace() {
+    StackTraceLineParser<String, StackTraceElementStringProxy> parser = getParser();
+    return ListUtils.map(STACKTRACE, parser::parse);
+  }
+
+  private static StackTraceLineParser<String, StackTraceElementStringProxy> getParser() {
+    return StackTraceLineParser.createRegularExpressionParser(
+        RetraceOptions.defaultRegularExpression());
+  }
+
+  private static Retrace<String, StackTraceElementStringProxy> getRetraceProxy() {
+    ProguardMappingSupplier mappingSupplier =
+        ProguardMappingSupplier.builder()
+            .setProguardMapProducer(ProguardMapProducer.fromString(MAPPING))
+            .build();
+    Retrace<String, StackTraceElementStringProxy> retracer =
+        new Retrace<>(getParser(), mappingSupplier, new DiagnosticsHandler() {}, false);
+    return retracer;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/StringRetraceAmbiguousTest.java b/src/test/java/com/android/tools/r8/retrace/StringRetraceAmbiguousTest.java
new file mode 100644
index 0000000..a092b7d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/StringRetraceAmbiguousTest.java
@@ -0,0 +1,142 @@
+// Copyright (c) 2023, 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.retrace;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class StringRetraceAmbiguousTest extends TestBase {
+
+  private static final String MAPPING =
+      StringUtils.unixLines(
+          "# {'id':'com.android.tools.r8.mapping','version':'2.2'}",
+          "package.Class$$ExternalSyntheticOutline0 -> package.internal.X:",
+          "# {'id':'sourceFile','fileName':'R8$$SyntheticClass'}",
+          "# {'id':'com.android.tools.r8.synthesized'}",
+          "    1:2:long package.$HASH$0.m(long,long,long):0:1 -> a",
+          "    # {'id':'com.android.tools.r8.synthesized'}",
+          "package.Class -> package.internal.Y:",
+          "# {'id':'sourceFile','fileName':'FieldDefinition.java'}",
+          "    1:10:void foo():0:0 -> a",
+          "    11:20:void bar():0:0 -> a");
+
+  private static final List<String> STACKTRACE =
+      ImmutableList.of(
+          "Error in something",
+          "  at package.internal.X.a(SourceFile:1)",
+          "  at package.internal.Y.a(SourceFile)");
+
+  // TODO(b/305292991): The result should be ambiguous with 'foo' and 'bar' frames.
+  private static final List<String> UNEXPECTED_UNAMBIGUOUS =
+      ImmutableList.of("Error in something", "  at package.Class.foo(FieldDefinition.java)");
+
+  // TODO(b/305292991): The result should be ambiguous with 'foo' and 'bar' frames.
+  private static final List<String> UNEXPECTED_AMBIGUOUS =
+      ImmutableList.of(
+          "Error in something",
+          "alternative 0:\n  at package.Class.foo(FieldDefinition.java)",
+          "alternative 1:\n  at package.Class.foo(FieldDefinition.java)");
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  public StringRetraceAmbiguousTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
+
+  @Test
+  public void testStringRetraceAllFrames() {
+    StringRetrace stringRetrace = getStringRetrace();
+    RetraceStackFrameResultWithContext<String> result =
+        stringRetrace.retrace(STACKTRACE, RetraceStackTraceContext.empty());
+    assertEquals(UNEXPECTED_UNAMBIGUOUS, result.getResult());
+  }
+
+  @Test
+  public void testStringRetraceSingletonStack() {
+    StringRetrace stringRetrace = getStringRetrace();
+    RetraceStackTraceContext context = RetraceStackTraceContext.empty();
+    List<String> retraced = new ArrayList<>();
+    for (String line : STACKTRACE) {
+      RetraceStackFrameResultWithContext<String> result =
+          stringRetrace.retrace(Collections.singletonList(line), context);
+      context = result.getContext();
+      result.forEach(retraced::add);
+    }
+    assertEquals(UNEXPECTED_UNAMBIGUOUS, retraced);
+  }
+
+  @Test
+  public void testStringRetraceByFrame() {
+    StringRetrace stringRetrace = getStringRetrace();
+    RetraceStackTraceContext context = RetraceStackTraceContext.empty();
+    List<String> retraced = new ArrayList<>();
+    for (String line : STACKTRACE) {
+      RetraceStackFrameResultWithContext<String> result = stringRetrace.retrace(line, context);
+      context = result.getContext();
+      result.forEach(retraced::add);
+    }
+    assertEquals(UNEXPECTED_UNAMBIGUOUS, retraced);
+  }
+
+  @Test
+  public void testStringRetraceByAmbiguousFrame() {
+    StringRetrace stringRetrace = getStringRetrace();
+    RetraceStackTraceContext context = RetraceStackTraceContext.empty();
+    List<String> retraced = new ArrayList<>();
+    for (String line : STACKTRACE) {
+      RetraceStackFrameAmbiguousResultWithContext<String> result =
+          stringRetrace.retraceFrame(line, context);
+      context = result.getContext();
+      if (result.isAmbiguous()) {
+        for (int i = 0; i < result.size(); i++) {
+          StringBuilder builder = new StringBuilder("alternative ").append(i).append(":\n");
+          result.get(i).forEach(s -> builder.append(s).append('\n'));
+          // Remove the last newline to stay consistent with "list of lines".
+          String str = builder.toString();
+          retraced.add(str.substring(0, str.length() - 1));
+        }
+      } else if (!result.isEmpty()) {
+        result.get(0).forEach(retraced::add);
+      }
+    }
+    assertEquals(UNEXPECTED_AMBIGUOUS, retraced);
+  }
+
+  @Test
+  public void testStringRetraceSupplier() {
+    StringRetrace stringRetrace = getStringRetrace();
+    Iterator<String> iterator = STACKTRACE.iterator();
+    List<String> retraced = new ArrayList<>();
+    stringRetrace.retraceSupplier(() -> iterator.hasNext() ? iterator.next() : null, retraced::add);
+    assertEquals(UNEXPECTED_UNAMBIGUOUS, retraced);
+  }
+
+  private static StringRetrace getStringRetrace() {
+    ProguardMappingSupplier mappingSupplier =
+        ProguardMappingSupplier.builder()
+            .setProguardMapProducer(ProguardMapProducer.fromString(MAPPING))
+            .build();
+
+    StringRetrace stringRetrace =
+        StringRetrace.create(RetraceOptions.builder().setMappingSupplier(mappingSupplier).build());
+    return stringRetrace;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/StringRetraceOutlineTest.java b/src/test/java/com/android/tools/r8/retrace/StringRetraceOutlineTest.java
new file mode 100644
index 0000000..475a131
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/StringRetraceOutlineTest.java
@@ -0,0 +1,143 @@
+// Copyright (c) 2023, 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.retrace;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class StringRetraceOutlineTest extends TestBase {
+
+  private static final String MAPPING =
+      StringUtils.unixLines(
+          "# {'id':'com.android.tools.r8.mapping','version':'2.2'}",
+          "package.Class$$ExternalSyntheticOutline0 -> package.internal.X:",
+          "# {'id':'sourceFile','fileName':'R8$$SyntheticClass'}",
+          "# {'id':'com.android.tools.r8.synthesized'}",
+          "    1:2:long package.$HASH$0.m(long,long,long):0:1 -> a",
+          "    # {'id':'com.android.tools.r8.outline'}",
+          "package.Class -> package.internal.Y:",
+          "# {'id':'sourceFile','fileName':'FieldDefinition.java'}",
+          "    1:6:void foo():21:26 -> a",
+          "    7:7:void foo():0:0 -> a",
+          "    # {'id':'com.android.tools.r8.outlineCallsite',"
+              + "'positions':{'1':10,'2':11},"
+              + "'outline':'Lpackage/internal/X;a(JJJ)J'}",
+          "    8:9:void foo():38:39 -> a",
+          "    10:10:void inlineeInOutline():1337:1337 -> a",
+          "    10:10:void foo():42 -> a",
+          "    11:11:void foo():44:44 -> a");
+
+  private static final List<String> STACKTRACE =
+      ImmutableList.of(
+          "Error in something",
+          "  at package.internal.X.a(SourceFile:1)",
+          "  at package.internal.Y.a(SourceFile:7)");
+
+  private static final List<String> EXPECTED =
+      ImmutableList.of(
+          "Error in something",
+          "  at package.Class.inlineeInOutline(FieldDefinition.java:1337)",
+          "  at package.Class.foo(FieldDefinition.java:42)");
+
+  // TODO(b/305292991): These should all report EXPECTED.
+  private static final List<String> UNEXPECTED =
+      ImmutableList.of("Error in something", "  at package.Class.foo(FieldDefinition.java)");
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  public StringRetraceOutlineTest(TestParameters parameters) {
+    parameters.assertNoneRuntime();
+  }
+
+  @Test
+  public void testStringRetraceAllFrames() {
+    StringRetrace stringRetrace = getStringRetrace();
+    RetraceStackFrameResultWithContext<String> result =
+        stringRetrace.retrace(STACKTRACE, RetraceStackTraceContext.empty());
+    assertEquals(EXPECTED, result.getResult());
+  }
+
+  @Test
+  public void testStringRetraceSingletonStack() {
+    StringRetrace stringRetrace = getStringRetrace();
+    RetraceStackTraceContext context = RetraceStackTraceContext.empty();
+    List<String> retraced = new ArrayList<>();
+    for (String line : STACKTRACE) {
+      RetraceStackFrameResultWithContext<String> result =
+          stringRetrace.retrace(Collections.singletonList(line), context);
+      context = result.getContext();
+      result.forEach(retraced::add);
+    }
+    assertEquals(EXPECTED, retraced);
+  }
+
+  @Test
+  public void testStringRetraceByFrame() {
+    StringRetrace stringRetrace = getStringRetrace();
+    RetraceStackTraceContext context = RetraceStackTraceContext.empty();
+    List<String> retraced = new ArrayList<>();
+    for (String line : STACKTRACE) {
+      RetraceStackFrameResultWithContext<String> result = stringRetrace.retrace(line, context);
+      context = result.getContext();
+      result.forEach(retraced::add);
+    }
+    assertEquals(UNEXPECTED, retraced);
+  }
+
+  @Test
+  public void testStringRetraceByAmbiguousFrame() {
+    StringRetrace stringRetrace = getStringRetrace();
+    RetraceStackTraceContext context = RetraceStackTraceContext.empty();
+    List<String> retraced = new ArrayList<>();
+    for (String line : STACKTRACE) {
+      RetraceStackFrameAmbiguousResultWithContext<String> result =
+          stringRetrace.retraceFrame(line, context);
+      context = result.getContext();
+      assertFalse(result.isAmbiguous());
+      if (!result.isEmpty()) {
+        assertEquals(1, result.size());
+        result.get(0).forEach(retraced::add);
+      }
+    }
+    assertEquals(UNEXPECTED, retraced);
+  }
+
+  @Test
+  public void testStringRetraceSupplier() {
+    StringRetrace stringRetrace = getStringRetrace();
+    Iterator<String> iterator = STACKTRACE.iterator();
+    List<String> retraced = new ArrayList<>();
+    stringRetrace.retraceSupplier(() -> iterator.hasNext() ? iterator.next() : null, retraced::add);
+    assertEquals(UNEXPECTED, retraced);
+  }
+
+  private static StringRetrace getStringRetrace() {
+    ProguardMappingSupplier mappingSupplier =
+        ProguardMappingSupplier.builder()
+            .setProguardMapProducer(ProguardMapProducer.fromString(MAPPING))
+            .build();
+
+    StringRetrace stringRetrace =
+        StringRetrace.create(RetraceOptions.builder().setMappingSupplier(mappingSupplier).build());
+    return stringRetrace;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java b/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java
index 8ce1d80..1aaffaf 100644
--- a/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java
@@ -8,6 +8,7 @@
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
 
 import com.android.tools.r8.ByteDataView;
 import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
@@ -152,10 +153,12 @@
       CodeInspector inspector = compiled.inspector();
       ClassSubject classSubject = inspector.clazz(B.class.getName());
       assertThat(classSubject, isPresentAndRenamed());
-      assertThat(classSubject.method("void", "m1", ImmutableList.of()), isPresent());
       assertThat(classSubject.method("void", "m2", ImmutableList.of()), isAbsent());
       assertThat(classSubject.method("void", "m3", ImmutableList.of()), isAbsent());
       assertThat(classSubject.method("void", "m4", ImmutableList.of()), isAbsent());
+      assertThat(classSubject.uniqueInstanceInitializer(), isPresent());
+      // The remaining method is the private method corresponding to m1 to ensure IAE.
+      assertEquals(2, classSubject.allMethods().size());
     }
   }
 
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
index 3f11198..2e03043 100644
--- a/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
@@ -15,24 +15,17 @@
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.diagnostic.MissingDefinitionsDiagnostic;
+import com.android.tools.r8.mappingcompose.ComposeTestHelpers;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.StringUtils;
-import java.io.BufferedReader;
-import java.io.StringReader;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.List;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
 
-@Ignore("Will be fixed by update in CL 84843")
 @RunWith(Parameterized.class)
 public class TreeShakingSpecificTest extends TestBase {
 
@@ -94,6 +87,36 @@
             });
   }
 
+  private String getExpectedCf() {
+    return StringUtils.lines(
+        "shaking1.Shaking -> shaking1.Shaking:",
+        "# {'id':'sourceFile','fileName':'Shaking.java'}",
+        "    1:2:void main(java.lang.String[]):8:9 -> main",
+        "shaking1.Used -> a.a:",
+        "# {'id':'sourceFile','fileName':'Used.java'}",
+        "    java.lang.String name -> a",
+        "    1:14:void <init>(java.lang.String):0:13 -> <init>",
+        "    1:1:java.lang.String method():17:17 -> a",
+        "    1:1:java.lang.String aMethodThatIsNotUsedButKept():21:21 "
+            + "-> aMethodThatIsNotUsedButKept");
+  }
+
+  private String getExpectedDex() {
+    return StringUtils.lines(
+        "shaking1.Shaking -> shaking1.Shaking:",
+        "# {'id':'sourceFile','fileName':'Shaking.java'}",
+        "    0:6:void main(java.lang.String[]):8:8 -> main",
+        "    7:21:void main(java.lang.String[]):9:9 -> main",
+        "shaking1.Used -> a.a:",
+        "# {'id':'sourceFile','fileName':'Used.java'}",
+        "    java.lang.String name -> a",
+        "    0:2:void <init>(java.lang.String):12:12 -> <init>",
+        "    3:5:void <init>(java.lang.String):13:13 -> <init>",
+        "    0:16:java.lang.String method():17:17 -> a",
+        "    0:2:java.lang.String aMethodThatIsNotUsedButKept():21:21 "
+            + "-> aMethodThatIsNotUsedButKept");
+  }
+
   @Test
   public void testPrintMapping() throws Throwable {
     // Generate R8 processed version without library option.
@@ -106,25 +129,20 @@
         .compile()
         .inspectProguardMap(
             proguardMap -> {
-              // Remove comments.
-              String actualMapping =
-                  Stream.of(proguardMap.split("\n"))
-                      .filter(line -> !line.startsWith("#"))
-                      .collect(Collectors.joining("\n"));
-              String refMapping =
-                  new String(
-                      Files.readAllBytes(
-                          Paths.get(
-                              EXAMPLES_DIR,
-                              "shaking1",
-                              "print-mapping-" + StringUtils.toLowerCase(backend.name()) + ".ref")),
-                      StandardCharsets.UTF_8);
-              assertEquals(sorted(refMapping), sorted(actualMapping));
+              // Remove header.
+              List<String> lines = StringUtils.splitLines(proguardMap);
+              int firstNonHeaderLine = 0;
+              for (String line : lines) {
+                if (line.startsWith("#")) {
+                  firstNonHeaderLine++;
+                } else {
+                  break;
+                }
+              }
+              assertEquals(
+                  backend.isCf() ? getExpectedCf() : getExpectedDex(),
+                  ComposeTestHelpers.doubleToSingleQuote(
+                      StringUtils.lines(lines.subList(firstNonHeaderLine, lines.size()))));
             });
   }
-
-  private static String sorted(String str) {
-    return new BufferedReader(new StringReader(str))
-        .lines().sorted().filter(s -> !s.isEmpty()).collect(Collectors.joining("\n"));
-  }
 }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
index dc0053b..e7146dc 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
@@ -696,4 +696,10 @@
     System.out.println(processResult.stdout);
     return processResult.stdout;
   }
+
+  public MemberNaming getMethodMappingInfo(DexEncodedMethod dexMethod) {
+    return mapping
+        .getNaming()
+        .lookup(MethodSignature.fromDexMethod(dexMethod.getReference(), false));
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
index fd18f5a..8cc6ab0 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
@@ -340,7 +340,8 @@
     }
     Object2IntMap<InstructionSubject> lineNumberTable = new Object2IntOpenHashMap<>();
     DexDebugPositionState state =
-        new DexDebugPositionState(info.startLine, getMethod().getReference());
+        new DexDebugPositionState(
+            info.startLine, getMethod().getReference(), getMethod().isD8R8Synthesized());
     Iterator<DexDebugEvent> iterator = Arrays.asList(info.events).iterator();
     for (DexInstruction insn : code.instructions) {
       int offset = insn.getOffset();
@@ -446,4 +447,10 @@
         getDefaultMethodPrefix() + reference.getMethodName(),
         p);
   }
+
+  @Override
+  public boolean isCompilerSynthesized() {
+    MemberNaming naming = clazz.getMethodMappingInfo(dexMethod);
+    return naming.isCompilerSynthesized();
+  }
 }
diff --git a/third_party/dependencies_plugin.tar.gz.sha1 b/third_party/dependencies_plugin.tar.gz.sha1
new file mode 100644
index 0000000..08fb793
--- /dev/null
+++ b/third_party/dependencies_plugin.tar.gz.sha1
@@ -0,0 +1 @@
+77f5f4042c4340df908bb39cdb40a67009195cac
\ No newline at end of file
diff --git a/tools/archive.py b/tools/archive.py
index 84c0da3..1632231 100755
--- a/tools/archive.py
+++ b/tools/archive.py
@@ -222,6 +222,12 @@
         default_pom_file = os.path.join(temp, 'r8.pom')
         create_maven_release.write_default_r8_pom_file(default_pom_file,
                                                        version)
+        gradle.RunGradle([
+            ':main:spdxSbom',
+            '-PspdxVersion=' + version,
+            '-PspdxRevision=' + GetGitHash()
+        ])
+
         for_archiving = [
             utils.R8_JAR, utils.R8LIB_JAR, utils.R8LIB_JAR + '.map',
             utils.R8LIB_JAR + '_map.zip', utils.R8_FULL_EXCLUDE_DEPS_JAR,
@@ -237,7 +243,9 @@
             utils.R8RETRACE_JAR + '_map.zip', utils.R8RETRACE_EXCLUDE_DEPS_JAR,
             utils.R8RETRACE_EXCLUDE_DEPS_JAR + '.map',
             utils.R8RETRACE_EXCLUDE_DEPS_JAR + '_map.zip',
-            utils.KEEPANNO_ANNOTATIONS_JAR, utils.GENERATED_LICENSE
+            utils.KEEPANNO_ANNOTATIONS_JAR,
+            utils.GENERATED_LICENSE,
+            'd8_r8/main/build/spdx/r8.spdx.json'
         ]
         for file in for_archiving:
             file_name = os.path.basename(file)
diff --git a/tools/create_local_maven_with_dependencies.py b/tools/create_local_maven_with_dependencies.py
index 4ae9d25..e5d8885 100755
--- a/tools/create_local_maven_with_dependencies.py
+++ b/tools/create_local_maven_with_dependencies.py
@@ -11,13 +11,13 @@
 
 import utils
 
-# The local_maven_repository_generator orderes the repositories by name, so
+# The local_maven_repository_generator orders the repositories by name, so
 # prefix with X- to control the order, as many dependencies are present
-# in several repositories.
+# in several repositories. Save A- for additional local repository.
 REPOSITORIES = [
-    'A-Google=https://maven.google.com/',
-    'B-Maven Central=https://repo1.maven.org/maven2/',
-    "C-Gradle Plugins=https://plugins.gradle.org/m2/",
+    'B-Google=https://maven.google.com/',
+    'C-Maven Central=https://repo1.maven.org/maven2/',
+    "D-Gradle Plugins=https://plugins.gradle.org/m2/",
 ]
 
 ANDRDID_SUPPORT_VERSION = '25.4.0'
@@ -97,6 +97,11 @@
         version=PROTOBUF_VERSION),
 ]
 
+PLUGIN_DEPENDENCIES = [
+  'org.spdx.sbom:org.spdx.sbom.gradle.plugin:0.2.0-r8-patch01',
+  # See https://github.com/FasterXML/jackson-core/issues/999.
+  'ch.randelshofer:fastdoubleparser:0.8.0',
+]
 
 def dependencies_tar(dependencies_path):
     return os.path.join(os.path.dirname(dependencies_path),
@@ -143,32 +148,89 @@
         required=True,
         help='Path to a studio-main checkout (to get the tool '
         '//tools/base/bazel:local_maven_repository_generator_cli)')
+    result.add_argument(
+        '--plugin-deps',
+        '--plugin_deps',
+        default=False,
+        action='store_true',
+        help='Build repository Gradle plugin dependncies')
+    result.add_argument(
+        '--include-maven-local',
+        '--include_maven_local',
+        metavar=('<path>'),
+        default=False,
+        help='Path to maven local repository to include as dependency source')
+    result.add_argument(
+        '--no-upload',
+        '--no_upload',
+        default=False,
+        action='store_true',
+        help="Don't upload to Google CLoud Storage")
     return result.parse_args()
 
 
+def set_utime(path):
+    os.utime(path, (0, 0))
+    for root, dirs, files in os.walk(path):
+      for f in files:
+          os.utime(os.path.join(root, f), (0, 0))
+      for d in dirs:
+          os.utime(os.path.join(root, d), (0, 0))
+
 def main():
     args = parse_options()
 
-    dependencies_path = os.path.join(utils.THIRD_PARTY, 'dependencies')
-    print("Downloading to " + dependencies_path)
-    remove_local_maven_repository(dependencies_path)
-    create_local_maven_repository(args, dependencies_path, REPOSITORIES,
-                                  BUILD_DEPENDENCIES + TEST_DEPENDENCIES)
+    repositories = REPOSITORIES
+    if args.include_maven_local:
+        # Add the local repository as the first for it to take precedence.
+        # Use A- prefix as current local_maven_repository_generator orderes by name.
+        repositories = ['A-Local=file://%s' % args.include_maven_local] + REPOSITORIES
 
-    dependencies_new_path = os.path.join(utils.THIRD_PARTY, 'dependencies_new')
-    print("Downloading to " + dependencies_new_path)
-    remove_local_maven_repository(dependencies_new_path)
-    create_local_maven_repository(args, dependencies_new_path, REPOSITORIES,
-                                  NEW_DEPENDENCIES)
+    dependencies = []
 
-    print("Uploading to Google Cloud Storage:")
-    with utils.ChangedWorkingDirectory(utils.THIRD_PARTY):
-        for dependency in ['dependencies', 'dependencies_new']:
-            cmd = [
-                'upload_to_google_storage.py', '-a', '--bucket', 'r8-deps',
-                dependency
-            ]
-            subprocess.check_call(cmd)
+    if args.plugin_deps:
+        dependencies_plugin_path = os.path.join(utils.THIRD_PARTY, 'dependencies_plugin')
+        remove_local_maven_repository(dependencies_plugin_path)
+        print("Downloading to " + dependencies_plugin_path)
+        create_local_maven_repository(
+            args, dependencies_plugin_path, repositories, PLUGIN_DEPENDENCIES)
+        set_utime(dependencies_plugin_path)
+        dependencies.append('dependencies_plugin')
+    else:
+        dependencies_path = os.path.join(utils.THIRD_PARTY, 'dependencies')
+        remove_local_maven_repository(dependencies_path)
+        print("Downloading to " + dependencies_path)
+        create_local_maven_repository(
+            args, dependencies_path, repositories, BUILD_DEPENDENCIES + TEST_DEPENDENCIES)
+        set_utime(dependencies_path)
+        dependencies.append('dependencies')
+        dependencies_new_path = os.path.join(utils.THIRD_PARTY, 'dependencies_new')
+        remove_local_maven_repository(dependencies_new_path)
+        print("Downloading to " + dependencies_new_path)
+        create_local_maven_repository(
+           args, dependencies_new_path, repositories, NEW_DEPENDENCIES)
+        set_utime(dependencies_new_path)
+        dependencies.append('dependencies_new')
+
+    upload_cmds = []
+    for dependency in dependencies:
+        upload_cmds.append([
+            'upload_to_google_storage.py',
+            '-a',
+            '--bucket',
+            'r8-deps',
+            dependency])
+
+    if not args.no_upload:
+        print("Uploading to Google Cloud Storage:")
+        with utils.ChangedWorkingDirectory(utils.THIRD_PARTY):
+            for cmd in upload_cmds:
+                subprocess.check_call(cmd)
+    else:
+        print("Not uploading to Google Cloud Storage. "
+            + "Run the following commands in %s to do so manually" % utils.THIRD_PARTY)
+        for cmd in upload_cmds:
+            print(" ".join(cmd))
 
 
 if __name__ == '__main__':