Refactor feature split optimization logic to single class
Change-Id: I8a77721e53c4472c26a51ff8ac62a5fff8b362a4
diff --git a/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java b/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
index 68af78a..fd710bb 100644
--- a/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
+++ b/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
@@ -18,7 +18,6 @@
import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.PrunedItems;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.synthesis.SyntheticItems;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Reporter;
@@ -327,11 +326,13 @@
// Static helpers to avoid verbose predicates.
- private static ClassToFeatureSplitMap getMap(AppView<AppInfoWithLiveness> appView) {
+ private static ClassToFeatureSplitMap getMap(
+ AppView<? extends AppInfoWithClassHierarchy> appView) {
return appView.appInfo().getClassToFeatureSplitMap();
}
- public static boolean isInFeature(DexProgramClass clazz, AppView<AppInfoWithLiveness> appView) {
+ public static boolean isInFeature(
+ DexProgramClass clazz, AppView<? extends AppInfoWithClassHierarchy> appView) {
return getMap(appView)
.isInFeature(
clazz,
diff --git a/src/main/java/com/android/tools/r8/features/FeatureSplitBoundaryOptimizationUtils.java b/src/main/java/com/android/tools/r8/features/FeatureSplitBoundaryOptimizationUtils.java
new file mode 100644
index 0000000..451ff92
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/features/FeatureSplitBoundaryOptimizationUtils.java
@@ -0,0 +1,75 @@
+// 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.features;
+
+import com.android.tools.r8.FeatureSplit;
+import com.android.tools.r8.experimental.startup.StartupOrder;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMember;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ProgramDefinition;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
+import com.android.tools.r8.synthesis.SyntheticItems;
+import com.android.tools.r8.utils.InternalOptions;
+
+public class FeatureSplitBoundaryOptimizationUtils {
+
+ public static ConstraintWithTarget getInliningConstraintForResolvedMember(
+ ProgramMethod method,
+ DexEncodedMember<?, ?> resolvedMember,
+ AppView<? extends AppInfoWithClassHierarchy> appView) {
+ ClassToFeatureSplitMap classToFeatureSplitMap = appView.appInfo().getClassToFeatureSplitMap();
+ // We never inline into the base from a feature (calls should never happen) and we never inline
+ // between features, so this check should be sufficient.
+ if (classToFeatureSplitMap.isInBaseOrSameFeatureAs(
+ resolvedMember.getHolderType(), method, appView)) {
+ return ConstraintWithTarget.ALWAYS;
+ }
+ return ConstraintWithTarget.NEVER;
+ }
+
+ public static FeatureSplit getMergeKeyForHorizontalClassMerging(
+ DexProgramClass clazz, AppView<? extends AppInfoWithClassHierarchy> appView) {
+ ClassToFeatureSplitMap classToFeatureSplitMap = appView.appInfo().getClassToFeatureSplitMap();
+ return classToFeatureSplitMap.getFeatureSplit(clazz, appView);
+ }
+
+ public static boolean isSafeForAccess(
+ DexProgramClass accessedClass,
+ ProgramDefinition accessor,
+ ClassToFeatureSplitMap classToFeatureSplitMap,
+ InternalOptions options,
+ StartupOrder startupOrder,
+ SyntheticItems syntheticItems) {
+ return classToFeatureSplitMap.isInBaseOrSameFeatureAs(
+ accessedClass, accessor, options, startupOrder, syntheticItems);
+ }
+
+ public static boolean isSafeForInlining(
+ ProgramMethod caller,
+ ProgramMethod callee,
+ AppView<? extends AppInfoWithClassHierarchy> appView) {
+ ClassToFeatureSplitMap classToFeatureSplitMap = appView.appInfo().getClassToFeatureSplitMap();
+ if (classToFeatureSplitMap.isInSameFeatureOrBothInSameBase(callee, caller, appView)) {
+ return true;
+ }
+ // Still allow inlining if we inline from the base into a feature.
+ if (classToFeatureSplitMap.isInBase(callee.getHolder(), appView)) {
+ return true;
+ }
+ return false;
+ }
+
+ public static boolean isSafeForVerticalClassMerging(
+ DexProgramClass sourceClass,
+ DexProgramClass targetClass,
+ AppView<? extends AppInfoWithClassHierarchy> appView) {
+ ClassToFeatureSplitMap classToFeatureSplitMap = appView.appInfo().getClassToFeatureSplitMap();
+ return classToFeatureSplitMap.isInSameFeatureOrBothInSameBase(
+ sourceClass, targetClass, appView);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/AccessControl.java b/src/main/java/com/android/tools/r8/graph/AccessControl.java
index 43bbfa2..9211db6 100644
--- a/src/main/java/com/android/tools/r8/graph/AccessControl.java
+++ b/src/main/java/com/android/tools/r8/graph/AccessControl.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.experimental.startup.StartupOrder;
import com.android.tools.r8.features.ClassToFeatureSplitMap;
+import com.android.tools.r8.features.FeatureSplitBoundaryOptimizationUtils;
import com.android.tools.r8.synthesis.SyntheticItems;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.OptionalBool;
@@ -42,9 +43,10 @@
}
if (clazz.isProgramClass()
&& context.isProgramDefinition()
- && !classToFeatureSplitMap.isInBaseOrSameFeatureAs(
+ && !FeatureSplitBoundaryOptimizationUtils.isSafeForAccess(
clazz.asProgramClass(),
context.asProgramDefinition(),
+ classToFeatureSplitMap,
options,
startupOrder,
syntheticItems)) {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameFeatureSplit.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameFeatureSplit.java
index 73dfa2a..5ad8747 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameFeatureSplit.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameFeatureSplit.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.horizontalclassmerging.policies;
import com.android.tools.r8.FeatureSplit;
+import com.android.tools.r8.features.FeatureSplitBoundaryOptimizationUtils;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
@@ -19,7 +20,8 @@
@Override
public FeatureSplit getMergeKey(DexProgramClass clazz) {
- return appView.appInfo().getClassToFeatureSplitMap().getFeatureSplit(clazz, appView);
+ return FeatureSplitBoundaryOptimizationUtils.getMergeKeyForHorizontalClassMerging(
+ clazz, appView);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
index d3d0ece..ac4c0ef 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -11,7 +11,7 @@
import com.android.tools.r8.dex.code.DexMoveResultObject;
import com.android.tools.r8.dex.code.DexMoveResultWide;
import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.features.ClassToFeatureSplitMap;
+import com.android.tools.r8.features.FeatureSplitBoundaryOptimizationUtils;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexEncodedField;
@@ -163,13 +163,9 @@
return false;
}
- ClassToFeatureSplitMap classToFeatureSplitMap = appView.appInfo().getClassToFeatureSplitMap();
- if (!classToFeatureSplitMap.isInSameFeatureOrBothInSameBase(singleTarget, method, appView)) {
- // Still allow inlining if we inline from the base into a feature.
- if (!classToFeatureSplitMap.isInBase(singleTarget.getHolder(), appView)) {
- whyAreYouNotInliningReporter.reportInliningAcrossFeatureSplit();
- return false;
- }
+ if (!FeatureSplitBoundaryOptimizationUtils.isSafeForInlining(method, singleTarget, appView)) {
+ whyAreYouNotInliningReporter.reportInliningAcrossFeatureSplit();
+ return false;
}
Set<Reason> validInliningReasons = appView.testing().validInliningReasons;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
index 96b79b1..6dc28aa 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.optimize;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.features.FeatureSplitBoundaryOptimizationUtils;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
@@ -382,14 +383,13 @@
// This will fail at runtime.
return ConstraintWithTarget.NEVER;
}
- if (!appView
- .appInfo()
- .getClassToFeatureSplitMap()
- .isInBaseOrSameFeatureAs(
- resolvedMember.getHolderType(), context.asProgramMethod(), appView)) {
- // We never inline into the base from a feature (calls should never happen) and we
- // never inline between features, so this check should be sufficient.
- return ConstraintWithTarget.NEVER;
+ ConstraintWithTarget featureSplitInliningConstraint =
+ FeatureSplitBoundaryOptimizationUtils.getInliningConstraintForResolvedMember(
+ context, resolvedMember, appView);
+ assert featureSplitInliningConstraint == ConstraintWithTarget.ALWAYS
+ || featureSplitInliningConstraint == ConstraintWithTarget.NEVER;
+ if (featureSplitInliningConstraint == ConstraintWithTarget.NEVER) {
+ return featureSplitInliningConstraint;
}
DexType resolvedHolder = graphLens.lookupType(resolvedMember.getHolderType());
assert initialResolutionHolder != null;
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 37641f0..8760fb8 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.features.FeatureSplitBoundaryOptimizationUtils;
import com.android.tools.r8.graph.AccessControl;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
@@ -387,9 +388,8 @@
.map(DexEncodedMember::getReference)
.noneMatch(appInfo::isPinned);
- if (!appInfo
- .getClassToFeatureSplitMap()
- .isInSameFeatureOrBothInSameBase(sourceClass, targetClass, appView)) {
+ if (!FeatureSplitBoundaryOptimizationUtils.isSafeForVerticalClassMerging(
+ sourceClass, targetClass, appView)) {
return false;
}
if (appView.appServices().allServiceTypes().contains(sourceClass.type)