Support for writing app using startup list in D8

Bug: b/242570743
Change-Id: I54cbca67e3adfa5f9bc6df2c47bc2d69b51ef778
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 03ec1dd..8be1456 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -11,7 +11,7 @@
 import com.android.tools.r8.dex.ApplicationWriter;
 import com.android.tools.r8.dex.Marker;
 import com.android.tools.r8.dex.Marker.Tool;
-import com.android.tools.r8.experimental.startup.StartupInstrumentation;
+import com.android.tools.r8.experimental.startup.instrumentation.StartupInstrumentation;
 import com.android.tools.r8.graph.AppInfo;
 import com.android.tools.r8.graph.AppServices;
 import com.android.tools.r8.graph.AppView;
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index 3cba5ca..4932691 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -24,6 +24,7 @@
 import com.android.tools.r8.dex.VirtualFile.ItemUseInfo;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.experimental.startup.StartupCompleteness;
+import com.android.tools.r8.experimental.startup.StartupOrder;
 import com.android.tools.r8.features.FeatureSplitConfiguration.DataResourceProvidersAndConsumer;
 import com.android.tools.r8.graph.AppServices;
 import com.android.tools.r8.graph.AppView;
@@ -217,7 +218,16 @@
         && options.enableMainDexListCheck) {
       distributor = new VirtualFile.MonoDexDistributor(this, classes, options);
     } else {
-      distributor = new VirtualFile.FillFilesDistributor(this, classes, options, executorService);
+      // Retrieve the startup order for writing the app. In R8, the startup order is created
+      // up-front to guide optimizations through-out the compilation. In D8, the startup
+      // order is only used for writing the app, so we create it here for the first time.
+      StartupOrder startupOrder =
+          appView.appInfo().hasClassHierarchy()
+              ? appView.appInfoWithClassHierarchy().getStartupOrder()
+              : StartupOrder.createInitialStartupOrder(options);
+      distributor =
+          new VirtualFile.FillFilesDistributor(
+              this, classes, options, executorService, startupOrder);
     }
 
     List<VirtualFile> virtualFiles = distributor.run();
diff --git a/src/main/java/com/android/tools/r8/dex/MixedSectionLayoutStrategy.java b/src/main/java/com/android/tools/r8/dex/MixedSectionLayoutStrategy.java
index 2381e6f..13a4a2c 100644
--- a/src/main/java/com/android/tools/r8/dex/MixedSectionLayoutStrategy.java
+++ b/src/main/java/com/android/tools/r8/dex/MixedSectionLayoutStrategy.java
@@ -22,15 +22,16 @@
 
   public static MixedSectionLayoutStrategy create(
       AppView<?> appView, MixedSectionOffsets mixedSectionOffsets, VirtualFile virtualFile) {
-    StartupOrder startupOrderForWriting =
-        appView.options().getStartupOptions().isStartupLayoutOptimizationsEnabled()
-                && virtualFile.getId() == 0
-                && appView.hasClassHierarchy()
-            ? appView
-                .appInfoWithClassHierarchy()
-                .getStartupOrder()
-                .toStartupOrderForWriting(appView)
-            : StartupOrder.empty();
+    StartupOrder startupOrderForWriting;
+    if (virtualFile.getStartupOrder().isEmpty()) {
+      startupOrderForWriting = StartupOrder.empty();
+    } else {
+      assert virtualFile.getId() == 0;
+      startupOrderForWriting =
+          appView.options().getStartupOptions().isStartupLayoutOptimizationsEnabled()
+              ? virtualFile.getStartupOrder().toStartupOrderForWriting(appView)
+              : StartupOrder.empty();
+    }
     MixedSectionLayoutStrategy mixedSectionLayoutStrategy =
         startupOrderForWriting.isEmpty()
             ? new DefaultMixedSectionLayoutStrategy(appView, mixedSectionOffsets)
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
index 1fa09a7..785bfd2 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -4,7 +4,6 @@
 package com.android.tools.r8.dex;
 
 import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
-import static com.google.common.base.Predicates.alwaysFalse;
 
 import com.android.tools.r8.FeatureSplit;
 import com.android.tools.r8.debuginfo.DebugRepresentation;
@@ -74,33 +73,35 @@
   public final VirtualFileIndexedItemCollection indexedItems;
   private final IndexedItemTransaction transaction;
   private final FeatureSplit featureSplit;
+  private final StartupOrder startupOrder;
 
   private final DexString primaryClassDescriptor;
   private DebugRepresentation debugRepresentation;
 
   VirtualFile(int id, AppView<?> appView) {
-    this(id, appView, null, null);
+    this(id, appView, null, null, StartupOrder.empty());
   }
 
   VirtualFile(
       int id,
       AppView<?> appView,
       FeatureSplit featureSplit) {
-    this(id, appView, null, featureSplit);
+    this(id, appView, null, featureSplit, StartupOrder.empty());
   }
 
   private VirtualFile(
       int id,
       AppView<?> appView,
       DexProgramClass primaryClass) {
-    this(id, appView, primaryClass, null);
+    this(id, appView, primaryClass, null, StartupOrder.empty());
   }
 
   private VirtualFile(
       int id,
       AppView<?> appView,
       DexProgramClass primaryClass,
-      FeatureSplit featureSplit) {
+      FeatureSplit featureSplit,
+      StartupOrder startupOrder) {
     this.id = id;
     this.indexedItems = new VirtualFileIndexedItemCollection(appView);
     this.transaction = new IndexedItemTransaction(indexedItems, appView);
@@ -109,6 +110,7 @@
             ? null
             : appView.getNamingLens().lookupClassDescriptor(primaryClass.type);
     this.featureSplit = featureSplit;
+    this.startupOrder = startupOrder;
   }
 
   public int getId() {
@@ -128,6 +130,10 @@
     return featureSplit;
   }
 
+  public StartupOrder getStartupOrder() {
+    return startupOrder;
+  }
+
   public String getPrimaryClassDescriptor() {
     return primaryClassDescriptor == null ? null : primaryClassDescriptor.toString();
   }
@@ -352,13 +358,17 @@
     protected final InternalOptions options;
 
     DistributorBase(
-        ApplicationWriter writer, Collection<DexProgramClass> classes, InternalOptions options) {
+        ApplicationWriter writer,
+        Collection<DexProgramClass> classes,
+        InternalOptions options,
+        StartupOrder startupOrder) {
       super(writer);
       this.options = options;
       this.classes = SetUtils.newIdentityHashSet(classes);
 
-      // Create the primary dex file. The distribution will add more if needed.
-      mainDexFile = new VirtualFile(0, appView);
+      // Create the primary dex file. The distribution will add more if needed. We use the startup
+      // order (if any) to guide the layout of the primary dex file.
+      mainDexFile = new VirtualFile(0, appView, null, null, startupOrder);
       assert virtualFiles.isEmpty();
       virtualFiles.add(mainDexFile);
       addMarkers(mainDexFile);
@@ -429,7 +439,7 @@
     }
 
     protected void addFeatureSplitFiles(
-        Map<FeatureSplit, Set<DexProgramClass>> featureSplitClasses) {
+        Map<FeatureSplit, Set<DexProgramClass>> featureSplitClasses, StartupOrder startupOrder) {
       if (featureSplitClasses.isEmpty()) {
         return;
       }
@@ -452,6 +462,7 @@
                 appView,
                 featureSplitSetEntry.getValue(),
                 originalNames,
+                startupOrder,
                 nextFileId)
             .run();
       }
