blob: 0d3bf0d686cfbd34afc31a0d66247bec4fde05ee [file] [log] [blame]
// Copyright (c) 2021, 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.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
import com.android.tools.r8.horizontalclassmerging.policies.AllInstantiatedOrUninstantiated;
import com.android.tools.r8.horizontalclassmerging.policies.CheckAbstractClasses;
import com.android.tools.r8.horizontalclassmerging.policies.CheckSyntheticClasses;
import com.android.tools.r8.horizontalclassmerging.policies.FinalizeMergeGroup;
import com.android.tools.r8.horizontalclassmerging.policies.LimitClassGroups;
import com.android.tools.r8.horizontalclassmerging.policies.MinimizeInstanceFieldCasts;
import com.android.tools.r8.horizontalclassmerging.policies.NoAnnotationClasses;
import com.android.tools.r8.horizontalclassmerging.policies.NoClassAnnotationCollisions;
import com.android.tools.r8.horizontalclassmerging.policies.NoClassInitializerWithObservableSideEffects;
import com.android.tools.r8.horizontalclassmerging.policies.NoConstructorCollisions;
import com.android.tools.r8.horizontalclassmerging.policies.NoDeadEnumLiteMaps;
import com.android.tools.r8.horizontalclassmerging.policies.NoDeadLocks;
import com.android.tools.r8.horizontalclassmerging.policies.NoDefaultInterfaceMethodCollisions;
import com.android.tools.r8.horizontalclassmerging.policies.NoDefaultInterfaceMethodMerging;
import com.android.tools.r8.horizontalclassmerging.policies.NoDirectRuntimeTypeChecks;
import com.android.tools.r8.horizontalclassmerging.policies.NoEnums;
import com.android.tools.r8.horizontalclassmerging.policies.NoIllegalInlining;
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.NoInstanceInitializerMerging;
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;
import com.android.tools.r8.horizontalclassmerging.policies.NoServiceLoaders;
import com.android.tools.r8.horizontalclassmerging.policies.NoVerticallyMergedClasses;
import com.android.tools.r8.horizontalclassmerging.policies.NoVirtualMethodMerging;
import com.android.tools.r8.horizontalclassmerging.policies.NotMatchedByNoHorizontalClassMerging;
import com.android.tools.r8.horizontalclassmerging.policies.OnlyDirectlyConnectedOrUnrelatedInterfaces;
import com.android.tools.r8.horizontalclassmerging.policies.PreserveMethodCharacteristics;
import com.android.tools.r8.horizontalclassmerging.policies.PreventClassMethodAndDefaultMethodCollisions;
import com.android.tools.r8.horizontalclassmerging.policies.RespectPackageBoundaries;
import com.android.tools.r8.horizontalclassmerging.policies.SameFeatureSplit;
import com.android.tools.r8.horizontalclassmerging.policies.SameInstanceFields;
import com.android.tools.r8.horizontalclassmerging.policies.SameMainDexGroup;
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.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.RuntimeTypeCheckInfo;
import com.android.tools.r8.utils.ListUtils;
import com.google.common.collect.ImmutableList;
import java.util.List;
public class PolicyScheduler {
public static List<Policy> getPolicies(
AppView<? extends AppInfoWithClassHierarchy> appView,
IRCodeProvider codeProvider,
Mode mode,
RuntimeTypeCheckInfo runtimeTypeCheckInfo) {
List<Policy> policies =
ImmutableList.<Policy>builder()
.addAll(getSingleClassPolicies(appView, mode, runtimeTypeCheckInfo))
.addAll(getMultiClassPolicies(appView, codeProvider, mode, runtimeTypeCheckInfo))
.build();
assert verifyPolicyOrderingConstraints(policies);
return policies;
}
private static List<SingleClassPolicy> getSingleClassPolicies(
AppView<? extends AppInfoWithClassHierarchy> appView,
Mode mode,
RuntimeTypeCheckInfo runtimeTypeCheckInfo) {
ImmutableList.Builder<SingleClassPolicy> builder = ImmutableList.builder();
addRequiredSingleClassPolicies(appView, builder);
if (mode.isInitial()) {
AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
builder.add(
new NoDeadEnumLiteMaps(appViewWithLiveness, mode),
new NoIllegalInlining(appViewWithLiveness, mode),
new NoVerticallyMergedClasses(appViewWithLiveness, mode));
}
if (appView.options().horizontalClassMergerOptions().isRestrictedToSynthetics()) {
assert verifySingleClassPoliciesIrrelevantForMergingSynthetics(appView, mode, builder);
} else {
AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
addSingleClassPoliciesForMergingNonSyntheticClasses(
appViewWithLiveness, mode, runtimeTypeCheckInfo, builder);
}
return builder.build();
}
private static void addRequiredSingleClassPolicies(
AppView<? extends AppInfoWithClassHierarchy> appView,
ImmutableList.Builder<SingleClassPolicy> builder) {
builder.add(
new CheckSyntheticClasses(appView),
new NoKeepRules(appView),
new NoClassInitializerWithObservableSideEffects());
}
private static void addSingleClassPoliciesForMergingNonSyntheticClasses(
AppView<AppInfoWithLiveness> appView,
Mode mode,
RuntimeTypeCheckInfo runtimeTypeCheckInfo,
ImmutableList.Builder<SingleClassPolicy> builder) {
builder.add(
new NotMatchedByNoHorizontalClassMerging(appView),
new NoAnnotationClasses(),
new NoDirectRuntimeTypeChecks(appView, mode, runtimeTypeCheckInfo),
new NoEnums(appView),
new NoInterfaces(appView, mode),
new NoInnerClasses(),
new NoInstanceFieldAnnotations(),
new NoKotlinMetadata(),
new NoNativeMethods(),
new NoServiceLoaders(appView));
}
private static boolean verifySingleClassPoliciesIrrelevantForMergingSynthetics(
AppView<? extends AppInfoWithClassHierarchy> appView,
Mode mode,
ImmutableList.Builder<SingleClassPolicy> builder) {
List<SingleClassPolicy> policies =
ImmutableList.of(
new NoAnnotationClasses(),
new NoDirectRuntimeTypeChecks(appView, mode),
new NoEnums(appView),
new NoInterfaces(appView, mode),
new NoInnerClasses(),
new NoInstanceFieldAnnotations(),
new NoKotlinMetadata(),
new NoNativeMethods(),
new NoServiceLoaders(appView));
policies.stream().map(VerifyPolicyAlwaysSatisfied::new).forEach(builder::add);
return true;
}
private static List<Policy> getMultiClassPolicies(
AppView<? extends AppInfoWithClassHierarchy> appView,
IRCodeProvider codeProvider,
Mode mode,
RuntimeTypeCheckInfo runtimeTypeCheckInfo) {
ImmutableList.Builder<Policy> builder = ImmutableList.builder();
addRequiredMultiClassPolicies(appView, mode, builder);
if (!appView.options().horizontalClassMergerOptions().isRestrictedToSynthetics()) {
AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
addMultiClassPoliciesForMergingNonSyntheticClasses(
appViewWithLiveness, runtimeTypeCheckInfo, builder);
}
if (mode.isInitial()) {
AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
builder.add(
new AllInstantiatedOrUninstantiated(appViewWithLiveness, mode),
new PreserveMethodCharacteristics(appViewWithLiveness, mode),
new MinimizeInstanceFieldCasts());
} else {
assert mode.isFinal();
builder.add(
new NoVirtualMethodMerging(appView, mode),
new NoConstructorCollisions(appView, mode));
}
addMultiClassPoliciesForInterfaceMerging(appView, mode, builder);
builder.add(new LimitClassGroups(appView));
if (mode.isFinal()) {
// This needs to reason about equivalence of instance initializers, which relies on the
// mapping from instance fields on source classes to the instance fields on target classes.
// This policy therefore selects a target for each merge group and creates the mapping for
// instance fields. For this reason we run this policy in the very end.
builder.add(new NoInstanceInitializerMerging(appView, codeProvider, mode));
}
return builder.add(new FinalizeMergeGroup(appView, mode)).build();
}
private static void addRequiredMultiClassPolicies(
AppView<? extends AppInfoWithClassHierarchy> appView,
Mode mode,
ImmutableList.Builder<Policy> builder) {
builder.add(
new CheckAbstractClasses(appView),
new NoClassAnnotationCollisions(),
new SameFeatureSplit(appView),
new SameInstanceFields(appView, mode),
new SameMainDexGroup(appView),
new SameNestHost(appView),
new SameParentClass(),
new SyntheticItemsPolicy(appView, mode),
new RespectPackageBoundaries(appView),
new PreventClassMethodAndDefaultMethodCollisions(appView));
}
private static void addMultiClassPoliciesForMergingNonSyntheticClasses(
AppView<AppInfoWithLiveness> appView,
RuntimeTypeCheckInfo runtimeTypeCheckInfo,
ImmutableList.Builder<Policy> builder) {
builder.add(
new NoDeadLocks(appView), new NoIndirectRuntimeTypeChecks(appView, runtimeTypeCheckInfo));
}
private static void addMultiClassPoliciesForInterfaceMerging(
AppView<? extends AppInfoWithClassHierarchy> appView,
Mode mode,
ImmutableList.Builder<Policy> builder) {
builder.add(
new NoDefaultInterfaceMethodMerging(appView, mode),
new NoDefaultInterfaceMethodCollisions(appView, mode),
new OnlyDirectlyConnectedOrUnrelatedInterfaces(appView, mode));
}
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
// does not lead to any cycles in the interface hierarchy, which may be invalidated if merge
// groups are split after the policy has run.
int onlyDirectlyConnectedOrUnrelatedInterfacesIndex =
ListUtils.lastIndexMatching(
policies, policy -> policy instanceof OnlyDirectlyConnectedOrUnrelatedInterfaces);
if (onlyDirectlyConnectedOrUnrelatedInterfacesIndex >= 0) {
for (Policy successorPolicy :
policies.subList(onlyDirectlyConnectedOrUnrelatedInterfacesIndex + 1, policies.size())) {
assert successorPolicy.isIdentityForInterfaceGroups();
}
}
return true;
}
}