A small framework for extending the Enqueuer
Change-Id: I40a59b6da5ea34339241d482373ba24119450756
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java
index 1a54866..7ad4c5b 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerAnalysis.java
@@ -4,12 +4,33 @@
package com.android.tools.r8.graph.analysis;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.shaking.Enqueuer;
+import com.android.tools.r8.shaking.EnqueuerWorklist;
import com.android.tools.r8.shaking.KeepReason;
-public interface EnqueuerAnalysis {
+public abstract class EnqueuerAnalysis {
- void processNewlyInstantiatedClass(DexProgramClass clazz, KeepReason reason);
+ /** Called when a class is found to be instantiated. */
+ public void processNewlyInstantiatedClass(DexProgramClass clazz, KeepReason reason) {}
- void done();
+ /** Called when a field is found to be live. */
+ public void processNewlyLiveField(DexEncodedField field) {}
+
+ /** Called when a method is found to be live. */
+ public void processNewlyLiveMethod(DexEncodedMethod method) {}
+
+ /**
+ * Called when the Enqueuer reaches a fixpoint. This may happen multiple times, since each
+ * analysis may enqueue items into the worklist upon the fixpoint using {@param worklist}.
+ */
+ public void notifyFixpoint(Enqueuer enqueuer, EnqueuerWorklist worklist) {}
+
+ /**
+ * Called when the Enqueuer has reached the final fixpoint. Each analysis may use this callback to
+ * perform some post-processing.
+ */
+ public void done() {}
}
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java
index eb7f627..54b03a6 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/InitializedClassesInInstanceMethodsAnalysis.java
@@ -13,7 +13,7 @@
import java.util.IdentityHashMap;
import java.util.Map;
-public class InitializedClassesInInstanceMethodsAnalysis implements EnqueuerAnalysis {
+public class InitializedClassesInInstanceMethodsAnalysis extends EnqueuerAnalysis {
// A simple structure that stores the result of the analysis.
public static class InitializedClassesInInstanceMethods {
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 4bf7fb3..217d655 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -248,10 +248,8 @@
private final SetWithReason<DexType> instantiatedLambdas =
new SetWithReason<>(this::registerType);
- /**
- * A queue of items that need processing. Different items trigger different actions:
- */
- private final Queue<Action> workList = Queues.newArrayDeque();
+ /** A queue of items that need processing. Different items trigger different actions. */
+ private final EnqueuerWorklist workList = new EnqueuerWorklist();
/**
* A queue of items that have been added to try to keep Proguard compatibility.
@@ -611,7 +609,7 @@
Log.verbose(getClass(), "Register Iput `%s`.", field);
}
// TODO(herhut): We have to add this, but DCR should eliminate dead writes.
- workList.add(Action.markReachableField(field, KeepReason.fieldReferencedIn(currentMethod)));
+ workList.enqueueMarkReachableFieldAction(field, KeepReason.fieldReferencedIn(currentMethod));
return true;
}
@@ -623,7 +621,7 @@
if (Log.ENABLED) {
Log.verbose(getClass(), "Register Iget `%s`.", field);
}
- workList.add(Action.markReachableField(field, KeepReason.fieldReferencedIn(currentMethod)));
+ workList.enqueueMarkReachableFieldAction(field, KeepReason.fieldReferencedIn(currentMethod));
return true;
}
@@ -1391,6 +1389,9 @@
// Add all dependent members to the workqueue.
enqueueRootItems(rootSet.getDependentItems(encodedField));
+
+ // Notify analyses.
+ analyses.forEach(analysis -> analysis.processNewlyLiveField(encodedField));
}
private void markInstanceFieldAsLive(DexEncodedField field, KeepReason reason) {
@@ -1404,8 +1405,12 @@
processAnnotations(field, field.annotations.annotations);
liveFields.add(field, reason);
collectProguardCompatibilityRule(reason);
+
// Add all dependent members to the workqueue.
enqueueRootItems(rootSet.getDependentItems(field));
+
+ // Notify analyses.
+ analyses.forEach(analysis -> analysis.processNewlyLiveField(field));
}
private void markInstantiated(DexType type, KeepReason reason) {
@@ -1894,17 +1899,31 @@
}
}
- // Continue fix-point processing while there are additional work items to ensure
- // items that are passed to Java reflections are traced.
- if (proguardCompatibilityWorkList.isEmpty()
- && pendingReflectiveUses.isEmpty()) {
- break;
+ // Continue fix-point processing while there are additional work items to ensure items that
+ // are passed to Java reflections are traced.
+ if (!pendingReflectiveUses.isEmpty()) {
+ pendingReflectiveUses.forEach(this::handleReflectiveBehavior);
+ pendingReflectiveUses.clear();
}
- pendingReflectiveUses.forEach(this::handleReflectiveBehavior);
- workList.addAll(proguardCompatibilityWorkList);
- proguardCompatibilityWorkList.clear();
- pendingReflectiveUses.clear();
+ if (!proguardCompatibilityWorkList.isEmpty()) {
+ workList.addAll(proguardCompatibilityWorkList);
+ proguardCompatibilityWorkList.clear();
+ }
+ if (!workList.isEmpty()) {
+ continue;
+ }
+
+ // Notify each analysis that a fixpoint has been reached, and give each analysis an
+ // opportunity to add items to the worklist.
+ analyses.forEach(analysis -> analysis.notifyFixpoint(this, workList));
+ if (!workList.isEmpty()) {
+ continue;
+ }
+
+ // Reached the fixpoint.
+ break;
}
+
if (Log.ENABLED) {
Set<DexEncodedMethod> allLive = Sets.newIdentityHashSet();
for (Entry<DexType, SetWithReason<DexEncodedMethod>> entry : reachableVirtualMethods
@@ -2068,8 +2087,12 @@
annotation -> processAnnotation(method, annotation));
}
method.registerCodeReferences(new UseRegistry(options.itemFactory, method));
+
// Add all dependent members to the workqueue.
enqueueRootItems(rootSet.getDependentItems(method));
+
+ // Notify analyses.
+ analyses.forEach(analysis -> analysis.processNewlyLiveMethod(method));
}
}
@@ -2430,7 +2453,7 @@
}
}
- private static class Action {
+ static class Action {
final Kind kind;
final DexItem target;
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
new file mode 100644
index 0000000..7493979
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
@@ -0,0 +1,37 @@
+// Copyright (c) 2019, 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.shaking;
+
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.shaking.Enqueuer.Action;
+import com.google.common.collect.Queues;
+import java.util.Collection;
+import java.util.Queue;
+
+public class EnqueuerWorklist {
+
+ /** A queue of items that need processing. Different items trigger different actions. */
+ private final Queue<Action> worklist = Queues.newArrayDeque();
+
+ public void add(Action action) {
+ worklist.add(action);
+ }
+
+ public void addAll(Collection<Action> action) {
+ worklist.addAll(action);
+ }
+
+ public boolean isEmpty() {
+ return worklist.isEmpty();
+ }
+
+ public Action poll() {
+ return worklist.poll();
+ }
+
+ public void enqueueMarkReachableFieldAction(DexField field, KeepReason reason) {
+ add(Action.markReachableField(field, reason));
+ }
+}