@@ -459,15 +470,19 @@
   }
 
   public static class FillFilesDistributor extends DistributorBase {
+
     private final ExecutorService executorService;
+    private final StartupOrder startupOrder;
 
     FillFilesDistributor(
         ApplicationWriter writer,
         Collection<DexProgramClass> classes,
         InternalOptions options,
-        ExecutorService executorService) {
-      super(writer, classes, options);
+        ExecutorService executorService,
+        StartupOrder startupOrder) {
+      super(writer, classes, options, startupOrder);
       this.executorService = executorService;
+      this.startupOrder = startupOrder;
     }
 
     @Override
@@ -508,10 +523,16 @@
             .distribute();
       } else {
         new PackageSplitPopulator(
-                virtualFiles, filesForDistribution, appView, classes, originalNames, nextFileId)
+                virtualFiles,
+                filesForDistribution,
+                appView,
+                classes,
+                originalNames,
+                startupOrder,
+                nextFileId)
             .run();
       }
-      addFeatureSplitFiles(featureSplitClasses);
+      addFeatureSplitFiles(featureSplitClasses, startupOrder);
 
       assert totalClassNumber == virtualFiles.stream().mapToInt(dex -> dex.classes().size()).sum();
       return virtualFiles;
@@ -521,7 +542,7 @@
   public static class MonoDexDistributor extends DistributorBase {
     MonoDexDistributor(
         ApplicationWriter writer, Collection<DexProgramClass> classes, InternalOptions options) {
-      super(writer, classes, options);
+      super(writer, classes, options, StartupOrder.empty());
     }
 
     @Override
@@ -537,7 +558,7 @@
       if (options.featureSplitConfiguration != null) {
         if (!featureSplitClasses.isEmpty()) {
           // TODO(141334414): Figure out if we allow multidex in features even when mono-dexing
-          addFeatureSplitFiles(featureSplitClasses);
+          addFeatureSplitFiles(featureSplitClasses, StartupOrder.empty());
         }
       }
       return virtualFiles;
@@ -1242,12 +1263,13 @@
 
       public static PackageSplitClassPartioning create(
           Collection<DexProgramClass> classes,
-          AppView<?> appView,
-          Map<DexProgramClass, String> originalNames) {
+          Map<DexProgramClass, String> originalNames,
+          StartupOrder startupOrder,
+          SyntheticItems syntheticItems) {
         return create(
             classes,
             getClassesByPackageComparator(originalNames),
-            getStartupClassPredicate(appView));
+            getStartupClassPredicate(startupOrder, syntheticItems));
       }
 
       private static PackageSplitClassPartioning create(
@@ -1297,12 +1319,8 @@
         };
       }
 
-      private static Predicate<DexProgramClass> getStartupClassPredicate(AppView<?> appView) {
-        if (!appView.hasClassHierarchy()) {
-          return alwaysFalse();
-        }
-        StartupOrder startupOrder = appView.appInfoWithClassHierarchy().getStartupOrder();
-        SyntheticItems syntheticItems = appView.getSyntheticItems();
+      private static Predicate<DexProgramClass> getStartupClassPredicate(
+          StartupOrder startupOrder, SyntheticItems syntheticItems) {
         return clazz -> startupOrder.contains(clazz.getType(), syntheticItems);
       }
 
@@ -1339,8 +1357,11 @@
         AppView<?> appView,
         Collection<DexProgramClass> classes,
         Map<DexProgramClass, String> originalNames,
+        StartupOrder startupOrder,
         IntBox nextFileId) {
-      this.classPartioning = PackageSplitClassPartioning.create(classes, appView, originalNames);
+      this.classPartioning =
+          PackageSplitClassPartioning.create(
+              classes, originalNames, startupOrder, appView.getSyntheticItems());
       this.originalNames = originalNames;
       this.dexItemFactory = appView.dexItemFactory();
       this.options = appView.options();
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupCompleteness.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupCompleteness.java
index 8d75d7c..ca0b88f 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupCompleteness.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupCompleteness.java
@@ -31,7 +31,7 @@
     this.startupOrder =
         appView.hasClassHierarchy()
             ? appView.appInfoWithClassHierarchy().getStartupOrder()
-            : StartupOrder.empty();
+            : StartupOrder.createInitialStartupOrder(appView.options());
   }
 
   /**
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupOptions.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupOptions.java
index f1b771c..05fd544 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupOptions.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupOptions.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.experimental.startup;
 
-import static com.android.tools.r8.utils.SystemPropertyUtils.getSystemPropertyForDevelopment;
 import static com.android.tools.r8.utils.SystemPropertyUtils.parseSystemPropertyForDevelopmentOrDefault;
 
 public class StartupOptions {
@@ -39,76 +38,14 @@
           "com.android.tools.r8.startup.completenesscheck", false);
 
   /**
-   * When enabled, each method will be instrumented to notify the startup InstrumentationServer that
-   * it has been executed.
-   *
-   * <p>This will also inject the startup runtime library (i.e., the InstrumentationServer) into the
-   * app.
-   */
-  private boolean enableStartupInstrumentation =
-      parseSystemPropertyForDevelopmentOrDefault("com.android.tools.r8.startup.instrument", false);
-
-  /**
    * When enabled, the layout of the primary dex file will be generated using the startup list,
    * using {@link com.android.tools.r8.dex.StartupMixedSectionLayoutStrategy}.
    */
   private boolean enableStartupLayoutOptimizations =
       parseSystemPropertyForDevelopmentOrDefault("com.android.tools.r8.startup.layout", true);
 
