Use startup list in determination of when to return startup base
Change-Id: I1b1423bb997a8d9e21c361ea79f896b335826ea9
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 839545d..cb15cb2 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -10,7 +10,6 @@
import com.android.tools.r8.debuginfo.DebugRepresentation;
import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
import com.android.tools.r8.errors.InternalCompilerError;
-import com.android.tools.r8.experimental.startup.StartupClass;
import com.android.tools.r8.experimental.startup.StartupOrder;
import com.android.tools.r8.features.ClassToFeatureSplitMap;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
@@ -47,7 +46,6 @@
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMaps;
@@ -947,17 +945,7 @@
}
StartupOrder startupOrder = appView.appInfoWithClassHierarchy().getStartupOrder();
SyntheticItems syntheticItems = appView.getSyntheticItems();
- return clazz -> {
- if (syntheticItems.isSyntheticClass(clazz)) {
- return Iterables.any(
- syntheticItems.getSynthesizingContextTypes(clazz.getType()),
- startupOrder::containsSyntheticClassesSynthesizedFrom);
- } else {
- StartupClass<DexType> startupClass =
- StartupClass.<DexType>builder().setReference(clazz.getType()).build();
- return startupOrder.contains(startupClass);
- }
- };
+ return clazz -> startupOrder.contains(clazz.getType(), syntheticItems);
}
public List<DexProgramClass> getStartupClasses() {
diff --git a/src/main/java/com/android/tools/r8/dump/DumpOptions.java b/src/main/java/com/android/tools/r8/dump/DumpOptions.java
index a604608..da85b0e 100644
--- a/src/main/java/com/android/tools/r8/dump/DumpOptions.java
+++ b/src/main/java/com/android/tools/r8/dump/DumpOptions.java
@@ -6,7 +6,6 @@
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.dex.Marker.Tool;
-import com.android.tools.r8.experimental.startup.StartupConfiguration;
import com.android.tools.r8.features.FeatureSplitConfiguration;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
import com.android.tools.r8.shaking.ProguardConfiguration;
@@ -243,11 +242,6 @@
return featureSplitConfiguration;
}
- public StartupConfiguration getStartupConfiguration() {
- // The startup configuration is not included in dumps.
- return null;
- }
-
public String getParsedProguardConfiguration() {
return proguardConfiguration == null ? null : proguardConfiguration.getParsedConfiguration();
}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/EmptyStartupOrder.java b/src/main/java/com/android/tools/r8/experimental/startup/EmptyStartupOrder.java
index 00f0db2..3566ad1 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/EmptyStartupOrder.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/EmptyStartupOrder.java
@@ -17,12 +17,7 @@
EmptyStartupOrder() {}
@Override
- public boolean contains(StartupClass<DexType> startupClass) {
- return false;
- }
-
- @Override
- public boolean containsSyntheticClassesSynthesizedFrom(DexType synthesizingContextType) {
+ public boolean contains(DexType type, SyntheticItems syntheticItems) {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/NonEmptyStartupOrder.java b/src/main/java/com/android/tools/r8/experimental/startup/NonEmptyStartupOrder.java
index 1b8eab9..673e0de 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/NonEmptyStartupOrder.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/NonEmptyStartupOrder.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.synthesis.SyntheticItems;
import com.android.tools.r8.utils.LazyBox;
+import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Collection;
@@ -26,23 +27,42 @@
private final LinkedHashSet<StartupClass<DexType>> startupClasses;
+ // Redundant sets to allow efficient querying without boxing.
+ private final Set<DexType> nonSyntheticStartupClasses = Sets.newIdentityHashSet();
+ private final Set<DexType> syntheticStartupClasses = Sets.newIdentityHashSet();
+
NonEmptyStartupOrder(LinkedHashSet<StartupClass<DexType>> startupClasses) {
assert !startupClasses.isEmpty();
this.startupClasses = startupClasses;
+ for (StartupClass<DexType> startupClass : startupClasses) {
+ if (startupClass.isSynthetic()) {
+ syntheticStartupClasses.add(startupClass.getReference());
+ } else {
+ nonSyntheticStartupClasses.add(startupClass.getReference());
+ }
+ }
}
@Override
- public boolean contains(StartupClass<DexType> startupClass) {
- return startupClasses.contains(startupClass);
+ public boolean contains(DexType type, SyntheticItems syntheticItems) {
+ return syntheticItems.isSyntheticClass(type)
+ ? containsSyntheticClass(type, syntheticItems)
+ : containsNonSyntheticClass(type);
}
- @Override
- public boolean containsSyntheticClassesSynthesizedFrom(DexType synthesizingContextType) {
- return contains(
- StartupClass.<DexType>builder()
- .setReference(synthesizingContextType)
- .setSynthetic()
- .build());
+ private boolean containsNonSyntheticClass(DexType type) {
+ return nonSyntheticStartupClasses.contains(type);
+ }
+
+ private boolean containsSyntheticClass(DexType type, SyntheticItems syntheticItems) {
+ assert syntheticItems.isSyntheticClass(type);
+ return Iterables.any(
+ syntheticItems.getSynthesizingContextTypes(type),
+ this::containsSyntheticClassesSynthesizedFrom);
+ }
+
+ private boolean containsSyntheticClassesSynthesizedFrom(DexType synthesizingContextType) {
+ return syntheticStartupClasses.contains(synthesizingContextType);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupOrder.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupOrder.java
index 78126fd..5d67d8c 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupOrder.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupOrder.java
@@ -33,9 +33,7 @@
return new EmptyStartupOrder();
}
- public abstract boolean contains(StartupClass<DexType> startupClass);
-
- public abstract boolean containsSyntheticClassesSynthesizedFrom(DexType synthesizingContextType);
+ public abstract boolean contains(DexType type, SyntheticItems syntheticItems);
public abstract Collection<StartupClass<DexType>> getClasses();
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 b721f6f..2113b52 100644
--- a/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
+++ b/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
@@ -8,8 +8,6 @@
import com.android.tools.r8.ProgramResource;
import com.android.tools.r8.ProgramResourceProvider;
import com.android.tools.r8.ResourceException;
-import com.android.tools.r8.experimental.startup.StartupClass;
-import com.android.tools.r8.experimental.startup.StartupConfiguration;
import com.android.tools.r8.experimental.startup.StartupOrder;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
@@ -31,21 +29,18 @@
public class ClassToFeatureSplitMap {
- private final FeatureSplit baseStartup;
private final Map<DexType, FeatureSplit> classToFeatureSplitMap;
private final Map<FeatureSplit, String> representativeStringsForFeatureSplit;
private ClassToFeatureSplitMap(
- FeatureSplit baseStartup,
Map<DexType, FeatureSplit> classToFeatureSplitMap,
Map<FeatureSplit, String> representativeStringsForFeatureSplit) {
- this.baseStartup = baseStartup;
this.classToFeatureSplitMap = classToFeatureSplitMap;
this.representativeStringsForFeatureSplit = representativeStringsForFeatureSplit;
}
public static ClassToFeatureSplitMap createEmptyClassToFeatureSplitMap() {
- return new ClassToFeatureSplitMap(FeatureSplit.BASE, new IdentityHashMap<>(), null);
+ return new ClassToFeatureSplitMap(new IdentityHashMap<>(), null);
}
public static ClassToFeatureSplitMap createInitialClassToFeatureSplitMap(
@@ -53,73 +48,42 @@
return createInitialClassToFeatureSplitMap(
options.dexItemFactory(),
options.featureSplitConfiguration,
- options.getStartupOptions().getStartupConfiguration(),
options.reporter);
}
public static ClassToFeatureSplitMap createInitialClassToFeatureSplitMap(
DexItemFactory dexItemFactory,
FeatureSplitConfiguration featureSplitConfiguration,
- StartupConfiguration startupConfiguration,
Reporter reporter) {
- if (featureSplitConfiguration == null && startupConfiguration == null) {
+ if (featureSplitConfiguration == null) {
return createEmptyClassToFeatureSplitMap();
}
Map<DexType, FeatureSplit> classToFeatureSplitMap = new IdentityHashMap<>();
Map<FeatureSplit, String> representativeStringsForFeatureSplit = new IdentityHashMap<>();
- if (featureSplitConfiguration != null) {
- for (FeatureSplit featureSplit : featureSplitConfiguration.getFeatureSplits()) {
- String representativeType = null;
- for (ProgramResourceProvider programResourceProvider :
- featureSplit.getProgramResourceProviders()) {
- try {
- for (ProgramResource programResource : programResourceProvider.getProgramResources()) {
- for (String classDescriptor : programResource.getClassDescriptors()) {
- DexType type = dexItemFactory.createType(classDescriptor);
- classToFeatureSplitMap.put(type, featureSplit);
- if (representativeType == null
- || classDescriptor.compareTo(representativeType) > 0) {
- representativeType = classDescriptor;
- }
+ for (FeatureSplit featureSplit : featureSplitConfiguration.getFeatureSplits()) {
+ String representativeType = null;
+ for (ProgramResourceProvider programResourceProvider :
+ featureSplit.getProgramResourceProviders()) {
+ try {
+ for (ProgramResource programResource : programResourceProvider.getProgramResources()) {
+ for (String classDescriptor : programResource.getClassDescriptors()) {
+ DexType type = dexItemFactory.createType(classDescriptor);
+ classToFeatureSplitMap.put(type, featureSplit);
+ if (representativeType == null || classDescriptor.compareTo(representativeType) > 0) {
+ representativeType = classDescriptor;
}
}
- } catch (ResourceException e) {
- throw reporter.fatalError(e.getMessage());
}
- }
- if (representativeType != null) {
- representativeStringsForFeatureSplit.put(featureSplit, representativeType);
+ } catch (ResourceException e) {
+ throw reporter.fatalError(e.getMessage());
}
}
- }
-
- FeatureSplit baseStartup;
- if (startupConfiguration != null && startupConfiguration.hasStartupClasses()) {
- StartupClass<DexType> representativeStartupClass = null;
- for (StartupClass<DexType> startupClass : startupConfiguration.getStartupClasses()) {
- if (startupClass.isSynthetic()
- || classToFeatureSplitMap.containsKey(startupClass.getReference())) {
- continue;
- }
- classToFeatureSplitMap.put(startupClass.getReference(), FeatureSplit.BASE_STARTUP);
- if (representativeStartupClass == null
- || startupClass
- .getReference()
- .getDescriptor()
- .compareTo(representativeStartupClass.getReference().getDescriptor())
- > 0) {
- representativeStartupClass = startupClass;
- }
+ if (representativeType != null) {
+ representativeStringsForFeatureSplit.put(featureSplit, representativeType);
}
- baseStartup = FeatureSplit.BASE_STARTUP;
- representativeStringsForFeatureSplit.put(
- baseStartup, representativeStartupClass.getReference().toDescriptorString());
- } else {
- baseStartup = FeatureSplit.BASE;
}
- return new ClassToFeatureSplitMap(
- baseStartup, classToFeatureSplitMap, representativeStringsForFeatureSplit);
+ return new ClassToFeatureSplitMap(classToFeatureSplitMap, representativeStringsForFeatureSplit);
}
public int compareFeatureSplits(FeatureSplit featureSplitA, FeatureSplit featureSplitB) {
@@ -140,14 +104,6 @@
.compareTo(representativeStringsForFeatureSplit.get(featureSplitB));
}
- /**
- * Returns the base startup if there are any startup classes given on input. Otherwise returns
- * base.
- */
- public FeatureSplit getBaseStartup() {
- return baseStartup;
- }
-
public Map<FeatureSplit, Set<DexProgramClass>> getFeatureSplitClasses(
Set<DexProgramClass> classes, AppView<? extends AppInfoWithClassHierarchy> appView) {
return getFeatureSplitClasses(
@@ -184,23 +140,32 @@
public FeatureSplit getFeatureSplit(
DexType type, StartupOrder startupOrder, SyntheticItems syntheticItems) {
- FeatureSplit feature = classToFeatureSplitMap.get(type);
- if (feature != null) {
- assert !syntheticItems.isSyntheticClass(type);
- return feature;
+ if (syntheticItems == null) {
+ // Called from AndroidApp.dumpProgramResources().
+ assert startupOrder.isEmpty();
+ return classToFeatureSplitMap.getOrDefault(type, FeatureSplit.BASE);
}
- if (syntheticItems != null) {
- feature = syntheticItems.getContextualFeatureSplit(type, this);
- if (feature != null && !feature.isBase()) {
- return feature;
+ FeatureSplit feature;
+ boolean isSynthetic = syntheticItems.isSyntheticClass(type);
+ if (isSynthetic) {
+ assert !classToFeatureSplitMap.containsKey(type);
+ if (syntheticItems.isSyntheticOfKind(type, k -> k.ENUM_UNBOXING_SHARED_UTILITY_CLASS)) {
+ // Use the startup base if there is one, such that we don't merge non-startup classes with
+ // 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;
}
- for (DexType context : syntheticItems.getSynthesizingContextTypes(type)) {
- if (startupOrder.containsSyntheticClassesSynthesizedFrom(context)) {
- return FeatureSplit.BASE_STARTUP;
- }
- }
+ feature = syntheticItems.getContextualFeatureSplitOrDefault(type, FeatureSplit.BASE);
+ } else {
+ feature = classToFeatureSplitMap.getOrDefault(type, FeatureSplit.BASE);
}
- return FeatureSplit.BASE;
+ if (feature.isBase()) {
+ return startupOrder.contains(type, syntheticItems)
+ ? FeatureSplit.BASE_STARTUP
+ : FeatureSplit.BASE;
+ }
+ return feature;
}
// Note, this predicate may be misleading as the map does not include synthetics.
@@ -299,7 +264,7 @@
assert existing == null || existing == featureSplit;
});
return new ClassToFeatureSplitMap(
- baseStartup, rewrittenClassToFeatureSplitMap, representativeStringsForFeatureSplit);
+ rewrittenClassToFeatureSplitMap, representativeStringsForFeatureSplit);
}
public ClassToFeatureSplitMap withoutPrunedItems(PrunedItems prunedItems) {
@@ -311,7 +276,7 @@
}
});
return new ClassToFeatureSplitMap(
- baseStartup, rewrittenClassToFeatureSplitMap, representativeStringsForFeatureSplit);
+ rewrittenClassToFeatureSplitMap, representativeStringsForFeatureSplit);
}
// Static helpers to avoid verbose predicates.
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 2c2f503..08d9d27 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -433,18 +433,16 @@
return committed.containsSyntheticInput(clazz.getType());
}
- public FeatureSplit getContextualFeatureSplit(
- DexType type, ClassToFeatureSplitMap classToFeatureSplitMap) {
+ public FeatureSplit getContextualFeatureSplitOrDefault(DexType type, FeatureSplit defaultValue) {
+ assert isSyntheticClass(type);
if (isSyntheticOfKind(type, kinds -> kinds.ENUM_UNBOXING_SHARED_UTILITY_CLASS)) {
- // Use the startup base if there is one, such that we don't merge non-startup classes with 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 classToFeatureSplitMap.getBaseStartup();
+ return FeatureSplit.BASE;
}
List<SynthesizingContext> contexts = getSynthesizingContexts(type);
if (contexts.isEmpty()) {
- return null;
+ assert false
+ : "Expected synthetic to have at least one synthesizing context: " + type.getTypeName();
+ return defaultValue;
}
assert verifyAllHaveSameFeature(contexts, SynthesizingContext::getFeatureSplit);
return contexts.get(0).getFeatureSplit();
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 40c5a67..8060f36 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -34,7 +34,6 @@
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.InternalCompilerError;
import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.experimental.startup.StartupConfiguration;
import com.android.tools.r8.experimental.startup.StartupOrder;
import com.android.tools.r8.features.ClassToFeatureSplitMap;
import com.android.tools.r8.features.FeatureSplitConfiguration;
@@ -530,7 +529,6 @@
dumpProgramResources(
dumpProgramFileName,
options.getFeatureSplitConfiguration(),
- options.getStartupConfiguration(),
nextDexIndex,
out,
reporter,
@@ -584,7 +582,6 @@
private int dumpProgramResources(
String archiveName,
FeatureSplitConfiguration featureSplitConfiguration,
- StartupConfiguration startupConfiguration,
int nextDexIndex,
ZipOutputStream out,
Reporter reporter,
@@ -599,7 +596,7 @@
try {
ClassToFeatureSplitMap classToFeatureSplitMap =
ClassToFeatureSplitMap.createInitialClassToFeatureSplitMap(
- dexItemFactory, featureSplitConfiguration, startupConfiguration, reporter);
+ dexItemFactory, featureSplitConfiguration, reporter);
if (featureSplitConfiguration != null) {
for (FeatureSplit featureSplit : featureSplitConfiguration.getFeatureSplits()) {
ByteArrayOutputStream archiveByteStream = new ByteArrayOutputStream();