Tests for adding synthetics to baseline profile

Bug: b/265729283
Change-Id: I736069f4ceeecf5783f75cd6b608cf89dce5e670
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 428bdd0..310856f 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
@@ -28,7 +28,7 @@
 /** This class defines the desugaring of a single invoke-special instruction. */
 public class InvokeSpecialToSelfDesugaring implements CfInstructionDesugaring {
 
-  private static final String INVOKE_SPECIAL_BRIDGE_PREFIX = "$invoke$special$";
+  public static final String INVOKE_SPECIAL_BRIDGE_PREFIX = "$invoke$special$";
 
   private final DexItemFactory dexItemFactory;
 
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java
index 1b3e48e..ad59051 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/nest/NestBasedAccessDesugaring.java
@@ -56,14 +56,13 @@
 
   // Short names to avoid creating long strings
   public static final String NEST_ACCESS_NAME_PREFIX = "-$$Nest$";
-  private static final String NEST_ACCESS_METHOD_NAME_PREFIX = NEST_ACCESS_NAME_PREFIX + "m";
-  private static final String NEST_ACCESS_STATIC_METHOD_NAME_PREFIX =
-      NEST_ACCESS_NAME_PREFIX + "sm";
-  private static final String NEST_ACCESS_FIELD_GET_NAME_PREFIX = NEST_ACCESS_NAME_PREFIX + "fget";
-  private static final String NEST_ACCESS_STATIC_GET_FIELD_NAME_PREFIX =
+  public static final String NEST_ACCESS_METHOD_NAME_PREFIX = NEST_ACCESS_NAME_PREFIX + "m";
+  public static final String NEST_ACCESS_STATIC_METHOD_NAME_PREFIX = NEST_ACCESS_NAME_PREFIX + "sm";
+  public static final String NEST_ACCESS_FIELD_GET_NAME_PREFIX = NEST_ACCESS_NAME_PREFIX + "fget";
+  public static final String NEST_ACCESS_STATIC_GET_FIELD_NAME_PREFIX =
       NEST_ACCESS_NAME_PREFIX + "sfget";
-  private static final String NEST_ACCESS_FIELD_PUT_NAME_PREFIX = NEST_ACCESS_NAME_PREFIX + "fput";
-  private static final String NEST_ACCESS_STATIC_PUT_FIELD_NAME_PREFIX =
+  public static final String NEST_ACCESS_FIELD_PUT_NAME_PREFIX = NEST_ACCESS_NAME_PREFIX + "fput";
+  public static final String NEST_ACCESS_STATIC_PUT_FIELD_NAME_PREFIX =
       NEST_ACCESS_NAME_PREFIX + "sfput";
 
   protected final AppView<?> appView;
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index b95ac53..335e328 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -68,6 +68,7 @@
 import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
 import com.android.tools.r8.ir.desugar.nest.Nest;
 import com.android.tools.r8.ir.optimize.Inliner;
+import com.android.tools.r8.ir.optimize.Inliner.Reason;
 import com.android.tools.r8.ir.optimize.enums.EnumDataMap;
 import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.naming.MapVersion;
@@ -819,7 +820,7 @@
       new CallSiteOptimizationOptions();
   private final CfCodeAnalysisOptions cfCodeAnalysisOptions = new CfCodeAnalysisOptions();
   private final ClassInlinerOptions classInlinerOptions = new ClassInlinerOptions();
-  private final InlinerOptions inlinerOptions = new InlinerOptions();
+  private final InlinerOptions inlinerOptions = new InlinerOptions(this);
   private final HorizontalClassMergerOptions horizontalClassMergerOptions =
       new HorizontalClassMergerOptions();
   private final OpenClosedInterfacesOptions openClosedInterfacesOptions =
@@ -1528,7 +1529,7 @@
     boolean test(AppView<?> appView, ProgramMethod method, int inliningDepth);
   }
 
