Log eligibility decisions in ClassInliner.
Bug: 120920488
Change-Id: I1befdec6ce888a2b5a00f2d1720ec0561ad6c3cb
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
index 1e85790..b656678 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInliner.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.ir.optimize.Inliner;
import com.android.tools.r8.ir.optimize.InliningOracle;
import com.android.tools.r8.ir.optimize.string.StringOptimizer;
+import com.android.tools.r8.logging.Log;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.Streams;
import java.util.Iterator;
@@ -27,6 +28,23 @@
public final class ClassInliner {
+ enum EligibilityStatus {
+ // Used by InlineCandidateProcessor#isInstanceEligible
+ UNUSED_INSTANCE,
+ NON_CLASS_TYPE,
+ UNKNOWN_TYPE,
+
+ // Used by InlineCandidateProcessor#isClassAndUsageEligible
+ INELIGIBLE_CLASS,
+ HAS_CLINIT,
+ HAS_INSTANCE_FIELDS,
+ NON_FINAL_TYPE,
+ NOT_INITIALIZED_AT_INIT,
+ PINNED_FIELD,
+
+ ELIGIBLE
+ }
+
private final LambdaRewriter lambdaRewriter;
private final ConcurrentHashMap<DexClass, Boolean> knownClasses = new ConcurrentHashMap<>();
@@ -34,6 +52,14 @@
this.lambdaRewriter = lambdaRewriter;
}
+ private void logEligibilityStatus(
+ DexEncodedMethod context, Instruction root, EligibilityStatus status) {
+ if (Log.ENABLED && Log.isLoggingEnabledFor(ClassInliner.class)) {
+ Log.info(getClass(), "At %s,", context.toSourceString());
+ Log.info(getClass(), "ClassInlining eligibility of %s: %s,", root, status);
+ }
+ }
+
// Process method code and inline eligible class instantiations, in short:
//
// - collect all 'new-instance' and 'static-get' instructions (called roots below) in
@@ -150,8 +176,16 @@
root);
// Assess eligibility of instance and class.
- if (!processor.isInstanceEligible() ||
- !processor.isClassAndUsageEligible()) {
+ EligibilityStatus status = processor.isInstanceEligible();
+ if (status != EligibilityStatus.ELIGIBLE) {
+ logEligibilityStatus(code.method, root, status);
+ // This root will never be inlined.
+ rootsIterator.remove();
+ continue;
+ }
+ status = processor.isClassAndUsageEligible();
+ logEligibilityStatus(code.method, root, status);
+ if (status != EligibilityStatus.ELIGIBLE) {
// This root will never be inlined.
rootsIterator.remove();
continue;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index 2883353..a2e9358 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -35,6 +35,7 @@
import com.android.tools.r8.ir.optimize.Inliner.InliningInfo;
import com.android.tools.r8.ir.optimize.Inliner.Reason;
import com.android.tools.r8.ir.optimize.InliningOracle;
+import com.android.tools.r8.ir.optimize.classinliner.ClassInliner.EligibilityStatus;
import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.ParameterUsagesInfo.ParameterUsage;
import com.android.tools.r8.kotlin.KotlinInfo;
@@ -101,16 +102,16 @@
// Checks if the root instruction defines eligible value, i.e. the value
// exists and we have a definition of its class.
- boolean isInstanceEligible() {
+ EligibilityStatus isInstanceEligible() {
eligibleInstance = root.outValue();
if (eligibleInstance == null) {
- return false;
+ return EligibilityStatus.UNUSED_INSTANCE;
}
eligibleClass =
root.isNewInstance() ? root.asNewInstance().clazz : root.asStaticGet().getField().type;
if (!eligibleClass.isClassType()) {
- return false;
+ return EligibilityStatus.NON_CLASS_TYPE;
}
if (lambdaRewriter != null) {
// Check if the class is synthesized for a desugared lambda
@@ -120,7 +121,11 @@
if (eligibleClassDefinition == null) {
eligibleClassDefinition = appView.definitionFor(eligibleClass);
}
- return eligibleClassDefinition != null;
+ if (eligibleClassDefinition != null) {
+ return EligibilityStatus.ELIGIBLE;
+ } else {
+ return EligibilityStatus.UNKNOWN_TYPE;
+ }
}
// Checks if the class is eligible and is properly used. Regarding general class
@@ -134,9 +139,9 @@
// * class is final
// * class has class initializer marked as TrivialClassInitializer, and
// class initializer initializes the field we are reading here.
- boolean isClassAndUsageEligible() {
+ EligibilityStatus isClassAndUsageEligible() {
if (!isClassEligible.test(eligibleClassDefinition)) {
- return false;
+ return EligibilityStatus.INELIGIBLE_CLASS;
}
if (root.isNewInstance()) {
@@ -145,14 +150,18 @@
// TrivialInstanceInitializer. This will be checked in areInstanceUsersEligible(...).
// There must be no static initializer on the class itself.
- return !eligibleClassDefinition.hasClassInitializer();
+ if (eligibleClassDefinition.hasClassInitializer()) {
+ return EligibilityStatus.HAS_CLINIT;
+ } else {
+ return EligibilityStatus.ELIGIBLE;
+ }
}
assert root.isStaticGet();
// We know that desugared lambda classes satisfy eligibility requirements.
if (isDesugaredLambda) {
- return true;
+ return EligibilityStatus.ELIGIBLE;
}
// Checking if we can safely inline class implemented following singleton-like
@@ -206,17 +215,17 @@
//
if (eligibleClassDefinition.instanceFields().size() > 0) {
- return false;
+ return EligibilityStatus.HAS_INSTANCE_FIELDS;
}
if (appView.appInfo().hasSubtypes(eligibleClassDefinition.type)) {
assert !eligibleClassDefinition.accessFlags.isFinal();
- return false;
+ return EligibilityStatus.NON_FINAL_TYPE;
}
// Singleton instance must be initialized in class constructor.
DexEncodedMethod classInitializer = eligibleClassDefinition.getClassInitializer();
if (classInitializer == null || isProcessedConcurrently.test(classInitializer)) {
- return false;
+ return EligibilityStatus.NOT_INITIALIZED_AT_INIT;
}
TrivialInitializer info =
@@ -224,11 +233,16 @@
assert info == null || info instanceof TrivialClassInitializer;
DexField instanceField = root.asStaticGet().getField();
// Singleton instance field must NOT be pinned.
- return info != null
+ boolean notPinned = info != null
&& ((TrivialClassInitializer) info).field == instanceField
&& !appView
.appInfo()
.isPinned(eligibleClassDefinition.lookupStaticField(instanceField).field);
+ if (notPinned) {
+ return EligibilityStatus.ELIGIBLE;
+ } else {
+ return EligibilityStatus.PINNED_FIELD;
+ }
}
/**