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