Only evaluate if rules without members once per class
Bug: b/206086945
Change-Id: I52a596ae7808d7fd613958ec26fac29b87a8e997
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 a1cef17..022157e 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -4766,7 +4766,6 @@
// opportunity to add items to the worklist.
for (EnqueuerAnalysis analysis : analyses) {
analysis.notifyFixpoint(this, worklist, executorService, timing);
- assert getNumberOfLiveItems() == numberOfLiveItemsAfterProcessing;
}
if (!worklist.isEmpty()) {
continue;
diff --git a/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java b/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
index 4841933..8da99d1 100644
--- a/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
+++ b/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.shaking.RootSetUtils.RootSetBuilder;
import com.android.tools.r8.threading.TaskCollection;
import com.android.tools.r8.utils.MapUtils;
+import com.android.tools.r8.utils.UncheckedExecutionException;
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
@@ -52,7 +53,7 @@
this.tasks = tasks;
}
- public void run(
+ public void processActiveIfRulesWithMembers(
Map<Wrapper<ProguardIfRule>, Set<ProguardIfRule>> ifRules,
Set<DexProgramClass> effectivelyFakeLiveClasses)
throws ExecutionException {
@@ -90,6 +91,28 @@
tasks.await();
}
+ public void processActiveIfRulesWithoutMembers(
+ Set<ProguardIfRule> ifRules, Iterable<DexProgramClass> newlyLiveClasses)
+ throws ExecutionException {
+ ifRules.removeIf(
+ ifRule -> {
+ // Depending on which types that trigger the -if rule, the application of the subsequent
+ // -keep rule may vary (due to back references). So, we need to try all pairs of -if
+ // rule and live types.
+ for (DexProgramClass clazz : newlyLiveClasses) {
+ if (evaluateClassForIfRule(ifRule, clazz)) {
+ registerClassCapture(ifRule, clazz, clazz);
+ uncheckedMaterializeIfRule(ifRule, clazz);
+ if (canRemoveSubsequentKeepRule(ifRule)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ });
+ tasks.await();
+ }
+
private boolean isEffectivelyLive(
DexProgramClass clazz, Set<DexProgramClass> effectivelyFakeLiveClasses) {
return enqueuer.isTypeLive(clazz) || effectivelyFakeLiveClasses.contains(clazz);
@@ -281,6 +304,14 @@
return field.getOrComputeIsInlinableByJavaC(appView.dexItemFactory());
}
+ private void uncheckedMaterializeIfRule(ProguardIfRule rule, DexProgramClass precondition) {
+ try {
+ materializeIfRule(rule, precondition);
+ } catch (ExecutionException e) {
+ throw new UncheckedExecutionException(e);
+ }
+ }
+
private void materializeIfRule(ProguardIfRule rule, DexProgramClass precondition)
throws ExecutionException {
DexItemFactory dexItemFactory = appView.dexItemFactory();
diff --git a/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluatorFactory.java b/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluatorFactory.java
index dc438f3..d1f15d8c 100644
--- a/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluatorFactory.java
+++ b/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluatorFactory.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.threading.TaskCollection;
import com.android.tools.r8.utils.Timing;
import com.google.common.base.Equivalence.Wrapper;
+import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import java.util.Collections;
import java.util.HashMap;
@@ -31,10 +32,14 @@
private final AppView<? extends AppInfoWithClassHierarchy> appView;
/** Map of active if rules. This is important for speeding up aapt2 generated keep rules. */
- private final Map<Wrapper<ProguardIfRule>, Set<ProguardIfRule>> activeIfRules;
+ private final Map<Wrapper<ProguardIfRule>, Set<ProguardIfRule>> activeIfRulesWithMembers;
+
+ private final Set<ProguardIfRule> activeIfRulesWithoutMembers;
private final Set<DexProgramClass> effectivelyFakeLiveClasses;
private final Set<DexProgramClass> newlyLiveClasses = Sets.newIdentityHashSet();
+
+ private boolean seenFixpoint;
private long previousNumberOfLiveItems = 0;
private final TaskCollection<?> tasks;
@@ -44,7 +49,8 @@
Enqueuer enqueuer,
ExecutorService executorService) {
this.appView = appView;
- this.activeIfRules = createActiveIfRules(appView.rootSet().ifRules);
+ this.activeIfRulesWithMembers = createActiveIfRulesWithMembers(appView.rootSet().ifRules);
+ this.activeIfRulesWithoutMembers = createActiveIfRulesWithoutMembers(appView.rootSet().ifRules);
this.effectivelyFakeLiveClasses = createEffectivelyFakeLiveClasses(appView, enqueuer);
this.tasks = new TaskCollection<>(appView.options(), executorService);
}
@@ -53,25 +59,37 @@
AppView<? extends AppInfoWithClassHierarchy> appView,
Enqueuer enqueuer,
ExecutorService executorService) {
- Set<ProguardIfRule> ifRules = appView.rootSet().ifRules;
+ Set<ProguardIfRule> ifRules =
+ appView.hasRootSet() ? appView.rootSet().ifRules : Collections.emptySet();
if (ifRules != null && !ifRules.isEmpty()) {
enqueuer.registerAnalysis(new IfRuleEvaluatorFactory(appView, enqueuer, executorService));
}
}
- @SuppressWarnings("MixedMutabilityReturnType")
- private static Map<Wrapper<ProguardIfRule>, Set<ProguardIfRule>> createActiveIfRules(
+ private static Map<Wrapper<ProguardIfRule>, Set<ProguardIfRule>> createActiveIfRulesWithMembers(
Set<ProguardIfRule> ifRules) {
- if (ifRules == null || ifRules.isEmpty()) {
- return Collections.emptyMap();
- }
// Build the mapping of active if rules. We use a single collection of if-rules to allow
// removing if rules that have a constant sequent keep rule when they materialize.
Map<Wrapper<ProguardIfRule>, Set<ProguardIfRule>> activeIfRules = new HashMap<>(ifRules.size());
IfRuleClassPartEquivalence equivalence = new IfRuleClassPartEquivalence();
for (ProguardIfRule ifRule : ifRules) {
- Wrapper<ProguardIfRule> wrap = equivalence.wrap(ifRule);
- activeIfRules.computeIfAbsent(wrap, ignoreKey(LinkedHashSet::new)).add(ifRule);
+ if (!ifRule.getMemberRules().isEmpty()) {
+ Wrapper<ProguardIfRule> wrap = equivalence.wrap(ifRule);
+ activeIfRules.computeIfAbsent(wrap, ignoreKey(LinkedHashSet::new)).add(ifRule);
+ }
+ }
+ return activeIfRules;
+ }
+
+ private static Set<ProguardIfRule> createActiveIfRulesWithoutMembers(
+ Set<ProguardIfRule> ifRules) {
+ // Build the mapping of active if rules. We use a single collection of if-rules to allow
+ // removing if rules that have a constant sequent keep rule when they materialize.
+ Set<ProguardIfRule> activeIfRules = new LinkedHashSet<>(ifRules.size());
+ for (ProguardIfRule ifRule : ifRules) {
+ if (ifRule.getMemberRules().isEmpty()) {
+ activeIfRules.add(ifRule);
+ }
}
return activeIfRules;
}
@@ -112,24 +130,76 @@
public void notifyFixpoint(
Enqueuer enqueuer, EnqueuerWorklist worklist, ExecutorService executorService, Timing timing)
throws ExecutionException {
- // TODO(b/206086945): Leverage newlyLiveClasses.
- if (activeIfRules.isEmpty()) {
+ boolean isFirstFixpoint = setSeenFixpoint();
+ if (!shouldProcessActiveIfRulesWithMembers(enqueuer)
+ && !shouldProcessActiveIfRulesWithoutMembers(isFirstFixpoint)) {
return;
}
- long numberOfLiveItems = enqueuer.getNumberOfLiveItems();
- if (numberOfLiveItems == previousNumberOfLiveItems) {
- return;
- }
+ long numberOfLiveItemsAtStart = enqueuer.getNumberOfLiveItems();
+ ConsequentRootSet consequentRootSet =
+ timing.time(
+ "Find consequent items for -if rules...",
+ () -> processActiveIfRules(enqueuer, isFirstFixpoint));
+ enqueuer.addConsequentRootSet(consequentRootSet);
+ long numberOfLiveItemsAtEnd = enqueuer.getNumberOfLiveItems();
+ assert numberOfLiveItemsAtEnd == numberOfLiveItemsAtStart;
+ previousNumberOfLiveItems = numberOfLiveItemsAtEnd;
+ }
+
+ private boolean shouldProcessActiveIfRulesWithMembers(Enqueuer enqueuer) {
+ return !activeIfRulesWithMembers.isEmpty()
+ && enqueuer.getNumberOfLiveItems() > previousNumberOfLiveItems;
+ }
+
+ private ConsequentRootSet processActiveIfRules(Enqueuer enqueuer, boolean isFirstFixpoint)
+ throws ExecutionException {
SubtypingInfo subtypingInfo = enqueuer.getSubtypingInfo();
ConsequentRootSetBuilder consequentRootSetBuilder =
ConsequentRootSet.builder(appView, enqueuer, subtypingInfo);
IfRuleEvaluator evaluator =
new IfRuleEvaluator(appView, subtypingInfo, enqueuer, consequentRootSetBuilder, tasks);
- timing.time(
- "Find consequent items for -if rules...",
- () -> evaluator.run(activeIfRules, effectivelyFakeLiveClasses));
- enqueuer.addConsequentRootSet(consequentRootSetBuilder.buildConsequentRootSet());
- previousNumberOfLiveItems = numberOfLiveItems;
+ if (shouldProcessActiveIfRulesWithMembers(enqueuer)) {
+ processActiveIfRulesWithMembers(evaluator);
+ }
+ if (shouldProcessActiveIfRulesWithoutMembers(isFirstFixpoint)) {
+ processActiveIfRulesWithoutMembers(evaluator, isFirstFixpoint);
+ }
+ return consequentRootSetBuilder.buildConsequentRootSet();
+ }
+
+ private void processActiveIfRulesWithMembers(IfRuleEvaluator evaluator)
+ throws ExecutionException {
+ evaluator.processActiveIfRulesWithMembers(activeIfRulesWithMembers, effectivelyFakeLiveClasses);
+ }
+
+ private boolean shouldProcessActiveIfRulesWithoutMembers(boolean isFirstFixpoint) {
+ if (activeIfRulesWithoutMembers.isEmpty()) {
+ return false;
+ }
+ if (isFirstFixpoint && !effectivelyFakeLiveClasses.isEmpty()) {
+ return true;
+ }
+ return !newlyLiveClasses.isEmpty();
+ }
+
+ private void processActiveIfRulesWithoutMembers(
+ IfRuleEvaluator evaluator, boolean isFirstFixpoint) throws ExecutionException {
+ if (isFirstFixpoint && !effectivelyFakeLiveClasses.isEmpty()) {
+ evaluator.processActiveIfRulesWithoutMembers(
+ activeIfRulesWithoutMembers,
+ Iterables.concat(effectivelyFakeLiveClasses, newlyLiveClasses));
+ } else {
+ evaluator.processActiveIfRulesWithoutMembers(activeIfRulesWithoutMembers, newlyLiveClasses);
+ }
+ newlyLiveClasses.clear();
+ }
+
+ private boolean setSeenFixpoint() {
+ if (!seenFixpoint) {
+ seenFixpoint = true;
+ return true;
+ }
+ return false;
}
@Override