Option to enable startup/non-startup boundary optimizations
Change-Id: I0a0037e9fef75e9810990600688ac44bee508b08
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index 31967b1..a64b883 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -166,7 +166,7 @@
}
Path dumpOutput = dumpInputFlags.getDumpPath();
timing.begin("ApplicationReader.dump");
- inputApp.dump(dumpOutput, dumpOptions, options.reporter, options.dexItemFactory());
+ inputApp.dump(dumpOutput, dumpOptions, options);
timing.end();
Diagnostic message = new StringDiagnostic("Dumped compilation inputs to: " + dumpOutput);
if (dumpInputFlags.shouldFailCompilation()) {
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 47c7834..0a97199 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
@@ -18,6 +18,16 @@
"com.android.tools.r8.startup.minimalstartupdex", false);
/**
+ * When enabled, optimizations crossing the startup/non-startup boundary will be allowed.
+ *
+ * <p>The disabling of this may help to avoid that more code may be loaded during startup as a
+ * result of optimizations such as inlining and class merging.
+ */
+ private boolean enableStartupBoundaryOptimizations =
+ parseSystemPropertyForDevelopmentOrDefault(
+ "com.android.tools.r8.startup.boundaryoptimizations", false);
+
+ /**
* When enabled, each method that is not classified as a startup method at the end of compilation
* will be changed to have a throwing method body.
*
@@ -101,6 +111,10 @@
return this;
}
+ public boolean isStartupBoundaryOptimizationsEnabled() {
+ return enableStartupBoundaryOptimizations;
+ }
+
public boolean isStartupInstrumentationEnabled() {
return enableStartupInstrumentation;
}
diff --git a/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java b/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
index 2113b52..5b8e3db 100644
--- a/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
+++ b/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
@@ -107,14 +107,20 @@
public Map<FeatureSplit, Set<DexProgramClass>> getFeatureSplitClasses(
Set<DexProgramClass> classes, AppView<? extends AppInfoWithClassHierarchy> appView) {
return getFeatureSplitClasses(
- classes, appView.appInfo().getStartupOrder(), appView.getSyntheticItems());
+ classes,
+ appView.options(),
+ appView.appInfo().getStartupOrder(),
+ appView.getSyntheticItems());
}
public Map<FeatureSplit, Set<DexProgramClass>> getFeatureSplitClasses(
- Set<DexProgramClass> classes, StartupOrder startupOrder, SyntheticItems syntheticItems) {
+ Set<DexProgramClass> classes,
+ InternalOptions options,
+ StartupOrder startupOrder,
+ SyntheticItems syntheticItems) {
Map<FeatureSplit, Set<DexProgramClass>> result = new IdentityHashMap<>();
for (DexProgramClass clazz : classes) {
- FeatureSplit featureSplit = getFeatureSplit(clazz, startupOrder, syntheticItems);
+ FeatureSplit featureSplit = getFeatureSplit(clazz, options, startupOrder, syntheticItems);
if (featureSplit != null && !featureSplit.isBase()) {
result.computeIfAbsent(featureSplit, ignore -> Sets.newIdentityHashSet()).add(clazz);
}
@@ -125,21 +131,31 @@
public FeatureSplit getFeatureSplit(
ProgramDefinition definition, AppView<? extends AppInfoWithClassHierarchy> appView) {
return getFeatureSplit(
- definition, appView.appInfo().getStartupOrder(), appView.getSyntheticItems());
+ definition,
+ appView.options(),
+ appView.appInfo().getStartupOrder(),
+ appView.getSyntheticItems());
}
public FeatureSplit getFeatureSplit(
- ProgramDefinition definition, StartupOrder startupOrder, SyntheticItems syntheticItems) {
- return getFeatureSplit(definition.getContextType(), startupOrder, syntheticItems);
+ ProgramDefinition definition,
+ InternalOptions options,
+ StartupOrder startupOrder,
+ SyntheticItems syntheticItems) {
+ return getFeatureSplit(definition.getContextType(), options, startupOrder, syntheticItems);
}
public FeatureSplit getFeatureSplit(
DexType type, AppView<? extends AppInfoWithClassHierarchy> appView) {
- return getFeatureSplit(type, appView.appInfo().getStartupOrder(), appView.getSyntheticItems());
+ return getFeatureSplit(
+ type, appView.options(), appView.appInfo().getStartupOrder(), appView.getSyntheticItems());
}
public FeatureSplit getFeatureSplit(
- DexType type, StartupOrder startupOrder, SyntheticItems syntheticItems) {
+ DexType type,
+ InternalOptions options,
+ StartupOrder startupOrder,
+ SyntheticItems syntheticItems) {
if (syntheticItems == null) {
// Called from AndroidApp.dumpProgramResources().
assert startupOrder.isEmpty();
@@ -154,16 +170,20 @@
// the shared utility class in case it is used during startup. The use of base startup
// allows for merging startup classes with the shared utility class, however, which could be
// bad for startup if the shared utility class is not used during startup.
- return startupOrder.isEmpty() ? FeatureSplit.BASE : FeatureSplit.BASE_STARTUP;
+ return startupOrder.isEmpty()
+ || options.getStartupOptions().isStartupBoundaryOptimizationsEnabled()
+ ? FeatureSplit.BASE
+ : FeatureSplit.BASE_STARTUP;
}
feature = syntheticItems.getContextualFeatureSplitOrDefault(type, FeatureSplit.BASE);
} else {
feature = classToFeatureSplitMap.getOrDefault(type, FeatureSplit.BASE);
}
if (feature.isBase()) {
- return startupOrder.contains(type, syntheticItems)
- ? FeatureSplit.BASE_STARTUP
- : FeatureSplit.BASE;
+ return !startupOrder.contains(type, syntheticItems)
+ || options.getStartupOptions().isStartupBoundaryOptimizationsEnabled()
+ ? FeatureSplit.BASE
+ : FeatureSplit.BASE_STARTUP;
}
return feature;
}
@@ -177,12 +197,16 @@
public boolean isInBase(
DexProgramClass clazz, AppView<? extends AppInfoWithClassHierarchy> appView) {
- return isInBase(clazz, appView.appInfo().getStartupOrder(), appView.getSyntheticItems());
+ return isInBase(
+ clazz, appView.options(), appView.appInfo().getStartupOrder(), appView.getSyntheticItems());
}
public boolean isInBase(
- DexProgramClass clazz, StartupOrder startupOrder, SyntheticItems syntheticItems) {
- return getFeatureSplit(clazz, startupOrder, syntheticItems).isBase();
+ DexProgramClass clazz,
+ InternalOptions options,
+ StartupOrder startupOrder,
+ SyntheticItems syntheticItems) {
+ return getFeatureSplit(clazz, options, startupOrder, syntheticItems).isBase();
}
public boolean isInBaseOrSameFeatureAs(
@@ -190,15 +214,21 @@
ProgramDefinition context,
AppView<? extends AppInfoWithClassHierarchy> appView) {
return isInBaseOrSameFeatureAs(
- clazz, context, appView.appInfo().getStartupOrder(), appView.getSyntheticItems());
+ clazz,
+ context,
+ appView.options(),
+ appView.appInfo().getStartupOrder(),
+ appView.getSyntheticItems());
}
public boolean isInBaseOrSameFeatureAs(
DexProgramClass clazz,
ProgramDefinition context,
+ InternalOptions options,
StartupOrder startupOrder,
SyntheticItems syntheticItems) {
- return isInBaseOrSameFeatureAs(clazz.getContextType(), context, startupOrder, syntheticItems);
+ return isInBaseOrSameFeatureAs(
+ clazz.getContextType(), context, options, startupOrder, syntheticItems);
}
public boolean isInBaseOrSameFeatureAs(
@@ -206,48 +236,62 @@
ProgramDefinition context,
AppView<? extends AppInfoWithClassHierarchy> appView) {
return isInBaseOrSameFeatureAs(
- clazz, context, appView.appInfo().getStartupOrder(), appView.getSyntheticItems());
+ clazz,
+ context,
+ appView.options(),
+ appView.appInfo().getStartupOrder(),
+ appView.getSyntheticItems());
}
public boolean isInBaseOrSameFeatureAs(
DexType clazz,
ProgramDefinition context,
+ InternalOptions options,
StartupOrder startupOrder,
SyntheticItems syntheticItems) {
- FeatureSplit split = getFeatureSplit(clazz, startupOrder, syntheticItems);
- return split.isBase() || split == getFeatureSplit(context, startupOrder, syntheticItems);
+ FeatureSplit split = getFeatureSplit(clazz, options, startupOrder, syntheticItems);
+ return split.isBase()
+ || split == getFeatureSplit(context, options, startupOrder, syntheticItems);
}
public boolean isInFeature(
- DexProgramClass clazz, StartupOrder startupOrder, SyntheticItems syntheticItems) {
- return !isInBase(clazz, startupOrder, syntheticItems);
+ DexProgramClass clazz,
+ InternalOptions options,
+ StartupOrder startupOrder,
+ SyntheticItems syntheticItems) {
+ return !isInBase(clazz, options, startupOrder, syntheticItems);
}
public boolean isInSameFeatureOrBothInSameBase(
ProgramMethod a, ProgramMethod b, AppView<? extends AppInfoWithClassHierarchy> appView) {
return isInSameFeatureOrBothInSameBase(
- a, b, appView.appInfo().getStartupOrder(), appView.getSyntheticItems());
+ a, b, appView.options(), appView.appInfo().getStartupOrder(), appView.getSyntheticItems());
}
public boolean isInSameFeatureOrBothInSameBase(
- ProgramMethod a, ProgramMethod b, StartupOrder startupOrder, SyntheticItems syntheticItems) {
+ ProgramMethod a,
+ ProgramMethod b,
+ InternalOptions options,
+ StartupOrder startupOrder,
+ SyntheticItems syntheticItems) {
return isInSameFeatureOrBothInSameBase(
- a.getHolder(), b.getHolder(), startupOrder, syntheticItems);
+ a.getHolder(), b.getHolder(), options, startupOrder, syntheticItems);
}
public boolean isInSameFeatureOrBothInSameBase(
DexProgramClass a, DexProgramClass b, AppView<? extends AppInfoWithClassHierarchy> appView) {
return isInSameFeatureOrBothInSameBase(
- a, b, appView.appInfo().getStartupOrder(), appView.getSyntheticItems());
+ a, b, appView.options(), appView.appInfo().getStartupOrder(), appView.getSyntheticItems());
}
public boolean isInSameFeatureOrBothInSameBase(
DexProgramClass a,
DexProgramClass b,
+ InternalOptions options,
StartupOrder startupOrder,
SyntheticItems syntheticItems) {
- return getFeatureSplit(a, startupOrder, syntheticItems)
- == getFeatureSplit(b, startupOrder, syntheticItems);
+ return getFeatureSplit(a, options, startupOrder, syntheticItems)
+ == getFeatureSplit(b, options, startupOrder, syntheticItems);
}
public ClassToFeatureSplitMap rewrittenWithLens(GraphLens lens) {
@@ -287,6 +331,10 @@
public static boolean isInFeature(DexProgramClass clazz, AppView<AppInfoWithLiveness> appView) {
return getMap(appView)
- .isInFeature(clazz, appView.appInfo().getStartupOrder(), appView.getSyntheticItems());
+ .isInFeature(
+ clazz,
+ appView.options(),
+ appView.appInfo().getStartupOrder(),
+ appView.getSyntheticItems());
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/AccessControl.java b/src/main/java/com/android/tools/r8/graph/AccessControl.java
index 428d0fd..43bbfa2 100644
--- a/src/main/java/com/android/tools/r8/graph/AccessControl.java
+++ b/src/main/java/com/android/tools/r8/graph/AccessControl.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.experimental.startup.StartupOrder;
import com.android.tools.r8.features.ClassToFeatureSplitMap;
import com.android.tools.r8.synthesis.SyntheticItems;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.OptionalBool;
/**
@@ -24,6 +25,7 @@
clazz,
context,
appView.appInfo().getClassToFeatureSplitMap(),
+ appView.options(),
appView.appInfo().getStartupOrder(),
appView.getSyntheticItems());
}
@@ -32,6 +34,7 @@
DexClass clazz,
Definition context,
ClassToFeatureSplitMap classToFeatureSplitMap,
+ InternalOptions options,
StartupOrder startupOrder,
SyntheticItems syntheticItems) {
if (!clazz.isPublic() && !clazz.getType().isSamePackage(context.getContextType())) {
@@ -40,7 +43,11 @@
if (clazz.isProgramClass()
&& context.isProgramDefinition()
&& !classToFeatureSplitMap.isInBaseOrSameFeatureAs(
- clazz.asProgramClass(), context.asProgramDefinition(), startupOrder, syntheticItems)) {
+ clazz.asProgramClass(),
+ context.asProgramDefinition(),
+ options,
+ startupOrder,
+ syntheticItems)) {
return OptionalBool.UNKNOWN;
}
return OptionalBool.TRUE;
@@ -78,6 +85,7 @@
initialResolutionHolder,
context,
appInfo.getClassToFeatureSplitMap(),
+ appInfo.options(),
appInfo.getStartupOrder(),
appInfo.getSyntheticItems());
if (classAccessibility.isFalse()) {
diff --git a/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java b/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
index 5b43e8d..94bb52d 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
@@ -1272,6 +1272,7 @@
clazz,
context,
appInfo.getClassToFeatureSplitMap(),
+ appInfo.options(),
appInfo.getStartupOrder(),
appInfo.getSyntheticItems())
.isPossiblyFalse())),
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index de636a8..323c745 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -40,6 +40,7 @@
import com.android.tools.r8.synthesis.SyntheticFinalization.Result;
import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.android.tools.r8.utils.ConsumerUtils;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.SetUtils;
@@ -544,18 +545,25 @@
private SynthesizingContext getSynthesizingContext(
ProgramDefinition context, AppView<?> appView) {
+ InternalOptions options = appView.options();
if (appView.hasClassHierarchy()) {
AppInfoWithClassHierarchy appInfo = appView.appInfoWithClassHierarchy();
return getSynthesizingContext(
- context, appInfo.getClassToFeatureSplitMap(), appInfo.getStartupOrder());
+ context, appInfo.getClassToFeatureSplitMap(), options, appInfo.getStartupOrder());
}
return getSynthesizingContext(
- context, ClassToFeatureSplitMap.createEmptyClassToFeatureSplitMap(), StartupOrder.empty());
+ context,
+ ClassToFeatureSplitMap.createEmptyClassToFeatureSplitMap(),
+ options,
+ StartupOrder.empty());
}
/** Used to find the synthesizing context for a new synthetic that is about to be created. */
private SynthesizingContext getSynthesizingContext(
- ProgramDefinition context, ClassToFeatureSplitMap featureSplits, StartupOrder startupOrder) {
+ ProgramDefinition context,
+ ClassToFeatureSplitMap featureSplits,
+ InternalOptions options,
+ StartupOrder startupOrder) {
DexType contextType = context.getContextType();
SyntheticDefinition<?, ?, ?> existingDefinition = pending.definitions.get(contextType);
if (existingDefinition != null) {
@@ -571,7 +579,7 @@
.getContext();
}
// This context is not nested in an existing synthetic context so create a new "leaf" context.
- FeatureSplit featureSplit = featureSplits.getFeatureSplit(context, startupOrder, this);
+ FeatureSplit featureSplit = featureSplits.getFeatureSplit(context, options, startupOrder, this);
return SynthesizingContext.fromNonSyntheticInputContext(context, featureSplit);
}
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
index fbf344b..14b4d67 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -37,7 +37,6 @@
import com.android.tools.r8.experimental.startup.StartupOrder;
import com.android.tools.r8.features.ClassToFeatureSplitMap;
import com.android.tools.r8.features.FeatureSplitConfiguration;
-import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.origin.ArchiveEntryOrigin;
import com.android.tools.r8.origin.Origin;
@@ -466,7 +465,7 @@
return programResourcesMainDescriptor.get(resource);
}
- public void dump(Path output, DumpOptions options, Reporter reporter, DexItemFactory factory) {
+ public void dump(Path output, DumpOptions dumpOptions, InternalOptions options) {
int nextDexIndex = 0;
OpenOption[] openOptions =
new OpenOption[] {StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING};
@@ -476,26 +475,26 @@
writeToZipStream(
out,
dumpBuildPropertiesFileName,
- options.getBuildPropertiesFileContent().getBytes(),
+ dumpOptions.getBuildPropertiesFileContent().getBytes(),
ZipEntry.DEFLATED);
- if (options.getDesugaredLibraryJsonSource() != null) {
+ if (dumpOptions.getDesugaredLibraryJsonSource() != null) {
writeToZipStream(
out,
dumpDesugaredLibraryFileName,
- options.getDesugaredLibraryJsonSource().getBytes(),
+ dumpOptions.getDesugaredLibraryJsonSource().getBytes(),
ZipEntry.DEFLATED);
- if (options.dumpInputToFile()) {
- reporter.warning(
+ if (dumpOptions.dumpInputToFile()) {
+ options.reporter.warning(
"Dumping a compilation with desugared library on a file may prevent reproduction,"
+ " use dumpInputToDirectory property instead.");
}
}
- if (options.getParsedProguardConfiguration() != null) {
- String proguardConfig = options.getParsedProguardConfiguration();
+ if (dumpOptions.getParsedProguardConfiguration() != null) {
+ String proguardConfig = dumpOptions.getParsedProguardConfiguration();
writeToZipStream(out, dumpConfigFileName, proguardConfig.getBytes(), ZipEntry.DEFLATED);
}
if (proguardMapInputData != null) {
- reporter.warning(
+ options.reporter.warning(
"Dumping proguard map input data may have side effects due to I/O on Paths.");
writeToZipStream(
out,
@@ -506,7 +505,7 @@
if (hasMainDexList()) {
List<String> mainDexList = new ArrayList<>();
if (hasMainDexListResources()) {
- reporter.warning(
+ options.reporter.warning(
"Dumping main dex list resources may have side effects due to I/O on Paths.");
for (StringResource mainDexListResource : getMainDexListResources()) {
mainDexList.add(mainDexListResource.getString());
@@ -518,25 +517,24 @@
String join = StringUtils.join("\n", mainDexList);
writeToZipStream(out, dumpMainDexListResourceFileName, join.getBytes(), ZipEntry.DEFLATED);
}
- if (options.hasMainDexKeepRules()) {
+ if (dumpOptions.hasMainDexKeepRules()) {
writeToZipStream(
out,
dumpMainDexRulesResourceFileName,
- StringUtils.joinLines(options.getMainDexKeepRules()).getBytes(),
+ StringUtils.joinLines(dumpOptions.getMainDexKeepRules()).getBytes(),
ZipEntry.DEFLATED);
}
nextDexIndex =
dumpProgramResources(
dumpProgramFileName,
- options.getFeatureSplitConfiguration(),
+ dumpOptions.getFeatureSplitConfiguration(),
nextDexIndex,
out,
- reporter,
- factory);
+ options);
nextDexIndex = dumpClasspathResources(nextDexIndex, out);
nextDexIndex = dumpLibraryResources(nextDexIndex, out);
} catch (IOException | ResourceException e) {
- throw reporter.fatalError(new ExceptionDiagnostic(e));
+ throw options.reporter.fatalError(new ExceptionDiagnostic(e));
}
}
@@ -584,10 +582,8 @@
FeatureSplitConfiguration featureSplitConfiguration,
int nextDexIndex,
ZipOutputStream out,
- Reporter reporter,
- DexItemFactory dexItemFactory)
+ InternalOptions options)
throws IOException, ResourceException {
-
Map<FeatureSplit, String> featureSplitArchiveNames =
dumpFeatureSplitFileNames(featureSplitConfiguration);
Map<FeatureSplit, ByteArrayOutputStream> featureSplitArchiveByteStreams =
@@ -596,7 +592,7 @@
try {
ClassToFeatureSplitMap classToFeatureSplitMap =
ClassToFeatureSplitMap.createInitialClassToFeatureSplitMap(
- dexItemFactory, featureSplitConfiguration, reporter);
+ options.dexItemFactory(), featureSplitConfiguration, options.reporter);
if (featureSplitConfiguration != null) {
for (FeatureSplit featureSplit : featureSplitConfiguration.getFeatureSplits()) {
ByteArrayOutputStream archiveByteStream = new ByteArrayOutputStream();
@@ -624,11 +620,11 @@
nextDexIndex,
classDescriptor -> {
if (featureSplitConfiguration != null) {
- DexType type = dexItemFactory.createType(classDescriptor);
+ DexType type = options.dexItemFactory().createType(classDescriptor);
SyntheticItems syntheticItems = null;
FeatureSplit featureSplit =
classToFeatureSplitMap.getFeatureSplit(
- type, StartupOrder.empty(), syntheticItems);
+ type, options, StartupOrder.empty(), syntheticItems);
if (featureSplit != null && !featureSplit.isBase()) {
return featureSplitArchiveOutputStreams.get(featureSplit);
}
diff --git a/src/test/java/com/android/tools/r8/AndroidAppDumpsTest.java b/src/test/java/com/android/tools/r8/AndroidAppDumpsTest.java
index 28be79a..4fda093 100644
--- a/src/test/java/com/android/tools/r8/AndroidAppDumpsTest.java
+++ b/src/test/java/com/android/tools/r8/AndroidAppDumpsTest.java
@@ -68,7 +68,7 @@
.build();
Path dumpFile = temp.newFolder().toPath().resolve("dump.zip");
- appIn.dump(dumpFile, options.dumpOptions, options.reporter, options.dexItemFactory());
+ appIn.dump(dumpFile, options.dumpOptions, options);
AndroidApp appOut = AndroidApp.builder(options.reporter).addDump(dumpFile).build();
assertEquals(1, appOut.getClassProgramResourcesForTesting().size());