-  /**
-   * Specifies the synthetic context of the startup runtime library. When this is set, the startup
-   * runtime library will only be injected into the app when the synthetic context is in the
-   * program. This can be used to avoid that the startup runtime library is injected multiple times
-   * in presence of separate compilation.
-   *
-   * <p>Example synthetic context: "app.tivi.home.MainActivity".
-   *
-   * <p>Note that this is only meaningful when {@link #enableStartupInstrumentation} is set to true.
-   */
-  private String startupInstrumentationServerSyntheticContext =
-      getSystemPropertyForDevelopment(
-          "com.android.tools.r8.startup.instrumentationserversyntheticcontext");
-
-  /**
-   * Specifies the logcat tag that should be used by the InstrumentationServer when logging events.
-   *
-   * <p>When a logcat tag is not specified, the InstrumentationServer will not print events to
-   * logcat. Instead, the startup events must be obtained by requesting the InstrumentationServer to
-   * write the events to a file.
-   */
-  private String startupInstrumentationTag =
-      getSystemPropertyForDevelopment("com.android.tools.r8.startup.instrumentationtag");
-
   private StartupConfiguration startupConfiguration;
 
-  public boolean hasStartupInstrumentationServerSyntheticContext() {
-    return startupInstrumentationServerSyntheticContext != null;
-  }
-
-  public String getStartupInstrumentationServerSyntheticContext() {
-    return startupInstrumentationServerSyntheticContext;
-  }
-
-  public StartupOptions setStartupInstrumentationServerSyntheticContext(
-      String startupInstrumentationServerSyntheticContext) {
-    this.startupInstrumentationServerSyntheticContext =
-        startupInstrumentationServerSyntheticContext;
-    return this;
-  }
-
-  public boolean hasStartupInstrumentationTag() {
-    return startupInstrumentationTag != null;
-  }
-
-  public String getStartupInstrumentationTag() {
-    return startupInstrumentationTag;
-  }
-
-  public StartupOptions setStartupInstrumentationTag(String startupInstrumentationTag) {
-    this.startupInstrumentationTag = startupInstrumentationTag;
-    return this;
-  }
-
   public boolean isMinimalStartupDexEnabled() {
     return enableMinimalStartupDex;
   }
@@ -128,15 +65,6 @@
     return this;
   }
 
-  public boolean isStartupInstrumentationEnabled() {
-    return enableStartupInstrumentation;
-  }
-
-  public StartupOptions setEnableStartupInstrumentation() {
-    enableStartupInstrumentation = true;
-    return this;
-  }
-
   public boolean isStartupLayoutOptimizationsEnabled() {
     return enableStartupLayoutOptimizations;
   }
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupInstrumentation.java b/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentation.java
similarity index 88%
rename from src/main/java/com/android/tools/r8/experimental/startup/StartupInstrumentation.java
rename to src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentation.java
index 2dfa4f2..7e8db58 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupInstrumentation.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentation.java
@@ -2,7 +2,7 @@
 // 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.experimental.startup;
+package com.android.tools.r8.experimental.startup.instrumentation;
 
 import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
 import static com.android.tools.r8.utils.PredicateUtils.not;
@@ -54,21 +54,21 @@
   private final IRConverter converter;
   private final DexItemFactory dexItemFactory;
   private final InternalOptions options;
