Allow using startup profile only for guiding optimizations
Bug: b/284929119
Change-Id: I0ce07973f570f7e60d9de27593e14b9e057d1111
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 07b3ef5..d2647bb 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -137,6 +137,7 @@
private boolean enableMissingLibraryApiModeling = false;
private boolean enableExperimentalKeepAnnotations =
System.getProperty("com.android.tools.r8.enableKeepAnnotations") != null;
+ public boolean enableStartupLayoutOptimization = true;
private SemanticVersion fakeCompilerVersion = null;
private AndroidResourceProvider androidResourceProvider = null;
private AndroidResourceConsumer androidResourceConsumer = null;
@@ -290,7 +291,7 @@
return super.setProguardMapOutputPath(proguardMapOutput);
}
- /** Set input proguard map used for distribution of classes in multi-dex. */
+ /** Set input proguard map used for distribution of classes in multi-DEX. */
public Builder setProguardMapInputFile(Path proguardInputMap) {
getAppBuilder().setProguardMapInputData(proguardInputMap);
return self();
@@ -507,11 +508,11 @@
/**
* Add a collection of startup profile providers that should be used for distributing the
- * program classes in dex. The given startup profiles are also used to disallow optimizations
+ * program classes in DEX. The given startup profiles are also used to disallow optimizations
* across the startup and post-startup boundary.
*
* <p>NOTE: Startup profiles are ignored when compiling to class files or the min-API level does
- * not support native multidex (API<=20).
+ * not support native multi-DEX (API<=20).
*/
@Override
public Builder addStartupProfileProviders(StartupProfileProvider... startupProfileProviders) {
@@ -520,11 +521,12 @@
/**
* Add a collection of startup profile providers that should be used for distributing the
- * program classes in dex. The given startup profiles are also used to disallow optimizations
- * across the startup and post-startup boundary.
+ * program classes in DEX, unless turned off using {@link #setEnableStartupLayoutOptimization}.
+ * The given startup profiles are also used to disallow optimizations across the startup and
+ * post-startup boundary.
*
* <p>NOTE: Startup profiles are ignored when compiling to class files or the min-API level does
- * not support native multidex (API<=20).
+ * not support native multi-DEX (API<=20).
*/
@Override
public Builder addStartupProfileProviders(
@@ -533,6 +535,18 @@
}
/**
+ * API for specifying whether R8 should use the provided startup profiles to layout the DEX.
+ * When this is set to {@code false}, the given startup profiles are then only used to disallow
+ * optimizations across the startup and post-startup boundary.
+ *
+ * <p>Defaults to true.
+ */
+ public Builder setEnableStartupLayoutOptimization(boolean enable) {
+ enableStartupLayoutOptimization = enable;
+ return this;
+ }
+
+ /**
* Exprimental API for supporting android resource shrinking.
*
* <p>Add an android resource provider, providing the resource table, manifest and res table
@@ -717,6 +731,7 @@
getMapIdProvider(),
getSourceFileProvider(),
enableMissingLibraryApiModeling,
+ enableStartupLayoutOptimization,
getAndroidPlatformBuild(),
getArtProfilesForRewriting(),
getStartupProfileProviders(),
@@ -912,6 +927,7 @@
private final FeatureSplitConfiguration featureSplitConfiguration;
private final String synthesizedClassPrefix;
private final boolean enableMissingLibraryApiModeling;
+ private final boolean enableStartupLayoutOptimization;
private final AndroidResourceProvider androidResourceProvider;
private final AndroidResourceConsumer androidResourceConsumer;
private final ResourceShrinkerConfiguration resourceShrinkerConfiguration;
@@ -1004,6 +1020,7 @@
MapIdProvider mapIdProvider,
SourceFileProvider sourceFileProvider,
boolean enableMissingLibraryApiModeling,
+ boolean enableStartupLayoutOptimization,
boolean isAndroidPlatformBuild,
List<ArtProfileForRewriting> artProfilesForRewriting,
List<StartupProfileProvider> startupProfileProviders,
@@ -1055,6 +1072,7 @@
this.featureSplitConfiguration = featureSplitConfiguration;
this.synthesizedClassPrefix = synthesizedClassPrefix;
this.enableMissingLibraryApiModeling = enableMissingLibraryApiModeling;
+ this.enableStartupLayoutOptimization = enableStartupLayoutOptimization;
this.androidResourceProvider = androidResourceProvider;
this.androidResourceConsumer = androidResourceConsumer;
this.resourceShrinkerConfiguration = resourceShrinkerConfiguration;
@@ -1081,6 +1099,7 @@
featureSplitConfiguration = null;
synthesizedClassPrefix = null;
enableMissingLibraryApiModeling = false;
+ enableStartupLayoutOptimization = true;
androidResourceProvider = null;
androidResourceConsumer = null;
resourceShrinkerConfiguration = null;
@@ -1199,7 +1218,7 @@
internal.apiModelingOptions().disableOutliningAndStubbing();
}
- // Default is to remove all javac generated assertion code when generating dex.
+ // Default is to remove all javac generated assertion code when generating DEX.
assert internal.assertionsConfiguration == null;
AssertionsConfiguration.Builder builder = AssertionsConfiguration.builder(getReporter());
internal.assertionsConfiguration =
@@ -1244,7 +1263,10 @@
internal.getArtProfileOptions().setArtProfilesForRewriting(getArtProfilesForRewriting());
if (!getStartupProfileProviders().isEmpty()) {
- internal.getStartupOptions().setStartupProfileProviders(getStartupProfileProviders());
+ internal
+ .getStartupOptions()
+ .setStartupProfileProviders(getStartupProfileProviders())
+ .setEnableStartupLayoutOptimization(enableStartupLayoutOptimization);
}
internal.programClassConflictResolver =
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 105ea1c..37a55ce 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -241,10 +241,15 @@
// 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.
- StartupProfile startupProfile =
- appView.appInfo().hasClassHierarchy()
- ? appView.getStartupProfile()
- : StartupProfile.createInitialStartupProfileForD8(appView);
+ StartupProfile startupProfile;
+ if (options.getStartupOptions().isStartupLayoutOptimizationEnabled()) {
+ startupProfile =
+ appView.appInfo().hasClassHierarchy()
+ ? appView.getStartupProfile()
+ : StartupProfile.createInitialStartupProfileForD8(appView);
+ } else {
+ startupProfile = StartupProfile.empty();
+ }
distributor =
new VirtualFile.FillFilesDistributor(
this, classes, options, executorService, startupProfile);
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 158b912..1e5e242 100644
--- a/src/main/java/com/android/tools/r8/dex/MixedSectionLayoutStrategy.java
+++ b/src/main/java/com/android/tools/r8/dex/MixedSectionLayoutStrategy.java
@@ -28,7 +28,7 @@
} else {
assert virtualFile.getId() == 0;
startupProfileForWriting =
- appView.options().getStartupOptions().isStartupLayoutOptimizationsEnabled()
+ appView.options().getStartupOptions().isStartupMixedSectionLayoutOptimizationsEnabled()
? virtualFile.getStartupProfile().toStartupProfileForWriting(appView)
: StartupProfile.empty();
}
diff --git a/src/main/java/com/android/tools/r8/profile/startup/StartupOptions.java b/src/main/java/com/android/tools/r8/profile/startup/StartupOptions.java
index 632fd74..e6406bc 100644
--- a/src/main/java/com/android/tools/r8/profile/startup/StartupOptions.java
+++ b/src/main/java/com/android/tools/r8/profile/startup/StartupOptions.java
@@ -42,11 +42,14 @@
private boolean enableStartupCompletenessCheckForTesting =
parseSystemPropertyOrDefault("com.android.tools.r8.startup.completenesscheck", false);
+ /** When enabled, the startup profile is used to layout the DEX. */
+ private boolean enableStartupLayoutOptimization = true;
+
/**
- * When enabled, the layout of the primary dex file will be generated using the startup list,
- * using {@link com.android.tools.r8.dex.StartupMixedSectionLayoutStrategy}.
+ * When enabled, the mixed section layout of the primary dex file will be generated using the
+ * startup list, using {@link com.android.tools.r8.dex.StartupMixedSectionLayoutStrategy}.
*/
- private boolean enableStartupLayoutOptimizations =
+ private boolean enableStartupMixedSectionLayoutOptimizations =
parseSystemPropertyOrDefault("com.android.tools.r8.startup.layout", true);
private String multiStartupDexDistributionStrategyName =
@@ -84,14 +87,18 @@
return this;
}
- public boolean isStartupLayoutOptimizationsEnabled() {
- return enableStartupLayoutOptimizations;
+ public boolean isStartupMixedSectionLayoutOptimizationsEnabled() {
+ return enableStartupMixedSectionLayoutOptimizations;
}
public boolean isStartupCompletenessCheckForTestingEnabled() {
return enableStartupCompletenessCheckForTesting;
}
+ public boolean isStartupLayoutOptimizationEnabled() {
+ return enableStartupLayoutOptimization;
+ }
+
public StartupOptions setEnableStartupCompletenessCheckForTesting() {
return setEnableStartupCompletenessCheckForTesting(true);
}
@@ -102,6 +109,12 @@
return this;
}
+ public StartupOptions setEnableStartupLayoutOptimization(
+ boolean enableStartupLayoutOptimization) {
+ this.enableStartupLayoutOptimization = enableStartupLayoutOptimization;
+ return this;
+ }
+
public String getMultiStartupDexDistributionStrategyName() {
return multiStartupDexDistributionStrategyName;
}
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index 2183979..abafdc8 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -76,6 +76,7 @@
private AllowedDiagnosticMessages allowedDiagnosticMessages = AllowedDiagnosticMessages.NONE;
private boolean allowUnusedProguardConfigurationRules = false;
private boolean enableMissingLibraryApiModeling = true;
+ private boolean enableStartupLayoutOptimization = true;
private CollectingGraphConsumer graphConsumer = null;
private final List<ExternalArtProfile> residualArtProfiles = new ArrayList<>();
private final List<String> keepRules = new ArrayList<>();
@@ -152,6 +153,7 @@
builder, rules -> box.syntheticProguardRules = rules);
libraryDesugaringTestConfiguration.configure(builder);
builder.setEnableExperimentalMissingLibraryApiModeling(enableMissingLibraryApiModeling);
+ builder.setEnableStartupLayoutOptimization(enableStartupLayoutOptimization);
StringBuilder pgConfOutput = wrapProguardConfigConsumer(builder);
ToolHelper.runAndBenchmarkR8WithoutResult(builder, optionsConsumer, benchmarkResults);
R8TestCompileResult compileResult =
@@ -880,6 +882,11 @@
return self();
}
+ public T enableStartupLayoutOptimization(boolean enableStartupLayoutOptimization) {
+ this.enableStartupLayoutOptimization = enableStartupLayoutOptimization;
+ return self();
+ }
+
public T setFakeCompilerVersion(SemanticVersion version) {
getBuilder().setFakeCompilerVersion(version);
return self();
diff --git a/src/test/java/com/android/tools/r8/R8TestCompileResult.java b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
index 25001ac..8237f9b 100644
--- a/src/test/java/com/android/tools/r8/R8TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
@@ -25,6 +25,7 @@
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.graphinspector.GraphInspector;
import java.io.IOException;
+import java.io.UncheckedIOException;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.List;
@@ -136,8 +137,12 @@
@SafeVarargs
@Override
public final <E extends Throwable> R8TestCompileResult inspectMultiDex(
- ThrowingConsumer<CodeInspector, E>... consumers) throws IOException, E {
- return inspectMultiDex(writeProguardMap(), consumers);
+ ThrowingConsumer<CodeInspector, E>... consumers) throws E {
+ try {
+ return inspectMultiDex(writeProguardMap(), consumers);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
}
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 dfdfa9d..4c2d6a8 100644
--- a/src/test/java/com/android/tools/r8/TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/TestCompileResult.java
@@ -39,6 +39,7 @@
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
+import java.io.UncheckedIOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
@@ -456,21 +457,25 @@
@SuppressWarnings("unchecked")
public <E extends Throwable> CR inspectMultiDex(ThrowingConsumer<CodeInspector, E>... consumers)
- throws IOException, E {
+ throws 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);
+ Path mappingFile, ThrowingConsumer<CodeInspector, E>... consumers) throws E {
+ try {
+ 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);
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
}
return self();
}
diff --git a/src/test/java/com/android/tools/r8/TestParameters.java b/src/test/java/com/android/tools/r8/TestParameters.java
index 892c68f..0515b8d 100644
--- a/src/test/java/com/android/tools/r8/TestParameters.java
+++ b/src/test/java/com/android/tools/r8/TestParameters.java
@@ -90,6 +90,10 @@
return isDexRuntime() && getDexRuntimeVersion().isNewerThanOrEqual(DexVm.Version.V8_1_0);
}
+ public boolean canUseNativeMultidex() {
+ return isDexRuntime() && getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L);
+ }
+
public boolean canUseNestBasedAccesses() {
assert isCfRuntime() || isDexRuntime();
return isCfRuntime() && getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11);
diff --git a/src/test/java/com/android/tools/r8/startup/optimization/NoStartupBoundaryOptimizationsWithoutStartupLayoutOptimizationTest.java b/src/test/java/com/android/tools/r8/startup/optimization/NoStartupBoundaryOptimizationsWithoutStartupLayoutOptimizationTest.java
new file mode 100644
index 0000000..4ca9640
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/startup/optimization/NoStartupBoundaryOptimizationsWithoutStartupLayoutOptimizationTest.java
@@ -0,0 +1,169 @@
+// 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.startup.optimization;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
+import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsentIf;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.errors.StartupClassesNonStartupFractionDiagnostic;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.startup.profile.ExternalStartupClass;
+import com.android.tools.r8.startup.profile.ExternalStartupItem;
+import com.android.tools.r8.startup.profile.ExternalStartupMethod;
+import com.android.tools.r8.startup.utils.StartupTestingUtils;
+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.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;
+
+/**
+ * Tests the option to provide a startup profile for guiding optimizations without impacting the DEX
+ * layout.
+ */
+@RunWith(Parameterized.class)
+public class NoStartupBoundaryOptimizationsWithoutStartupLayoutOptimizationTest extends TestBase {
+
+ @Parameter(0)
+ public boolean enableStartupProfile;
+
+ @Parameter(1)
+ public boolean enableStartupLayoutOptimization;
+
+ @Parameter(2)
+ public TestParameters parameters;
+
+ @Parameters(name = "{2}, profile: {0}, layout: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(),
+ BooleanUtils.values(),
+ getTestParameters().withDexRuntimesAndAllApiLevels().build());
+ }
+
+ @Test
+ public void test() throws Exception {
+ // Startup profile requires native multi dex.
+ assumeTrue(!enableStartupProfile || parameters.canUseNativeMultidex());
+ // Startup layout optimization is meaningless without startup profile.
+ assumeTrue(enableStartupProfile || !enableStartupLayoutOptimization);
+ List<ExternalStartupItem> startupProfile =
+ ImmutableList.of(
+ ExternalStartupClass.builder()
+ .setClassReference(Reference.classFromClass(Main.class))
+ .build(),
+ ExternalStartupMethod.builder()
+ .setMethodReference(MethodReferenceUtils.mainMethod(Main.class))
+ .build());
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .allowDiagnosticMessages()
+ .applyIf(
+ enableStartupProfile,
+ testBuilder ->
+ testBuilder
+ .apply(StartupTestingUtils.addStartupProfile(startupProfile))
+ .enableStartupLayoutOptimization(enableStartupLayoutOptimization))
+ .setMinApi(parameters)
+ .compileWithExpectedDiagnostics(this::inspectDiagnostics)
+ .applyIf(
+ !enableStartupProfile || !enableStartupLayoutOptimization,
+ compileResult ->
+ compileResult.inspectMultiDex(
+ primaryDexInspector ->
+ inspectPrimaryDex(primaryDexInspector, compileResult.inspector())),
+ compileResult ->
+ compileResult.inspectMultiDex(
+ primaryDexInspector ->
+ inspectPrimaryDex(primaryDexInspector, compileResult.inspector()),
+ this::inspectSecondaryDex))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Click!");
+ }
+
+ private void inspectDiagnostics(TestDiagnosticMessages diagnostics) {
+ if (enableStartupProfile && enableStartupLayoutOptimization) {
+ diagnostics.assertInfosMatch(
+ diagnosticType(StartupClassesNonStartupFractionDiagnostic.class));
+ } else {
+ diagnostics.assertNoMessages();
+ }
+ }
+
+ private void inspectPrimaryDex(CodeInspector primaryDexInspector, CodeInspector appInspector) {
+ if (!enableStartupProfile) {
+ // Everything should be inlined into main().
+ assertEquals(1, primaryDexInspector.allClasses().size());
+ assertEquals(1, primaryDexInspector.clazz(Main.class).allMethods().size());
+ return;
+ }
+
+ // Main.onClick() should be inlined into Main.main(), but OnClickHandler.handle() should
+ // remain.
+ ClassSubject onClickHandlerClassSubject = appInspector.clazz(OnClickHandler.class);
+ assertThat(onClickHandlerClassSubject, isPresent());
+
+ MethodSubject handleMethodSubject =
+ onClickHandlerClassSubject.uniqueMethodWithOriginalName("handle");
+ assertThat(handleMethodSubject, isPresent());
+
+ ClassSubject mainClassSubject = primaryDexInspector.clazz(Main.class);
+ assertThat(mainClassSubject, isPresent());
+ assertEquals(1, mainClassSubject.allMethods().size());
+ assertThat(mainClassSubject.mainMethod(), invokesMethod(handleMethodSubject));
+
+ // OnClickHandler should not be in the primary DEX when startup layout is enabled.
+ assertThat(
+ primaryDexInspector.clazz(OnClickHandler.class),
+ isAbsentIf(enableStartupLayoutOptimization));
+ }
+
+ private void inspectSecondaryDex(CodeInspector secondaryDexInspector) {
+ assertTrue(enableStartupProfile);
+ assertTrue(enableStartupLayoutOptimization);
+ assertEquals(1, secondaryDexInspector.allClasses().size());
+
+ ClassSubject onClickHandlerClassSubject = secondaryDexInspector.clazz(OnClickHandler.class);
+ assertThat(onClickHandlerClassSubject, isPresent());
+
+ MethodSubject handleMethodSubject =
+ onClickHandlerClassSubject.uniqueMethodWithOriginalName("handle");
+ assertThat(handleMethodSubject, isPresent());
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ onClick();
+ }
+
+ static void onClick() {
+ OnClickHandler.handle();
+ }
+ }
+
+ static class OnClickHandler {
+
+ static void handle() {
+ System.out.println("Click!");
+ }
+ }
+}
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 8805931..f51ab4b 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,6 +10,7 @@
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.TestDiagnosticMessagesImpl;
@@ -192,6 +193,11 @@
}
}
+ public static <B extends R8TestBuilder<B>> ThrowableConsumer<B> addStartupProfile(
+ Collection<ExternalStartupItem> startupItems) {
+ return testBuilder -> addStartupProfile(testBuilder, startupItems);
+ }
+
private static byte[] getTransformedAndroidUtilLog() throws IOException {
return transformer(Log.class).setClassDescriptor("Landroid/util/Log;").transform();
}