Add support for horizontal merging in D8
Bug: 187675788
Change-Id: I41c18db9b0f97d0267e8df91dee57616d97ca36a
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index f57e064..26a14b4 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -20,6 +20,7 @@
import com.android.tools.r8.graph.LazyLoadedDexApplication;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.analysis.ClassInitializerAssertionEnablingAnalysis;
+import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger;
import com.android.tools.r8.inspector.internal.InspectorImpl;
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.desugar.TypeRewriter;
@@ -311,6 +312,8 @@
finalizeApplication(appView, executor);
+ HorizontalClassMerger.createForD8ClassMerging(appView).runIfNecessary(executor, timing);
+
new GenericSignatureRewriter(appView, namingLens)
.runForD8(appView.appInfo().classes(), executor);
new KotlinMetadataRewriter(appView, namingLens).runForD8(executor);
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 583c6bb..9884112 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -10,7 +10,6 @@
import com.android.tools.r8.dump.DumpOptions;
import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger;
import com.android.tools.r8.inspector.Inspector;
import com.android.tools.r8.inspector.internal.InspectorImpl;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
@@ -516,12 +515,14 @@
// Disable global optimizations.
internal.disableGlobalOptimizations();
- // TODO(b/187675788): Enable class merging for synthetics in D8.
HorizontalClassMergerOptions horizontalClassMergerOptions =
internal.horizontalClassMergerOptions();
- horizontalClassMergerOptions.disable();
- assert !horizontalClassMergerOptions.isEnabled(HorizontalClassMerger.Mode.INITIAL);
- assert !horizontalClassMergerOptions.isEnabled(HorizontalClassMerger.Mode.FINAL);
+ if (internal.isGeneratingDex()) {
+ horizontalClassMergerOptions.setRestrictToSynthetics();
+ } else {
+ assert internal.isGeneratingClassFiles();
+ horizontalClassMergerOptions.disable();
+ }
internal.setDumpInputFlags(getDumpInputFlags(), skipDump);
internal.dumpOptions = dumpOptions();
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index d479e46..afea47d 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -508,7 +508,7 @@
assert appView.verticallyMergedClasses() != null;
HorizontalClassMerger.createForInitialClassMerging(appViewWithLiveness)
- .runIfNecessary(runtimeTypeCheckInfo, executorService, timing);
+ .runIfNecessary(executorService, timing, runtimeTypeCheckInfo);
}
new ProtoNormalizer(appViewWithLiveness).run(executorService, timing);
@@ -751,11 +751,11 @@
// are always merged.
HorizontalClassMerger.createForFinalClassMerging(appView)
.runIfNecessary(
+ executorService,
+ timing,
classMergingEnqueuerExtensionBuilder != null
? classMergingEnqueuerExtensionBuilder.build(appView.graphLens())
- : null,
- executorService,
- timing);
+ : null);
// Perform minification.
NamingLens namingLens;
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java
index 682da64..464db91 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java
@@ -23,21 +23,31 @@
import java.util.Set;
import java.util.function.BiConsumer;
-public class ClassInstanceFieldsMerger {
+public interface ClassInstanceFieldsMerger {
- private final AppView<? extends AppInfoWithClassHierarchy> appView;
- private final MergeGroup group;
- private final Builder lensBuilder;
+ void setClassIdField(DexEncodedField classIdField);
- private DexEncodedField classIdField;
+ DexEncodedField[] merge();
- public ClassInstanceFieldsMerger(
- AppView<? extends AppInfoWithClassHierarchy> appView,
- HorizontalClassMergerGraphLens.Builder lensBuilder,
- MergeGroup group) {
- this.appView = appView;
- this.group = group;
- this.lensBuilder = lensBuilder;
+ static ClassInstanceFieldsMerger create(
+ AppView<?> appView, HorizontalClassMergerGraphLens.Builder lensBuilder, MergeGroup group) {
+ if (appView.hasClassHierarchy()) {
+ return new ClassInstanceFieldsMergerImpl(appView.withClassHierarchy(), lensBuilder, group);
+ } else {
+ assert group.getInstanceFieldMap().isEmpty();
+ appView.options().horizontalClassMergerOptions().isRestrictedToSynthetics();
+ return new ClassInstanceFieldsMerger() {
+ @Override
+ public void setClassIdField(DexEncodedField classIdField) {
+ throw new UnsupportedOperationException("No instance field merging in D8");
+ }
+
+ @Override
+ public DexEncodedField[] merge() {
+ return DexEncodedField.EMPTY_ARRAY;
+ }
+ };
+ }
}
/**
@@ -50,7 +60,7 @@
* Bar has fields 'A b' and 'B a'), we make a prepass that matches fields with the same reference
* type.
*/
- public static void mapFields(
+ static void mapFields(
AppView<? extends AppInfoWithClassHierarchy> appView,
DexProgramClass source,
DexProgramClass target,
@@ -90,7 +100,7 @@
}
}
- private static Map<InstanceFieldInfo, LinkedList<DexEncodedField>> getAvailableFieldsByExactInfo(
+ static Map<InstanceFieldInfo, LinkedList<DexEncodedField>> getAvailableFieldsByExactInfo(
DexProgramClass target) {
Map<InstanceFieldInfo, LinkedList<DexEncodedField>> availableFieldsByInfo =
new LinkedHashMap<>();
@@ -102,10 +112,9 @@
return availableFieldsByInfo;
}
- private static Map<InstanceFieldInfo, LinkedList<DexEncodedField>>
- getAvailableFieldsByRelaxedInfo(
- AppView<? extends AppInfoWithClassHierarchy> appView,
- Map<InstanceFieldInfo, LinkedList<DexEncodedField>> availableFieldsByExactInfo) {
+ static Map<InstanceFieldInfo, LinkedList<DexEncodedField>> getAvailableFieldsByRelaxedInfo(
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ Map<InstanceFieldInfo, LinkedList<DexEncodedField>> availableFieldsByExactInfo) {
Map<InstanceFieldInfo, LinkedList<DexEncodedField>> availableFieldsByRelaxedInfo =
new LinkedHashMap<>();
availableFieldsByExactInfo.forEach(
@@ -118,64 +127,85 @@
return availableFieldsByRelaxedInfo;
}
- private void fixAccessFlags(DexEncodedField newField, Collection<DexEncodedField> oldFields) {
- if (newField.isSynthetic() && Iterables.any(oldFields, oldField -> !oldField.isSynthetic())) {
- newField.getAccessFlags().demoteFromSynthetic();
- }
- if (newField.isFinal() && Iterables.any(oldFields, oldField -> !oldField.isFinal())) {
- newField.getAccessFlags().demoteFromFinal();
- }
- }
+ class ClassInstanceFieldsMergerImpl implements ClassInstanceFieldsMerger {
- public void setClassIdField(DexEncodedField classIdField) {
- this.classIdField = classIdField;
- }
+ private final AppView<? extends AppInfoWithClassHierarchy> appView;
+ private final MergeGroup group;
+ private final Builder lensBuilder;
- public DexEncodedField[] merge() {
- assert group.hasInstanceFieldMap();
- List<DexEncodedField> newFields = new ArrayList<>();
- if (classIdField != null) {
- newFields.add(classIdField);
- }
- group
- .getInstanceFieldMap()
- .forEachManyToOneMapping(
- (sourceFields, targetField) ->
- newFields.add(mergeSourceFieldsToTargetField(targetField, sourceFields)));
- return newFields.toArray(DexEncodedField.EMPTY_ARRAY);
- }
+ private DexEncodedField classIdField;
- private DexEncodedField mergeSourceFieldsToTargetField(
- DexEncodedField targetField, Set<DexEncodedField> sourceFields) {
- fixAccessFlags(targetField, sourceFields);
-
- DexEncodedField newField;
- if (needsRelaxedType(targetField, sourceFields)) {
- DexType newFieldType =
- DexTypeUtils.computeLeastUpperBound(
- appView,
- Iterables.transform(
- Iterables.concat(IterableUtils.singleton(targetField), sourceFields),
- DexEncodedField::getType));
- newField =
- targetField.toTypeSubstitutedField(
- appView, targetField.getReference().withType(newFieldType, appView.dexItemFactory()));
- } else {
- newField = targetField;
+ private ClassInstanceFieldsMergerImpl(
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ HorizontalClassMergerGraphLens.Builder lensBuilder,
+ MergeGroup group) {
+ this.appView = appView;
+ this.group = group;
+ this.lensBuilder = lensBuilder;
}
- lensBuilder.recordNewFieldSignature(
- Iterables.transform(
- IterableUtils.append(sourceFields, targetField), DexEncodedField::getReference),
- newField.getReference(),
- targetField.getReference());
+ @Override
+ public void setClassIdField(DexEncodedField classIdField) {
+ this.classIdField = classIdField;
+ }
- return newField;
- }
+ @Override
+ public DexEncodedField[] merge() {
+ assert group.hasInstanceFieldMap();
+ List<DexEncodedField> newFields = new ArrayList<>();
+ if (classIdField != null) {
+ newFields.add(classIdField);
+ }
+ group
+ .getInstanceFieldMap()
+ .forEachManyToOneMapping(
+ (sourceFields, targetField) ->
+ newFields.add(mergeSourceFieldsToTargetField(targetField, sourceFields)));
+ return newFields.toArray(DexEncodedField.EMPTY_ARRAY);
+ }
- private boolean needsRelaxedType(
- DexEncodedField targetField, Iterable<DexEncodedField> sourceFields) {
- return Iterables.any(
- sourceFields, sourceField -> sourceField.getType() != targetField.getType());
+ private DexEncodedField mergeSourceFieldsToTargetField(
+ DexEncodedField targetField, Set<DexEncodedField> sourceFields) {
+ fixAccessFlags(targetField, sourceFields);
+
+ DexEncodedField newField;
+ if (needsRelaxedType(targetField, sourceFields)) {
+ DexType newFieldType =
+ DexTypeUtils.computeLeastUpperBound(
+ appView,
+ Iterables.transform(
+ Iterables.concat(IterableUtils.singleton(targetField), sourceFields),
+ DexEncodedField::getType));
+ newField =
+ targetField.toTypeSubstitutedField(
+ appView,
+ targetField.getReference().withType(newFieldType, appView.dexItemFactory()));
+ } else {
+ newField = targetField;
+ }
+
+ lensBuilder.recordNewFieldSignature(
+ Iterables.transform(
+ IterableUtils.append(sourceFields, targetField), DexEncodedField::getReference),
+ newField.getReference(),
+ targetField.getReference());
+
+ return newField;
+ }
+
+ private void fixAccessFlags(DexEncodedField newField, Collection<DexEncodedField> oldFields) {
+ if (newField.isSynthetic() && Iterables.any(oldFields, oldField -> !oldField.isSynthetic())) {
+ newField.getAccessFlags().demoteFromSynthetic();
+ }
+ if (newField.isFinal() && Iterables.any(oldFields, oldField -> !oldField.isFinal())) {
+ newField.getAccessFlags().demoteFromFinal();
+ }
+ }
+
+ private boolean needsRelaxedType(
+ DexEncodedField targetField, Iterable<DexEncodedField> sourceFields) {
+ return Iterables.any(
+ sourceFields, sourceField -> sourceField.getType() != targetField.getType());
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
index 303efef..0751f8e 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -7,7 +7,6 @@
import static com.google.common.base.Predicates.not;
import com.android.tools.r8.androidapi.ComputedApiLevel;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexDefinition;
@@ -37,6 +36,7 @@
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -55,7 +55,7 @@
private static final OptimizationFeedback feedback = OptimizationFeedbackSimple.getInstance();
- private final AppView<? extends AppInfoWithClassHierarchy> appView;
+ private final AppView<?> appView;
private final Mode mode;
private final MergeGroup group;
private final DexItemFactory dexItemFactory;
@@ -74,7 +74,7 @@
private final Collection<VirtualMethodMerger> virtualMethodMergers;
private ClassMerger(
- AppView<? extends AppInfoWithClassHierarchy> appView,
+ AppView<?> appView,
IRCodeProvider codeProvider,
Mode mode,
HorizontalClassMergerGraphLens.Builder lensBuilder,
@@ -88,7 +88,7 @@
// Field mergers.
this.classStaticFieldsMerger = new ClassStaticFieldsMerger(appView, lensBuilder, group);
- this.classInstanceFieldsMerger = new ClassInstanceFieldsMerger(appView, lensBuilder, group);
+ this.classInstanceFieldsMerger = ClassInstanceFieldsMerger.create(appView, lensBuilder, group);
// Method mergers.
this.classInitializerMerger = ClassInitializerMerger.create(group);
@@ -344,16 +344,12 @@
}
public static class Builder {
- private final AppView<? extends AppInfoWithClassHierarchy> appView;
+ private final AppView<?> appView;
private final IRCodeProvider codeProvider;
- private Mode mode;
+ private final Mode mode;
private final MergeGroup group;
- public Builder(
- AppView<? extends AppInfoWithClassHierarchy> appView,
- IRCodeProvider codeProvider,
- MergeGroup group,
- Mode mode) {
+ public Builder(AppView<?> appView, IRCodeProvider codeProvider, MergeGroup group, Mode mode) {
this.appView = appView;
this.codeProvider = codeProvider;
this.group = group;
@@ -361,6 +357,24 @@
}
private List<VirtualMethodMerger> createVirtualMethodMergers() {
+ if (!appView.hasClassHierarchy()) {
+ assert getVirtualMethodMergerBuilders().isEmpty();
+ return Collections.emptyList();
+ }
+ Map<DexMethodSignature, VirtualMethodMerger.Builder> virtualMethodMergerBuilders =
+ getVirtualMethodMergerBuilders();
+ if (virtualMethodMergerBuilders.isEmpty()) {
+ return Collections.emptyList();
+ }
+ List<VirtualMethodMerger> virtualMethodMergers =
+ new ArrayList<>(virtualMethodMergerBuilders.size());
+ for (VirtualMethodMerger.Builder builder : virtualMethodMergerBuilders.values()) {
+ virtualMethodMergers.add(builder.build(appView.withClassHierarchy(), group));
+ }
+ return virtualMethodMergers;
+ }
+
+ private Map<DexMethodSignature, VirtualMethodMerger.Builder> getVirtualMethodMergerBuilders() {
Map<DexMethodSignature, VirtualMethodMerger.Builder> virtualMethodMergerBuilders =
new LinkedHashMap<>();
group.forEach(
@@ -372,12 +386,7 @@
virtualMethod.getReference().getSignature(),
ignore -> new VirtualMethodMerger.Builder())
.add(virtualMethod)));
- List<VirtualMethodMerger> virtualMethodMergers =
- new ArrayList<>(virtualMethodMergerBuilders.size());
- for (VirtualMethodMerger.Builder builder : virtualMethodMergerBuilders.values()) {
- virtualMethodMergers.add(builder.build(appView, group));
- }
- return virtualMethodMergers;
+ return virtualMethodMergerBuilders;
}
private void createClassIdField() {
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 df258fe..a3df5dd 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -6,11 +6,12 @@
import static com.android.tools.r8.graph.DexClassAndMethod.asProgramMethodOrNull;
+import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.graph.GraphLens.MethodLookupResult;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.PrunedItems;
@@ -18,8 +19,8 @@
import com.android.tools.r8.ir.code.Invoke.Type;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
-import com.android.tools.r8.shaking.KeepInfoCollection;
import com.android.tools.r8.shaking.RuntimeTypeCheckInfo;
+import com.android.tools.r8.synthesis.SyntheticItems;
import com.android.tools.r8.utils.InternalOptions.HorizontalClassMergerOptions;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
@@ -47,11 +48,11 @@
}
}
- private final AppView<? extends AppInfoWithClassHierarchy> appView;
+ private final AppView<?> appView;
private final Mode mode;
private final HorizontalClassMergerOptions options;
- private HorizontalClassMerger(AppView<? extends AppInfoWithClassHierarchy> appView, Mode mode) {
+ private HorizontalClassMerger(AppView<?> appView, Mode mode) {
this.appView = appView;
this.mode = mode;
this.options = appView.options().horizontalClassMergerOptions();
@@ -67,12 +68,26 @@
return new HorizontalClassMerger(appView, Mode.FINAL);
}
+ public static HorizontalClassMerger createForD8ClassMerging(AppView<?> appView) {
+ assert appView.options().horizontalClassMergerOptions().isRestrictedToSynthetics();
+ return new HorizontalClassMerger(appView, Mode.FINAL);
+ }
+
+ public void runIfNecessary(ExecutorService executorService, Timing timing)
+ throws ExecutionException {
+ runIfNecessary(executorService, timing, null);
+ }
+
public void runIfNecessary(
- RuntimeTypeCheckInfo runtimeTypeCheckInfo, ExecutorService executorService, Timing timing)
+ ExecutorService executorService, Timing timing, RuntimeTypeCheckInfo runtimeTypeCheckInfo)
throws ExecutionException {
if (options.isEnabled(mode)) {
timing.begin("HorizontalClassMerger (" + mode.toString() + ")");
- run(runtimeTypeCheckInfo, executorService, timing);
+ IRCodeProvider codeProvider =
+ appView.hasClassHierarchy()
+ ? IRCodeProvider.create(appView.withClassHierarchy())
+ : IRCodeProvider.createThrowing();
+ run(runtimeTypeCheckInfo, codeProvider, executorService, timing);
// Clear type elements cache after IR building.
appView.dexItemFactory().clearTypeElementsCache();
@@ -84,10 +99,11 @@
}
private void run(
- RuntimeTypeCheckInfo runtimeTypeCheckInfo, ExecutorService executorService, Timing timing)
+ RuntimeTypeCheckInfo runtimeTypeCheckInfo,
+ IRCodeProvider codeProvider,
+ ExecutorService executorService,
+ Timing timing)
throws ExecutionException {
- IRCodeProvider codeProvider = new IRCodeProvider(appView);
-
// Run the policies on all program classes to produce a final grouping.
List<Policy> policies =
PolicyScheduler.getPolicies(appView, codeProvider, mode, runtimeTypeCheckInfo);
@@ -121,6 +137,7 @@
SyntheticInitializerConverter syntheticInitializerConverter =
syntheticInitializerConverterBuilder.build();
+ assert syntheticInitializerConverter.isEmpty() || appView.enableWholeProgramOptimizations();
syntheticInitializerConverter.convertClassInitializers(executorService);
// Generate the graph lens.
@@ -131,16 +148,31 @@
HorizontalClassMergerGraphLens horizontalClassMergerGraphLens =
createLens(mergedClasses, lensBuilder, mode, syntheticArgumentClass);
- assert verifyNoCyclesInInterfaceHierarchies(groups);
-
- // Prune keep info.
- KeepInfoCollection keepInfo = appView.getKeepInfo();
- keepInfo.mutate(mutator -> mutator.removeKeepInfoForMergedClasses(prunedItems));
+ assert verifyNoCyclesInInterfaceHierarchies(appView, groups);
// Must rewrite AppInfoWithLiveness before pruning the merged classes, to ensure that allocation
// sites, fields accesses, etc. are correctly transferred to the target classes.
- appView.rewriteWithLensAndApplication(
- horizontalClassMergerGraphLens, getNewApplication(mergedClasses));
+ DexApplication newApplication = getNewApplication(mergedClasses);
+ if (appView.hasClassHierarchy()) {
+ appView.rewriteWithLensAndApplication(
+ horizontalClassMergerGraphLens, newApplication.toDirect());
+ } else {
+ assert mode.isFinal();
+ assert !appView.enableWholeProgramOptimizations();
+ SyntheticItems syntheticItems = appView.appInfo().getSyntheticItems();
+ assert !syntheticItems.hasPendingSyntheticClasses();
+ appView
+ .withoutClassHierarchy()
+ .setAppInfo(
+ new AppInfo(
+ syntheticItems.commitRewrittenWithLens(
+ newApplication, horizontalClassMergerGraphLens),
+ appView
+ .appInfo()
+ .getMainDexInfo()
+ .rewrittenWithLens(syntheticItems, horizontalClassMergerGraphLens)));
+ appView.setGraphLens(horizontalClassMergerGraphLens);
+ }
codeProvider.setGraphLens(horizontalClassMergerGraphLens);
// Record where the synthesized $r8$classId fields are read and written.
@@ -162,6 +194,10 @@
private void amendKeepInfo(
HorizontalClassMergerGraphLens horizontalClassMergerGraphLens,
List<VirtuallyMergedMethodsKeepInfo> virtuallyMergedMethodsKeepInfos) {
+ if (!appView.enableWholeProgramOptimizations()) {
+ assert virtuallyMergedMethodsKeepInfos.isEmpty();
+ return;
+ }
appView
.getKeepInfo()
.mutate(
@@ -213,6 +249,10 @@
HorizontalClassMergerGraphLens horizontalClassMergerGraphLens,
ExecutorService executorService)
throws ExecutionException {
+ if (!appView.hasClassHierarchy()) {
+ assert verifyNoIncompleteCode(groups, executorService);
+ return;
+ }
ThreadUtils.processItems(
groups,
group -> {
@@ -226,23 +266,46 @@
(IncompleteHorizontalClassMergerCode) method.getDefinition().getCode();
method
.setCode(
- code.toCfCode(appView, method, horizontalClassMergerGraphLens), appView);
+ code.toCfCode(
+ appView.withClassHierarchy(), method, horizontalClassMergerGraphLens),
+ appView);
});
},
executorService);
}
- private DirectMappedDexApplication getNewApplication(HorizontallyMergedClasses mergedClasses) {
+ private boolean verifyNoIncompleteCode(
+ Collection<MergeGroup> groups, ExecutorService executorService) throws ExecutionException {
+ ThreadUtils.processItems(
+ groups,
+ group -> {
+ assert !group
+ .getTarget()
+ .methods(
+ method ->
+ method.hasCode()
+ && method.getCode().isIncompleteHorizontalClassMergerCode())
+ .iterator()
+ .hasNext()
+ : "Expected no incomplete code";
+ },
+ executorService);
+ return true;
+ }
+
+ private DexApplication getNewApplication(HorizontallyMergedClasses mergedClasses) {
// In the second round of class merging, we must forcefully remove the merged classes from the
// application, since we won't run tree shaking before writing the application.
- DirectMappedDexApplication application = appView.appInfo().app().asDirect();
- return mode.isInitial()
- ? application
- : application
- .builder()
- .removeProgramClasses(
- clazz -> mergedClasses.hasBeenMergedIntoDifferentType(clazz.getType()))
- .build();
+ if (mode.isInitial()) {
+ return appView.app();
+ } else {
+ return appView
+ .app()
+ .builder()
+ .removeProgramClasses(
+ clazz -> mergedClasses.hasBeenMergedIntoDifferentType(clazz.getType()))
+ .build();
+ }
}
private List<MergeGroup> getInitialGroups() {
@@ -312,13 +375,16 @@
.fixupTypeReferences();
}
- private boolean verifyNoCyclesInInterfaceHierarchies(Collection<MergeGroup> groups) {
+ private static boolean verifyNoCyclesInInterfaceHierarchies(
+ AppView<?> appView, Collection<MergeGroup> groups) {
for (MergeGroup group : groups) {
if (group.isClassGroup()) {
continue;
}
+ assert appView.hasClassHierarchy();
DexProgramClass interfaceClass = group.getTarget();
appView
+ .withClassHierarchy()
.appInfo()
.traverseSuperTypes(
interfaceClass,
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/IRCodeProvider.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/IRCodeProvider.java
index 9f74d8e..166bb1f 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/IRCodeProvider.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/IRCodeProvider.java
@@ -11,31 +11,56 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.IRCode;
-public class IRCodeProvider {
+public interface IRCodeProvider {
- private final AppView<AppInfo> appViewForConversion;
+ IRCode buildIR(ProgramMethod method);
- public IRCodeProvider(AppView<? extends AppInfoWithClassHierarchy> appView) {
- // At this point the code rewritings described by repackaging and synthetic finalization have
- // not been applied to the code objects. These code rewritings will be applied in the
- // application writer. We therefore simulate that we are in D8, to allow building IR for each of
- // the class initializers without applying the unapplied code rewritings, to avoid that we apply
- // the lens more than once to the same piece of code.
- AppView<AppInfo> appViewForConversion =
- AppView.createForD8(AppInfo.createInitialAppInfo(appView.appInfo().app()));
- appViewForConversion.setGraphLens(appView.graphLens());
- appViewForConversion.setCodeLens(appView.codeLens());
- this.appViewForConversion = appViewForConversion;
+ void setGraphLens(GraphLens graphLens);
+
+ static IRCodeProvider create(AppView<? extends AppInfoWithClassHierarchy> appView) {
+ return new IRCodeProviderImpl(appView);
}
- public IRCode buildIR(ProgramMethod method) {
- return method
- .getDefinition()
- .getCode()
- .buildIR(method, appViewForConversion, method.getOrigin());
+ static IRCodeProvider createThrowing() {
+ return new IRCodeProvider() {
+ @Override
+ public IRCode buildIR(ProgramMethod method) {
+ throw new UnsupportedOperationException("Should never build IR for methods in D8");
+ }
+
+ @Override
+ public void setGraphLens(GraphLens graphLens) {}
+ };
}
- public void setGraphLens(GraphLens graphLens) {
- appViewForConversion.setGraphLens(graphLens);
+ class IRCodeProviderImpl implements IRCodeProvider {
+
+ private final AppView<AppInfo> appViewForConversion;
+
+ private IRCodeProviderImpl(AppView<? extends AppInfoWithClassHierarchy> appView) {
+ // At this point the code rewritings described by repackaging and synthetic finalization have
+ // not been applied to the code objects. These code rewritings will be applied in the
+ // application writer. We therefore simulate that we are in D8, to allow building IR for each
+ // of the class initializers without applying the unapplied code rewritings, to avoid that we
+ // apply the lens more than once to the same piece of code.
+ AppView<AppInfo> appViewForConversion =
+ AppView.createForD8(AppInfo.createInitialAppInfo(appView.appInfo().app()));
+ appViewForConversion.setGraphLens(appView.graphLens());
+ appViewForConversion.setCodeLens(appView.codeLens());
+ this.appViewForConversion = appViewForConversion;
+ }
+
+ @Override
+ public IRCode buildIR(ProgramMethod method) {
+ return method
+ .getDefinition()
+ .getCode()
+ .buildIR(method, appViewForConversion, method.getOrigin());
+ }
+
+ @Override
+ public void setGraphLens(GraphLens graphLens) {
+ appViewForConversion.setGraphLens(graphLens);
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMergerCollection.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMergerCollection.java
index dbf0943..952421e 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMergerCollection.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMergerCollection.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
@@ -37,14 +38,21 @@
}
public static InstanceInitializerMergerCollection create(
- AppView<? extends AppInfoWithClassHierarchy> appView,
+ AppView<?> appView,
Reference2IntMap<DexType> classIdentifiers,
IRCodeProvider codeProvider,
MergeGroup group,
HorizontalClassMergerGraphLens.Builder lensBuilder,
Mode mode) {
+ if (!appView.hasClassHierarchy()) {
+ assert appView.options().horizontalClassMergerOptions().isRestrictedToSynthetics();
+ assert verifyNoInstanceInitializers(group);
+ return new InstanceInitializerMergerCollection(
+ Collections.emptyList(), Collections.emptyMap());
+ }
// Create an instance initializer merger for each group of instance initializers that are
// equivalent.
+ AppView<AppInfoWithClassHierarchy> appViewWithClassHierarchy = appView.withClassHierarchy();
Map<InstanceInitializerDescription, Builder> buildersByDescription = new LinkedHashMap<>();
ProgramMethodSet buildersWithoutDescription = ProgramMethodSet.createLinked();
group.forEach(
@@ -54,7 +62,7 @@
instanceInitializer -> {
InstanceInitializerDescription description =
InstanceInitializerAnalysis.analyze(
- appView, codeProvider, group, instanceInitializer);
+ appViewWithClassHierarchy, codeProvider, group, instanceInitializer);
if (description != null) {
buildersByDescription
.computeIfAbsent(
@@ -62,7 +70,10 @@
ignoreKey(
() ->
new InstanceInitializerMerger.Builder(
- appView, classIdentifiers, lensBuilder, mode)))
+ appViewWithClassHierarchy,
+ classIdentifiers,
+ lensBuilder,
+ mode)))
.addEquivalent(instanceInitializer);
} else {
buildersWithoutDescription.add(instanceInitializer);
@@ -95,7 +106,7 @@
instanceInitializer.getDefinition().getProto(),
ignore ->
new InstanceInitializerMerger.Builder(
- appView, classIdentifiers, lensBuilder, mode))
+ appViewWithClassHierarchy, classIdentifiers, lensBuilder, mode))
.add(instanceInitializer));
for (InstanceInitializerMerger.Builder builder : buildersByProto.values()) {
instanceInitializerMergers.addAll(builder.build(group));
@@ -105,7 +116,7 @@
instanceInitializer ->
instanceInitializerMergers.addAll(
new InstanceInitializerMerger.Builder(
- appView, classIdentifiers, lensBuilder, mode)
+ appViewWithClassHierarchy, classIdentifiers, lensBuilder, mode)
.add(instanceInitializer)
.build(group)));
}
@@ -118,6 +129,14 @@
instanceInitializerMergers, equivalentInstanceInitializerMergers);
}
+ private static boolean verifyNoInstanceInitializers(MergeGroup group) {
+ group.forEach(
+ clazz -> {
+ assert !clazz.programInstanceInitializers().iterator().hasNext();
+ });
+ return true;
+ }
+
public void forEach(Consumer<InstanceInitializerMerger> consumer) {
instanceInitializerMergers.forEach(consumer);
equivalentInstanceInitializerMergers.values().forEach(consumer);
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/MergeGroup.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/MergeGroup.java
index 458f0ba..7c7a68d 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/MergeGroup.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/MergeGroup.java
@@ -14,7 +14,7 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramField;
-import com.android.tools.r8.shaking.KeepClassInfo;
+import com.android.tools.r8.shaking.KeepInfoCollection;
import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.IteratorUtils;
import com.android.tools.r8.utils.collections.BidirectionalManyToOneHashMap;
@@ -167,17 +167,18 @@
return new ProgramField(getTarget(), targetField);
}
- public void selectTarget(AppView<? extends AppInfoWithClassHierarchy> appView) {
+ public void selectTarget(AppView<?> appView) {
Iterable<DexProgramClass> candidates = Iterables.filter(getClasses(), DexClass::isPublic);
if (IterableUtils.isEmpty(candidates)) {
candidates = getClasses();
}
Iterator<DexProgramClass> candidateIterator = candidates.iterator();
DexProgramClass target = IterableUtils.first(candidates);
+ KeepInfoCollection keepInfo = appView.getKeepInfo();
while (candidateIterator.hasNext()) {
DexProgramClass current = candidateIterator.next();
- KeepClassInfo keepClassInfo = appView.getKeepInfo().getClassInfo(current);
- if (keepClassInfo.isMinificationAllowed(appView.options())) {
+ if (keepInfo != null
+ && keepInfo.getClassInfo(current).isMinificationAllowed(appView.options())) {
target = current;
break;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
index 5e76e2d..2ff1bfb 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.horizontalclassmerging;
+import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
@@ -43,6 +44,7 @@
import com.android.tools.r8.horizontalclassmerging.policies.NoVirtualMethodMerging;
import com.android.tools.r8.horizontalclassmerging.policies.NoWeakerAccessPrivileges;
import com.android.tools.r8.horizontalclassmerging.policies.NotMatchedByNoHorizontalClassMerging;
+import com.android.tools.r8.horizontalclassmerging.policies.OnlyClassesWithStaticDefinitions;
import com.android.tools.r8.horizontalclassmerging.policies.OnlyDirectlyConnectedOrUnrelatedInterfaces;
import com.android.tools.r8.horizontalclassmerging.policies.PreserveMethodCharacteristics;
import com.android.tools.r8.horizontalclassmerging.policies.PreventClassMethodAndDefaultMethodCollisions;
@@ -53,7 +55,8 @@
import com.android.tools.r8.horizontalclassmerging.policies.SameNestHost;
import com.android.tools.r8.horizontalclassmerging.policies.SameParentClass;
import com.android.tools.r8.horizontalclassmerging.policies.SyntheticItemsPolicy;
-import com.android.tools.r8.horizontalclassmerging.policies.VerifyPolicyAlwaysSatisfied;
+import com.android.tools.r8.horizontalclassmerging.policies.VerifyMultiClassPolicyAlwaysSatisfied;
+import com.android.tools.r8.horizontalclassmerging.policies.VerifySingleClassPolicyAlwaysSatisfied;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.RuntimeTypeCheckInfo;
import com.android.tools.r8.utils.ListUtils;
@@ -63,6 +66,31 @@
public class PolicyScheduler {
public static List<Policy> getPolicies(
+ AppView<?> appView,
+ IRCodeProvider codeProvider,
+ Mode mode,
+ RuntimeTypeCheckInfo runtimeTypeCheckInfo) {
+ if (appView.hasClassHierarchy()) {
+ return getPoliciesForR8(
+ appView.withClassHierarchy(), codeProvider, mode, runtimeTypeCheckInfo);
+ } else {
+ return getPoliciesForD8(appView.withoutClassHierarchy(), mode);
+ }
+ }
+
+ private static List<Policy> getPoliciesForD8(AppView<AppInfo> appView, Mode mode) {
+ assert mode.isFinal();
+ List<Policy> policies =
+ ImmutableList.<Policy>builder()
+ .addAll(getSingleClassPoliciesForD8(appView, mode))
+ .addAll(getMultiClassPoliciesForD8(appView, mode))
+ .build();
+ policies = appView.options().testing.horizontalClassMergingPolicyRewriter.apply(policies);
+ assert verifyPolicyOrderingConstraints(policies);
+ return policies;
+ }
+
+ private static List<Policy> getPoliciesForR8(
AppView<? extends AppInfoWithClassHierarchy> appView,
IRCodeProvider codeProvider,
Mode mode,
@@ -104,6 +132,16 @@
return builder.build();
}
+ private static List<SingleClassPolicy> getSingleClassPoliciesForD8(
+ AppView<AppInfo> appView, Mode mode) {
+ ImmutableList.Builder<SingleClassPolicy> builder =
+ ImmutableList.<SingleClassPolicy>builder()
+ .add(new CheckSyntheticClasses(appView))
+ .add(new OnlyClassesWithStaticDefinitions());
+ assert verifySingleClassPoliciesIrrelevantForMergingSyntheticsInD8(appView, mode, builder);
+ return builder.build();
+ }
+
private static void addRequiredSingleClassPolicies(
AppView<? extends AppInfoWithClassHierarchy> appView,
ImmutableList.Builder<SingleClassPolicy> builder) {
@@ -148,7 +186,22 @@
new NoKotlinMetadata(),
new NoNativeMethods(),
new NoServiceLoaders(appView));
- policies.stream().map(VerifyPolicyAlwaysSatisfied::new).forEach(builder::add);
+ policies.stream().map(VerifySingleClassPolicyAlwaysSatisfied::new).forEach(builder::add);
+ return true;
+ }
+
+ private static boolean verifySingleClassPoliciesIrrelevantForMergingSyntheticsInD8(
+ AppView<AppInfo> appView, Mode mode, ImmutableList.Builder<SingleClassPolicy> builder) {
+ List<SingleClassPolicy> policies =
+ ImmutableList.of(
+ new NoAnnotationClasses(),
+ new NoDirectRuntimeTypeChecks(appView, mode),
+ new NoInterfaces(appView, mode),
+ new NoInnerClasses(),
+ new NoInstanceFieldAnnotations(),
+ new NoKotlinMetadata(),
+ new NoNativeMethods());
+ policies.stream().map(VerifySingleClassPolicyAlwaysSatisfied::new).forEach(builder::add);
return true;
}
@@ -194,6 +247,22 @@
return builder.add(new FinalizeMergeGroup(appView, mode)).build();
}
+ private static List<? extends Policy> getMultiClassPoliciesForD8(
+ AppView<AppInfo> appView, Mode mode) {
+ ImmutableList.Builder<MultiClassPolicy> builder = ImmutableList.builder();
+ builder.add(
+ new CheckAbstractClasses(appView),
+ new SameMainDexGroup(appView),
+ new SameNestHost(appView),
+ new SameParentClass(),
+ new SyntheticItemsPolicy(appView, mode),
+ new NoDifferentApiReferenceLevel(appView),
+ new LimitClassGroups(appView));
+ assert verifyMultiClassPoliciesIrrelevantForMergingSyntheticsInD8(appView, mode, builder);
+ builder.add(new FinalizeMergeGroup(appView, mode));
+ return builder.build();
+ }
+
private static void addRequiredMultiClassPolicies(
AppView<? extends AppInfoWithClassHierarchy> appView,
Mode mode,
@@ -234,6 +303,14 @@
new OnlyDirectlyConnectedOrUnrelatedInterfaces(appView, mode));
}
+ private static boolean verifyMultiClassPoliciesIrrelevantForMergingSyntheticsInD8(
+ AppView<AppInfo> appView, Mode mode, ImmutableList.Builder<MultiClassPolicy> builder) {
+ List<MultiClassPolicy> policies =
+ ImmutableList.of(new SyntheticItemsPolicy(appView, mode), new SameParentClass());
+ policies.stream().map(VerifyMultiClassPolicyAlwaysSatisfied::new).forEach(builder::add);
+ return true;
+ }
+
private static boolean verifyPolicyOrderingConstraints(List<Policy> policies) {
// No policies that may split interface groups are allowed to run after the
// OnlyDirectlyConnectedOrUnrelatedInterfaces policy. This policy ensures that interface merging
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
index 183664a..31753bf 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.horizontalclassmerging;
import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DefaultInstanceInitializerCode;
import com.android.tools.r8.graph.DexEncodedField;
@@ -43,7 +42,7 @@
*/
class TreeFixer extends TreeFixerBase {
- private final AppView<? extends AppInfoWithClassHierarchy> appView;
+ private final AppView<?> appView;
private final HorizontallyMergedClasses mergedClasses;
private final Mode mode;
private final HorizontalClassMergerGraphLens.Builder lensBuilder;
@@ -55,7 +54,7 @@
HashBiMap.create();
public TreeFixer(
- AppView<? extends AppInfoWithClassHierarchy> appView,
+ AppView<?> appView,
HorizontallyMergedClasses mergedClasses,
HorizontalClassMergerGraphLens.Builder lensBuilder,
Mode mode,
@@ -123,17 +122,20 @@
* </ul>
*/
public HorizontalClassMergerGraphLens fixupTypeReferences() {
- Collection<DexProgramClass> classes = appView.appInfo().classesWithDeterministicOrder();
- Iterables.filter(classes, DexProgramClass::isInterface).forEach(this::fixupInterfaceClass);
- classes.forEach(this::fixupAttributes);
- classes.forEach(this::fixupProgramClassSuperTypes);
- SubtypingForrestForClasses subtypingForrest = new SubtypingForrestForClasses(appView);
- // TODO(b/170078037): parallelize this code segment.
- for (DexProgramClass root : subtypingForrest.getProgramRoots()) {
- subtypingForrest.traverseNodeDepthFirst(root, HashBiMap.create(), this::fixupProgramClass);
- }
HorizontalClassMergerGraphLens lens = lensBuilder.build(appView, mergedClasses);
- new AnnotationFixer(lens).run(appView.appInfo().classes());
+ if (appView.enableWholeProgramOptimizations()) {
+ Collection<DexProgramClass> classes = appView.appInfo().classesWithDeterministicOrder();
+ Iterables.filter(classes, DexProgramClass::isInterface).forEach(this::fixupInterfaceClass);
+ classes.forEach(this::fixupAttributes);
+ classes.forEach(this::fixupProgramClassSuperTypes);
+ SubtypingForrestForClasses subtypingForrest =
+ new SubtypingForrestForClasses(appView.withClassHierarchy());
+ // TODO(b/170078037): parallelize this code segment.
+ for (DexProgramClass root : subtypingForrest.getProgramRoots()) {
+ subtypingForrest.traverseNodeDepthFirst(root, HashBiMap.create(), this::fixupProgramClass);
+ }
+ new AnnotationFixer(lens).run(appView.appInfo().classes());
+ }
return lens;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java
index 446d245..7912f4c 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.horizontalclassmerging.code;
import com.android.tools.r8.graph.AppInfo;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.horizontalclassmerging.IRCodeProvider;
@@ -25,13 +24,13 @@
*/
public class SyntheticInitializerConverter {
- private final AppView<? extends AppInfoWithClassHierarchy> appView;
+ private final AppView<?> appView;
private final IRCodeProvider codeProvider;
private final List<ProgramMethod> classInitializers;
private final List<ProgramMethod> instanceInitializers;
private SyntheticInitializerConverter(
- AppView<? extends AppInfoWithClassHierarchy> appView,
+ AppView<?> appView,
IRCodeProvider codeProvider,
List<ProgramMethod> classInitializers,
List<ProgramMethod> instanceInitializers) {
@@ -41,8 +40,7 @@
this.instanceInitializers = instanceInitializers;
}
- public static Builder builder(
- AppView<? extends AppInfoWithClassHierarchy> appView, IRCodeProvider codeProvider) {
+ public static Builder builder(AppView<?> appView, IRCodeProvider codeProvider) {
return new Builder(appView, codeProvider);
}
@@ -88,13 +86,12 @@
public static class Builder {
- private final AppView<? extends AppInfoWithClassHierarchy> appView;
+ private final AppView<?> appView;
private final IRCodeProvider codeProvider;
private final List<ProgramMethod> classInitializers = new ArrayList<>();
private final List<ProgramMethod> instanceInitializers = new ArrayList<>();
- private Builder(
- AppView<? extends AppInfoWithClassHierarchy> appView, IRCodeProvider codeProvider) {
+ private Builder(AppView<?> appView, IRCodeProvider codeProvider) {
this.appView = appView;
this.codeProvider = codeProvider;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/CheckAbstractClasses.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/CheckAbstractClasses.java
index 41fba55..06874f0 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/CheckAbstractClasses.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/CheckAbstractClasses.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.horizontalclassmerging.policies;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.horizontalclassmerging.MultiClassSameReferencePolicy;
@@ -20,7 +19,7 @@
private final InternalOptions options;
- public CheckAbstractClasses(AppView<? extends AppInfoWithClassHierarchy> appView) {
+ public CheckAbstractClasses(AppView<?> appView) {
this.options = appView.options();
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/CheckSyntheticClasses.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/CheckSyntheticClasses.java
index 8c08712..4d2554a 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/CheckSyntheticClasses.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/CheckSyntheticClasses.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.horizontalclassmerging.policies;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
@@ -16,7 +15,7 @@
private final HorizontalClassMergerOptions options;
private final SyntheticItems syntheticItems;
- public CheckSyntheticClasses(AppView<? extends AppInfoWithClassHierarchy> appView) {
+ public CheckSyntheticClasses(AppView<?> appView) {
this.options = appView.options().horizontalClassMergerOptions();
this.syntheticItems = appView.getSyntheticItems();
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/FinalizeMergeGroup.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/FinalizeMergeGroup.java
index 705c8cd..b9c4c3c 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/FinalizeMergeGroup.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/FinalizeMergeGroup.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.horizontalclassmerging.policies;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
@@ -12,6 +11,7 @@
import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.SetUtils;
+import com.android.tools.r8.utils.collections.EmptyBidirectionalOneToOneMap;
import java.util.Collection;
import java.util.Set;
@@ -25,23 +25,30 @@
*/
public class FinalizeMergeGroup extends MultiClassPolicy {
- private final AppView<? extends AppInfoWithClassHierarchy> appView;
+ private final AppView<?> appView;
private final Mode mode;
- public FinalizeMergeGroup(AppView<? extends AppInfoWithClassHierarchy> appView, Mode mode) {
+ public FinalizeMergeGroup(AppView<?> appView, Mode mode) {
this.appView = appView;
this.mode = mode;
}
@Override
public Collection<MergeGroup> apply(MergeGroup group) {
- if (mode.isInitial() || group.isInterfaceGroup()) {
- group.selectTarget(appView);
- group.selectInstanceFieldMap(appView);
+ if (appView.enableWholeProgramOptimizations()) {
+ if (mode.isInitial() || group.isInterfaceGroup()) {
+ group.selectTarget(appView);
+ group.selectInstanceFieldMap(appView.withClassHierarchy());
+ } else {
+ // In the final round of merging each group should be finalized by the
+ // NoInstanceInitializerMerging policy.
+ assert verifyAlreadyFinalized(group);
+ }
} else {
- // In the final round of merging each group should be finalized by the
- // NoInstanceInitializerMerging policy.
- assert verifyAlreadyFinalized(group);
+ assert !group.hasTarget();
+ assert !group.hasInstanceFieldMap();
+ group.selectTarget(appView);
+ group.setInstanceFieldMap(new EmptyBidirectionalOneToOneMap<>());
}
return ListUtils.newLinkedList(group);
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/LimitClassGroups.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/LimitClassGroups.java
index 2752544..55d0472 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/LimitClassGroups.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/LimitClassGroups.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.horizontalclassmerging.policies;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.horizontalclassmerging.MergeGroup;
@@ -17,8 +16,11 @@
private final int maxGroupSize;
- public LimitClassGroups(AppView<? extends AppInfoWithClassHierarchy> appView) {
- maxGroupSize = appView.options().horizontalClassMergerOptions().getMaxClassGroupSize();
+ public LimitClassGroups(AppView<?> appView) {
+ maxGroupSize =
+ appView.enableWholeProgramOptimizations()
+ ? appView.options().horizontalClassMergerOptions().getMaxClassGroupSizeInR8()
+ : appView.options().horizontalClassMergerOptions().getMaxClassGroupSizeInD8();
assert maxGroupSize >= 2;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInterfaces.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInterfaces.java
index 79b6601..feabe1c 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInterfaces.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInterfaces.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.horizontalclassmerging.policies;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
@@ -16,7 +15,7 @@
private final Mode mode;
private final HorizontalClassMergerOptions options;
- public NoInterfaces(AppView<? extends AppInfoWithClassHierarchy> appView, Mode mode) {
+ public NoInterfaces(AppView<?> appView, Mode mode) {
this.mode = mode;
this.options = appView.options().horizontalClassMergerOptions();
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyClassesWithStaticDefinitions.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyClassesWithStaticDefinitions.java
new file mode 100644
index 0000000..736ede0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/OnlyClassesWithStaticDefinitions.java
@@ -0,0 +1,23 @@
+// Copyright (c) 2022, 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.horizontalclassmerging.policies;
+
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
+import com.google.common.collect.Iterables;
+
+/** Prevent merging of classes that has non-static methods or fields. */
+public class OnlyClassesWithStaticDefinitions extends SingleClassPolicy {
+
+ @Override
+ public boolean canMerge(DexProgramClass program) {
+ return !Iterables.any(program.members(), member -> !member.isStatic());
+ }
+
+ @Override
+ public String getName() {
+ return "OnlyStaticDefinitions";
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameMainDexGroup.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameMainDexGroup.java
index 6df343a..82dcbed 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameMainDexGroup.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameMainDexGroup.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.horizontalclassmerging.policies;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.horizontalclassmerging.MultiClassSameReferencePolicy;
@@ -17,7 +16,7 @@
private final MainDexInfo mainDexInfo;
private final SyntheticItems synthetics;
- public SameMainDexGroup(AppView<? extends AppInfoWithClassHierarchy> appView) {
+ public SameMainDexGroup(AppView<?> appView) {
mainDexInfo = appView.appInfo().getMainDexInfo();
synthetics = appView.getSyntheticItems();
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameNestHost.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameNestHost.java
index a4ba653..0f6e5ae 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameNestHost.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameNestHost.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.horizontalclassmerging.policies;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
@@ -15,7 +14,7 @@
private final DexItemFactory dexItemFactory;
- public SameNestHost(AppView<? extends AppInfoWithClassHierarchy> appView) {
+ public SameNestHost(AppView<?> appView) {
this.dexItemFactory = appView.dexItemFactory();
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SyntheticItemsPolicy.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SyntheticItemsPolicy.java
index f40b722..95764f9 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SyntheticItemsPolicy.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SyntheticItemsPolicy.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.horizontalclassmerging.policies;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
@@ -22,7 +21,7 @@
private final Mode mode;
private final SyntheticItems syntheticItems;
- public SyntheticItemsPolicy(AppView<? extends AppInfoWithClassHierarchy> appView, Mode mode) {
+ public SyntheticItemsPolicy(AppView<?> appView, Mode mode) {
this.mode = mode;
this.syntheticItems = appView.getSyntheticItems();
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/VerifyMultiClassPolicyAlwaysSatisfied.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/VerifyMultiClassPolicyAlwaysSatisfied.java
new file mode 100644
index 0000000..7ad1e60
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/VerifyMultiClassPolicyAlwaysSatisfied.java
@@ -0,0 +1,44 @@
+// Copyright (c) 2022, 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.horizontalclassmerging.policies;
+
+import com.android.tools.r8.horizontalclassmerging.MergeGroup;
+import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
+import com.android.tools.r8.utils.InternalOptions;
+import java.util.Collection;
+import java.util.Collections;
+
+public class VerifyMultiClassPolicyAlwaysSatisfied extends MultiClassPolicy {
+
+ private final MultiClassPolicy policy;
+
+ public VerifyMultiClassPolicyAlwaysSatisfied(MultiClassPolicy policy) {
+ this.policy = policy;
+ }
+
+ @Override
+ public String getName() {
+ return "VerifyMultiClassPolicyAlwaysSatisfied(" + policy.getName() + ")";
+ }
+
+ @Override
+ public boolean shouldSkipPolicy() {
+ return !InternalOptions.assertionsEnabled() || policy.shouldSkipPolicy();
+ }
+
+ @Override
+ public Collection<MergeGroup> apply(MergeGroup group) {
+ assert verifySameAppliedGroup(group);
+ return Collections.singletonList(group);
+ }
+
+ private boolean verifySameAppliedGroup(MergeGroup group) {
+ Collection<MergeGroup> applied = policy.apply(group);
+ assert applied.size() == 1;
+ MergeGroup appliedGroup = applied.iterator().next();
+ assert appliedGroup.size() == group.size() && group.containsAll(appliedGroup);
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/VerifyPolicyAlwaysSatisfied.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/VerifySingleClassPolicyAlwaysSatisfied.java
similarity index 65%
rename from src/main/java/com/android/tools/r8/horizontalclassmerging/policies/VerifyPolicyAlwaysSatisfied.java
rename to src/main/java/com/android/tools/r8/horizontalclassmerging/policies/VerifySingleClassPolicyAlwaysSatisfied.java
index 1fc559c..51d33b7 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/VerifyPolicyAlwaysSatisfied.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/VerifySingleClassPolicyAlwaysSatisfied.java
@@ -6,12 +6,13 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
+import com.android.tools.r8.utils.InternalOptions;
-public class VerifyPolicyAlwaysSatisfied extends SingleClassPolicy {
+public class VerifySingleClassPolicyAlwaysSatisfied extends SingleClassPolicy {
private final SingleClassPolicy policy;
- public VerifyPolicyAlwaysSatisfied(SingleClassPolicy policy) {
+ public VerifySingleClassPolicyAlwaysSatisfied(SingleClassPolicy policy) {
this.policy = policy;
}
@@ -23,11 +24,11 @@
@Override
public String getName() {
- return "VerifyAlwaysSatisfied(" + policy.getName() + ")";
+ return "VerifySingleClassPolicyAlwaysSatisfied(" + policy.getName() + ")";
}
@Override
public boolean shouldSkipPolicy() {
- return policy.shouldSkipPolicy();
+ return !InternalOptions.assertionsEnabled() || policy.shouldSkipPolicy();
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index b7a666d..210ea92 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -1449,10 +1449,14 @@
this.enable = enable;
}
- public int getMaxClassGroupSize() {
+ public int getMaxClassGroupSizeInR8() {
return 30;
}
+ public int getMaxClassGroupSizeInD8() {
+ return 100;
+ }
+
public int getMaxInterfaceGroupSize() {
return 100;
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java
index 31f5bda..e8d6257 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java
@@ -78,9 +78,9 @@
@Test
public void testD8Debug() throws Exception {
// TODO(b/197078995): Make this work on 12+.
- assumeTrue(
- parameters.isDexRuntime()
- && parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0));
+ assumeFalse(
+ parameters.isCfRuntime()
+ || parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
testForD8()
.setMode(CompilationMode.DEBUG)
.apply(this::setupTestBuilder)
@@ -109,9 +109,8 @@
@Test
public void testR8() throws Exception {
// TODO(b/197078995): Make this work on 12+.
- assumeFalse(
- parameters.isDexRuntime()
- && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
+ assumeTrue(
+ parameters.isCfRuntime() || parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0));
testForR8(parameters.getBackend())
.apply(this::setupTestBuilder)
.addKeepMainRule(Main.class)
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java
index d00efb9..fb98821 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java
@@ -105,7 +105,7 @@
.apply(this::checkOutput)
.inspect(
inspector -> {
- // TODO(b/187675788): Update when horizontal merging is enabled for D8.
+ // TODO(b/187675788): Update when horizontal merging is enabled for D8 for debug mode.
if (parameters.getApiLevel().isLessThan(firstMethodApiLevel)) {
// We have generated 4 outlines two having api level 23 and two having api level 27.
assertEquals(7, inspector.allClasses().size());
@@ -133,19 +133,7 @@
b -> b.addBootClasspathClasses(LibraryClass.class, OtherLibraryClass.class))
.run(parameters.getRuntime(), Main.class)
.apply(this::checkOutput)
- .inspect(
- inspector -> {
- // TODO(b/187675788): Update when horizontal merging is enabled for D8.
- if (parameters.getApiLevel().isLessThan(firstMethodApiLevel)) {
- // We have generated 4 outlines two having api level 23 and two having api level 27.
- assertEquals(7, inspector.allClasses().size());
- } else if (parameters.getApiLevel().isLessThan(secondMethodApiLevel)) {
- assertEquals(5, inspector.allClasses().size());
- } else {
- // No outlining on this api level.
- assertEquals(3, inspector.allClasses().size());
- }
- });
+ .inspect(this::inspect);
}
@Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaGroupGCLimitTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaGroupGCLimitTest.java
index 89f57c8..e021fe5 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaGroupGCLimitTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaGroupGCLimitTest.java
@@ -74,7 +74,7 @@
mergeGroup ->
mergeGroup.size()
<= defaultHorizontalClassMergerOptions
- .getMaxClassGroupSize()));
+ .getMaxClassGroupSizeInR8()));
})
.allowDiagnosticWarningMessages()
.compile()