Remove unnecessary clinit checks

Bug: 20852802
Change-Id: Ia6db8017ac22d45456845704a69ddffcc6917f4e
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index be28755..424a393 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -265,6 +265,11 @@
     StartAttributeStream("kind") << barrier->GetBarrierKind();
   }
 
+  void VisitLoadClass(HLoadClass* load_cass) OVERRIDE {
+    StartAttributeStream("gen_clinit_check") << std::boolalpha
+        << load_cass->MustGenerateClinitCheck() << std::noboolalpha;
+  }
+
   bool IsPass(const char* name) {
     return strcmp(pass_name_, name) == 0;
   }
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 680fb47..b712e5e 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -3444,8 +3444,8 @@
     return generate_clinit_check_;
   }
 
-  void SetMustGenerateClinitCheck() {
-    generate_clinit_check_ = true;
+  void SetMustGenerateClinitCheck(bool generate_clinit_check) {
+    generate_clinit_check_ = generate_clinit_check;
   }
 
   bool CanCallRuntime() const {
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
index 78d1185..538736b 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -53,7 +53,7 @@
   if (check->GetPrevious() == cls) {
     // Pass the initialization duty to the `HLoadClass` instruction,
     // and remove the instruction from the graph.
-    cls->SetMustGenerateClinitCheck();
+    cls->SetMustGenerateClinitCheck(true);
     check->GetBlock()->RemoveInstruction(check);
   }
 }
@@ -82,8 +82,15 @@
 void PrepareForRegisterAllocation::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
   if (invoke->IsStaticWithExplicitClinitCheck()) {
     size_t last_input_index = invoke->InputCount() - 1;
-    HInstruction* last_input = invoke->InputAt(last_input_index);
-    DCHECK(last_input->IsLoadClass()) << last_input->DebugName();
+    HLoadClass* last_input = invoke->InputAt(last_input_index)->AsLoadClass();
+    DCHECK(last_input != nullptr)
+        << "Last input is not HLoadClass. It is " << last_input->DebugName();
+
+    // The static call will initialize the class so there's no need for a clinit check if
+    // it's the first user.
+    if (last_input == invoke->GetPrevious()) {
+      last_input->SetMustGenerateClinitCheck(false);
+    }
 
     // Remove a load class instruction as last input of a static
     // invoke, which has been added (along with a clinit check,
diff --git a/test/478-checker-clinit-check-pruning/src/Main.java b/test/478-checker-clinit-check-pruning/src/Main.java
index 61199a7..e8739b8 100644
--- a/test/478-checker-clinit-check-pruning/src/Main.java
+++ b/test/478-checker-clinit-check-pruning/src/Main.java
@@ -24,12 +24,12 @@
    */
 
   // CHECK-START: void Main.invokeStaticInlined() builder (after)
-  // CHECK-DAG:     <<LoadClass:l\d+>>    LoadClass
+  // CHECK-DAG:     <<LoadClass:l\d+>>    LoadClass gen_clinit_check:false
   // CHECK-DAG:     <<ClinitCheck:l\d+>>  ClinitCheck [<<LoadClass>>]
   // CHECK-DAG:                           InvokeStaticOrDirect [<<ClinitCheck>>]
 
   // CHECK-START: void Main.invokeStaticInlined() inliner (after)
-  // CHECK-DAG:     <<LoadClass:l\d+>>    LoadClass
+  // CHECK-DAG:     <<LoadClass:l\d+>>    LoadClass gen_clinit_check:false
   // CHECK-DAG:     <<ClinitCheck:l\d+>>  ClinitCheck [<<LoadClass>>]
 
   // CHECK-START: void Main.invokeStaticInlined() inliner (after)
@@ -42,7 +42,7 @@
   // CFG as it is before the next pass (liveness analysis) instead.
 
   // CHECK-START: void Main.invokeStaticInlined() liveness (before)
-  // CHECK-DAG:                           LoadClass
+  // CHECK-DAG:                           LoadClass gen_clinit_check:true
 
   // CHECK-START: void Main.invokeStaticInlined() liveness (before)
   // CHECK-NOT:                           ClinitCheck
@@ -67,12 +67,12 @@
    */
 
   // CHECK-START: void Main.invokeStaticNotInlined() builder (after)
-  // CHECK-DAG:     <<LoadClass:l\d+>>    LoadClass
+  // CHECK-DAG:     <<LoadClass:l\d+>>    LoadClass gen_clinit_check:false
   // CHECK-DAG:     <<ClinitCheck:l\d+>>  ClinitCheck [<<LoadClass>>]
   // CHECK-DAG:                           InvokeStaticOrDirect [<<ClinitCheck>>]
 
   // CHECK-START: void Main.invokeStaticNotInlined() inliner (after)
-  // CHECK-DAG:     <<LoadClass:l\d+>>    LoadClass
+  // CHECK-DAG:     <<LoadClass:l\d+>>    LoadClass gen_clinit_check:false
   // CHECK-DAG:     <<ClinitCheck:l\d+>>  ClinitCheck [<<LoadClass>>]
   // CHECK-DAG:                           InvokeStaticOrDirect [<<ClinitCheck>>]
 
@@ -260,6 +260,44 @@
     }
   }
 
+
+  /*
+   * Verify that if we have a static call immediately after the load class
+   * we don't do generate a clinit check.
+   */
+
+  // CHECK-START: void Main.noClinitBecauseOfInvokeStatic() liveness (before)
+  // CHECK-DAG:     <<IntConstant:i\d+>>  IntConstant 0
+  // CHECK-DAG:     <<LoadClass:l\d+>>    LoadClass gen_clinit_check:false
+  // CHECK-DAG:                           InvokeStaticOrDirect
+  // CHECK-DAG:                           StaticFieldSet [<<LoadClass>>,<<IntConstant>>]
+
+  // CHECK-START: void Main.noClinitBecauseOfInvokeStatic() liveness (before)
+  // CHECK-NOT:                           ClinitCheck
+
+  static void noClinitBecauseOfInvokeStatic() {
+    ClassWithClinit2.staticMethod();
+    ClassWithClinit2.doThrow = false;
+  }
+
+  /*
+   * Verify that if the static call is after a field access, the load class
+   * will generate a clinit check.
+   */
+
+  // CHECK-START: void Main.clinitBecauseOfFieldAccess() liveness (before)
+  // CHECK-DAG:     <<IntConstant:i\d+>>  IntConstant 0
+  // CHECK-DAG:     <<LoadClass:l\d+>>    LoadClass gen_clinit_check:true
+  // CHECK-DAG:                           StaticFieldSet [<<LoadClass>>,<<IntConstant>>]
+  // CHECK-DAG:                           InvokeStaticOrDirect
+
+  // CHECK-START: void Main.clinitBecauseOfFieldAccess() liveness (before)
+  // CHECK-NOT:                           ClinitCheck
+  static void clinitBecauseOfFieldAccess() {
+    ClassWithClinit2.doThrow = false;
+    ClassWithClinit2.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