Switch from Nested to VerticalClassMergerGraphLense
Change-Id: I24c0f64c6309d4df691b50687557649613b89598
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 2377e3a..d196dd2 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -52,18 +52,16 @@
* <p>A common use-case for this is to merge an interface into its single implementation.
*
* <p>The class merger only fixes the structure of the graph but leaves the actual instructions
- * untouched. Fixup of instructions is deferred via a {@link GraphLense} to the Ir building phase.
+ * untouched. Fixup of instructions is deferred via a {@link GraphLense} to the IR building phase.
*/
public class VerticalClassMerger {
private final DexApplication application;
private final AppInfoWithLiveness appInfo;
private final GraphLense graphLense;
- private final GraphLense.Builder renamedMembersLense = GraphLense.builder();
private final Map<DexType, DexType> mergedClasses = new IdentityHashMap<>();
private final Timing timing;
private Collection<DexMethod> invokes;
- private int numberOfMerges = 0;
public VerticalClassMerger(
DexApplication application,
@@ -170,6 +168,12 @@
Deque<DexProgramClass> worklist = new ArrayDeque<>();
Set<DexProgramClass> seenBefore = new HashSet<>();
+ int numberOfMerges = 0;
+
+ // The resulting graph lense that should be used after class merging.
+ VerticalClassMergerGraphLense.Builder renamedMembersLense =
+ new VerticalClassMergerGraphLense.Builder();
+
Iterator<DexProgramClass> classIterator = classes.iterator();
// Visit the program classes in a top-down order according to the class hierarchy.
@@ -218,7 +222,12 @@
}
continue;
}
- boolean merged = new ClassMerger(clazz, targetClass).merge();
+ ClassMerger merger = new ClassMerger(clazz, targetClass);
+ boolean merged = merger.merge();
+ if (merged) {
+ // Commit the changes to the graph lense.
+ renamedMembersLense.merge(merger.getRenamings());
+ }
if (Log.ENABLED) {
if (merged) {
numberOfMerges++;
@@ -239,8 +248,7 @@
if (Log.ENABLED) {
Log.debug(getClass(), "Merged %d classes.", numberOfMerges);
}
- return new VerticalClassMergerGraphLense(
- renamedMembersLense.build(application.dexItemFactory, graphLense));
+ return renamedMembersLense.build(graphLense);
}
private class ClassMerger {
@@ -249,7 +257,8 @@
private final DexClass source;
private final DexClass target;
- private final Map<DexEncodedMethod, DexEncodedMethod> deferredRenamings = new HashMap<>();
+ private final VerticalClassMergerGraphLense.Builder deferredRenamings =
+ new VerticalClassMergerGraphLense.Builder();
private boolean abortMerge = false;
private ClassMerger(DexClass source, DexClass target) {
@@ -338,17 +347,19 @@
source.interfaces = DexTypeList.empty();
// Step 4: Record merging.
mergedClasses.put(source.type, target.type);
- // Step 5: Make deferred renamings final.
- deferredRenamings.forEach((from, to) -> renamedMembersLense.map(from.method, to.method));
return true;
}
+ public VerticalClassMergerGraphLense.Builder getRenamings() {
+ return deferredRenamings;
+ }
+
private DexEncodedMethod filterShadowedInterfaceMethods(DexEncodedMethod m) {
DexEncodedMethod actual = appInfo.resolveMethod(target.type, m.method).asSingleTarget();
assert actual != null;
if (actual != m) {
// We will drop a method here, so record it as a potential renaming.
- deferredRenamings.put(m, actual);
+ deferredRenamings.map(m.method, actual.method);
return null;
}
// We will keep the method, so the class better be abstract.
@@ -426,7 +437,7 @@
if (method.accessFlags.isAbstract()) {
// We make a method disappear here, so record the renaming so that calls to the previous
// target get forwarded properly.
- deferredRenamings.put(method, existing);
+ deferredRenamings.map(method.method, existing.method);
return existing;
} else if (existing.accessFlags.isBridge()) {
InvokeSingleTargetExtractor extractor = new InvokeSingleTargetExtractor();
@@ -450,7 +461,7 @@
DexEncodedMethod result = method
.toRenamedMethod(makeMergedName(CONSTRUCTOR_NAME, holder), application.dexItemFactory);
result.markForceInline();
- deferredRenamings.put(method, result);
+ deferredRenamings.map(method.method, result.method);
// Renamed constructors turn into ordinary private functions. They can be private, as
// they are only references from their direct subclass, which they were merged into.
result.accessFlags.unsetConstructor();
@@ -468,7 +479,7 @@
String name = method.method.name.toSourceString();
DexEncodedMethod result = method
.toRenamedMethod(makeMergedName(name, holder), application.dexItemFactory);
- renamedMembersLense.map(method.method, result.method);
+ deferredRenamings.map(method.method, result.method);
return result;
}
@@ -478,7 +489,7 @@
DexEncodedField result = field
.toRenamedField(makeMergedName(oldName.toSourceString(), holder),
application.dexItemFactory);
- renamedMembersLense.map(field.field, result.field);
+ deferredRenamings.map(field.field, result.field);
return result;
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java
index 1bdeb7c..54393b5 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java
@@ -10,7 +10,9 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.ir.code.Invoke.Type;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
+import java.util.Map;
import java.util.Set;
// This graph lense is instantiated during vertical class merging. The graph lense is context
@@ -40,8 +42,16 @@
public class VerticalClassMergerGraphLense extends GraphLense {
private final GraphLense previousLense;
- public VerticalClassMergerGraphLense(GraphLense previousLense) {
+ private final Map<DexField, DexField> fieldMap;
+ private final Map<DexMethod, DexMethod> methodMap;
+
+ public VerticalClassMergerGraphLense(
+ Map<DexField, DexField> fieldMap,
+ Map<DexMethod, DexMethod> methodMap,
+ GraphLense previousLense) {
this.previousLense = previousLense;
+ this.fieldMap = fieldMap;
+ this.methodMap = methodMap;
}
@Override
@@ -54,32 +64,66 @@
// TODO(christofferqa): If [type] is Type.SUPER and [method] has been merged into the class of
// [context], then return the DIRECT method that has been created for [method] by SimpleClass-
// Merger. Otherwise, return the VIRTUAL method corresponding to [method].
- return previousLense.lookupMethod(method, context, type);
+ assert isContextFreeForMethod(method) || context != null;
+ DexMethod previous = previousLense.lookupMethod(method, context, type);
+ return methodMap.getOrDefault(previous, previous);
}
@Override
public Set<DexMethod> lookupMethodInAllContexts(DexMethod method) {
- DexMethod result = lookupMethod(method);
- if (result != null) {
- return ImmutableSet.of(result);
+ ImmutableSet.Builder<DexMethod> builder = ImmutableSet.builder();
+ for (DexMethod previous : previousLense.lookupMethodInAllContexts(method)) {
+ builder.add(methodMap.getOrDefault(previous, previous));
}
- return ImmutableSet.of();
+ return builder.build();
}
@Override
public DexField lookupField(DexField field) {
- return previousLense.lookupField(field);
+ DexField previous = previousLense.lookupField(field);
+ return fieldMap.getOrDefault(previous, previous);
}
@Override
public boolean isContextFreeForMethods() {
- return false;
+ return previousLense.isContextFreeForMethods();
}
@Override
public boolean isContextFreeForMethod(DexMethod method) {
// TODO(christofferqa): Should return false for methods where this graph lense is context
// sensitive.
- return true;
+ return previousLense.isContextFreeForMethod(method);
+ }
+
+ public static class Builder {
+
+ private final ImmutableMap.Builder<DexField, DexField> fieldMapBuilder = ImmutableMap.builder();
+ private final ImmutableMap.Builder<DexMethod, DexMethod> methodMapBuilder =
+ ImmutableMap.builder();
+
+ public Builder() {}
+
+ public GraphLense build(GraphLense previousLense) {
+ Map<DexField, DexField> fieldMap = fieldMapBuilder.build();
+ Map<DexMethod, DexMethod> methodMap = methodMapBuilder.build();
+ if (fieldMap.isEmpty() && methodMap.isEmpty()) {
+ return previousLense;
+ }
+ return new VerticalClassMergerGraphLense(fieldMap, methodMap, previousLense);
+ }
+
+ public void map(DexField from, DexField to) {
+ fieldMapBuilder.put(from, to);
+ }
+
+ public void map(DexMethod from, DexMethod to) {
+ methodMapBuilder.put(from, to);
+ }
+
+ public void merge(VerticalClassMergerGraphLense.Builder builder) {
+ fieldMapBuilder.putAll(builder.fieldMapBuilder.build());
+ methodMapBuilder.putAll(builder.methodMapBuilder.build());
+ }
}
}