Reduce the number of explicit clinit checks for static invokes.

Do not generate explicit clinit checks for static invokes
when the class of the called method is a super class of the
caller method's class (referrer class).

Change-Id: I86ba18facef261fbb71f7ed20867756630ed3c53
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index bb20807..2f8a605 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -666,9 +666,10 @@
                                                                    &storage_index);
       }
 
-      if (is_referrer_class) {
-        // If the declaring class is the referrer class, no class
-        // initialization is needed before the static method call.
+      if (referrer_class.Get()->IsSubClass(resolved_method->GetDeclaringClass())) {
+        // If the referrer class is the declaring class or a subclass
+        // of the declaring class, no class initialization is needed
+        // before the static method call.
         clinit_check_requirement = HInvokeStaticOrDirect::ClinitCheckRequirement::kNone;
       } else if (storage_index != DexFile::kDexNoIndex) {
         // If the method's class type index is available, check
diff --git a/test/478-checker-clinit-check-pruning/expected.txt b/test/478-checker-clinit-check-pruning/expected.txt
index 39d9d75..387e1a7 100644
--- a/test/478-checker-clinit-check-pruning/expected.txt
+++ b/test/478-checker-clinit-check-pruning/expected.txt
@@ -2,3 +2,5 @@
 Main$ClassWithClinit2's static initializer
 Main$ClassWithClinit3's static initializer
 Main$ClassWithClinit4's static initializer
+Main$ClassWithClinit5's static initializer
+Main$ClassWithClinit6's static initializer
diff --git a/test/478-checker-clinit-check-pruning/src/Main.java b/test/478-checker-clinit-check-pruning/src/Main.java
index 5370f86..6da8945 100644
--- a/test/478-checker-clinit-check-pruning/src/Main.java
+++ b/test/478-checker-clinit-check-pruning/src/Main.java
@@ -186,6 +186,80 @@
     }
   }
 
+  /*
+   * Ensure an inlined call to a static method whose declaring class
+   * is a super class of the caller's class does not require an
+   * explicit clinit check.
+   */
+
+  // CHECK-START: void Main$SubClassOfClassWithClinit5.invokeStaticInlined() builder (after)
+  // CHECK-DAG:                           InvokeStaticOrDirect
+
+  // CHECK-START: void Main$SubClassOfClassWithClinit5.invokeStaticInlined() builder (after)
+  // CHECK-NOT:                           LoadClass
+  // CHECK-NOT:                           ClinitCheck
+
+  // CHECK-START: void Main$SubClassOfClassWithClinit5.invokeStaticInlined() inliner (after)
+  // CHECK-NOT:                           LoadClass
+  // CHECK-NOT:                           ClinitCheck
+  // CHECK-NOT:                           InvokeStaticOrDirect
+
+  static class ClassWithClinit5 {
+    static void $opt$inline$StaticMethod() {
+    }
+
+    static {
+      System.out.println("Main$ClassWithClinit5's static initializer");
+    }
+  }
+
+  static class SubClassOfClassWithClinit5 extends ClassWithClinit5 {
+    static void invokeStaticInlined() {
+      ClassWithClinit5.$opt$inline$StaticMethod();
+    }
+  }
+
+  /*
+   * Ensure an non-inlined call to a static method whose declaring
+   * class is a super class of the caller's class does not require an
+   * explicit clinit check.
+   */
+
+  // CHECK-START: void Main$SubClassOfClassWithClinit6.invokeStaticNotInlined() builder (after)
+  // CHECK-DAG:                           InvokeStaticOrDirect
+
+  // CHECK-START: void Main$SubClassOfClassWithClinit6.invokeStaticNotInlined() builder (after)
+  // CHECK-NOT:                           LoadClass
+  // CHECK-NOT:                           ClinitCheck
+
+  // CHECK-START: void Main$SubClassOfClassWithClinit6.invokeStaticNotInlined() inliner (after)
+  // CHECK-DAG:                           InvokeStaticOrDirect
+
+  // CHECK-START: void Main$SubClassOfClassWithClinit6.invokeStaticNotInlined() inliner (after)
+  // CHECK-NOT:                           LoadClass
+  // CHECK-NOT:                           ClinitCheck
+
+  static class ClassWithClinit6 {
+    static boolean doThrow = false;
+
+    static void staticMethod() {
+      if (doThrow) {
+        // Try defeating inlining.
+        throw new Error();
+      }
+    }
+
+    static {
+      System.out.println("Main$ClassWithClinit6's static initializer");
+    }
+  }
+
+  static class SubClassOfClassWithClinit6 extends ClassWithClinit6 {
+    static void invokeStaticNotInlined() {
+      ClassWithClinit6.staticMethod();
+    }
+  }
+
   // TODO: Add a test for the case of a static method whose declaring
   // class type index is not available (i.e. when `storage_index`
   // equals `DexFile::kDexNoIndex` in
@@ -196,5 +270,7 @@
     invokeStaticNotInlined();
     ClassWithClinit3.invokeStaticInlined();
     ClassWithClinit4.invokeStaticNotInlined();
+    SubClassOfClassWithClinit5.invokeStaticInlined();
+    SubClassOfClassWithClinit6.invokeStaticNotInlined();
   }
 }