-  private final StartupReferences references;
-  private final StartupOptions startupOptions;
+  private final StartupInstrumentationReferences references;
+  private final StartupInstrumentationOptions startupInstrumentationOptions;
 
   private StartupInstrumentation(AppView<AppInfo> appView) {
     this.appView = appView;
     this.converter = new IRConverter(appView, Timing.empty());
     this.dexItemFactory = appView.dexItemFactory();
     this.options = appView.options();
-    this.references = new StartupReferences(dexItemFactory);
-    this.startupOptions = options.getStartupOptions();
+    this.references = new StartupInstrumentationReferences(dexItemFactory);
+    this.startupInstrumentationOptions = options.getStartupInstrumentationOptions();
   }
 
   public static void run(AppView<AppInfo> appView, ExecutorService executorService)
       throws ExecutionException {
-    if (appView.options().getStartupOptions().isStartupInstrumentationEnabled()) {
+    if (appView.options().getStartupInstrumentationOptions().isStartupInstrumentationEnabled()) {
       StartupInstrumentation startupInstrumentation = new StartupInstrumentation(appView);
       startupInstrumentation.instrumentAllClasses(executorService);
       startupInstrumentation.injectStartupRuntimeLibrary(executorService);
@@ -89,11 +89,11 @@
     // If the startup options has a synthetic context for the startup instrumentation server, then
     // only inject the runtime library if the synthetic context exists in program to avoid injecting
     // the runtime library multiple times when there is separate compilation.
-    if (startupOptions.hasStartupInstrumentationServerSyntheticContext()) {
+    if (startupInstrumentationOptions.hasStartupInstrumentationServerSyntheticContext()) {
       DexType syntheticContext =
           dexItemFactory.createType(
               DescriptorUtils.javaTypeToDescriptor(
-                  startupOptions.getStartupInstrumentationServerSyntheticContext()));
+                  startupInstrumentationOptions.getStartupInstrumentationServerSyntheticContext()));
       if (asProgramClassOrNull(appView.definitionFor(syntheticContext)) == null) {
         return;
       }
@@ -113,7 +113,7 @@
   private List<DexProgramClass> createStartupRuntimeLibraryClasses() {
     DexProgramClass instrumentationServerImplClass =
         InstrumentationServerImplFactory.createClass(dexItemFactory);
-    if (startupOptions.hasStartupInstrumentationTag()) {
+    if (startupInstrumentationOptions.hasStartupInstrumentationTag()) {
       instrumentationServerImplClass
           .lookupUniqueStaticFieldWithName(dexItemFactory.createString("writeToLogcat"))
           .setStaticValue(DexValueBoolean.create(true));
@@ -121,7 +121,8 @@
           .lookupUniqueStaticFieldWithName(dexItemFactory.createString("logcatTag"))
           .setStaticValue(
               new DexValueString(
-                  dexItemFactory.createString(startupOptions.getStartupInstrumentationTag())));
+                  dexItemFactory.createString(
+                      startupInstrumentationOptions.getStartupInstrumentationTag())));
     }
 
     return ImmutableList.of(
@@ -174,8 +175,11 @@
 
     // Insert invoke to record that the enclosing class is a startup class.
     SyntheticItems syntheticItems = appView.getSyntheticItems();
-    boolean isSyntheticClass = syntheticItems.isSyntheticClass(method.getHolder());
-    if (method.getDefinition().isClassInitializer() && !isSyntheticClass) {
+    boolean generalizeSyntheticToSyntheticOfSyntheticContexts =
+        startupInstrumentationOptions.isGeneralizationOfSyntheticsToSyntheticContextEnabled()
+            && syntheticItems.isSyntheticClass(method.getHolder());
+    if (method.getDefinition().isClassInitializer()
+        && !generalizeSyntheticToSyntheticOfSyntheticContexts) {
       DexMethod methodToInvoke = references.addNonSyntheticMethod;
       DexType classToPrint = method.getHolderType();
       Value descriptorValue =
@@ -193,7 +197,7 @@
     if (!skipMethodLogging) {
       DexMethod methodToInvoke;
       DexReference referenceToPrint;
-      if (isSyntheticClass) {
+      if (generalizeSyntheticToSyntheticOfSyntheticContexts) {
         Collection<DexType> synthesizingContexts =
             syntheticItems.getSynthesizingContextTypes(method.getHolderType());
         assert synthesizingContexts.size() == 1;
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentationOptions.java b/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentationOptions.java
new file mode 100644
index 0000000..bfb3f87
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentationOptions.java
@@ -0,0 +1,104 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.experimental.startup.instrumentation;
+
+import static com.android.tools.r8.utils.SystemPropertyUtils.getSystemPropertyForDevelopment;
+import static com.android.tools.r8.utils.SystemPropertyUtils.parseSystemPropertyForDevelopmentOrDefault;
+
+public class StartupInstrumentationOptions {
+
+  /**
+   * When enabled, the instrumentation for synthetics will print the name of the synthetic context
+   * instead of printing the name of the synthetic itself.
+   */
+  private boolean enableGeneralizationOfSyntheticsToSyntheticContext =
+      parseSystemPropertyForDevelopmentOrDefault(
+          "com.android.tools.r8.startup.instrumentation.generalizesynthetics", false);
+
+  /**
+   * When enabled, each method will be instrumented to notify the startup InstrumentationServer that
+   * it has been executed.
+   *
+   * <p>This will also inject the startup runtime library (i.e., the InstrumentationServer) into the
+   * app.
+   */
+  private boolean enableStartupInstrumentation =
+      parseSystemPropertyForDevelopmentOrDefault(
+          "com.android.tools.r8.startup.instrumentation.instrument", false);
+
+  /**
+   * Specifies the synthetic context of the startup runtime library. When this is set, the startup
+   * runtime library will only be injected into the app when the synthetic context is in the
+   * program. This can be used to avoid that the startup runtime library is injected multiple times
+   * in presence of separate compilation.
+   *
+   * <p>Example synthetic context: "app.tivi.home.MainActivity".
+   *
+   * <p>Note that this is only meaningful when {@link #enableStartupInstrumentation} is set to true.
+   */
+  private String startupInstrumentationServerSyntheticContext =
+      getSystemPropertyForDevelopment(
+          "com.android.tools.r8.startup.instrumentation.instrumentationserversyntheticcontext");
+
+  /**
+   * Specifies the logcat tag that should be used by the InstrumentationServer when logging events.
+   *
+   * <p>When a logcat tag is not specified, the InstrumentationServer will not print events to
+   * logcat. Instead, the startup events must be obtained by requesting the InstrumentationServer to
+   * write the events to a file.
+   */
+  private String startupInstrumentationTag =
+      getSystemPropertyForDevelopment(
+          "com.android.tools.r8.startup.instrumentation.instrumentationtag");
+
+  public boolean hasStartupInstrumentationServerSyntheticContext() {
+    return startupInstrumentationServerSyntheticContext != null;
+  }
+
+  public String getStartupInstrumentationServerSyntheticContext() {
+    return startupInstrumentationServerSyntheticContext;
+  }
+
+  public StartupInstrumentationOptions setStartupInstrumentationServerSyntheticContext(
+      String startupInstrumentationServerSyntheticContext) {
+    this.startupInstrumentationServerSyntheticContext =
+        startupInstrumentationServerSyntheticContext;
+    return this;
+  }
+
+  public boolean hasStartupInstrumentationTag() {
+    return startupInstrumentationTag != null;
+  }
+
+  public String getStartupInstrumentationTag() {
+    return startupInstrumentationTag;
+  }
+
+  public StartupInstrumentationOptions setStartupInstrumentationTag(
+      String startupInstrumentationTag) {
+    this.startupInstrumentationTag = startupInstrumentationTag;
+    return this;
+  }
+
+  public boolean isGeneralizationOfSyntheticsToSyntheticContextEnabled() {
+    return enableGeneralizationOfSyntheticsToSyntheticContext;
+  }
+
+  public StartupInstrumentationOptions setEnableGeneralizationOfSyntheticsToSyntheticContext(
+      boolean enableGeneralizationOfSyntheticsToSyntheticContext) {
+    this.enableGeneralizationOfSyntheticsToSyntheticContext =
+        enableGeneralizationOfSyntheticsToSyntheticContext;
+    return this;
+  }
+
+  public boolean isStartupInstrumentationEnabled() {
+    return enableStartupInstrumentation;
+  }
+
+  public StartupInstrumentationOptions setEnableStartupInstrumentation() {
+    enableStartupInstrumentation = true;
+    return this;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupReferences.java b/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentationReferences.java
similarity index 87%
rename from src/main/java/com/android/tools/r8/experimental/startup/StartupReferences.java
rename to src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentationReferences.java
index a041f1e..fbfa5e2 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupReferences.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentationReferences.java
@@ -2,20 +2,20 @@
 // 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.experimental.startup;
+package com.android.tools.r8.experimental.startup.instrumentation;
 
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
 
-public class StartupReferences {
+class StartupInstrumentationReferences {
 
   final DexType instrumentationServerType;
   final DexType instrumentationServerImplType;
   final DexMethod addNonSyntheticMethod;
   final DexMethod addSyntheticMethod;
 
-  StartupReferences(DexItemFactory dexItemFactory) {
+  StartupInstrumentationReferences(DexItemFactory dexItemFactory) {
     instrumentationServerType =
         dexItemFactory.createType("Lcom/android/tools/r8/startup/InstrumentationServer;");
     instrumentationServerImplType =
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
index 6065527..0d767c6 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -676,7 +676,11 @@
     // actually equal.
     GraphLens graphLens = appView.graphLens();
     boolean includeContext =
-        intermediate || appView.options().getStartupOptions().isStartupInstrumentationEnabled();
+        intermediate
+            || appView
+                .options()
+                .getStartupInstrumentationOptions()
+                .isStartupInstrumentationEnabled();
     List<T> sortedPotentialMembers =
         ListUtils.sort(
             potentialEquivalence,
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 065369b..d784613 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -39,6 +39,7 @@
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
 import com.android.tools.r8.experimental.startup.StartupOptions;
+import com.android.tools.r8.experimental.startup.instrumentation.StartupInstrumentationOptions;
 import com.android.tools.r8.features.FeatureSplitConfiguration;
 import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
 import com.android.tools.r8.graph.AppView;
@@ -816,6 +817,8 @@
   private final ApiModelTestingOptions apiModelTestingOptions = new ApiModelTestingOptions();
   private final DesugarSpecificOptions desugarSpecificOptions = new DesugarSpecificOptions();
   private final StartupOptions startupOptions = new StartupOptions();
+  private final StartupInstrumentationOptions startupInstrumentationOptions =
+      new StartupInstrumentationOptions();
   public final TestingOptions testing = new TestingOptions();
 
   public List<ProguardConfigurationRule> mainDexKeepRules = ImmutableList.of();
@@ -879,6 +882,10 @@
     return startupOptions;
   }
 
+  public StartupInstrumentationOptions getStartupInstrumentationOptions() {
+    return startupInstrumentationOptions;
+  }
+
   public TestingOptions getTestingOptions() {
     return testing;
   }
diff --git a/src/test/java/com/android/tools/r8/R8TestCompileResult.java b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
index f343c0f..be01cef 100644
--- a/src/test/java/com/android/tools/r8/R8TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
@@ -112,18 +112,10 @@
   }
 
   @SafeVarargs
+  @Override
   public final <E extends Throwable> R8TestCompileResult inspectMultiDex(
       ThrowingConsumer<CodeInspector, E>... consumers) throws IOException, E {
-    Path out = state.getNewTempFolder();
-    getApp().writeToDirectory(out, OutputMode.DexIndexed);
-    consumers[0].accept(new CodeInspector(out.resolve("classes.dex"), getProguardMap()));
-    for (int i = 1; i < consumers.length; i++) {
-      Path dex = out.resolve("classes" + (i + 1) + ".dex");
-      CodeInspector inspector =
-          dex.toFile().exists() ? new CodeInspector(dex, getProguardMap()) : CodeInspector.empty();
-      consumers[i].accept(inspector);
-    }
-    return self();
+    return inspectMultiDex(writeProguardMap(), consumers);
   }
 
   public final <E extends Throwable> R8TestCompileResult inspectGraph(
diff --git a/src/test/java/com/android/tools/r8/TestCompileResult.java b/src/test/java/com/android/tools/r8/TestCompileResult.java
index 4d557ea..b15e669 100644
--- a/src/test/java/com/android/tools/r8/TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/TestCompileResult.java
@@ -444,6 +444,27 @@
     return self();
   }
 
+  @SuppressWarnings("unchecked")
+  public <E extends Throwable> CR inspectMultiDex(ThrowingConsumer<CodeInspector, E>... consumers)
+      throws IOException, E {
+    return inspectMultiDex(null, consumers);
+  }
+
+  @SafeVarargs
+  public final <E extends Throwable> CR inspectMultiDex(
+      Path mappingFile, ThrowingConsumer<CodeInspector, E>... consumers) throws IOException, E {
+    Path out = state.getNewTempFolder();
+    getApp().writeToDirectory(out, OutputMode.DexIndexed);
+    consumers[0].accept(new CodeInspector(out.resolve("classes.dex"), mappingFile));
+    for (int i = 1; i < consumers.length; i++) {
+      Path dex = out.resolve("classes" + (i + 1) + ".dex");
+      CodeInspector inspector =
+          dex.toFile().exists() ? new CodeInspector(dex, mappingFile) : CodeInspector.empty();
+      consumers[i].accept(inspector);
+    }
+    return self();
+  }
+
   public <E extends Throwable> CR inspectWithOptions(
       ThrowingConsumer<CodeInspector, E> consumer, Consumer<InternalOptions> debugOptionsConsumer)
       throws IOException, E {
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeV1719Test.java b/src/test/java/com/android/tools/r8/internal/YouTubeV1719Test.java
index 0b404a6..c460799 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeV1719Test.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeV1719Test.java
@@ -90,7 +90,7 @@
             .addOptionsModification(
                 options ->
                     options
-                        .getStartupOptions()
+                        .getStartupInstrumentationOptions()
                         .setEnableStartupInstrumentation()
                         .setStartupInstrumentationTag("r8"))
             .enableCoreLibraryDesugaring(
diff --git a/src/test/java/com/android/tools/r8/internal/startup/ChromeStartupTest.java b/src/test/java/com/android/tools/r8/internal/startup/ChromeStartupTest.java
index 390e702..77f33b2 100644
--- a/src/test/java/com/android/tools/r8/internal/startup/ChromeStartupTest.java
+++ b/src/test/java/com/android/tools/r8/internal/startup/ChromeStartupTest.java
@@ -78,7 +78,7 @@
         .addOptionsModification(
             options ->
                 options
-                    .getStartupOptions()
+                    .getStartupInstrumentationOptions()
                     .setEnableStartupInstrumentation()
                     .setStartupInstrumentationTag("r8"))
         .setMinApi(apiLevel)
@@ -98,7 +98,7 @@
         .addOptionsModification(
             options ->
                 options
-                    .getStartupOptions()
+                    .getStartupInstrumentationOptions()
                     .setEnableStartupInstrumentation()
                     .setStartupInstrumentationTag("r8"))
         .setMinApi(apiLevel)
diff --git a/src/test/java/com/android/tools/r8/startup/MinimalStartupDexTest.java b/src/test/java/com/android/tools/r8/startup/MinimalStartupDexTest.java
index a433265..35df0b6 100644
--- a/src/test/java/com/android/tools/r8/startup/MinimalStartupDexTest.java
+++ b/src/test/java/com/android/tools/r8/startup/MinimalStartupDexTest.java
@@ -15,7 +15,6 @@
 import com.android.tools.r8.experimental.startup.StartupItem;
 import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.references.MethodReference;
-import com.android.tools.r8.startup.StartupSyntheticPlacementTest.Main;
 import com.android.tools.r8.startup.utils.StartupTestingUtils;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -49,7 +48,8 @@
     List<StartupItem<ClassReference, MethodReference, ?>> startupList = new ArrayList<>();
     testForD8(parameters.getBackend())
         .addInnerClasses(getClass())
-        .apply(StartupTestingUtils.enableStartupInstrumentationUsingLogcat(parameters))
+        .apply(
+            StartupTestingUtils.enableStartupInstrumentationForOriginalAppUsingLogcat(parameters))
         .release()
         .setMinApi(parameters.getApiLevel())
         .compile()
diff --git a/src/test/java/com/android/tools/r8/startup/StartupInstrumentationTest.java b/src/test/java/com/android/tools/r8/startup/StartupInstrumentationTest.java
index 07f4487..bc0292f 100644
--- a/src/test/java/com/android/tools/r8/startup/StartupInstrumentationTest.java
+++ b/src/test/java/com/android/tools/r8/startup/StartupInstrumentationTest.java
@@ -53,8 +53,8 @@
         .addInnerClasses(getClass())
         .applyIf(
             logcat,
-            StartupTestingUtils.enableStartupInstrumentationUsingLogcat(parameters),
-            StartupTestingUtils.enableStartupInstrumentationUsingFile(parameters))
+            StartupTestingUtils.enableStartupInstrumentationForOriginalAppUsingLogcat(parameters),
+            StartupTestingUtils.enableStartupInstrumentationForOriginalAppUsingFile(parameters))
         .release()
         .setMinApi(parameters.getApiLevel())
         .compile()
diff --git a/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java b/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java
index 0cce865..e89bee0 100644
--- a/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java
+++ b/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java
@@ -11,12 +11,16 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import com.android.tools.r8.D8TestCompileResult;
+import com.android.tools.r8.R8TestCompileResult;
 import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.experimental.startup.StartupClass;
 import com.android.tools.r8.experimental.startup.StartupItem;
 import com.android.tools.r8.experimental.startup.StartupMethod;
 import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.ir.desugar.LambdaClass;
 import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.references.MethodReference;
 import com.android.tools.r8.references.Reference;
@@ -26,8 +30,11 @@
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.BooleanUtils;
 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.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
@@ -64,35 +71,89 @@
   }
 
   @Test
-  public void test() throws Exception {
+  public void testLayoutUsingD8() throws Exception {
+    // First build the app using R8.
+    R8TestCompileResult r8CompileResult =
+        testForR8(parameters.getBackend())
+            .addInnerClasses(getClass())
+            .addKeepMainRule(Main.class)
+            .addKeepClassAndMembersRules(A.class, B.class, C.class)
+            .addDontOptimize()
+            .setMinApi(parameters.getApiLevel())
+            .compile();
+
+    // Verify that the build works.
+    r8CompileResult
+        .run(parameters.getRuntime(), Main.class, Boolean.toString(useLambda))
+        .assertSuccessWithOutputLines(getExpectedOutput());
+
+    Path optimizedApp = r8CompileResult.writeToZip();
+
+    // Then instrument the app to generate a startup list for the minified app.
     List<StartupItem<ClassReference, MethodReference, ?>> startupList = new ArrayList<>();
     testForD8(parameters.getBackend())
-        .addInnerClasses(getClass())
-        .apply(StartupTestingUtils.enableStartupInstrumentationUsingLogcat(parameters))
+        .addProgramFiles(optimizedApp)
+        .apply(
+            StartupTestingUtils.enableStartupInstrumentationForOptimizedAppUsingLogcat(parameters))
         .release()
         .setMinApi(parameters.getApiLevel())
         .compile()
         .addRunClasspathFiles(StartupTestingUtils.getAndroidUtilLog(temp))
         .run(parameters.getRuntime(), Main.class, Boolean.toString(useLambda))
         .apply(StartupTestingUtils.removeStartupListFromStdout(startupList::add))
-        .assertSuccessWithOutputLines(getExpectedOutput());
-    assertEquals(getExpectedStartupList(), startupList);
+        .assertSuccessWithOutputLines(getExpectedOutput())
+        .apply(
+            runResult ->
+                assertEquals(
+                    getExpectedStartupList(r8CompileResult.inspector(), false), startupList));
 
+    // Finally rebuild the minified app using D8 and the startup list.
+    testForD8(parameters.getBackend())
+        .addProgramFiles(optimizedApp)
+        .apply(
+            testBuilder ->
+                configureStartupOptions(testBuilder, r8CompileResult.inspector(), startupList))
+        .release()
+        .setMinApi(parameters.getApiLevel())
+        .compile()
+        .inspectMultiDex(
+            r8CompileResult.writeProguardMap(), this::inspectPrimaryDex, this::inspectSecondaryDex)
+        .run(parameters.getRuntime(), Main.class, Boolean.toString(useLambda))
+        .assertSuccessWithOutputLines(getExpectedOutput());
+  }
+
+  @Test
+  public void testLayoutUsingR8() throws Exception {
+    // First generate a startup list for the original app.
+    List<StartupItem<ClassReference, MethodReference, ?>> startupList = new ArrayList<>();
+    D8TestCompileResult instrumentationCompileResult =
+        testForD8(parameters.getBackend())
+            .addInnerClasses(getClass())
+            .apply(
+                StartupTestingUtils.enableStartupInstrumentationForOriginalAppUsingLogcat(
+                    parameters))
+            .release()
+            .setMinApi(parameters.getApiLevel())
+            .compile();
+
+    instrumentationCompileResult
+        .addRunClasspathFiles(StartupTestingUtils.getAndroidUtilLog(temp))
+        .run(parameters.getRuntime(), Main.class, Boolean.toString(useLambda))
+        .apply(StartupTestingUtils.removeStartupListFromStdout(startupList::add))
+        .assertSuccessWithOutputLines(getExpectedOutput())
+        .apply(
+            runResult ->
+                assertEquals(getExpectedStartupList(runResult.inspector(), true), startupList));
+
+    // Then build the app using the startup list that is based on original names.
     testForR8(parameters.getBackend())
         .addInnerClasses(getClass())
         .addKeepMainRule(Main.class)
         .addKeepClassAndMembersRules(A.class, B.class, C.class)
-        .addOptionsModification(
-            options -> {
-              options
-                  .getStartupOptions()
-                  .setEnableMinimalStartupDex(enableMinimalStartupDex)
-                  .setEnableStartupCompletenessCheckForTesting(enableStartupCompletenessCheck);
-              options
-                  .getTestingOptions()
-                  .setMixedSectionLayoutStrategyInspector(getMixedSectionLayoutInspector());
-            })
-        .apply(testBuilder -> StartupTestingUtils.setStartupConfiguration(testBuilder, startupList))
+        .apply(
+            testBuilder ->
+                configureStartupOptions(
+                    testBuilder, instrumentationCompileResult.inspector(), startupList))
         .setMinApi(parameters.getApiLevel())
         .compile()
         .inspectMultiDex(this::inspectPrimaryDex, this::inspectSecondaryDex)
@@ -103,12 +164,31 @@
             runResult -> runResult.assertSuccessWithOutputLines(getExpectedOutput()));
   }
 
+  private void configureStartupOptions(
+      TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder,
+      CodeInspector inspector,
+      List<StartupItem<ClassReference, MethodReference, ?>> startupList) {
+    testBuilder
+        .addOptionsModification(
+            options -> {
+              options
+                  .getStartupOptions()
+                  .setEnableMinimalStartupDex(enableMinimalStartupDex)
+                  .setEnableStartupCompletenessCheckForTesting(enableStartupCompletenessCheck);
+              options
+                  .getTestingOptions()
+                  .setMixedSectionLayoutStrategyInspector(
+                      getMixedSectionLayoutInspector(inspector));
+            })
+        .apply(ignore -> StartupTestingUtils.setStartupConfiguration(testBuilder, startupList));
+  }
+
   private List<String> getExpectedOutput() {
     return ImmutableList.of("A", "B", "C");
   }
 
-  private List<StartupItem<ClassReference, MethodReference, ?>> getExpectedStartupList()
-      throws NoSuchMethodException {
+  private List<StartupItem<ClassReference, MethodReference, ?>> getExpectedStartupList(
+      CodeInspector inspector, boolean isStartupListForOriginalApp) throws NoSuchMethodException {
     ImmutableList.Builder<StartupItem<ClassReference, MethodReference, ?>> builder =
         ImmutableList.builder();
     builder.add(
@@ -132,11 +212,56 @@
                 Reference.methodFromMethod(B.class.getDeclaredMethod("b", boolean.class)))
             .build());
     if (useLambda) {
+      if (isStartupListForOriginalApp) {
+        builder.add(
+            StartupClass.referenceBuilder()
+                .setClassReference(Reference.classFromClass(B.class))
+                .setSynthetic()
+                .build());
+      } else {
+        ClassSubject bClassSubject = inspector.clazz(B.class);
+
+        MethodSubject syntheticLambdaAccessorMethod =
+            bClassSubject.uniqueMethodThatMatches(
+                method ->
+                    method
+                        .getOriginalName()
+                        .startsWith(LambdaClass.R8_LAMBDA_ACCESSOR_METHOD_PREFIX));
+        assertThat(syntheticLambdaAccessorMethod, isPresent());
+
+        ClassSubject externalSyntheticLambdaClassSubject =
+            inspector.clazz(getSyntheticLambdaClassReference());
+        assertThat(externalSyntheticLambdaClassSubject, isPresent());
+
+        ClassReference externalSyntheticLambdaClassReference =
+            externalSyntheticLambdaClassSubject.getFinalReference();
+
+        builder.add(
+            StartupClass.referenceBuilder()
+                .setClassReference(externalSyntheticLambdaClassReference)
+                .build(),
+            StartupMethod.referenceBuilder()
+                .setMethodReference(
+                    MethodReferenceUtils.instanceConstructor(externalSyntheticLambdaClassReference))
+                .build(),
+            StartupMethod.referenceBuilder()
+                .setMethodReference(
+                    Reference.method(
+                        externalSyntheticLambdaClassReference,
+                        "accept",
+                        ImmutableList.of(Reference.classFromClass(Object.class)),
+                        null))
+                .build(),
+            StartupMethod.referenceBuilder()
+                .setMethodReference(
+                    Reference.method(
+                        Reference.classFromClass(B.class),
+                        syntheticLambdaAccessorMethod.getFinalName(),
+                        ImmutableList.of(Reference.classFromClass(Object.class)),
+                        null))
+                .build());
+      }
       builder.add(
-          StartupClass.referenceBuilder()
-              .setClassReference(Reference.classFromClass(B.class))
-              .setSynthetic()
-              .build(),
           StartupMethod.referenceBuilder()
               .setMethodReference(
                   Reference.methodFromMethod(B.class.getDeclaredMethod("lambda$b$0", Object.class)))
@@ -152,7 +277,10 @@
     return builder.build();
   }
 
-  private List<ClassReference> getExpectedClassDataLayout(int virtualFile) {
+  private List<ClassReference> getExpectedClassDataLayout(
+      CodeInspector inspector, int virtualFile) {
+    ClassSubject syntheticLambdaClassSubject = inspector.clazz(getSyntheticLambdaClassReference());
+
     // The synthetic lambda should only be placed alongside its synthetic context (B) if it is used.
     // Otherwise, it should be last, or in the second dex file if compiling with minimal startup.
     ImmutableList.Builder<ClassReference> layoutBuilder = ImmutableList.builder();
@@ -162,23 +290,24 @@
           Reference.classFromClass(A.class),
           Reference.classFromClass(B.class));
       if (useLambda) {
-        layoutBuilder.add(getSyntheticLambdaClassReference());
+        layoutBuilder.add(syntheticLambdaClassSubject.getFinalReference());
       }
       layoutBuilder.add(Reference.classFromClass(C.class));
     }
     if (!useLambda) {
       if (!enableMinimalStartupDex || virtualFile == 1) {
-        layoutBuilder.add(getSyntheticLambdaClassReference());
+        layoutBuilder.add(syntheticLambdaClassSubject.getFinalReference());
       }
     }
     return layoutBuilder.build();
   }
 
-  private MixedSectionLayoutInspector getMixedSectionLayoutInspector() {
+  private MixedSectionLayoutInspector getMixedSectionLayoutInspector(CodeInspector inspector) {
     return new MixedSectionLayoutInspector() {
       @Override
       public void inspectClassDataLayout(int virtualFile, Collection<DexProgramClass> layout) {
-        assertThat(layout, isEqualToClassDataLayout(getExpectedClassDataLayout(virtualFile)));
+        assertThat(
+            layout, isEqualToClassDataLayout(getExpectedClassDataLayout(inspector, virtualFile)));
       }
     };
   }
diff --git a/src/test/java/com/android/tools/r8/startup/StartupSyntheticWithoutContextTest.java b/src/test/java/com/android/tools/r8/startup/StartupSyntheticWithoutContextTest.java
index 13c7ca7..b9ceea2 100644
--- a/src/test/java/com/android/tools/r8/startup/StartupSyntheticWithoutContextTest.java
+++ b/src/test/java/com/android/tools/r8/startup/StartupSyntheticWithoutContextTest.java
@@ -64,7 +64,8 @@
     List<StartupItem<ClassReference, MethodReference, ?>> startupList = new ArrayList<>();
     testForD8(parameters.getBackend())
         .addInnerClasses(getClass())
-        .apply(StartupTestingUtils.enableStartupInstrumentationUsingLogcat(parameters))
+        .apply(
+            StartupTestingUtils.enableStartupInstrumentationForOriginalAppUsingLogcat(parameters))
         .release()
         .setMinApi(parameters.getApiLevel())
         .compile()
diff --git a/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java b/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java
index ae31fe8..04d673c 100644
--- a/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java
+++ b/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java
@@ -10,14 +10,14 @@
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.D8TestBuilder;
 import com.android.tools.r8.D8TestRunResult;
-import com.android.tools.r8.R8TestBuilder;
 import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.ThrowableConsumer;
 import com.android.tools.r8.experimental.startup.StartupConfiguration;
 import com.android.tools.r8.experimental.startup.StartupConfigurationParser;
 import com.android.tools.r8.experimental.startup.StartupItem;
-import com.android.tools.r8.experimental.startup.StartupOptions;
+import com.android.tools.r8.experimental.startup.instrumentation.StartupInstrumentationOptions;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
@@ -40,25 +40,54 @@
 
   private static String startupInstrumentationTag = "startup";
 
-  public static ThrowableConsumer<D8TestBuilder> enableStartupInstrumentationUsingFile(
-      TestParameters parameters) {
-    return testBuilder -> enableStartupInstrumentation(testBuilder, parameters, false);
+  private enum AppVariant {
+    ORIGINAL,
+    OPTIMIZED;
+
+    boolean isOriginal() {
+      return this == ORIGINAL;
+    }
   }
 
-  public static ThrowableConsumer<D8TestBuilder> enableStartupInstrumentationUsingLogcat(
-      TestParameters parameters) {
-    return testBuilder -> enableStartupInstrumentation(testBuilder, parameters, true);
+  public static ThrowableConsumer<D8TestBuilder>
+      enableStartupInstrumentationForOriginalAppUsingFile(TestParameters parameters) {
+    return testBuilder ->
+        enableStartupInstrumentation(testBuilder, parameters, AppVariant.ORIGINAL, false);
+  }
+
+  public static ThrowableConsumer<D8TestBuilder>
+      enableStartupInstrumentationForOriginalAppUsingLogcat(TestParameters parameters) {
+    return testBuilder ->
+        enableStartupInstrumentation(testBuilder, parameters, AppVariant.ORIGINAL, true);
+  }
+
+  public static ThrowableConsumer<D8TestBuilder>
+      enableStartupInstrumentationForOptimizedAppUsingFile(TestParameters parameters) {
+    return testBuilder ->
+        enableStartupInstrumentation(testBuilder, parameters, AppVariant.OPTIMIZED, false);
+  }
+
+  public static ThrowableConsumer<D8TestBuilder>
+      enableStartupInstrumentationForOptimizedAppUsingLogcat(TestParameters parameters) {
+    return testBuilder ->
+        enableStartupInstrumentation(testBuilder, parameters, AppVariant.OPTIMIZED, true);
   }
 
   private static void enableStartupInstrumentation(
-      D8TestBuilder testBuilder, TestParameters parameters, boolean logcat) throws IOException {
+      D8TestBuilder testBuilder, TestParameters parameters, AppVariant appVariant, boolean logcat)
+      throws IOException {
     testBuilder
         .addOptionsModification(
             options -> {
-              StartupOptions startupOptions =
-                  options.getStartupOptions().setEnableStartupInstrumentation();
+              StartupInstrumentationOptions startupInstrumentationOptions =
+                  options
+                      .getStartupInstrumentationOptions()
+                      .setEnableStartupInstrumentation()
+                      .setEnableGeneralizationOfSyntheticsToSyntheticContext(
+                          appVariant.isOriginal());
               if (logcat) {
-                startupOptions.setStartupInstrumentationTag(startupInstrumentationTag);
+                startupInstrumentationOptions.setStartupInstrumentationTag(
+                    startupInstrumentationTag);
               }
             })
         .addLibraryFiles(parameters.getDefaultRuntimeLibrary())
@@ -113,7 +142,7 @@
   }
 
   public static void setStartupConfiguration(
-      R8TestBuilder<?> testBuilder,
+      TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder,
       List<StartupItem<ClassReference, MethodReference, ?>> startupItems) {
     testBuilder.addOptionsModification(
         options -> {