-  public class InlinerOptions {
+  public static class InlinerOptions {
 
     public boolean enableInlining =
         !parseSystemPropertyForDevelopmentOrDefault("com.android.tools.r8.disableinlining", false);
@@ -1559,17 +1560,31 @@
 
     public ApplyInliningToInlineePredicate applyInliningToInlineePredicateForTesting = null;
 
+    private final InternalOptions options;
+
+    public InlinerOptions(InternalOptions options) {
+      this.options = options;
+    }
+
+    public static void disableInlining(InternalOptions options) {
+      options.inlinerOptions().enableInlining = false;
+    }
+
+    public static void setOnlyForceInlining(InternalOptions options) {
+      options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
+    }
+
     public int getSimpleInliningInstructionLimit() {
       // If a custom simple inlining instruction limit is set, then use that.
       if (simpleInliningInstructionLimit >= 0) {
         return simpleInliningInstructionLimit;
       }
       // Allow 3 instructions when generating to class files.
-      if (isGeneratingClassFiles()) {
+      if (options.isGeneratingClassFiles()) {
         return 3;
       }
       // Allow the size of the dex code to be up to 5 bytes.
-      assert isGeneratingDex();
+      assert options.isGeneratingDex();
       return 5;
     }
 
@@ -1578,7 +1593,7 @@
       if (applyInliningToInlineePredicateForTesting != null) {
         return applyInliningToInlineePredicateForTesting.test(appView, inlinee, inliningDepth);
       }
-      if (protoShrinking.shouldApplyInliningToInlinee(appView, inlinee, inliningDepth)) {
+      if (options.protoShrinking().shouldApplyInliningToInlinee(appView, inlinee, inliningDepth)) {
         return true;
       }
       return false;
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
index b487caf..8b2877f 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
@@ -205,7 +205,7 @@
     };
   }
 
-  static ApiModelingClassVerificationHelper verifyThat(
+  public static ApiModelingClassVerificationHelper verifyThat(
       CodeInspector inspector, TestParameters parameters, Class<?> clazz) {
     return new ApiModelingClassVerificationHelper(inspector, parameters, clazz);
   }
@@ -295,7 +295,7 @@
       hasNotOulinedInstructionWithClassReference(method, CodeMatchers::containsInstanceOf);
     }
 
-    void hasConstClassOutlinedFromUntil(Method method, AndroidApiLevel apiLevel) {
+    public void hasConstClassOutlinedFromUntil(Method method, AndroidApiLevel apiLevel) {
       if (parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(apiLevel)) {
         hasConstClassOutlinedFrom(method);
       } else {
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/ApiOutlineProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/ApiOutlineProfileRewritingTest.java
new file mode 100644
index 0000000..02a4fe6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/ApiOutlineProfileRewritingTest.java
@@ -0,0 +1,138 @@
+// 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.profile.art.completeness;
+
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.SingleTestRunResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.apimodel.ApiModelingTestHelper;
+import com.android.tools.r8.profile.art.model.ExternalArtProfile;
+import com.android.tools.r8.profile.art.utils.ArtProfileInspector;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+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 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 ApiOutlineProfileRewritingTest extends TestBase {
+
+  private static final AndroidApiLevel classApiLevel = AndroidApiLevel.M;
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+  }
+
+  public AndroidApiLevel getApiLevelForRuntime() {
+    return parameters.isCfRuntime()
+        ? AndroidApiLevel.B
+        : parameters.getRuntime().maxSupportedApiLevel();
+  }
+
+  public boolean isLibraryClassAlwaysPresent() {
+    return parameters.isCfRuntime()
+        || parameters.getApiLevel().isGreaterThanOrEqualTo(classApiLevel);
+  }
+
+  public boolean isLibraryClassPresentInCurrentRuntime() {
+    return getApiLevelForRuntime().isGreaterThanOrEqualTo(classApiLevel);
+  }
+
+  @Test
+  public void testReference() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    assumeTrue(parameters.getApiLevel() == AndroidApiLevel.B);
+    assertFalse(isLibraryClassPresentInCurrentRuntime());
+    testForJvm()
+        .addProgramClasses(Main.class)
+        .run(parameters.getRuntime(), Main.class)
+        .apply(this::inspectRunResult);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClasses(Main.class)
+        .addLibraryClasses(LibraryClass.class)
+        .addDefaultRuntimeLibrary(parameters)
+        .addKeepMainRule(Main.class)
+        .addArtProfileForRewriting(getArtProfile())
+        .apply(setMockApiLevelForClass(LibraryClass.class, classApiLevel))
+        .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+        .apply(ApiModelingTestHelper::enableOutliningOfMethods)
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspectResidualArtProfile(this::inspect)
+        .applyIf(
+            isLibraryClassPresentInCurrentRuntime(),
+            testBuilder -> testBuilder.addBootClasspathClasses(LibraryClass.class))
+        .run(parameters.getRuntime(), Main.class)
+        .apply(this::inspectRunResult);
+  }
+
+  private ExternalArtProfile getArtProfile() {
+    return ExternalArtProfile.builder()
+        .addMethodRule(MethodReferenceUtils.mainMethod(Main.class))
+        .build();
+  }
+
+  private void inspect(ArtProfileInspector profileInspector, CodeInspector inspector)
+      throws Exception {
+    // Verify that outlining happened.
+    verifyThat(inspector, parameters, LibraryClass.class)
+        .hasConstClassOutlinedFromUntil(
+            Main.class.getMethod("main", String[].class), classApiLevel);
+
+    // Check outline was added to ART profile.
+    ClassSubject apiOutlineClassSubject =
+        inspector.clazz(SyntheticItemsTestUtils.syntheticApiOutlineClass(Main.class, 0));
+    assertThat(apiOutlineClassSubject, notIf(isPresent(), isLibraryClassAlwaysPresent()));
+
+    MethodSubject apiOutlineMethodSubject = apiOutlineClassSubject.uniqueMethod();
+    assertThat(apiOutlineMethodSubject, notIf(isPresent(), isLibraryClassAlwaysPresent()));
+
+    // TODO(b/265729283): When outlining the residual profile should include the outline method and
+    //  its holder.
+    profileInspector
+        .assertContainsMethodRule(MethodReferenceUtils.mainMethod(Main.class))
+        .assertContainsNoOtherRules();
+  }
+
+  private void inspectRunResult(SingleTestRunResult<?> runResult) {
+    runResult.applyIf(
+        isLibraryClassPresentInCurrentRuntime(),
+        ignore -> runResult.assertSuccessWithOutputLines("class " + typeName(LibraryClass.class)),
+        ignore -> runResult.assertFailureWithErrorThatThrows(NoClassDefFoundError.class));
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      System.out.println(LibraryClass.class);
+    }
+  }
+
+  // Only present from api 23.
+  public static class LibraryClass {}
+}
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/BackportProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/BackportProfileRewritingTest.java
new file mode 100644
index 0000000..ba5013c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/BackportProfileRewritingTest.java
@@ -0,0 +1,87 @@
+// 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.profile.art.completeness;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.onlyIf;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.profile.art.model.ExternalArtProfile;
+import com.android.tools.r8.profile.art.utils.ArtProfileInspector;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.InternalOptions.InlinerOptions;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+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 java.util.Objects;
+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 BackportProfileRewritingTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addArtProfileForRewriting(getArtProfile())
+        .addOptionsModification(InlinerOptions::disableInlining)
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspectResidualArtProfile(this::inspect)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("true");
+  }
+
+  private ExternalArtProfile getArtProfile() {
+    return ExternalArtProfile.builder()
+        .addMethodRule(MethodReferenceUtils.mainMethod(Main.class))
+        .build();
+  }
+
+  private boolean isBackportingObjectsNonNull() {
+    return parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(AndroidApiLevel.N);
+  }
+
+  private void inspect(ArtProfileInspector profileInspector, CodeInspector inspector) {
+    ClassSubject backportClassSubject =
+        inspector.clazz(SyntheticItemsTestUtils.syntheticBackportClass(Main.class, 0));
+    assertThat(backportClassSubject, onlyIf(isBackportingObjectsNonNull(), isPresent()));
+
+    profileInspector.assertContainsMethodRules(MethodReferenceUtils.mainMethod(Main.class));
+
+    // TODO(b/265729283): Should include the backported method and its holder.
+    if (isBackportingObjectsNonNull()) {
+      MethodSubject backportMethodSubject = backportClassSubject.uniqueMethod();
+      assertThat(backportMethodSubject, isPresent());
+    }
+
+    profileInspector.assertContainsNoOtherRules();
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      System.out.println(Objects.nonNull(args));
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/DefaultInterfaceMethodProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/DefaultInterfaceMethodProfileRewritingTest.java
new file mode 100644
index 0000000..4e59b75
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/DefaultInterfaceMethodProfileRewritingTest.java
@@ -0,0 +1,117 @@
+// 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.profile.art.completeness;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.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.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 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 DefaultInterfaceMethodProfileRewritingTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addArtProfileForRewriting(getArtProfile())
+        .addOptionsModification(InlinerOptions::disableInlining)
+        .enableNoHorizontalClassMergingAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspectResidualArtProfile(this::inspect)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("Hello, world!");
+  }
+
+  private ExternalArtProfile getArtProfile() throws Exception {
+    return ExternalArtProfile.builder()
+        .addMethodRule(Reference.methodFromMethod(I.class.getDeclaredMethod("m")))
+        .build();
+  }
+
+  private void inspect(ArtProfileInspector profileInspector, CodeInspector inspector) {
+    if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
+      ClassSubject iClassSubject = inspector.clazz(I.class);
+      assertThat(iClassSubject, isPresent());
+
+      MethodSubject interfaceMethodSubject = iClassSubject.uniqueMethodWithOriginalName("m");
+      assertThat(interfaceMethodSubject, isPresent());
+
+      profileInspector.assertContainsMethodRule(interfaceMethodSubject);
+    } else {
+      ClassSubject iClassSubject = inspector.clazz(I.class);
+      assertThat(iClassSubject, isPresent());
+
+      ClassSubject aClassSubject = inspector.clazz(A.class);
+      assertThat(aClassSubject, isPresent());
+
+      ClassSubject companionClassSubject =
+          inspector.clazz(SyntheticItemsTestUtils.syntheticCompanionClass(I.class));
+      assertThat(companionClassSubject, isPresent());
+
+      MethodSubject interfaceMethodSubject = iClassSubject.uniqueMethodWithOriginalName("m");
+      assertThat(interfaceMethodSubject, isPresent());
+
+      MethodSubject implementationMethodSubject =
+          aClassSubject.method(interfaceMethodSubject.getFinalReference());
+      assertThat(implementationMethodSubject, isPresent());
+
+      MethodSubject movedMethodSubject = companionClassSubject.uniqueMethod();
+      assertThat(movedMethodSubject, isPresent());
+
+      // TODO(b/265729283): Should also include the two methods from desugaring.
+      profileInspector.assertContainsMethodRule(interfaceMethodSubject);
+    }
+
+    profileInspector.assertContainsNoOtherRules();
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      I i = System.currentTimeMillis() > 0 ? new A() : new B();
+      i.m();
+    }
+  }
+
+  interface I {
+
+    default void m() {
+      System.out.println("Hello, world!");
+    }
+  }
+
+  @NoHorizontalClassMerging
+  static class A implements I {}
+
+  @NoHorizontalClassMerging
+  static class B implements I {}
+}
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/EnumUnboxingUtilityMethodProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/EnumUnboxingUtilityMethodProfileRewritingTest.java
new file mode 100644
index 0000000..13a8c9d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/EnumUnboxingUtilityMethodProfileRewritingTest.java
@@ -0,0 +1,135 @@
+// 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.profile.art.completeness;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+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.InternalOptions.InlinerOptions;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+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 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 EnumUnboxingUtilityMethodProfileRewritingTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addArtProfileForRewriting(getArtProfile())
+        .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(MyEnum.class))
+        .addOptionsModification(InlinerOptions::disableInlining)
+        .noHorizontalClassMergingOfSynthetics()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspectResidualArtProfile(this::inspect)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("Hello, world!");
+  }
+
+  private ExternalArtProfile getArtProfile() throws Exception {
+    return ExternalArtProfile.builder()
+        .addMethodRule(MethodReferenceUtils.mainMethod(Main.class))
+        .addMethodRule(Reference.methodFromMethod(MyEnum.class.getDeclaredMethod("greet")))
+        .addMethodRule(Reference.methodFromMethod(MyEnum.class.getDeclaredMethod("other")))
+        .addMethodRule(Reference.methodFromMethod(MyEnum.class.getDeclaredMethod("values")))
+        .build();
+  }
+
+  private void inspect(ArtProfileInspector profileInspector, CodeInspector inspector) {
+    ClassSubject mainClassSubject = inspector.clazz(Main.class);
+    assertThat(mainClassSubject, isPresent());
+    assertThat(mainClassSubject.mainMethod(), isPresent());
+
+    ClassSubject enumUnboxingLocalUtilityClassSubject =
+        inspector.clazz(
+            SyntheticItemsTestUtils.syntheticEnumUnboxingLocalUtilityClass(MyEnum.class));
+    assertThat(enumUnboxingLocalUtilityClassSubject, isPresent());
+
+    MethodSubject localGreetMethodSubject =
+        enumUnboxingLocalUtilityClassSubject.uniqueMethodWithOriginalName("greet");
+    assertThat(localGreetMethodSubject, isPresent());
+
+    MethodSubject localOtherMethodSubject =
+        enumUnboxingLocalUtilityClassSubject.uniqueMethodWithOriginalName("other");
+    assertThat(localOtherMethodSubject, isPresent());
+
+    MethodSubject localValuesMethodSubject =
+        enumUnboxingLocalUtilityClassSubject.uniqueMethodWithOriginalName("values");
+    assertThat(localValuesMethodSubject, isPresent());
+
+    ClassSubject enumUnboxingSharedUtilityClassSubject =
+        inspector.clazz(
+            SyntheticItemsTestUtils.syntheticEnumUnboxingSharedUtilityClass(MyEnum.class));
+    assertThat(enumUnboxingSharedUtilityClassSubject, isPresent());
+    assertThat(enumUnboxingSharedUtilityClassSubject.clinit(), isPresent());
+    assertThat(
+        enumUnboxingSharedUtilityClassSubject.uniqueMethodWithOriginalName("ordinal"), isPresent());
+    assertThat(
+        enumUnboxingSharedUtilityClassSubject.uniqueMethodWithOriginalName("values"), isPresent());
+
+    // TODO(b/265729283): Should also include the above methods from enum unboxing.
+    profileInspector
+        .assertContainsMethodRules(
+            mainClassSubject.mainMethod(),
+            localGreetMethodSubject,
+            localOtherMethodSubject,
+            localValuesMethodSubject)
+        .assertContainsNoOtherRules();
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      MyEnum a = System.currentTimeMillis() > 0 ? MyEnum.A : MyEnum.B;
+      MyEnum b = a.other();
+      a.greet();
+      b.greet();
+    }
+  }
+
+  enum MyEnum {
+    A,
+    B;
+
+    void greet() {
+      switch (this) {
+        case A:
+          System.out.print("Hello");
+          break;
+        case B:
+          System.out.println(", world!");
+          break;
+      }
+    }
+
+    MyEnum other() {
+      return MyEnum.values()[1 - ordinal()];
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/HorizontallyMergedConstructorMethodProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/HorizontallyMergedConstructorMethodProfileRewritingTest.java
new file mode 100644
index 0000000..985b723
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/HorizontallyMergedConstructorMethodProfileRewritingTest.java
@@ -0,0 +1,113 @@
+// 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.profile.art.completeness;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+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.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 java.util.List;
+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 HorizontallyMergedConstructorMethodProfileRewritingTest extends TestBase {
+
+  private enum ArtProfileInputOutput {
+    A_CONSTRUCTOR,
+    B_CONSTRUCTOR;
+
+    public ExternalArtProfile getArtProfile() throws Exception {
+      switch (this) {
+        case A_CONSTRUCTOR:
+          return ExternalArtProfile.builder()
+              .addMethodRule(Reference.methodFromMethod(A.class.getDeclaredConstructor()))
+              .build();
+        case B_CONSTRUCTOR:
+          return ExternalArtProfile.builder()
+              .addMethodRule(Reference.methodFromMethod(B.class.getDeclaredConstructor()))
+              .build();
+        default:
+          throw new RuntimeException();
+      }
+    }
+
+    public void inspect(ArtProfileInspector profileInspector, CodeInspector inspector) {
+      ClassSubject aClassSubject = inspector.clazz(A.class);
+      assertThat(aClassSubject, isPresent());
+
+      MethodSubject syntheticConstructorSubject = aClassSubject.uniqueMethod();
+      assertThat(syntheticConstructorSubject, isPresent());
+
+      // TODO(b/265729283): Should contain the constructor.
+      profileInspector.assertEmpty();
+    }
+  }
+
+  @Parameter(0)
+  public ArtProfileInputOutput artProfileInputOutput;
+
+  @Parameter(1)
+  public TestParameters parameters;
+
+  @Parameters(name = "{1}, {0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        ArtProfileInputOutput.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addArtProfileForRewriting(artProfileInputOutput.getArtProfile())
+        .addHorizontallyMergedClassesInspector(
+            inspector -> inspector.assertMergedInto(B.class, A.class).assertNoOtherClassesMerged())
+        .addOptionsModification(InlinerOptions::setOnlyForceInlining)
+        .enableNeverClassInliningAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspectResidualArtProfile(artProfileInputOutput::inspect)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("Hello, world!");
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      new A();
+      new B();
+    }
+  }
+
+  @NeverClassInline
+  static class A {
+
+    public A() {
+      System.out.print("Hello");
+    }
+  }
+
+  @NeverClassInline
+  static class B {
+
+    public B() {
+      System.out.println(", world!");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/HorizontallyMergedVirtualMethodProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/HorizontallyMergedVirtualMethodProfileRewritingTest.java
new file mode 100644
index 0000000..29e39f9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/HorizontallyMergedVirtualMethodProfileRewritingTest.java
@@ -0,0 +1,124 @@
+// 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.profile.art.completeness;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+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.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.FoundMethodSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.List;
+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 HorizontallyMergedVirtualMethodProfileRewritingTest extends TestBase {
+
+  private enum ArtProfileInputOutput {
+    A_METHOD,
+    B_METHOD;
+
+    public ExternalArtProfile getArtProfile() throws Exception {
+      switch (this) {
+        case A_METHOD:
+          return ExternalArtProfile.builder()
+              .addMethodRule(Reference.methodFromMethod(A.class.getDeclaredMethod("m")))
+              .build();
+        case B_METHOD:
+          return ExternalArtProfile.builder()
+              .addMethodRule(Reference.methodFromMethod(B.class.getDeclaredMethod("m")))
+              .build();
+        default:
+          throw new RuntimeException();
+      }
+    }
+
+    public void inspect(ArtProfileInspector profileInspector, CodeInspector inspector) {
+      ClassSubject aClassSubject = inspector.clazz(A.class);
+      assertThat(aClassSubject, isPresent());
+
+      String stringConstInMovedMethod = this == A_METHOD ? "Hello" : ", world!";
+      MethodSubject movedMethodSubject =
+          aClassSubject.uniqueMethodThatMatches(
+              method ->
+                  method.isPrivate()
+                      && method
+                          .streamInstructions()
+                          .anyMatch(
+                              instruction -> instruction.isConstString(stringConstInMovedMethod)));
+      assertThat(movedMethodSubject, isPresent());
+
+      MethodSubject syntheticBridgeMethodSubject =
+          aClassSubject.uniqueMethodThatMatches(FoundMethodSubject::isVirtual);
+      assertThat(syntheticBridgeMethodSubject, isPresent());
+
+      // TODO(b/265729283): Should contain the synthetic bridge method from above.
+      profileInspector.assertContainsMethodRule(movedMethodSubject).assertContainsNoOtherRules();
+    }
+  }
+
+  @Parameter(0)
+  public ArtProfileInputOutput artProfileInputOutput;
+
+  @Parameter(1)
+  public TestParameters parameters;
+
+  @Parameters(name = "{1}, {0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        ArtProfileInputOutput.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addArtProfileForRewriting(artProfileInputOutput.getArtProfile())
+        .addHorizontallyMergedClassesInspector(
+            inspector -> inspector.assertMergedInto(B.class, A.class).assertNoOtherClassesMerged())
+        .addOptionsModification(InlinerOptions::setOnlyForceInlining)
+        .addOptionsModification(
+            options -> options.callSiteOptimizationOptions().setEnableMethodStaticizing(false))
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspectResidualArtProfile(artProfileInputOutput::inspect)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("Hello, world!");
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      new A().m();
+      new B().m();
+    }
+  }
+
+  static class A {
+
+    public void m() {
+      System.out.print("Hello");
+    }
+  }
+
+  static class B {
+
+    public void m() {
+      System.out.println(", world!");
+    }
+  }
+}
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
new file mode 100644
index 0000000..75b9758
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/InvokeSpecialToVirtualMethodProfileRewritingTest.java
@@ -0,0 +1,128 @@
+// 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.profile.art.completeness;
+
+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 static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.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 java.io.IOException;
+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;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class InvokeSpecialToVirtualMethodProfileRewritingTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void testJvm() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    testForJvm()
+        .addProgramClassFileData(getTransformedMain())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("Hello, world!", "Hello, world!");
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClassFileData(getTransformedMain())
+        .addKeepMainRule(Main.class)
+        .addArtProfileForRewriting(getArtProfile())
+        .addOptionsModification(InlinerOptions::disableInlining)
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspectResidualArtProfile(this::inspect)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("Hello, world!", "Hello, world!");
+  }
+
+  private byte[] getTransformedMain() throws IOException {
+    BooleanBox anyRewritten = new BooleanBox();
+    return transformer(Main.class)
+        .transformMethodInsnInMethod(
+            "main",
+            (opcode, owner, name, descriptor, isInterface, visitor) -> {
+              if (opcode == Opcodes.INVOKEVIRTUAL) {
+                assertEquals("m", name);
+                if (anyRewritten.isFalse()) {
+                  visitor.visitMethodInsn(
+                      Opcodes.INVOKESPECIAL, owner, name, descriptor, isInterface);
+                  anyRewritten.set();
+                  return;
+                }
+              }
+              visitor.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+            })
+        .transform();
+  }
+
+  private ExternalArtProfile getArtProfile() throws Exception {
+    return ExternalArtProfile.builder()
+        .addMethodRule(Reference.methodFromMethod(Main.class.getDeclaredMethod("m")))
+        .build();
+  }
+
+  private void inspect(ArtProfileInspector profileInspector, CodeInspector inspector)
+      throws Exception {
+    ClassSubject mainClassSubject = inspector.clazz(Main.class);
+    assertThat(mainClassSubject, isPresent());
+
+    MethodSubject mMethodSubject = mainClassSubject.uniqueMethodWithOriginalName("m");
+    assertThat(mMethodSubject, isPresent());
+
+    if (parameters.isDexRuntime()) {
+      MethodSubject mMovedMethodSubject =
+          mainClassSubject.method(
+              SyntheticItemsTestUtils.syntheticInvokeSpecialMethod(
+                  Main.class.getDeclaredMethod("m")));
+      assertThat(mMovedMethodSubject, isPresent());
+      assertNotEquals(
+          mMethodSubject.getProgramMethod().getName(),
+          mMovedMethodSubject.getProgramMethod().getName());
+    }
+
+    // TODO(b/265729283): Should also contain the synthetic method above when compiling to dex.
+    profileInspector.assertContainsMethodRule(mMethodSubject).assertContainsNoOtherRules();
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      Main main = new Main();
+      main.m(); // transformed to invoke-special
+      main.m(); // remains an invoke-virtual (so that Main.m() survives shaking)
+    }
+
+    public void m() {
+      System.out.println("Hello, world!");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/MovedStaticInterfaceMethodProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/MovedStaticInterfaceMethodProfileRewritingTest.java
new file mode 100644
index 0000000..e2f0c2d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/MovedStaticInterfaceMethodProfileRewritingTest.java
@@ -0,0 +1,94 @@
+// 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.profile.art.completeness;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.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.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class MovedStaticInterfaceMethodProfileRewritingTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addArtProfileForRewriting(getArtProfile())
+        .enableInliningAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspectResidualArtProfile(this::inspect)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("Hello, world!");
+  }
+
+  private ExternalArtProfile getArtProfile() throws Exception {
+    return ExternalArtProfile.builder()
+        .addMethodRule(Reference.methodFromMethod(I.class.getDeclaredMethod("m")))
+        .build();
+  }
+
+  private void inspect(ArtProfileInspector profileInspector, CodeInspector inspector) {
+    if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
+      ClassSubject iClassSubject = inspector.clazz(I.class);
+      assertThat(iClassSubject, isPresent());
+
+      MethodSubject mMethodSubject = iClassSubject.uniqueMethodWithOriginalName("m");
+      assertThat(mMethodSubject, isPresent());
+
+      profileInspector.assertContainsMethodRule(mMethodSubject).assertContainsNoOtherRules();
+    } else {
+      ClassSubject companionClassSubject =
+          inspector.clazz(SyntheticItemsTestUtils.syntheticCompanionClass(I.class));
+      assertThat(companionClassSubject, isPresent());
+
+      MethodSubject mMethodSubject = companionClassSubject.uniqueMethodWithOriginalName("m");
+      assertThat(mMethodSubject, isPresent());
+
+      // TODO(b/265729283): Should contain the companion method.
+      profileInspector.assertEmpty();
+    }
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      I.m();
+    }
+  }
+
+  interface I {
+
+    @NeverInline
+    static void m() {
+      System.out.println("Hello, world!");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.java
new file mode 100644
index 0000000..81a74af
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/NestBasedAccessBridgesProfileRewritingTest.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.profile.art.completeness;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.profile.art.completeness.NestBasedAccessBridgesProfileRewritingTest.Main.NestMember;
+import com.android.tools.r8.profile.art.model.ExternalArtProfile;
+import com.android.tools.r8.profile.art.utils.ArtProfileInspector;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+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.google.common.collect.ImmutableList;
+import java.util.List;
+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 NestBasedAccessBridgesProfileRewritingTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void testJvm() throws Exception {
+    assumeTrue(parameters.isCfRuntime());
+    assumeTrue(parameters.asCfRuntime().isNewerThanOrEqual(CfVm.JDK11));
+    testForJvm()
+        .addProgramClassFileData(getProgramClassFileData())
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("1", "2", "3", "4");
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    assumeFalse(parameters.isCfRuntime() && parameters.asCfRuntime().isOlderThan(CfVm.JDK11));
+    testForR8(parameters.getBackend())
+        .addProgramClassFileData(getProgramClassFileData())
+        .addKeepMainRule(Main.class)
+        .addArtProfileForRewriting(getArtProfile())
+        .enableInliningAnnotations()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspectResidualArtProfile(this::inspect)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("1", "2", "3", "4");
+  }
+
+  private List<byte[]> getProgramClassFileData() throws Exception {
+    return ImmutableList.of(
+        transformer(Main.class).setNest(Main.class, NestMember.class).transform(),
+        transformer(NestMember.class)
+            .setNest(Main.class, NestMember.class)
+            .setPrivate(NestMember.class.getDeclaredField("instanceField"))
+            .setPrivate(NestMember.class.getDeclaredField("staticField"))
+            .setPrivate(NestMember.class.getDeclaredMethod("instanceMethod"))
+            .setPrivate(NestMember.class.getDeclaredMethod("staticMethod"))
+            .transform());
+  }
+
+  private ExternalArtProfile getArtProfile() {
+    return ExternalArtProfile.builder()
+        .addMethodRule(MethodReferenceUtils.mainMethod(Main.class))
+        .build();
+  }
+
+  private void inspect(ArtProfileInspector profileInspector, CodeInspector inspector)
+      throws Exception {
+    ClassSubject nestMemberClassSubject = inspector.clazz(NestMember.class);
+    assertThat(nestMemberClassSubject, isPresent());
+
+    MethodSubject syntheticNestInstanceFieldGetterMethodSubject =
+        nestMemberClassSubject.uniqueMethodWithOriginalName(
+            SyntheticItemsTestUtils.syntheticNestInstanceFieldGetter(
+                    NestMember.class.getDeclaredField("instanceField"))
+                .getMethodName());
+    assertThat(
+        syntheticNestInstanceFieldGetterMethodSubject,
+        notIf(isPresent(), parameters.canUseNestBasedAccesses()));
+
+    MethodSubject syntheticNestInstanceFieldSetterMethodSubject =
+        nestMemberClassSubject.uniqueMethodWithOriginalName(
+            SyntheticItemsTestUtils.syntheticNestInstanceFieldSetter(
+                    NestMember.class.getDeclaredField("instanceField"))
+                .getMethodName());
+    assertThat(
+        syntheticNestInstanceFieldSetterMethodSubject,
+        notIf(isPresent(), parameters.canUseNestBasedAccesses()));
+
+    MethodSubject syntheticNestInstanceMethodAccessorMethodSubject =
+        nestMemberClassSubject.uniqueMethodWithOriginalName(
+            SyntheticItemsTestUtils.syntheticNestInstanceMethodAccessor(
+                    NestMember.class.getDeclaredMethod("instanceMethod"))
+                .getMethodName());
+    assertThat(
+        syntheticNestInstanceMethodAccessorMethodSubject,
+        notIf(isPresent(), parameters.canUseNestBasedAccesses()));
+
+    MethodSubject syntheticNestStaticFieldGetterMethodSubject =
+        nestMemberClassSubject.uniqueMethodWithOriginalName(
+            SyntheticItemsTestUtils.syntheticNestStaticFieldGetter(
+                    NestMember.class.getDeclaredField("staticField"))
+                .getMethodName());
+    assertThat(
+        syntheticNestStaticFieldGetterMethodSubject,
+        notIf(isPresent(), parameters.canUseNestBasedAccesses()));
+
+    MethodSubject syntheticNestStaticFieldSetterMethodSubject =
+        nestMemberClassSubject.uniqueMethodWithOriginalName(
+            SyntheticItemsTestUtils.syntheticNestStaticFieldSetter(
+                    NestMember.class.getDeclaredField("staticField"))
+                .getMethodName());
+    assertThat(
+        syntheticNestStaticFieldSetterMethodSubject,
+        notIf(isPresent(), parameters.canUseNestBasedAccesses()));
+
+    MethodSubject syntheticNestStaticMethodAccessorMethodSubject =
+        nestMemberClassSubject.uniqueMethodWithOriginalName(
+            SyntheticItemsTestUtils.syntheticNestStaticMethodAccessor(
+                    NestMember.class.getDeclaredMethod("staticMethod"))
+                .getMethodName());
+    assertThat(
+        syntheticNestStaticMethodAccessorMethodSubject,
+        notIf(isPresent(), parameters.canUseNestBasedAccesses()));
+
+    // TODO(b/265729283): Should contain the nest bridge methods.
+    profileInspector
+        .assertContainsMethodRule(MethodReferenceUtils.mainMethod(Main.class))
+        .assertContainsNoOtherRules();
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      NestMember nestMember = new NestMember();
+      nestMember.instanceField = System.currentTimeMillis() > 0 ? 1 : 0;
+      System.out.println(nestMember.instanceField);
+      System.out.println(nestMember.instanceMethod());
+      NestMember.staticField = System.currentTimeMillis() > 0 ? 3 : 0;
+      System.out.println(NestMember.staticField);
+      System.out.println(NestMember.staticMethod());
+    }
+
+    static class NestMember {
+
+      /*private*/ int instanceField;
+      /*private*/ static int staticField;
+
+      @NeverInline
+      /*private*/ int instanceMethod() {
+        return System.currentTimeMillis() > 0 ? 2 : 0;
+      }
+
+      @NeverInline
+      /*private*/ static int staticMethod() {
+        return System.currentTimeMillis() > 0 ? 4 : 0;
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/OutlineOptimizationProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/OutlineOptimizationProfileRewritingTest.java
new file mode 100644
index 0000000..59e0636
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/OutlineOptimizationProfileRewritingTest.java
@@ -0,0 +1,102 @@
+// 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.profile.art.completeness;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.profile.art.model.ExternalArtProfile;
+import com.android.tools.r8.profile.art.utils.ArtProfileInspector;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+import com.android.tools.r8.utils.InternalOptions.InlinerOptions;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+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 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 OutlineOptimizationProfileRewritingTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addArtProfileForRewriting(getArtProfile())
+        .addOptionsModification(InlinerOptions::disableInlining)
+        .addOptionsModification(
+            options -> {
+              options.outline.threshold = 2;
+              options.outline.minSize = 2;
+            })
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspectResidualArtProfile(this::inspect)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("Hello, world!", "Hello, world!");
+  }
+
+  private ExternalArtProfile getArtProfile() {
+    return ExternalArtProfile.builder()
+        .addMethodRule(MethodReferenceUtils.mainMethod(Main.class))
+        .build();
+  }
+
+  private void inspect(ArtProfileInspector profileInspector, CodeInspector inspector) {
+    ClassSubject outlineClassSubject =
+        inspector.clazz(SyntheticItemsTestUtils.syntheticOutlineClass(Main.class, 0));
+    assertThat(outlineClassSubject, isPresent());
+
+    MethodSubject outlineMethodSubject = outlineClassSubject.uniqueMethod();
+    assertThat(outlineMethodSubject, isPresent());
+
+    // TODO(b/265729283): Should contain the outline class and method.
+    profileInspector
+        .assertContainsMethodRule(MethodReferenceUtils.mainMethod(Main.class))
+        .assertContainsNoOtherRules();
+  }
+
+  public static class Main {
+
+    public static void main(String[] args) {
+      greet1();
+      greet2();
+    }
+
+    static void greet1() {
+      hello();
+      world();
+    }
+
+    static void greet2() {
+      hello();
+      world();
+    }
+
+    public static void hello() {
+      System.out.print("Hello");
+    }
+
+    public static void world() {
+      System.out.println(", world!");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/SyntheticLambdaClassProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/SyntheticLambdaClassProfileRewritingTest.java
index e45e4a5..704fe6a 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/SyntheticLambdaClassProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/SyntheticLambdaClassProfileRewritingTest.java
@@ -10,13 +10,16 @@
 
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
 import com.android.tools.r8.profile.art.model.ExternalArtProfile;
-import com.android.tools.r8.profile.art.model.ExternalArtProfileMethodRule;
 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.MethodReferenceUtils;
+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 java.util.Collections;
+import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -26,12 +29,115 @@
 @RunWith(Parameterized.class)
 public class SyntheticLambdaClassProfileRewritingTest extends TestBase {
 
+  private enum ArtProfileInputOutput {
+    MAIN_METHOD,
+    LAMBDA_BRIDGE_METHOD;
+
+    public ExternalArtProfile getArtProfile() {
+      switch (this) {
+        case MAIN_METHOD:
+          // Profile containing Main.main(). Should be rewritten to include the two lambda classes
+          // and their constructors.
+          return ExternalArtProfile.builder()
+              .addMethodRule(MethodReferenceUtils.mainMethod(Main.class))
+              .build();
+        case LAMBDA_BRIDGE_METHOD:
+          // Profile containing the two lambda implementation methods, Main.lambda$main${0,1}.
+          // Should be rewritten to include the two lambda accessibility bridge methods.
+          return ExternalArtProfile.builder()
+              .addMethodRule(
+                  Reference.method(
+                      Reference.classFromClass(Main.class),
+                      "lambda$main$0",
+                      Collections.emptyList(),
+                      null))
+              .addMethodRule(
+                  Reference.method(
+                      Reference.classFromClass(Main.class),
+                      "lambda$main$1",
+                      Collections.emptyList(),
+                      null))
+              .build();
+        default:
+          throw new RuntimeException();
+      }
+    }
+
+    public void inspect(
+        ArtProfileInspector profileInspector, CodeInspector inspector, TestParameters parameters) {
+      ClassSubject mainClassSubject = inspector.clazz(Main.class);
+      assertThat(mainClassSubject, isPresent());
+
+      MethodSubject mainMethodSubject = mainClassSubject.mainMethod();
+      assertThat(mainMethodSubject, isPresent());
+
+      MethodSubject lambdaImplementationMethod0 =
+          mainClassSubject.uniqueMethodWithOriginalName("lambda$main$0");
+      assertThat(lambdaImplementationMethod0, isPresent());
+
+      MethodSubject lambdaImplementationMethod1 =
+          mainClassSubject.uniqueMethodWithOriginalName("lambda$main$1");
+      assertThat(lambdaImplementationMethod1, isPresent());
+
+      // Verify that two lambdas were synthesized when compiling to dex.
+      assertThat(
+          inspector.clazz(SyntheticItemsTestUtils.syntheticLambdaClass(Main.class, 0)),
+          onlyIf(parameters.isDexRuntime(), isPresent()));
+      assertThat(
+          inspector.clazz(SyntheticItemsTestUtils.syntheticLambdaClass(Main.class, 1)),
+          onlyIf(parameters.isDexRuntime(), isPresent()));
+
+      if (parameters.isCfRuntime()) {
+        switch (this) {
+          case MAIN_METHOD:
+            profileInspector
+                .assertContainsMethodRule(mainMethodSubject)
+                .assertContainsNoOtherRules();
+            break;
+          case LAMBDA_BRIDGE_METHOD:
+            profileInspector
+                .assertContainsMethodRules(lambdaImplementationMethod0, lambdaImplementationMethod1)
+                .assertContainsNoOtherRules();
+            break;
+          default:
+            throw new RuntimeException();
+        }
+      } else {
+        assert parameters.isDexRuntime();
+        switch (this) {
+          case MAIN_METHOD:
+            // TODO(b/265729283): Since Main.main() is in the art profile, so should the two
+            //  synthetic lambdas be along with their initializers. Since Main.lambda$main$*() is
+            //  not in the art profile, the interface method implementation does not need to be
+            //  included in the profile.
+            profileInspector
+                .assertContainsMethodRule(mainMethodSubject)
+                .assertContainsNoOtherRules();
+            break;
+          case LAMBDA_BRIDGE_METHOD:
+            // TODO(b/265729283): Since Main.lambda$main$*() is in the art profile, so should the
+            //  two accessibility bridges be.
+            profileInspector
+                .assertContainsMethodRules(lambdaImplementationMethod0, lambdaImplementationMethod1)
+                .assertContainsNoOtherRules();
+            break;
+          default:
+            throw new RuntimeException();
+        }
+      }
+    }
+  }
+
   @Parameter(0)
+  public ArtProfileInputOutput artProfileInputOutput;
+
+  @Parameter(1)
   public TestParameters parameters;
 
-  @Parameters(name = "{0}")
-  public static TestParametersCollection data() {
-    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  @Parameters(name = "{1}, {0}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        ArtProfileInputOutput.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
   }
 
   @Test
@@ -39,43 +145,20 @@
     testForR8(parameters.getBackend())
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
-        .addArtProfileForRewriting(getArtProfile())
+        .addKeepRules(
+            "-neverinline class " + Main.class.getTypeName() + " { void lambda$main$*(); }")
+        .addArtProfileForRewriting(artProfileInputOutput.getArtProfile())
+        .enableProguardTestOptions()
         .noHorizontalClassMergingOfSynthetics()
         .setMinApi(parameters.getApiLevel())
         .compile()
-        .inspect(this::inspect)
-        .inspectResidualArtProfile(this::inspectResidualArtProfile)
+        .inspectResidualArtProfile(
+            (profileInspector, inspector) ->
+                artProfileInputOutput.inspect(profileInspector, inspector, parameters))
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutputLines("Hello, world!");
   }
 
-  private ExternalArtProfile getArtProfile() {
-    return ExternalArtProfile.builder()
-        .addRule(
-            ExternalArtProfileMethodRule.builder()
-                .setMethodReference(MethodReferenceUtils.mainMethod(Main.class))
-                .build())
-        .build();
-  }
-
-  private void inspect(CodeInspector inspector) {
-    // Verify that two lambdas were synthesized when compiling to dex.
-    assertThat(
-        inspector.clazz(SyntheticItemsTestUtils.syntheticLambdaClass(Main.class, 0)),
-        onlyIf(parameters.isDexRuntime(), isPresent()));
-    assertThat(
-        inspector.clazz(SyntheticItemsTestUtils.syntheticLambdaClass(Main.class, 1)),
-        onlyIf(parameters.isDexRuntime(), isPresent()));
-  }
-
-  private void inspectResidualArtProfile(ArtProfileInspector profileInspector) {
-    // TODO(b/265729283): Since Main.main() is in the art profile, so should the two synthetic
-    //  lambdas be when compiling to dex.
-    profileInspector
-        .assertContainsMethodRule(MethodReferenceUtils.mainMethod(Main.class))
-        .assertContainsNoOtherRules();
-  }
-
   static class Main {
 
     public static void main(String[] args) {
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/VerticalClassMergingBridgeProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/VerticalClassMergingBridgeProfileRewritingTest.java
new file mode 100644
index 0000000..b1e46f9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/VerticalClassMergingBridgeProfileRewritingTest.java
@@ -0,0 +1,93 @@
+// 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.profile.art.completeness;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+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.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.FoundMethodSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class VerticalClassMergingBridgeProfileRewritingTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(Main.class)
+        .addArtProfileForRewriting(getArtProfile())
+        .addOptionsModification(InlinerOptions::disableInlining)
+        .addOptionsModification(
+            options -> options.callSiteOptimizationOptions().setEnableMethodStaticizing(false))
+        .addVerticallyMergedClassesInspector(
+            inspector -> inspector.assertMergedIntoSubtype(A.class))
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspectResidualArtProfile(this::inspect)
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithOutputLines("Hello, world!");
+  }
+
+  private ExternalArtProfile getArtProfile() throws Exception {
+    return ExternalArtProfile.builder()
+        .addMethodRule(Reference.methodFromMethod(A.class.getDeclaredMethod("m")))
+        .build();
+  }
+
+  private void inspect(ArtProfileInspector profileInspector, CodeInspector inspector) {
+    ClassSubject bClassSubject = inspector.clazz(B.class);
+    assertThat(bClassSubject, isPresent());
+
+    MethodSubject movedMethodSubject =
+        bClassSubject.uniqueMethodThatMatches(FoundMethodSubject::isPrivate);
+    assertThat(movedMethodSubject, isPresent());
+
+    MethodSubject syntheticBridgeMethodSubject =
+        bClassSubject.uniqueMethodThatMatches(FoundMethodSubject::isVirtual);
+    assertThat(syntheticBridgeMethodSubject, isPresent());
+
+    // TODO(b/265729283): Should also contain the synthetic bridge method above.
+    profileInspector.assertContainsMethodRule(movedMethodSubject).assertContainsNoOtherRules();
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      new B().m();
+    }
+  }
+
+  static class A {
+
+    public void m() {
+      System.out.println("Hello, world!");
+    }
+  }
+
+  static class B extends A {}
+}
diff --git a/src/test/java/com/android/tools/r8/profile/art/utils/ArtProfileInspector.java b/src/test/java/com/android/tools/r8/profile/art/utils/ArtProfileInspector.java
index 22b5370..507dd67 100644
--- a/src/test/java/com/android/tools/r8/profile/art/utils/ArtProfileInspector.java
+++ b/src/test/java/com/android/tools/r8/profile/art/utils/ArtProfileInspector.java
@@ -63,13 +63,20 @@
     return this;
   }
 
+  public ArtProfileInspector assertContainsMethodRules(MethodReference... methodReferences) {
+    for (MethodReference methodReference : methodReferences) {
+      assertContainsMethodRule(methodReference);
+    }
+    return this;
+  }
+
   public ArtProfileInspector assertContainsMethodRule(MethodSubject methodSubject) {
     return assertContainsMethodRule(methodSubject.getFinalReference());
   }
 
-  public ArtProfileInspector assertContainsMethodRules(MethodReference... methodReferences) {
-    for (MethodReference methodReference : methodReferences) {
-      assertContainsMethodRule(methodReference);
+  public ArtProfileInspector assertContainsMethodRules(MethodSubject... methodSubjects) {
+    for (MethodSubject methodSubject : methodSubjects) {
+      assertContainsMethodRule(methodSubject);
     }
     return this;
   }
diff --git a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
index 1b67cfc..19ed989 100644
--- a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
+++ b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
@@ -3,18 +3,28 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.synthesis;
 
+import static com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring.NEST_ACCESS_FIELD_GET_NAME_PREFIX;
+import static com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring.NEST_ACCESS_FIELD_PUT_NAME_PREFIX;
+import static com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring.NEST_ACCESS_METHOD_NAME_PREFIX;
+import static com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring.NEST_ACCESS_STATIC_GET_FIELD_NAME_PREFIX;
+import static com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring.NEST_ACCESS_STATIC_METHOD_NAME_PREFIX;
+import static com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring.NEST_ACCESS_STATIC_PUT_FIELD_NAME_PREFIX;
 import static com.android.tools.r8.synthesis.SyntheticNaming.EXTERNAL_SYNTHETIC_CLASS_SEPARATOR;
 import static org.hamcrest.CoreMatchers.containsString;
 
+import com.android.tools.r8.ir.desugar.invokespecial.InvokeSpecialToSelfDesugaring;
 import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringForTesting;
 import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.FieldReference;
 import com.android.tools.r8.references.MethodReference;
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.references.TypeReference;
 import com.android.tools.r8.synthesis.SyntheticNaming.Phase;
 import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
 import com.google.common.collect.ImmutableList;
+import java.lang.reflect.Field;
 import java.lang.reflect.Method;
+import java.util.Collections;
 import org.hamcrest.Matcher;
 
 public class SyntheticItemsTestUtils {
@@ -53,6 +63,15 @@
         originalMethod.getMethodDescriptor());
   }
 
+  public static MethodReference syntheticInvokeSpecialMethod(Method method) {
+    MethodReference originalMethod = Reference.methodFromMethod(method);
+    return Reference.method(
+        originalMethod.getHolderClass(),
+        InvokeSpecialToSelfDesugaring.INVOKE_SPECIAL_BRIDGE_PREFIX + method.getName(),
+        originalMethod.getFormalTypes(),
+        originalMethod.getReturnType());
+  }
+
   public static MethodReference syntheticBackportWithForwardingMethod(
       ClassReference clazz, int id, MethodReference method) {
     // For backports with forwarding the backported method is not static, so the original method
@@ -127,6 +146,68 @@
         originalMethod.getMethodDescriptor());
   }
 
+  public static MethodReference syntheticNestInstanceFieldGetter(Field field) {
+    FieldReference fieldReference = Reference.fieldFromField(field);
+    return Reference.method(
+        fieldReference.getHolderClass(),
+        NEST_ACCESS_FIELD_GET_NAME_PREFIX + field.getName(),
+        Collections.emptyList(),
+        fieldReference.getFieldType());
+  }
+
+  public static MethodReference syntheticNestInstanceFieldSetter(Field field) {
+    FieldReference fieldReference = Reference.fieldFromField(field);
+    return Reference.method(
+        fieldReference.getHolderClass(),
+        NEST_ACCESS_FIELD_PUT_NAME_PREFIX + field.getName(),
+        ImmutableList.of(fieldReference.getFieldType()),
+        null);
+  }
+
+  public static MethodReference syntheticNestInstanceMethodAccessor(Method method) {
+    MethodReference originalMethod = Reference.methodFromMethod(method);
+    return Reference.methodFromDescriptor(
+        originalMethod.getHolderClass(),
+        NEST_ACCESS_METHOD_NAME_PREFIX + method.getName(),
+        originalMethod.getMethodDescriptor());
+  }
+
+  public static MethodReference syntheticNestStaticFieldGetter(Field field) {
+    FieldReference fieldReference = Reference.fieldFromField(field);
+    return Reference.method(
+        fieldReference.getHolderClass(),
+        NEST_ACCESS_STATIC_GET_FIELD_NAME_PREFIX + field.getName(),
+        Collections.emptyList(),
+        fieldReference.getFieldType());
+  }
+
+  public static MethodReference syntheticNestStaticFieldSetter(Field field) {
+    FieldReference fieldReference = Reference.fieldFromField(field);
+    return Reference.method(
+        fieldReference.getHolderClass(),
+        NEST_ACCESS_STATIC_PUT_FIELD_NAME_PREFIX + field.getName(),
+        ImmutableList.of(fieldReference.getFieldType()),
+        null);
+  }
+
+  public static MethodReference syntheticNestStaticMethodAccessor(Method method) {
+    MethodReference originalMethod = Reference.methodFromMethod(method);
+    return Reference.methodFromDescriptor(
+        originalMethod.getHolderClass(),
+        NEST_ACCESS_STATIC_METHOD_NAME_PREFIX + method.getName(),
+        originalMethod.getMethodDescriptor());
+  }
+
+  public static ClassReference syntheticEnumUnboxingLocalUtilityClass(Class<?> clazz) {
+    return Reference.classFromTypeName(
+        clazz.getTypeName() + naming.ENUM_UNBOXING_LOCAL_UTILITY_CLASS.getDescriptor());
+  }
+
+  public static ClassReference syntheticEnumUnboxingSharedUtilityClass(Class<?> clazz) {
+    return Reference.classFromTypeName(
+        clazz.getTypeName() + naming.ENUM_UNBOXING_SHARED_UTILITY_CLASS.getDescriptor());
+  }
+
   public static boolean isEnumUnboxingSharedUtilityClass(ClassReference reference) {
     return SyntheticNaming.isSynthetic(reference, null, naming.ENUM_UNBOXING_SHARED_UTILITY_CLASS);
   }