Refactor member pool collection.
Change-Id: I9b3a4a6dfc457258613f033f6e05e5cc195be0de
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberPoolCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberPoolCollection.java
new file mode 100644
index 0000000..f593f45
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberPoolCollection.java
@@ -0,0 +1,212 @@
+// 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.ir.optimize;
+
+import com.android.tools.r8.graph.Descriptor;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.base.Equivalence;
+import com.google.common.base.Equivalence.Wrapper;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.function.Predicate;
+
+// Per-class collection of member signatures.
+public abstract class MemberPoolCollection<T extends Descriptor> {
+
+ final Equivalence<T> equivalence;
+ final DexApplication application;
+ final Map<DexClass, MemberPool<T>> memberPools = new ConcurrentHashMap<>();
+
+ MemberPoolCollection(DexApplication application, Equivalence<T> equivalence) {
+ this.application = application;
+ this.equivalence = equivalence;
+ }
+
+ public void buildAll(ExecutorService executorService, Timing timing) throws ExecutionException {
+ timing.begin("Building member pool collection");
+ try {
+ List<Future<?>> futures = new ArrayList<>();
+ List<? extends DexClass> classes = application.classes();
+ submitAll(classes, futures, executorService);
+ ThreadUtils.awaitFutures(futures);
+ } finally {
+ timing.end();
+ }
+ }
+
+ public MemberPool<T> buildForHierarchy(
+ DexClass clazz, ExecutorService executorService, Timing timing) throws ExecutionException {
+ timing.begin("Building member pool collection");
+ try {
+ List<Future<?>> futures = new ArrayList<>();
+ submitAll(
+ getAllSuperTypesInclusive(clazz, memberPools::containsKey), futures, executorService);
+ submitAll(getAllSubTypesExclusive(clazz, memberPools::containsKey), futures, executorService);
+ ThreadUtils.awaitFutures(futures);
+ } finally {
+ timing.end();
+ }
+ return get(clazz);
+ }
+
+ public boolean hasPool(DexClass clazz) {
+ return memberPools.containsKey(clazz);
+ }
+
+ public MemberPool<T> get(DexClass clazz) {
+ assert hasPool(clazz);
+ return memberPools.get(clazz);
+ }
+
+ public boolean markIfNotSeen(DexClass clazz, T reference) {
+ MemberPool<T> memberPool = get(clazz);
+ Wrapper<T> key = equivalence.wrap(reference);
+ if (memberPool.hasSeen(key)) {
+ return true;
+ }
+ memberPool.seen(key);
+ return false;
+ }
+
+ private void submitAll(
+ Iterable<? extends DexClass> classes,
+ List<Future<?>> futures,
+ ExecutorService executorService) {
+ for (DexClass clazz : classes) {
+ futures.add(executorService.submit(computeMemberPoolForClass(clazz)));
+ }
+ }
+
+ abstract Runnable computeMemberPoolForClass(DexClass clazz);
+
+ // TODO(jsjeon): maybe be part of AppInfoWithSubtyping?
+ private Set<DexClass> getAllSuperTypesInclusive(
+ DexClass subject, Predicate<DexClass> stoppingCriterion) {
+ Set<DexClass> superTypes = new HashSet<>();
+ Deque<DexClass> worklist = new ArrayDeque<>();
+ worklist.add(subject);
+ while (!worklist.isEmpty()) {
+ DexClass clazz = worklist.pop();
+ if (stoppingCriterion.test(clazz)) {
+ continue;
+ }
+ if (superTypes.add(clazz)) {
+ if (clazz.superType != null) {
+ addNonNull(worklist, application.definitionFor(clazz.superType));
+ }
+ for (DexType interfaceType : clazz.interfaces.values) {
+ addNonNull(worklist, application.definitionFor(interfaceType));
+ }
+ }
+ }
+ return superTypes;
+ }
+
+ // TODO(jsjeon): maybe be part of AppInfoWithSubtyping?
+ private Set<DexClass> getAllSubTypesExclusive(
+ DexClass subject, Predicate<DexClass> stoppingCriterion) {
+ Set<DexClass> subTypes = new HashSet<>();
+ Deque<DexClass> worklist = new ArrayDeque<>();
+ subject.type.forAllExtendsSubtypes(
+ type -> addNonNull(worklist, application.definitionFor(type)));
+ subject.type.forAllImplementsSubtypes(
+ type -> addNonNull(worklist, application.definitionFor(type)));
+ while (!worklist.isEmpty()) {
+ DexClass clazz = worklist.pop();
+ if (stoppingCriterion.test(clazz)) {
+ continue;
+ }
+ if (subTypes.add(clazz)) {
+ clazz.type.forAllExtendsSubtypes(
+ type -> addNonNull(worklist, application.definitionFor(type)));
+ clazz.type.forAllImplementsSubtypes(
+ type -> addNonNull(worklist, application.definitionFor(type)));
+ }
+ }
+ return subTypes;
+ }
+
+ public static class MemberPool<T> {
+ private Equivalence<T> equivalence;
+ private MemberPool<T> superType;
+ private final Set<MemberPool<T>> interfaces = new HashSet<>();
+ private final Set<MemberPool<T>> subTypes = new HashSet<>();
+ private final Set<Wrapper<T>> memberPool = new HashSet<>();
+
+ MemberPool(Equivalence<T> equivalence) {
+ this.equivalence = equivalence;
+ }
+
+ synchronized void linkSupertype(MemberPool<T> superType) {
+ assert this.superType == null;
+ this.superType = superType;
+ }
+
+ synchronized void linkSubtype(MemberPool<T> subType) {
+ boolean added = subTypes.add(subType);
+ assert added;
+ }
+
+ synchronized void linkInterface(MemberPool<T> itf) {
+ boolean added = interfaces.add(itf);
+ assert added;
+ }
+
+ public void seen(T descriptor) {
+ seen(equivalence.wrap(descriptor));
+ }
+
+ public synchronized void seen(Wrapper<T> descriptor) {
+ boolean added = memberPool.add(descriptor);
+ assert added;
+ }
+
+ public boolean hasSeen(T descriptor) {
+ return hasSeen(equivalence.wrap(descriptor));
+ }
+
+ public boolean hasSeen(Wrapper<T> descriptor) {
+ return hasSeenUpwardRecursive(descriptor) || hasSeenDownwardRecursive(descriptor);
+ }
+
+ public boolean hasSeenDirectly(T descriptor) {
+ return hasSeenDirectly(equivalence.wrap(descriptor));
+ }
+
+ public boolean hasSeenDirectly(Wrapper<T> descriptor) {
+ return memberPool.contains(descriptor);
+ }
+
+ private boolean hasSeenUpwardRecursive(Wrapper<T> descriptor) {
+ return memberPool.contains(descriptor)
+ || (superType != null && superType.hasSeenUpwardRecursive(descriptor))
+ || interfaces.stream().anyMatch(itf -> itf.hasSeenUpwardRecursive(descriptor));
+ }
+
+ private boolean hasSeenDownwardRecursive(Wrapper<T> reference) {
+ return memberPool.contains(reference)
+ || subTypes.stream().anyMatch(subType -> subType.hasSeenDownwardRecursive(reference));
+ }
+ }
+
+ private static <T> void addNonNull(Collection<T> collection, T item) {
+ if (item != null) {
+ collection.add(item);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MethodPoolCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/MethodPoolCollection.java
index ca38f09..68b32ea 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MethodPoolCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MethodPoolCollection.java
@@ -7,93 +7,30 @@
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.utils.MethodSignatureEquivalence;
-import com.android.tools.r8.utils.ThreadUtils;
-import com.android.tools.r8.utils.Timing;
-import com.google.common.base.Equivalence;
-import com.google.common.base.Equivalence.Wrapper;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Deque;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-import java.util.function.Predicate;
// Per-class collection of method signatures.
//
-// Example use case: to determine if a certain method can be publicized or not.
-public class MethodPoolCollection {
-
- private static final Equivalence<DexMethod> equivalence = MethodSignatureEquivalence.get();
-
- private final DexApplication application;
- private final Map<DexClass, MethodPool> methodPools = new ConcurrentHashMap<>();
+// Example use cases:
+// *) in publicizer,
+// to determine if a private method does not collide with methods in that class hierarchy.
+// *) in vertical class merger,
+// before moving a default interface method to its subtype, check if it does not collide with one
+// in the given class hierarchy.
+// *) in uninstantiated type optimizer,
+// to avoid signature collisions while discarding unused return type or parameters.
+// TODO(b/66369976): to determine if a certain method can be made `final`.
+public class MethodPoolCollection extends MemberPoolCollection<DexMethod> {
public MethodPoolCollection(DexApplication application) {
- this.application = application;
+ super(application, MethodSignatureEquivalence.get());
}
- public void buildAll(ExecutorService executorService, Timing timing) throws ExecutionException {
- timing.begin("Building method pool collection");
- try {
- List<Future<?>> futures = new ArrayList<>();
- @SuppressWarnings("unchecked")
- List<DexClass> classes = (List) application.classes();
- submitAll(classes, futures, executorService);
- ThreadUtils.awaitFutures(futures);
- } finally {
- timing.end();
- }
- }
-
- public MethodPool buildForHierarchy(
- DexClass clazz, ExecutorService executorService, Timing timing) throws ExecutionException {
- timing.begin("Building method pool collection");
- try {
- List<Future<?>> futures = new ArrayList<>();
- submitAll(
- getAllSuperTypesInclusive(clazz, methodPools::containsKey), futures, executorService);
- submitAll(getAllSubTypesExclusive(clazz, methodPools::containsKey), futures, executorService);
- ThreadUtils.awaitFutures(futures);
- } finally {
- timing.end();
- }
- return get(clazz);
- }
-
- public MethodPool get(DexClass clazz) {
- assert methodPools.containsKey(clazz);
- return methodPools.get(clazz);
- }
-
- public boolean markIfNotSeen(DexClass clazz, DexMethod method) {
- MethodPool methodPool = get(clazz);
- Wrapper<DexMethod> key = equivalence.wrap(method);
- if (methodPool.hasSeen(key)) {
- return true;
- }
- methodPool.seen(key);
- return false;
- }
-
- private void submitAll(
- Iterable<DexClass> classes, List<Future<?>> futures, ExecutorService executorService) {
- for (DexClass clazz : classes) {
- futures.add(executorService.submit(computeMethodPoolPerClass(clazz)));
- }
- }
-
- private Runnable computeMethodPoolPerClass(DexClass clazz) {
+ @Override
+ Runnable computeMemberPoolForClass(DexClass clazz) {
return () -> {
- MethodPool methodPool = methodPools.computeIfAbsent(clazz, k -> new MethodPool());
+ MemberPool<DexMethod> methodPool =
+ memberPools.computeIfAbsent(clazz, k -> new MemberPool<>(equivalence));
clazz.forEachMethod(
encodedMethod -> {
// We will add private instance methods when we promote them.
@@ -104,7 +41,8 @@
if (clazz.superType != null) {
DexClass superClazz = application.definitionFor(clazz.superType);
if (superClazz != null) {
- MethodPool superPool = methodPools.computeIfAbsent(superClazz, k -> new MethodPool());
+ MemberPool<DexMethod> superPool =
+ memberPools.computeIfAbsent(superClazz, k -> new MemberPool<>(equivalence));
superPool.linkSubtype(methodPool);
methodPool.linkSupertype(superPool);
}
@@ -114,7 +52,8 @@
implementer -> {
DexClass subClazz = application.definitionFor(implementer);
if (subClazz != null) {
- MethodPool childPool = methodPools.computeIfAbsent(subClazz, k -> new MethodPool());
+ MemberPool<DexMethod> childPool =
+ memberPools.computeIfAbsent(subClazz, k -> new MemberPool<>(equivalence));
childPool.linkInterface(methodPool);
}
});
@@ -122,106 +61,4 @@
};
}
- private Set<DexClass> getAllSuperTypesInclusive(
- DexClass subject, Predicate<DexClass> stoppingCriterion) {
- Set<DexClass> superTypes = new HashSet<>();
- Deque<DexClass> worklist = new ArrayDeque<>();
- worklist.add(subject);
- while (!worklist.isEmpty()) {
- DexClass clazz = worklist.pop();
- if (stoppingCriterion.test(clazz)) {
- continue;
- }
- if (superTypes.add(clazz)) {
- if (clazz.superType != null) {
- addNonNull(worklist, application.definitionFor(clazz.superType));
- }
- for (DexType interfaceType : clazz.interfaces.values) {
- addNonNull(worklist, application.definitionFor(interfaceType));
- }
- }
- }
- return superTypes;
- }
-
- private Set<DexClass> getAllSubTypesExclusive(
- DexClass subject, Predicate<DexClass> stoppingCriterion) {
- Set<DexClass> subTypes = new HashSet<>();
- Deque<DexClass> worklist = new ArrayDeque<>();
- subject.type.forAllExtendsSubtypes(
- type -> addNonNull(worklist, application.definitionFor(type)));
- subject.type.forAllImplementsSubtypes(
- type -> addNonNull(worklist, application.definitionFor(type)));
- while (!worklist.isEmpty()) {
- DexClass clazz = worklist.pop();
- if (stoppingCriterion.test(clazz)) {
- continue;
- }
- if (subTypes.add(clazz)) {
- clazz.type.forAllExtendsSubtypes(
- type -> addNonNull(worklist, application.definitionFor(type)));
- clazz.type.forAllImplementsSubtypes(
- type -> addNonNull(worklist, application.definitionFor(type)));
- }
- }
- return subTypes;
- }
-
- public static class MethodPool {
- private MethodPool superType;
- private final Set<MethodPool> interfaces = new HashSet<>();
- private final Set<MethodPool> subTypes = new HashSet<>();
- private final Set<Wrapper<DexMethod>> methodPool = new HashSet<>();
-
- private MethodPool() {}
-
- synchronized void linkSupertype(MethodPool superType) {
- assert this.superType == null;
- this.superType = superType;
- }
-
- synchronized void linkSubtype(MethodPool subType) {
- boolean added = subTypes.add(subType);
- assert added;
- }
-
- synchronized void linkInterface(MethodPool itf) {
- boolean added = interfaces.add(itf);
- assert added;
- }
-
- public void seen(DexMethod method) {
- seen(MethodSignatureEquivalence.get().wrap(method));
- }
-
- public synchronized void seen(Wrapper<DexMethod> method) {
- boolean added = methodPool.add(method);
- assert added;
- }
-
- public boolean hasSeen(Wrapper<DexMethod> method) {
- return hasSeenUpwardRecursive(method) || hasSeenDownwardRecursive(method);
- }
-
- public boolean hasSeenDirectly(Wrapper<DexMethod> method) {
- return methodPool.contains(method);
- }
-
- private boolean hasSeenUpwardRecursive(Wrapper<DexMethod> method) {
- return methodPool.contains(method)
- || (superType != null && superType.hasSeenUpwardRecursive(method))
- || interfaces.stream().anyMatch(itf -> itf.hasSeenUpwardRecursive(method));
- }
-
- private boolean hasSeenDownwardRecursive(Wrapper<DexMethod> method) {
- return methodPool.contains(method)
- || subTypes.stream().anyMatch(subType -> subType.hasSeenDownwardRecursive(method));
- }
- }
-
- private static <T> void addNonNull(Collection<T> collection, T item) {
- if (item != null) {
- collection.add(item);
- }
- }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
index e41593f..4cc3207 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
@@ -37,7 +37,7 @@
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Throw;
import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.optimize.MethodPoolCollection.MethodPool;
+import com.android.tools.r8.ir.optimize.MemberPoolCollection.MemberPool;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
@@ -139,7 +139,7 @@
appView,
appView.appInfo().classes(),
clazz -> {
- MethodPool methodPool = methodPoolCollection.get(clazz);
+ MemberPool<DexMethod> methodPool = methodPoolCollection.get(clazz);
if (clazz.isInterface()) {
// Do not allow changing the prototype of methods that override an interface method.
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 fd90187..fe10045 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -36,8 +36,8 @@
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.code.Invoke.Type;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
+import com.android.tools.r8.ir.optimize.MemberPoolCollection.MemberPool;
import com.android.tools.r8.ir.optimize.MethodPoolCollection;
-import com.android.tools.r8.ir.optimize.MethodPoolCollection.MethodPool;
import com.android.tools.r8.ir.synthetic.AbstractSynthesizedCode;
import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode;
import com.android.tools.r8.logging.Log;
@@ -941,7 +941,7 @@
// due to the way invoke-super works on default interface methods. In order to be able
// to hit this method directly after the merge, we need to make it public, and find a
// method name that does not collide with one in the hierarchy of this class.
- MethodPool methodPoolForTarget =
+ MemberPool<DexMethod> methodPoolForTarget =
methodPoolCollection.buildForHierarchy(target, executorService, timing);
resultingDirectMethod =
renameMethod(