Initial structure for extra round of class merging
Bug: 187496738
Fixes: 187387876
Change-Id: I2c614e06783c058a02ddb02b9dec651c1d50d6a6
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index f1ed0cd..0ba1767 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.dex.Marker.Tool;
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.DesugaredLibraryConfiguration;
@@ -473,13 +474,14 @@
// Assert some of R8 optimizations are disabled.
assert !internal.enableInlining;
assert !internal.enableClassInlining;
- assert internal.horizontalClassMergerOptions().isDisabled();
assert !internal.enableVerticalClassMerging;
assert !internal.enableClassStaticizer;
assert !internal.enableEnumValueOptimization;
assert !internal.outline.enabled;
assert !internal.enableValuePropagation;
assert !internal.enableTreeShakingOfLibraryMethodOverrides;
+ assert !internal.horizontalClassMergerOptions().isEnabled(HorizontalClassMerger.Mode.INITIAL);
+ assert !internal.horizontalClassMergerOptions().isEnabled(HorizontalClassMerger.Mode.FINAL);
internal.desugarState = getDesugarState();
internal.encodeChecksums = getIncludeClassesChecksum();
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index ec9955d..69fa568 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.dex.Marker.Tool;
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.ir.desugar.DesugaredLibraryConfiguration;
import com.android.tools.r8.origin.Origin;
@@ -172,13 +173,14 @@
// Assert some of R8 optimizations are disabled.
assert !internal.enableInlining;
assert !internal.enableClassInlining;
- assert internal.horizontalClassMergerOptions().isDisabled();
assert !internal.enableVerticalClassMerging;
assert !internal.enableClassStaticizer;
assert !internal.enableEnumValueOptimization;
assert !internal.outline.enabled;
assert !internal.enableValuePropagation;
assert !internal.enableTreeShakingOfLibraryMethodOverrides;
+ assert !internal.horizontalClassMergerOptions().isEnabled(HorizontalClassMerger.Mode.INITIAL);
+ assert !internal.horizontalClassMergerOptions().isEnabled(HorizontalClassMerger.Mode.FINAL);
assert internal.desugarState == DesugarState.ON;
assert internal.enableInheritanceClassInDexDistributor;
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index e58264e..5eb2aa3 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -41,8 +41,6 @@
import com.android.tools.r8.graph.analysis.InitializedClassesInInstanceMethodsAnalysis;
import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger;
-import com.android.tools.r8.horizontalclassmerging.HorizontalClassMergerResult;
-import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses;
import com.android.tools.r8.inspector.internal.InspectorImpl;
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
@@ -323,7 +321,7 @@
List<ProguardConfigurationRule> synthesizedProguardRules = new ArrayList<>();
timing.begin("Strip unused code");
RuntimeTypeCheckInfo.Builder classMergingEnqueuerExtensionBuilder =
- new RuntimeTypeCheckInfo.Builder(appView.dexItemFactory());
+ new RuntimeTypeCheckInfo.Builder(appView);
try {
// Add synthesized -assumenosideeffects from min api if relevant.
if (options.isGeneratingDex()) {
@@ -453,7 +451,10 @@
boolean isKotlinLibraryCompilationWithInlinePassThrough =
options.enableCfByteCodePassThrough && appView.hasCfByteCodePassThroughMethods();
- RuntimeTypeCheckInfo runtimeTypeCheckInfo = classMergingEnqueuerExtensionBuilder.build();
+ RuntimeTypeCheckInfo runtimeTypeCheckInfo =
+ classMergingEnqueuerExtensionBuilder.build(appView.graphLens());
+ classMergingEnqueuerExtensionBuilder = null;
+
if (!isKotlinLibraryCompilationWithInlinePassThrough
&& options.getProguardConfiguration().isOptimizing()) {
if (options.enableVerticalClassMerging) {
@@ -499,33 +500,9 @@
timing.end();
}
}
- if (options.horizontalClassMergerOptions().isEnabled()
- && options.enableInlining
- && options.isShrinking()) {
- timing.begin("HorizontalClassMerger");
- HorizontalClassMerger merger = new HorizontalClassMerger(appViewWithLiveness);
- HorizontalClassMergerResult horizontalClassMergerResult =
- merger.run(runtimeTypeCheckInfo, timing);
- if (horizontalClassMergerResult != null) {
- // Must rewrite AppInfoWithLiveness before pruning the merged classes, to ensure that
- // allocations sites, fields accesses, etc. are correctly transferred to the target
- // classes.
- appView.rewriteWithLens(horizontalClassMergerResult.getGraphLens());
- horizontalClassMergerResult
- .getFieldAccessInfoCollectionModifier()
- .modify(appViewWithLiveness);
- appView.pruneItems(
- PrunedItems.builder()
- .setPrunedApp(appView.appInfo().app())
- .addRemovedClasses(appView.horizontallyMergedClasses().getSources())
- .addNoLongerSyntheticItems(appView.horizontallyMergedClasses().getSources())
- .build());
- }
- timing.end();
- } else {
- appView.setHorizontallyMergedClasses(HorizontallyMergedClasses.empty());
- }
+ HorizontalClassMerger.createForInitialClassMerging(appViewWithLiveness)
+ .runIfNecessary(runtimeTypeCheckInfo, timing);
}
// Clear traced methods roots to not hold on to the main dex live method set.
@@ -596,6 +573,10 @@
new SubtypingInfo(appView),
keptGraphConsumer,
prunedTypes);
+ if (options.isClassMergingExtensionRequired(enqueuer.getMode())) {
+ classMergingEnqueuerExtensionBuilder = new RuntimeTypeCheckInfo.Builder(appView);
+ classMergingEnqueuerExtensionBuilder.attach(enqueuer);
+ }
EnqueuerResult enqueuerResult =
enqueuer.traceApplication(appView.rootSet(), executorService, timing);
appView.setAppInfo(enqueuerResult.getAppInfo());
@@ -730,6 +711,15 @@
SyntheticFinalization.finalizeWithClassHierarchy(appView);
}
+ // Run horizontal class merging. This runs even if shrinking is disabled to ensure synthetics
+ // are always merged.
+ HorizontalClassMerger.createForFinalClassMerging(appView)
+ .runIfNecessary(
+ classMergingEnqueuerExtensionBuilder != null
+ ? classMergingEnqueuerExtensionBuilder.build(appView.graphLens())
+ : null,
+ timing);
+
// Perform minification.
NamingLens namingLens;
if (options.getProguardConfiguration().hasApplyMappingFile()) {
@@ -968,7 +958,7 @@
appView.dexItemFactory(), OptimizationFeedbackSimple.getInstance()));
}
- if (options.isClassMergingExtensionRequired()) {
+ if (options.isClassMergingExtensionRequired(enqueuer.getMode())) {
classMergingEnqueuerExtensionBuilder.attach(enqueuer);
}
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index e3de56c..8deaa9c 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
import com.android.tools.r8.features.FeatureSplitConfiguration;
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.DesugaredLibraryConfiguration;
@@ -32,6 +33,7 @@
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.DesugarState;
+import com.android.tools.r8.utils.InternalOptions.HorizontalClassMergerOptions;
import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
@@ -854,8 +856,12 @@
? LineNumberOptimization.ON
: LineNumberOptimization.OFF;
+ HorizontalClassMergerOptions horizontalClassMergerOptions =
+ internal.horizontalClassMergerOptions();
assert proguardConfiguration.isOptimizing()
- || internal.horizontalClassMergerOptions().isDisabled();
+ || (!horizontalClassMergerOptions.isEnabled(HorizontalClassMerger.Mode.INITIAL)
+ && !horizontalClassMergerOptions.isEnabled(HorizontalClassMerger.Mode.FINAL));
+
assert !internal.enableTreeShakingOfLibraryMethodOverrides;
assert internal.enableVerticalClassMerging || !proguardConfiguration.isOptimizing();
if (internal.debug) {
@@ -864,26 +870,19 @@
internal.getProguardConfiguration().getKeepAttributes().localVariableTypeTable = true;
internal.enableInlining = false;
internal.enableClassInlining = false;
- internal.horizontalClassMergerOptions().disable();
internal.enableVerticalClassMerging = false;
internal.enableClassStaticizer = false;
internal.outline.enabled = false;
internal.enableEnumUnboxing = false;
}
+
if (!internal.isShrinking()) {
// If R8 is not shrinking, there is no point in running various optimizations since the
// optimized classes will still remain in the program (the application size could increase).
internal.enableEnumUnboxing = false;
- internal.horizontalClassMergerOptions().disable();
internal.enableVerticalClassMerging = false;
}
- if (!internal.enableInlining) {
- // If R8 cannot perform inlining, then the synthetic constructors would not inline the called
- // constructors, producing invalid code.
- internal.horizontalClassMergerOptions().disable();
- }
-
// Amend the proguard-map consumer with options from the proguard configuration.
internal.proguardMapConsumer =
wrapStringConsumer(
@@ -939,7 +938,7 @@
if (internal.isGeneratingClassFiles()) {
internal.outline.enabled = false;
internal.enableEnumUnboxing = false;
- internal.horizontalClassMergerOptions().disable();
+ horizontalClassMergerOptions.disable();
}
// EXPERIMENTAL flags.
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index b3c5308..d9df6e8 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.graph.analysis.InitializedClassesInInstanceMethodsAnalysis.InitializedClassesInInstanceMethods;
import com.android.tools.r8.graph.classmerging.MergedClassesCollection;
import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
+import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger;
import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses;
import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraintFactory;
import com.android.tools.r8.ir.analysis.proto.EnumLiteProtoShrinker;
@@ -505,6 +506,10 @@
return collection;
}
+ public boolean hasHorizontallyMergedClasses() {
+ return horizontallyMergedClasses != null;
+ }
+
/**
* Get the result of horizontal class merging. Returns null if horizontal class merging has not
* been run.
@@ -513,10 +518,18 @@
return horizontallyMergedClasses;
}
- public void setHorizontallyMergedClasses(HorizontallyMergedClasses horizontallyMergedClasses) {
- assert this.horizontallyMergedClasses == null;
- this.horizontallyMergedClasses = horizontallyMergedClasses;
- testing().horizontallyMergedClassesConsumer.accept(dexItemFactory(), horizontallyMergedClasses);
+ public void setHorizontallyMergedClasses(
+ HorizontallyMergedClasses horizontallyMergedClasses, HorizontalClassMerger.Mode mode) {
+ if (mode.isInitial()) {
+ assert !hasHorizontallyMergedClasses();
+ this.horizontallyMergedClasses = horizontallyMergedClasses;
+ testing()
+ .horizontallyMergedClassesConsumer
+ .accept(dexItemFactory(), horizontallyMergedClasses);
+ } else {
+ // TODO(b/187496738): Enable final class merging.
+ assert horizontallyMergedClasses.isEmpty();
+ }
}
/**
diff --git a/src/main/java/com/android/tools/r8/graph/DexApplication.java b/src/main/java/com/android/tools/r8/graph/DexApplication.java
index d82db4a..31b83b2 100644
--- a/src/main/java/com/android/tools/r8/graph/DexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DexApplication.java
@@ -20,6 +20,7 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
+import java.util.function.Predicate;
public abstract class DexApplication {
@@ -177,6 +178,11 @@
return self();
}
+ public synchronized T removeProgramClasses(Predicate<DexProgramClass> predicate) {
+ this.programClasses.removeIf(predicate);
+ return self();
+ }
+
public synchronized T replaceProgramClasses(Collection<DexProgramClass> newProgramClasses) {
assert newProgramClasses != null;
this.programClasses.clear();
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 c94039b..2397bdb 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -7,6 +7,8 @@
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.graph.DirectMappedDexApplication;
+import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.horizontalclassmerging.policies.AllInstantiatedOrUninstantiated;
import com.android.tools.r8.horizontalclassmerging.policies.CheckAbstractClasses;
import com.android.tools.r8.horizontalclassmerging.policies.DontInlinePolicy;
@@ -24,6 +26,7 @@
import com.android.tools.r8.horizontalclassmerging.policies.NoIndirectRuntimeTypeChecks;
import com.android.tools.r8.horizontalclassmerging.policies.NoInnerClasses;
import com.android.tools.r8.horizontalclassmerging.policies.NoInstanceFieldAnnotations;
+import com.android.tools.r8.horizontalclassmerging.policies.NoInterfaces;
import com.android.tools.r8.horizontalclassmerging.policies.NoKeepRules;
import com.android.tools.r8.horizontalclassmerging.policies.NoKotlinMetadata;
import com.android.tools.r8.horizontalclassmerging.policies.NoNativeMethods;
@@ -42,7 +45,9 @@
import com.android.tools.r8.horizontalclassmerging.policies.SyntheticItemsPolicy;
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.utils.InternalOptions.HorizontalClassMergerOptions;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
@@ -52,32 +57,58 @@
public class HorizontalClassMerger {
- // TODO(b/181846319): Add 'FINAL' mode that runs after synthetic finalization.
public enum Mode {
- INITIAL;
+ INITIAL,
+ FINAL;
public boolean isInitial() {
return this == INITIAL;
}
+
+ public boolean isFinal() {
+ return this == FINAL;
+ }
}
private final AppView<? extends AppInfoWithClassHierarchy> appView;
- private final Mode mode = Mode.INITIAL;
+ private final Mode mode;
+ private final HorizontalClassMergerOptions options;
- public HorizontalClassMerger(AppView<? extends AppInfoWithClassHierarchy> appView) {
+ private HorizontalClassMerger(AppView<? extends AppInfoWithClassHierarchy> appView, Mode mode) {
this.appView = appView;
- assert appView.options().enableInlining;
+ this.mode = mode;
+ this.options = appView.options().horizontalClassMergerOptions();
}
- public HorizontalClassMergerResult run(RuntimeTypeCheckInfo runtimeTypeCheckInfo, Timing timing) {
+ public static HorizontalClassMerger createForInitialClassMerging(
+ AppView<AppInfoWithLiveness> appView) {
+ return new HorizontalClassMerger(appView, Mode.INITIAL);
+ }
+
+ public static HorizontalClassMerger createForFinalClassMerging(
+ AppView<? extends AppInfoWithClassHierarchy> appView) {
+ return new HorizontalClassMerger(appView, Mode.FINAL);
+ }
+
+ public void runIfNecessary(RuntimeTypeCheckInfo runtimeTypeCheckInfo, Timing timing) {
+ if (options.isEnabled(mode)) {
+ timing.begin("HorizontalClassMerger (" + mode.toString() + ")");
+ run(runtimeTypeCheckInfo, timing);
+ timing.end();
+ } else {
+ appView.setHorizontallyMergedClasses(HorizontallyMergedClasses.empty(), mode);
+ }
+ }
+
+ private void run(RuntimeTypeCheckInfo runtimeTypeCheckInfo, Timing timing) {
// Run the policies on all program classes to produce a final grouping.
List<Policy> policies = getPolicies(runtimeTypeCheckInfo);
Collection<MergeGroup> groups = new PolicyExecutor().run(getInitialGroups(), policies, timing);
// If there are no groups, then end horizontal class merging.
if (groups.isEmpty()) {
- appView.setHorizontallyMergedClasses(HorizontallyMergedClasses.empty());
- return null;
+ appView.setHorizontallyMergedClasses(HorizontallyMergedClasses.empty(), mode);
+ return;
}
HorizontalClassMergerGraphLens.Builder lensBuilder =
@@ -94,20 +125,38 @@
// Generate the graph lens.
HorizontallyMergedClasses mergedClasses =
HorizontallyMergedClasses.builder().addMergeGroups(groups).build();
- appView.setHorizontallyMergedClasses(mergedClasses);
- HorizontalClassMergerGraphLens lens =
+ appView.setHorizontallyMergedClasses(mergedClasses, mode);
+
+ HorizontalClassMergerGraphLens horizontalClassMergerGraphLens =
createLens(mergedClasses, lensBuilder, syntheticArgumentClass);
// Prune keep info.
- appView
- .getKeepInfo()
- .mutate(mutator -> mutator.removeKeepInfoForPrunedItems(mergedClasses.getSources()));
+ KeepInfoCollection keepInfo = appView.getKeepInfo();
+ keepInfo.mutate(mutator -> mutator.removeKeepInfoForPrunedItems(mergedClasses.getSources()));
- return new HorizontalClassMergerResult(createFieldAccessInfoCollectionModifier(groups), lens);
+ // 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));
+
+ // Record where the synthesized $r8$classId fields are read and written.
+ if (mode.isInitial()) {
+ createFieldAccessInfoCollectionModifier(groups).modify(appView.withLiveness());
+ } else {
+ assert groups.stream().noneMatch(MergeGroup::hasClassIdField);
+ }
+
+ appView.pruneItems(
+ PrunedItems.builder()
+ .setPrunedApp(appView.appInfo().app())
+ .addRemovedClasses(mergedClasses.getSources())
+ .addNoLongerSyntheticItems(mergedClasses.getSources())
+ .build());
}
private FieldAccessInfoCollectionModifier createFieldAccessInfoCollectionModifier(
Collection<MergeGroup> groups) {
+ assert mode.isInitial();
FieldAccessInfoCollectionModifier.Builder builder =
new FieldAccessInfoCollectionModifier.Builder();
for (MergeGroup group : groups) {
@@ -125,14 +174,25 @@
return builder.build();
}
+ private DirectMappedDexApplication 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();
+ }
+
private List<MergeGroup> getInitialGroups() {
MergeGroup initialClassGroup = new MergeGroup();
MergeGroup initialInterfaceGroup = new MergeGroup();
for (DexProgramClass clazz : appView.appInfo().classesWithDeterministicOrder()) {
if (clazz.isInterface()) {
- if (appView.options().horizontalClassMergerOptions().isInterfaceMergingEnabled()) {
- initialInterfaceGroup.add(clazz);
- }
+ initialInterfaceGroup.add(clazz);
} else {
initialClassGroup.add(clazz);
}
@@ -152,6 +212,7 @@
new NoDeadEnumLiteMaps(appViewWithLiveness),
new NoAnnotationClasses(),
new NoEnums(appView),
+ new NoInterfaces(appView),
new NoInnerClasses(),
new NoInstanceFieldAnnotations(),
new NoClassInitializerWithObservableSideEffects(),
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerResult.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerResult.java
deleted file mode 100644
index 820c24a..0000000
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerResult.java
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright (c) 2020, 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;
-
-import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
-
-public class HorizontalClassMergerResult {
-
- private final FieldAccessInfoCollectionModifier fieldAccessInfoCollectionModifier;
- private final HorizontalClassMergerGraphLens graphLens;
-
- HorizontalClassMergerResult(
- FieldAccessInfoCollectionModifier fieldAccessInfoCollectionModifier,
- HorizontalClassMergerGraphLens graphLens) {
- this.fieldAccessInfoCollectionModifier = fieldAccessInfoCollectionModifier;
- this.graphLens = graphLens;
- }
-
- public FieldAccessInfoCollectionModifier getFieldAccessInfoCollectionModifier() {
- return fieldAccessInfoCollectionModifier;
- }
-
- public HorizontalClassMergerGraphLens getGraphLens() {
- return graphLens;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
index 927e629..67d390b 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
@@ -58,6 +58,10 @@
return mergedClasses.containsKey(type);
}
+ public boolean isEmpty() {
+ return mergedClasses.isEmpty();
+ }
+
@Override
public boolean isMergeTarget(DexType type) {
return mergedClasses.containsValue(type);
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
new file mode 100644
index 0000000..a6babcd
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoInterfaces.java
@@ -0,0 +1,35 @@
+// Copyright (c) 2020, 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.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
+import com.android.tools.r8.utils.InternalOptions.HorizontalClassMergerOptions;
+
+public class NoInterfaces extends SingleClassPolicy {
+
+ private final HorizontalClassMergerOptions options;
+
+ public NoInterfaces(AppView<? extends AppInfoWithClassHierarchy> appView) {
+ this.options = appView.options().horizontalClassMergerOptions();
+ }
+
+ @Override
+ public boolean canMerge(DexProgramClass clazz) {
+ return !clazz.isInterface();
+ }
+
+ @Override
+ public boolean shouldSkipPolicy() {
+ return options.isInterfaceMergingEnabled();
+ }
+
+ @Override
+ public String getName() {
+ return "NoInterfaces";
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/RuntimeTypeCheckInfo.java b/src/main/java/com/android/tools/r8/shaking/RuntimeTypeCheckInfo.java
index fb3868a..fc2756a 100644
--- a/src/main/java/com/android/tools/r8/shaking/RuntimeTypeCheckInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/RuntimeTypeCheckInfo.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.shaking;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
@@ -33,18 +34,25 @@
implements EnqueuerInstanceOfAnalysis,
EnqueuerCheckCastAnalysis,
EnqueuerExceptionGuardAnalysis {
+
+ private final GraphLens appliedGraphLens;
private final DexItemFactory factory;
private final Set<DexType> instanceOfTypes = Sets.newIdentityHashSet();
private final Set<DexType> checkCastTypes = Sets.newIdentityHashSet();
private final Set<DexType> exceptionGuardTypes = Sets.newIdentityHashSet();
- public Builder(DexItemFactory factory) {
- this.factory = factory;
+ public Builder(AppView<?> appView) {
+ this.appliedGraphLens = appView.graphLens();
+ this.factory = appView.dexItemFactory();
}
- public RuntimeTypeCheckInfo build() {
- return new RuntimeTypeCheckInfo(instanceOfTypes, checkCastTypes, exceptionGuardTypes);
+ public RuntimeTypeCheckInfo build(GraphLens graphLens) {
+ RuntimeTypeCheckInfo runtimeTypeCheckInfo =
+ new RuntimeTypeCheckInfo(instanceOfTypes, checkCastTypes, exceptionGuardTypes);
+ return graphLens.isNonIdentityLens() && graphLens != appliedGraphLens
+ ? runtimeTypeCheckInfo.rewriteWithLens(graphLens.asNonIdentityLens())
+ : runtimeTypeCheckInfo;
}
@Override
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 74ed75a..f1a3bd9 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -40,6 +40,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
+import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger;
import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses;
import com.android.tools.r8.inspector.internal.InspectorImpl;
import com.android.tools.r8.ir.code.IRCode;
@@ -614,8 +615,16 @@
* If any non-static class merging is enabled, information about types referred to by instanceOf
* and check cast instructions needs to be collected.
*/
- public boolean isClassMergingExtensionRequired() {
- return horizontalClassMergerOptions.isEnabled() || enableVerticalClassMerging;
+ public boolean isClassMergingExtensionRequired(Enqueuer.Mode mode) {
+ if (mode.isInitialTreeShaking()) {
+ return horizontalClassMergerOptions.isEnabled(HorizontalClassMerger.Mode.INITIAL)
+ || enableVerticalClassMerging;
+ }
+ if (mode.isFinalTreeShaking()) {
+ return horizontalClassMergerOptions.isEnabled(HorizontalClassMerger.Mode.FINAL);
+ }
+ assert false;
+ return false;
}
@Override
@@ -1190,7 +1199,7 @@
}
}
- public static class HorizontalClassMergerOptions {
+ public class HorizontalClassMergerOptions {
// TODO(b/138781768): Set enable to true when this bug is resolved.
private boolean enable =
@@ -1226,12 +1235,16 @@
return enableConstructorMerging;
}
- public boolean isDisabled() {
- return !isEnabled();
- }
-
- public boolean isEnabled() {
- return enable;
+ public boolean isEnabled(HorizontalClassMerger.Mode mode) {
+ if (!enable || debug || intermediate) {
+ return false;
+ }
+ if (mode.isInitial()) {
+ return enableInlining && isShrinking();
+ }
+ assert mode.isFinal();
+ // TODO(b/187496738): Enable final class merging.
+ return false;
}
public boolean isIgnoreRuntimeTypeChecksForTestingEnabled() {
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 1f82c8b..0a80034 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
@@ -21,6 +21,7 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.HorizontalClassMergerOptions;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
@@ -66,7 +67,7 @@
.addHorizontallyMergedClassesInspector(
inspector -> {
HorizontalClassMergerOptions defaultHorizontalClassMergerOptions =
- new HorizontalClassMergerOptions();
+ new InternalOptions().horizontalClassMergerOptions();
assertEquals(4833, inspector.getSources().size());
assertEquals(167, inspector.getTargets().size());
assertTrue(