Prune methods from AppView in preparation for single caller inlined method pruning
Change-Id: Ie9d7581a8d873c5816fd9747a7259eb7abc83ca5
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 7f17170..c867e02 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -261,7 +261,7 @@
if (options.isGeneratingClassFiles()) {
ProguardMapSupplier proguardMapSupplier =
- finalizeApplication(inputApp, appView, namingLens);
+ finalizeApplication(inputApp, appView, executor, namingLens);
new CfApplicationWriter(
appView, marker, GraphLens.getIdentityLens(), namingLens, proguardMapSupplier)
.write(options.getClassFileConsumer());
@@ -306,7 +306,7 @@
appView.setAppInfo(appView.appInfo().rebuildWithMainDexInfo(mainDexInfo));
}
ProguardMapSupplier proguardMapSupplier =
- finalizeApplication(inputApp, appView, namingLens);
+ finalizeApplication(inputApp, appView, executor, namingLens);
new ApplicationWriter(
appView,
@@ -330,8 +330,12 @@
}
private static ProguardMapSupplier finalizeApplication(
- AndroidApp inputApp, AppView<AppInfo> appView, NamingLens namingLens) {
- SyntheticFinalization.finalize(appView);
+ AndroidApp inputApp,
+ AppView<AppInfo> appView,
+ ExecutorService executorService,
+ NamingLens namingLens)
+ throws ExecutionException {
+ SyntheticFinalization.finalize(appView, executorService);
if (appView.options().proguardMapConsumer == null) {
return null;
}
diff --git a/src/main/java/com/android/tools/r8/L8.java b/src/main/java/com/android/tools/r8/L8.java
index 8470275..9946047 100644
--- a/src/main/java/com/android/tools/r8/L8.java
+++ b/src/main/java/com/android/tools/r8/L8.java
@@ -141,7 +141,7 @@
new IRConverter(appView, timing).convert(appView, executor);
- SyntheticFinalization.finalize(appView);
+ SyntheticFinalization.finalize(appView, executor);
NamingLens namingLens = PrefixRewritingNamingLens.createPrefixRewritingNamingLens(appView);
new GenericSignatureRewriter(appView, namingLens).run(appView.appInfo().classes(), executor);
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 3eab5e6..ec0f361 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -419,7 +419,8 @@
.setPrunedApp(prunedApp)
.addRemovedClasses(removedClasses)
.addAdditionalPinnedItems(pruner.getMethodsToKeepForConfigurationDebugging())
- .build());
+ .build(),
+ executorService);
new AbstractMethodRemover(
appViewWithLiveness, appViewWithLiveness.appInfo().computeSubtypingInfo())
.run();
@@ -618,7 +619,8 @@
.setPrunedApp(application)
.addRemovedClasses(CollectionUtils.mergeSets(prunedTypes, removedClasses))
.addAdditionalPinnedItems(pruner.getMethodsToKeepForConfigurationDebugging())
- .build());
+ .build(),
+ executorService);
new BridgeHoisting(appViewWithLiveness).run();
@@ -728,9 +730,9 @@
}
if (appView.appInfo().hasLiveness()) {
- SyntheticFinalization.finalizeWithLiveness(appView.withLiveness());
+ SyntheticFinalization.finalizeWithLiveness(appView.withLiveness(), executorService);
} else {
- SyntheticFinalization.finalizeWithClassHierarchy(appView);
+ SyntheticFinalization.finalizeWithClassHierarchy(appView, executorService);
}
// Clear the reference type lattice element cache. This is required since class merging may
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index df45e6b..e5a744b 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -15,6 +15,8 @@
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.IterableUtils;
import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
import java.util.function.Consumer;
public class AppInfo implements DexDefinitionSupplier {
@@ -63,7 +65,8 @@
this.obsolete = obsolete;
}
- public AppInfo prunedCopyFrom(PrunedItems prunedItems) {
+ public AppInfo prunedCopyFrom(PrunedItems prunedItems, ExecutorService executorService)
+ throws ExecutionException {
assert getClass() == AppInfo.class;
assert checkIfObsolete();
assert prunedItems.getPrunedApp() == app();
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
index 12a5e8d..a13b905 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
@@ -40,6 +40,8 @@
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
import java.util.function.Function;
/* Specific subclass of AppInfo designed to support desugaring in D8. Desugaring requires a
@@ -123,7 +125,8 @@
}
@Override
- public AppInfoWithClassHierarchy prunedCopyFrom(PrunedItems prunedItems) {
+ public AppInfoWithClassHierarchy prunedCopyFrom(
+ PrunedItems prunedItems, ExecutorService executorService) throws ExecutionException {
assert getClass() == AppInfoWithClassHierarchy.class;
assert checkIfObsolete();
assert prunedItems.getPrunedApp() == app();
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 0e165f7..4d3d50a 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -51,6 +51,8 @@
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
@@ -687,19 +689,20 @@
return !cfByteCodePassThrough.isEmpty();
}
- public void pruneItems(PrunedItems prunedItems) {
+ public void pruneItems(PrunedItems prunedItems, ExecutorService executorService)
+ throws ExecutionException {
if (prunedItems.isEmpty()) {
assert appInfo().app() == prunedItems.getPrunedApp();
return;
}
if (appInfo.hasLiveness()) {
AppView<AppInfoWithLiveness> self = withLiveness();
- self.setAppInfo(self.appInfo().prunedCopyFrom(prunedItems));
+ self.setAppInfo(self.appInfo().prunedCopyFrom(prunedItems, executorService));
} else if (appInfo.hasClassHierarchy()) {
AppView<AppInfoWithClassHierarchy> self = withClassHierarchy();
- self.setAppInfo(self.appInfo().prunedCopyFrom(prunedItems));
+ self.setAppInfo(self.appInfo().prunedCopyFrom(prunedItems, executorService));
} else {
- pruneAppInfo(prunedItems, this);
+ pruneAppInfo(prunedItems, this, executorService);
}
if (appServices() != null) {
setAppServices(appServices().prunedCopy(prunedItems));
@@ -714,8 +717,11 @@
}
@SuppressWarnings("unchecked")
- private static void pruneAppInfo(PrunedItems prunedItems, AppView<?> appView) {
- ((AppView<AppInfo>) appView).setAppInfo(appView.appInfo().prunedCopyFrom(prunedItems));
+ private static void pruneAppInfo(
+ PrunedItems prunedItems, AppView<?> appView, ExecutorService executorService)
+ throws ExecutionException {
+ ((AppView<AppInfo>) appView)
+ .setAppInfo(appView.appInfo().prunedCopyFrom(prunedItems, executorService));
}
public void rewriteWithLens(NonIdentityGraphLens lens) {
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLens.java b/src/main/java/com/android/tools/r8/graph/GraphLens.java
index fdba79b..f0675ba 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -26,7 +26,6 @@
import it.unimi.dsi.fastutil.objects.Object2BooleanArrayMap;
import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
@@ -564,9 +563,9 @@
this::lookupType, this::getRenamedFieldSignature, this::getRenamedMethodSignature);
}
- public Set<DexReference> rewriteReferences(Set<DexReference> references) {
- Set<DexReference> result = SetUtils.newIdentityHashSet(references.size());
- for (DexReference reference : references) {
+ public <T extends DexReference> Set<T> rewriteReferences(Set<T> references) {
+ Set<T> result = SetUtils.newIdentityHashSet(references.size());
+ for (T reference : references) {
result.add(rewriteReference(reference));
}
return result;
@@ -602,30 +601,15 @@
return result;
}
- public Object2BooleanMap<DexReference> rewriteReferenceKeys(Object2BooleanMap<DexReference> map) {
- Object2BooleanMap<DexReference> result = new Object2BooleanArrayMap<>();
- for (Object2BooleanMap.Entry<DexReference> entry : map.object2BooleanEntrySet()) {
+ public <T extends DexReference> Object2BooleanMap<T> rewriteReferenceKeys(
+ Object2BooleanMap<T> map) {
+ Object2BooleanMap<T> result = new Object2BooleanArrayMap<>();
+ for (Object2BooleanMap.Entry<T> entry : map.object2BooleanEntrySet()) {
result.put(rewriteReference(entry.getKey()), entry.getBooleanValue());
}
return result;
}
- public ImmutableSet<DexMethod> rewriteMethods(Set<DexMethod> methods) {
- ImmutableSet.Builder<DexMethod> builder = ImmutableSet.builder();
- for (DexMethod method : methods) {
- builder.add(getRenamedMethodSignature(method));
- }
- return builder.build();
- }
-
- public ImmutableSet<DexField> rewriteFields(Set<DexField> fields) {
- ImmutableSet.Builder<DexField> builder = ImmutableSet.builder();
- for (DexField field : fields) {
- builder.add(getRenamedFieldSignature(field));
- }
- return builder.build();
- }
-
public <T> ImmutableMap<DexField, T> rewriteFieldKeys(Map<DexField, T> map) {
ImmutableMap.Builder<DexField, T> builder = ImmutableMap.builder();
map.forEach((field, value) -> builder.put(getRenamedFieldSignature(field), value));
@@ -649,7 +633,7 @@
newMap.put(
rewrittenType, previousValue != null ? merge.apply(value, previousValue) : value);
});
- return Collections.unmodifiableMap(newMap);
+ return newMap;
}
public boolean verifyMappingToOriginalProgram(
diff --git a/src/main/java/com/android/tools/r8/graph/PrunedItems.java b/src/main/java/com/android/tools/r8/graph/PrunedItems.java
index 1f8aa68..4489421 100644
--- a/src/main/java/com/android/tools/r8/graph/PrunedItems.java
+++ b/src/main/java/com/android/tools/r8/graph/PrunedItems.java
@@ -41,15 +41,18 @@
}
public boolean isEmpty() {
- return removedClasses.isEmpty() && additionalPinnedItems.isEmpty();
+ return removedClasses.isEmpty()
+ && removedFields.isEmpty()
+ && removedMethods.isEmpty()
+ && additionalPinnedItems.isEmpty();
}
public boolean isRemoved(DexField field) {
- return removedFields.contains(field);
+ return removedFields.contains(field) || removedClasses.contains(field.getHolderType());
}
public boolean isRemoved(DexMethod method) {
- return removedMethods.contains(method);
+ return removedMethods.contains(method) || removedClasses.contains(method.getHolderType());
}
public boolean isRemoved(DexType type) {
@@ -72,10 +75,26 @@
return !removedClasses.isEmpty();
}
+ public boolean hasRemovedFields() {
+ return !removedFields.isEmpty();
+ }
+
+ public boolean hasRemovedMembers() {
+ return hasRemovedFields() || hasRemovedMethods();
+ }
+
+ public boolean hasRemovedMethods() {
+ return !removedMethods.isEmpty();
+ }
+
public Set<DexType> getRemovedClasses() {
return removedClasses;
}
+ public Set<DexField> getRemovedFields() {
+ return removedFields;
+ }
+
public Set<DexMethod> getRemovedMethods() {
return removedMethods;
}
@@ -86,7 +105,7 @@
private final Set<DexReference> additionalPinnedItems = Sets.newIdentityHashSet();
private final Set<DexType> noLongerSyntheticItems = Sets.newIdentityHashSet();
- private final Set<DexType> removedClasses = Sets.newIdentityHashSet();
+ private Set<DexType> removedClasses = Sets.newIdentityHashSet();
private final Set<DexField> removedFields = Sets.newIdentityHashSet();
private final Set<DexMethod> removedMethods = Sets.newIdentityHashSet();
@@ -122,6 +141,11 @@
return this;
}
+ public Builder setRemovedClasses(Set<DexType> removedClasses) {
+ this.removedClasses = removedClasses;
+ return this;
+ }
+
public PrunedItems build() {
return new PrunedItems(
prunedApp,
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
index 666bd64..2f9a4ee 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -123,7 +123,10 @@
// Prune keep info.
KeepInfoCollection keepInfo = appView.getKeepInfo();
- keepInfo.mutate(mutator -> mutator.removeKeepInfoForPrunedItems(mergedClasses.getSources()));
+ keepInfo.mutate(
+ mutator ->
+ mutator.removeKeepInfoForPrunedItems(
+ PrunedItems.builder().setRemovedClasses(mergedClasses.getSources()).build()));
// Must rewrite AppInfoWithLiveness before pruning the merged classes, to ensure that allocation
// sites, fields accesses, etc. are correctly transferred to the target classes.
@@ -142,7 +145,8 @@
.setPrunedApp(appView.appInfo().app())
.addRemovedClasses(mergedClasses.getSources())
.addNoLongerSyntheticItems(mergedClasses.getSources())
- .build());
+ .build(),
+ executorService);
}
private FieldAccessInfoCollectionModifier createFieldAccessInfoCollectionModifier(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
index 854a4e0..ea6b5e5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
@@ -41,6 +41,7 @@
import com.android.tools.r8.graph.FieldResolutionResult;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues.EnumStaticFieldValues;
import com.android.tools.r8.ir.analysis.type.ArrayTypeElement;
@@ -705,7 +706,10 @@
private void updateKeepInfo(Set<DexType> enumsToUnbox) {
KeepInfoCollection keepInfo = appView.appInfo().getKeepInfo();
- keepInfo.mutate(mutator -> mutator.removeKeepInfoForPrunedItems(enumsToUnbox));
+ keepInfo.mutate(
+ mutator ->
+ mutator.removeKeepInfoForPrunedItems(
+ PrunedItems.builder().setRemovedClasses(enumsToUnbox).build()));
}
public EnumDataMap finishAnalysis() {
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
index e0de1d5..391f2c7 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMember;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexString;
@@ -52,7 +53,7 @@
public class IdentifierNameStringMarker {
private final AppView<AppInfoWithLiveness> appView;
- private final Object2BooleanMap<DexReference> identifierNameStrings;
+ private final Object2BooleanMap<DexMember<?, ?>> identifierNameStrings;
public IdentifierNameStringMarker(AppView<AppInfoWithLiveness> appView) {
this.appView = appView;
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 45630fa..6f3e907 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -58,6 +58,7 @@
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.PredicateSet;
+import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.TraversalContinuation;
import com.android.tools.r8.utils.Visibility;
import com.android.tools.r8.utils.WorkList;
@@ -68,12 +69,16 @@
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
import java.util.ArrayDeque;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@@ -179,13 +184,13 @@
* All methods and fields whose value *must* never be propagated due to a configuration directive.
* (testing only).
*/
- private final Set<DexReference> neverPropagateValue;
+ private final Set<DexMember<?, ?>> neverPropagateValue;
/**
* All items with -identifiernamestring rule. Bound boolean value indicates the rule is explicitly
* specified by users (<code>true</code>) or not, i.e., implicitly added by R8 (<code>false</code>
* ).
*/
- public final Object2BooleanMap<DexReference> identifierNameStrings;
+ public final Object2BooleanMap<DexMember<?, ?>> identifierNameStrings;
/** A set of types that have been removed by the {@link TreePruner}. */
final Set<DexType> prunedTypes;
/** A map from switchmap class types to their corresponding switchmaps. */
@@ -230,8 +235,8 @@
Set<DexType> noClassMerging,
Set<DexType> noVerticalClassMerging,
Set<DexType> noHorizontalClassMerging,
- Set<DexReference> neverPropagateValue,
- Object2BooleanMap<DexReference> identifierNameStrings,
+ Set<DexMember<?, ?>> neverPropagateValue,
+ Object2BooleanMap<DexMember<?, ?>> identifierNameStrings,
Set<DexType> prunedTypes,
Map<DexField, Int2ReferenceMap<DexField>> switchMaps,
Set<DexType> lockCandidates,
@@ -283,7 +288,7 @@
previous.getMainDexInfo(),
previous.deadProtoTypes,
previous.getMissingClasses(),
- CollectionUtils.mergeSets(previous.liveTypes, committedItems.getCommittedProgramTypes()),
+ CollectionUtils.addAll(previous.liveTypes, committedItems.getCommittedProgramTypes()),
previous.targetedMethods,
previous.failedMethodResolutionTargets,
previous.failedFieldResolutionTargets,
@@ -320,52 +325,208 @@
previous.initClassReferences);
}
- private AppInfoWithLiveness(AppInfoWithLiveness previous, PrunedItems prunedItems) {
+ private AppInfoWithLiveness(
+ AppInfoWithLiveness previous,
+ PrunedItems prunedItems,
+ ExecutorService executorService,
+ List<Future<?>> futures) {
this(
previous.getSyntheticItems().commitPrunedItems(prunedItems),
previous.getClassToFeatureSplitMap().withoutPrunedItems(prunedItems),
previous.getMainDexInfo().withoutPrunedItems(prunedItems),
previous.deadProtoTypes,
previous.getMissingClasses(),
- prunedItems.hasRemovedClasses()
- ? Sets.difference(previous.liveTypes, prunedItems.getRemovedClasses())
- : previous.liveTypes,
- previous.targetedMethods,
- previous.failedMethodResolutionTargets,
- previous.failedFieldResolutionTargets,
- previous.bootstrapMethods,
- previous.methodsTargetedByInvokeDynamic,
- previous.virtualMethodsTargetedByInvokeDirect,
- previous.liveMethods,
+ pruneClasses(previous.liveTypes, prunedItems, executorService, futures),
+ pruneMethods(previous.targetedMethods, prunedItems, executorService, futures),
+ pruneMethods(previous.failedMethodResolutionTargets, prunedItems, executorService, futures),
+ pruneFields(previous.failedFieldResolutionTargets, prunedItems, executorService, futures),
+ pruneMethods(previous.bootstrapMethods, prunedItems, executorService, futures),
+ pruneMethods(
+ previous.methodsTargetedByInvokeDynamic, prunedItems, executorService, futures),
+ pruneMethods(
+ previous.virtualMethodsTargetedByInvokeDirect, prunedItems, executorService, futures),
+ pruneMethods(previous.liveMethods, prunedItems, executorService, futures),
previous.fieldAccessInfoCollection,
previous.methodAccessInfoCollection,
previous.objectAllocationInfoCollection,
previous.callSites,
extendPinnedItems(previous, prunedItems.getAdditionalPinnedItems()),
previous.mayHaveSideEffects,
- previous.noSideEffects,
- previous.assumedValues,
- previous.alwaysInline,
- previous.neverInline,
- previous.neverInlineDueToSingleCaller,
- previous.whyAreYouNotInlining,
- previous.keepConstantArguments,
- previous.keepUnusedArguments,
- previous.reprocess,
- previous.neverReprocess,
+ pruneMapFromMembers(previous.noSideEffects, prunedItems, executorService, futures),
+ pruneMapFromMembers(previous.assumedValues, prunedItems, executorService, futures),
+ pruneMethods(previous.alwaysInline, prunedItems, executorService, futures),
+ pruneMethods(previous.neverInline, prunedItems, executorService, futures),
+ pruneMethods(previous.neverInlineDueToSingleCaller, prunedItems, executorService, futures),
+ pruneMethods(previous.whyAreYouNotInlining, prunedItems, executorService, futures),
+ pruneMethods(previous.keepConstantArguments, prunedItems, executorService, futures),
+ pruneMethods(previous.keepUnusedArguments, prunedItems, executorService, futures),
+ pruneMethods(previous.reprocess, prunedItems, executorService, futures),
+ pruneMethods(previous.neverReprocess, prunedItems, executorService, futures),
previous.alwaysClassInline,
- previous.neverClassInline,
- previous.noClassMerging,
- previous.noVerticalClassMerging,
- previous.noHorizontalClassMerging,
- previous.neverPropagateValue,
- previous.identifierNameStrings,
+ pruneClasses(previous.neverClassInline, prunedItems, executorService, futures),
+ pruneClasses(previous.noClassMerging, prunedItems, executorService, futures),
+ pruneClasses(previous.noVerticalClassMerging, prunedItems, executorService, futures),
+ pruneClasses(previous.noHorizontalClassMerging, prunedItems, executorService, futures),
+ pruneMembers(previous.neverPropagateValue, prunedItems, executorService, futures),
+ pruneMapFromMembers(previous.identifierNameStrings, prunedItems, executorService, futures),
prunedItems.hasRemovedClasses()
? CollectionUtils.mergeSets(previous.prunedTypes, prunedItems.getRemovedClasses())
: previous.prunedTypes,
previous.switchMaps,
- previous.lockCandidates,
- previous.initClassReferences);
+ pruneClasses(previous.lockCandidates, prunedItems, executorService, futures),
+ pruneMapFromClasses(previous.initClassReferences, prunedItems, executorService, futures));
+ }
+
+ private static Set<DexType> pruneClasses(
+ Set<DexType> methods,
+ PrunedItems prunedItems,
+ ExecutorService executorService,
+ List<Future<?>> futures) {
+ return pruneItems(methods, prunedItems.getRemovedClasses(), executorService, futures);
+ }
+
+ private static Set<DexField> pruneFields(
+ Set<DexField> fields,
+ PrunedItems prunedItems,
+ ExecutorService executorService,
+ List<Future<?>> futures) {
+ return pruneItems(fields, prunedItems.getRemovedFields(), executorService, futures);
+ }
+
+ private static Set<DexMember<?, ?>> pruneMembers(
+ Set<DexMember<?, ?>> members,
+ PrunedItems prunedItems,
+ ExecutorService executorService,
+ List<Future<?>> futures) {
+ if (prunedItems.hasRemovedMembers()) {
+ futures.add(
+ ThreadUtils.processAsynchronously(
+ () -> {
+ Set<DexField> removedFields = prunedItems.getRemovedFields();
+ Set<DexMethod> removedMethods = prunedItems.getRemovedMethods();
+ if (members.size() <= removedFields.size() + removedMethods.size()) {
+ members.removeIf(
+ member ->
+ member.isDexField()
+ ? removedFields.contains(member.asDexField())
+ : removedMethods.contains(member.asDexMethod()));
+ } else {
+ removedFields.forEach(members::remove);
+ removedMethods.forEach(members::remove);
+ }
+ },
+ executorService));
+ }
+ return members;
+ }
+
+ private static Set<DexMethod> pruneMethods(
+ Set<DexMethod> methods,
+ PrunedItems prunedItems,
+ ExecutorService executorService,
+ List<Future<?>> futures) {
+ return pruneItems(methods, prunedItems.getRemovedMethods(), executorService, futures);
+ }
+
+ private static <T> Set<T> pruneItems(
+ Set<T> items, Set<T> removedItems, ExecutorService executorService, List<Future<?>> futures) {
+ if (!removedItems.isEmpty()) {
+ futures.add(
+ ThreadUtils.processAsynchronously(
+ () -> {
+ if (items.size() <= removedItems.size()) {
+ items.removeAll(removedItems);
+ } else {
+ removedItems.forEach(items::remove);
+ }
+ },
+ executorService));
+ }
+ return items;
+ }
+
+ private static <V> Map<DexType, V> pruneMapFromClasses(
+ Map<DexType, V> map,
+ PrunedItems prunedItems,
+ ExecutorService executorService,
+ List<Future<?>> futures) {
+ return pruneMap(map, prunedItems.getRemovedClasses(), executorService, futures);
+ }
+
+ private static <V> Map<DexMember<?, ?>, V> pruneMapFromMembers(
+ Map<DexMember<?, ?>, V> map,
+ PrunedItems prunedItems,
+ ExecutorService executorService,
+ List<Future<?>> futures) {
+ if (prunedItems.hasRemovedMembers()) {
+ futures.add(
+ ThreadUtils.processAsynchronously(
+ () -> {
+ Set<DexField> removedFields = prunedItems.getRemovedFields();
+ Set<DexMethod> removedMethods = prunedItems.getRemovedMethods();
+ if (map.size() <= removedFields.size() + removedMethods.size()) {
+ map.keySet()
+ .removeIf(
+ member ->
+ member.isDexField()
+ ? removedFields.contains(member.asDexField())
+ : removedMethods.contains(member.asDexMethod()));
+ } else {
+ removedFields.forEach(map::remove);
+ removedMethods.forEach(map::remove);
+ }
+ },
+ executorService));
+ }
+ return map;
+ }
+
+ private static Object2BooleanMap<DexMember<?, ?>> pruneMapFromMembers(
+ Object2BooleanMap<DexMember<?, ?>> map,
+ PrunedItems prunedItems,
+ ExecutorService executorService,
+ List<Future<?>> futures) {
+ if (prunedItems.hasRemovedMembers()) {
+ futures.add(
+ ThreadUtils.processAsynchronously(
+ () -> {
+ Set<DexField> removedFields = prunedItems.getRemovedFields();
+ Set<DexMethod> removedMethods = prunedItems.getRemovedMethods();
+ if (map.size() <= removedFields.size() + removedMethods.size()) {
+ map.keySet()
+ .removeIf(
+ member ->
+ member.isDexField()
+ ? removedFields.contains(member.asDexField())
+ : removedMethods.contains(member.asDexMethod()));
+ } else {
+ removedFields.forEach(map::remove);
+ removedMethods.forEach(map::remove);
+ }
+ },
+ executorService));
+ }
+ return map;
+ }
+
+ private static <K, V> Map<K, V> pruneMap(
+ Map<K, V> map,
+ Set<K> removedItems,
+ ExecutorService executorService,
+ List<Future<?>> futures) {
+ if (!removedItems.isEmpty()) {
+ futures.add(
+ ThreadUtils.processAsynchronously(
+ () -> {
+ if (map.size() <= removedItems.size()) {
+ map.keySet().removeAll(removedItems);
+ } else {
+ removedItems.forEach(map::remove);
+ }
+ },
+ executorService));
+ }
+ return map;
}
private boolean verify() {
@@ -1038,7 +1199,8 @@
* DexApplication object.
*/
@Override
- public AppInfoWithLiveness prunedCopyFrom(PrunedItems prunedItems) {
+ public AppInfoWithLiveness prunedCopyFrom(
+ PrunedItems prunedItems, ExecutorService executorService) throws ExecutionException {
assert getClass() == AppInfoWithLiveness.class;
assert checkIfObsolete();
if (prunedItems.isEmpty()) {
@@ -1049,14 +1211,15 @@
// Rebuild the hierarchy.
objectAllocationInfoCollection.mutate(
mutator -> mutator.removeAllocationsForPrunedItems(prunedItems), this);
- keepInfo.mutate(
- keepInfo -> keepInfo.removeKeepInfoForPrunedItems(prunedItems.getRemovedClasses()));
+ keepInfo.mutate(keepInfo -> keepInfo.removeKeepInfoForPrunedItems(prunedItems));
+ } else if (prunedItems.hasRemovedMembers()) {
+ keepInfo.mutate(keepInfo -> keepInfo.removeKeepInfoForPrunedItems(prunedItems));
}
- if (!prunedItems.getRemovedMethods().isEmpty()) {
- keepInfo.mutate(
- keepInfo -> keepInfo.removeKeepInfoForPrunedItems(prunedItems.getRemovedMethods()));
- }
- return new AppInfoWithLiveness(this, prunedItems);
+ List<Future<?>> futures = new ArrayList<>();
+ AppInfoWithLiveness appInfoWithLiveness =
+ new AppInfoWithLiveness(this, prunedItems, executorService, futures);
+ ThreadUtils.awaitFutures(futures);
+ return appInfoWithLiveness;
}
public AppInfoWithLiveness rebuildWithLiveness(CommittedItems committedItems) {
@@ -1084,14 +1247,14 @@
getMainDexInfo().rewrittenWithLens(getSyntheticItems(), lens),
deadProtoTypes,
getMissingClasses().commitSyntheticItems(committedItems),
- lens.rewriteTypes(liveTypes),
- lens.rewriteMethods(targetedMethods),
- lens.rewriteMethods(failedMethodResolutionTargets),
- lens.rewriteFields(failedFieldResolutionTargets),
- lens.rewriteMethods(bootstrapMethods),
- lens.rewriteMethods(methodsTargetedByInvokeDynamic),
- lens.rewriteMethods(virtualMethodsTargetedByInvokeDirect),
- lens.rewriteMethods(liveMethods),
+ lens.rewriteReferences(liveTypes),
+ lens.rewriteReferences(targetedMethods),
+ lens.rewriteReferences(failedMethodResolutionTargets),
+ lens.rewriteReferences(failedFieldResolutionTargets),
+ lens.rewriteReferences(bootstrapMethods),
+ lens.rewriteReferences(methodsTargetedByInvokeDynamic),
+ lens.rewriteReferences(virtualMethodsTargetedByInvokeDirect),
+ lens.rewriteReferences(liveMethods),
fieldAccessInfoCollection.rewrittenWithLens(definitionSupplier, lens),
methodAccessInfoCollection.rewrittenWithLens(definitionSupplier, lens),
objectAllocationInfoCollection.rewrittenWithLens(definitionSupplier, lens),
@@ -1102,25 +1265,25 @@
// Drop assume rules in case of collisions.
lens.rewriteReferenceKeys(noSideEffects, rules -> null),
lens.rewriteReferenceKeys(assumedValues, rules -> null),
- lens.rewriteMethods(alwaysInline),
- lens.rewriteMethods(neverInline),
- lens.rewriteMethods(neverInlineDueToSingleCaller),
- lens.rewriteMethods(whyAreYouNotInlining),
- lens.rewriteMethods(keepConstantArguments),
- lens.rewriteMethods(keepUnusedArguments),
- lens.rewriteMethods(reprocess),
- lens.rewriteMethods(neverReprocess),
+ lens.rewriteReferences(alwaysInline),
+ lens.rewriteReferences(neverInline),
+ lens.rewriteReferences(neverInlineDueToSingleCaller),
+ lens.rewriteReferences(whyAreYouNotInlining),
+ lens.rewriteReferences(keepConstantArguments),
+ lens.rewriteReferences(keepUnusedArguments),
+ lens.rewriteReferences(reprocess),
+ lens.rewriteReferences(neverReprocess),
alwaysClassInline.rewriteItems(lens::lookupType),
- lens.rewriteTypes(neverClassInline),
- lens.rewriteTypes(noClassMerging),
- lens.rewriteTypes(noVerticalClassMerging),
- lens.rewriteTypes(noHorizontalClassMerging),
+ lens.rewriteReferences(neverClassInline),
+ lens.rewriteReferences(noClassMerging),
+ lens.rewriteReferences(noVerticalClassMerging),
+ lens.rewriteReferences(noHorizontalClassMerging),
lens.rewriteReferences(neverPropagateValue),
lens.rewriteReferenceKeys(identifierNameStrings),
// Don't rewrite pruned types - the removed types are identified by their original name.
prunedTypes,
lens.rewriteFieldKeys(switchMaps),
- lens.rewriteTypes(lockCandidates),
+ lens.rewriteReferences(lockCandidates),
rewriteInitClassReferences(lens));
}
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index ec6c061..a7b0a76 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -261,7 +261,7 @@
private final ObjectAllocationInfoCollectionImpl.Builder objectAllocationInfoCollection;
private final Map<DexCallSite, ProgramMethodSet> callSites = new IdentityHashMap<>();
- private final Set<DexReference> identifierNameStrings = Sets.newIdentityHashSet();
+ private final Set<DexMember<?, ?>> identifierNameStrings = Sets.newIdentityHashSet();
private final AndroidApiLevelCompute apiLevelCompute;
@@ -3830,13 +3830,13 @@
return builder.build();
}
- private static Object2BooleanMap<DexReference> joinIdentifierNameStrings(
- Set<DexReference> explicit, Set<DexReference> implicit) {
- Object2BooleanMap<DexReference> result = new Object2BooleanArrayMap<>();
- for (DexReference e : explicit) {
+ private static Object2BooleanMap<DexMember<?, ?>> joinIdentifierNameStrings(
+ Set<DexMember<?, ?>> explicit, Set<DexMember<?, ?>> implicit) {
+ Object2BooleanMap<DexMember<?, ?>> result = new Object2BooleanArrayMap<>();
+ for (DexMember<?, ?> e : explicit) {
result.putIfAbsent(e, true);
}
- for (DexReference i : implicit) {
+ for (DexMember<?, ?> i : implicit) {
result.putIfAbsent(i, false);
}
return result;
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
index c260f3e..36555ac 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
@@ -23,6 +23,7 @@
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMember;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.shaking.KeepFieldInfo.Joiner;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.MapUtils;
@@ -244,20 +245,16 @@
this.methodRuleInstances = methodRuleInstances;
}
- public void removeKeepInfoForPrunedItems(Set<? extends DexReference> removedReferences) {
- keepClassInfo.keySet().removeIf(removedReferences::contains);
- keepFieldInfo
- .keySet()
- .removeIf(
- field ->
- (removedReferences.contains(field)
- || removedReferences.contains(field.getHolderType())));
- keepMethodInfo
- .keySet()
- .removeIf(
- method ->
- (removedReferences.contains(method)
- || removedReferences.contains(method.getHolderType())));
+ public void removeKeepInfoForPrunedItems(PrunedItems prunedItems) {
+ if (prunedItems.hasRemovedClasses()) {
+ keepClassInfo.keySet().removeAll(prunedItems.getRemovedClasses());
+ }
+ if (prunedItems.hasRemovedClasses() || prunedItems.hasRemovedFields()) {
+ keepFieldInfo.keySet().removeIf(prunedItems::isRemoved);
+ }
+ if (prunedItems.hasRemovedClasses() || prunedItems.hasRemovedMembers()) {
+ keepMethodInfo.keySet().removeIf(prunedItems::isRemoved);
+ }
}
@Override
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
index 3dc1bc5..b105a80 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -122,14 +122,14 @@
private final Set<DexType> noUnusedInterfaceRemoval = Sets.newIdentityHashSet();
private final Set<DexType> noVerticalClassMerging = Sets.newIdentityHashSet();
private final Set<DexType> noHorizontalClassMerging = Sets.newIdentityHashSet();
- private final Set<DexReference> neverPropagateValue = Sets.newIdentityHashSet();
+ private final Set<DexMember<?, ?>> neverPropagateValue = Sets.newIdentityHashSet();
private final Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule =
new IdentityHashMap<>();
private final Map<DexReference, ProguardMemberRule> mayHaveSideEffects =
new IdentityHashMap<>();
private final Map<DexMember<?, ?>, ProguardMemberRule> noSideEffects = new IdentityHashMap<>();
private final Map<DexMember<?, ?>, ProguardMemberRule> assumedValues = new IdentityHashMap<>();
- private final Set<DexReference> identifierNameStrings = Sets.newIdentityHashSet();
+ private final Set<DexMember<?, ?>> identifierNameStrings = Sets.newIdentityHashSet();
private final Queue<DelayedRootSetActionItem> delayedRootSetActionItems =
new ConcurrentLinkedQueue<>();
private final InternalOptions options;
@@ -1612,11 +1612,11 @@
public final Set<DexType> noUnusedInterfaceRemoval;
public final Set<DexType> noVerticalClassMerging;
public final Set<DexType> noHorizontalClassMerging;
- public final Set<DexReference> neverPropagateValue;
+ public final Set<DexMember<?, ?>> neverPropagateValue;
public final Map<DexReference, ProguardMemberRule> mayHaveSideEffects;
public final Map<DexMember<?, ?>, ProguardMemberRule> noSideEffects;
public final Map<DexMember<?, ?>, ProguardMemberRule> assumedValues;
- public final Set<DexReference> identifierNameStrings;
+ public final Set<DexMember<?, ?>> identifierNameStrings;
public final Set<ProguardIfRule> ifRules;
private RootSet(
@@ -1636,12 +1636,12 @@
Set<DexType> noUnusedInterfaceRemoval,
Set<DexType> noVerticalClassMerging,
Set<DexType> noHorizontalClassMerging,
- Set<DexReference> neverPropagateValue,
+ Set<DexMember<?, ?>> neverPropagateValue,
Map<DexReference, ProguardMemberRule> mayHaveSideEffects,
Map<DexMember<?, ?>, ProguardMemberRule> noSideEffects,
Map<DexMember<?, ?>, ProguardMemberRule> assumedValues,
Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule,
- Set<DexReference> identifierNameStrings,
+ Set<DexMember<?, ?>> identifierNameStrings,
Set<ProguardIfRule> ifRules,
List<DelayedRootSetActionItem> delayedRootSetActionItems,
ProgramMethodMap<ProgramMethod> pendingMethodMoveInverse) {
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 583efa7..e157ff8 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -48,6 +48,7 @@
import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.graph.ObjectAllocationInfoCollection;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.graph.RewrittenPrototypeDescription;
import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.graph.TopDownClassHierarchyTraversal;
@@ -717,7 +718,10 @@
appView, lensBuilder, verticallyMergedClasses, synthesizedBridges)
.fixupTypeReferences();
KeepInfoCollection keepInfo = appView.appInfo().getKeepInfo();
- keepInfo.mutate(mutator -> mutator.removeKeepInfoForPrunedItems(mergedClasses.keySet()));
+ keepInfo.mutate(
+ mutator ->
+ mutator.removeKeepInfoForPrunedItems(
+ PrunedItems.builder().setRemovedClasses(mergedClasses.keySet()).build()));
timing.end();
assert lens != null;
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 53214f3..0ce464d 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -54,6 +54,8 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -152,7 +154,8 @@
this.committed = committed;
}
- public static void finalize(AppView<AppInfo> appView) {
+ public static void finalize(AppView<AppInfo> appView, ExecutorService executorService)
+ throws ExecutionException {
assert !appView.appInfo().hasClassHierarchy();
assert !appView.appInfo().hasLiveness();
Result result = appView.getSyntheticItems().computeFinalSynthetics(appView);
@@ -168,10 +171,12 @@
.rewrittenWithLens(appView.getSyntheticItems(), result.lens)));
appView.setGraphLens(result.lens);
}
- appView.pruneItems(result.prunedItems);
+ appView.pruneItems(result.prunedItems, executorService);
}
- public static void finalizeWithClassHierarchy(AppView<AppInfoWithClassHierarchy> appView) {
+ public static void finalizeWithClassHierarchy(
+ AppView<AppInfoWithClassHierarchy> appView, ExecutorService executorService)
+ throws ExecutionException {
assert !appView.appInfo().hasLiveness();
Result result = appView.getSyntheticItems().computeFinalSynthetics(appView);
appView.setAppInfo(appView.appInfo().rebuildWithClassHierarchy(result.commit));
@@ -187,10 +192,12 @@
.getMainDexInfo()
.rewrittenWithLens(appView.getSyntheticItems(), result.lens)));
}
- appView.pruneItems(result.prunedItems);
+ appView.pruneItems(result.prunedItems, executorService);
}
- public static void finalizeWithLiveness(AppView<AppInfoWithLiveness> appView) {
+ public static void finalizeWithLiveness(
+ AppView<AppInfoWithLiveness> appView, ExecutorService executorService)
+ throws ExecutionException {
Result result = appView.getSyntheticItems().computeFinalSynthetics(appView);
appView.setAppInfo(appView.appInfo().rebuildWithMainDexInfo(result.mainDexInfo));
if (result.lens != null) {
@@ -199,7 +206,7 @@
assert result.commit.getApplication() == appView.appInfo().app();
}
appView.setAppInfo(appView.appInfo().rebuildWithLiveness(result.commit));
- appView.pruneItems(result.prunedItems);
+ appView.pruneItems(result.prunedItems, executorService);
}
Result computeFinalSynthetics(AppView<?> appView) {
diff --git a/src/main/java/com/android/tools/r8/utils/CollectionUtils.java b/src/main/java/com/android/tools/r8/utils/CollectionUtils.java
index 6af263f..c0e8750 100644
--- a/src/main/java/com/android/tools/r8/utils/CollectionUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/CollectionUtils.java
@@ -13,6 +13,11 @@
public class CollectionUtils {
+ public static <S, T extends Collection<S>> T addAll(T collection, Collection<S> elementsToAdd) {
+ collection.addAll(elementsToAdd);
+ return collection;
+ }
+
public static <T> Set<T> mergeSets(Collection<T> first, Collection<T> second) {
ImmutableSet.Builder<T> builder = ImmutableSet.builder();
builder.addAll(first);
diff --git a/src/main/java/com/android/tools/r8/utils/ThreadUtils.java b/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
index a8fa794..3ae44fb 100644
--- a/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
@@ -20,6 +20,16 @@
public static final int NOT_SPECIFIED = -1;
public static <T> Future<T> processAsynchronously(
+ Action action, ExecutorService executorService) {
+ return processAsynchronously(
+ () -> {
+ action.execute();
+ return null;
+ },
+ executorService);
+ }
+
+ public static <T> Future<T> processAsynchronously(
Callable<T> callable, ExecutorService executorService) {
return executorService.submit(callable);
}