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;
+    }
   }
 
   /**