Ensure inlined static calls perform clinit checks in Optimizing.

Calls to static methods have implicit class initialization
(clinit) checks of the method's declaring class in
Optimizing.  However, when such a static call is inlined,
the implicit clinit check vanishes, possibly leading to an
incorrect behavior.

To ensure that inlining static methods does not change the
behavior of a program, add explicit class initialization
checks (art::HClinitCheck) as well as load class
instructions (art::HLoadClass) as last input of static
calls (art::HInvokeStaticOrDirect) in Optimizing' control
flow graphs, when the declaring class is reachable and not
known to be already initialized.  Then when considering the
inlining of a static method call, proceed only if the method
has no implicit clinit check requirement.

The added explicit clinit checks are already removed by the
art::PrepareForRegisterAllocation visitor.  This CL also
extends this visitor to turn explicit clinit checks from
static invokes into implicit ones after the inlining step,
by removing the added art::HLoadClass nodes mentioned
hereinbefore.

Change-Id: I9ba452b8bd09ae1fdd9a3797ef556e3e7e19c651
diff --git a/test/478-checker-clinit-check-pruning/src/Main.java b/test/478-checker-clinit-check-pruning/src/Main.java
new file mode 100644
index 0000000..5370f86
--- /dev/null
+++ b/test/478-checker-clinit-check-pruning/src/Main.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+
+  /*
+   * Ensure an inlined static invoke explicitly triggers the
+   * initialization check of the called method's declaring class, and
+   * that the corresponding load class instruction does not get
+   * removed before register allocation & code generation.
+   */
+
+  // CHECK-START: void Main.invokeStaticInlined() builder (after)
+  // CHECK-DAG:     [[LoadClass:l\d+]]    LoadClass
+  // 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:     [[ClinitCheck:l\d+]]  ClinitCheck [ [[LoadClass]] ]
+
+  // CHECK-START: void Main.invokeStaticInlined() inliner (after)
+  // CHECK-NOT:                           InvokeStaticOrDirect
+
+  // The following checks ensure the clinit check instruction added by
+  // the builder is pruned by the PrepareForRegisterAllocation, while
+  // the load class instruction is preserved.  As the control flow
+  // graph is not dumped after (nor before) this step, we check the
+  // CFG as it is before the next pass (liveness analysis) instead.
+
+  // CHECK-START: void Main.invokeStaticInlined() liveness (before)
+  // CHECK-DAG:                           LoadClass
+
+  // CHECK-START: void Main.invokeStaticInlined() liveness (before)
+  // CHECK-NOT:                           ClinitCheck
+  // CHECK-NOT:                           InvokeStaticOrDirect
+
+  static void invokeStaticInlined() {
+    ClassWithClinit1.$opt$inline$StaticMethod();
+  }
+
+  static class ClassWithClinit1 {
+    static {
+      System.out.println("Main$ClassWithClinit1's static initializer");
+    }
+
+    static void $opt$inline$StaticMethod() {
+    }
+  }
+
+  /*
+   * Ensure a non-inlined static invoke eventually has an implicit
+   * initialization check of the called method's declaring class.
+   */
+
+  // CHECK-START: void Main.invokeStaticNotInlined() builder (after)
+  // CHECK-DAG:     [[LoadClass:l\d+]]    LoadClass
+  // 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:     [[ClinitCheck:l\d+]]  ClinitCheck [ [[LoadClass]] ]
+  // CHECK-DAG:                           InvokeStaticOrDirect [ [[ClinitCheck]] ]
+
+  // The following checks ensure the clinit check and load class
+  // instructions added by the builder are pruned by the
+  // PrepareForRegisterAllocation.  As the control flow graph is not
+  // dumped after (nor before) this step, we check the CFG as it is
+  // before the next pass (liveness analysis) instead.
+
+  // CHECK-START: void Main.invokeStaticNotInlined() liveness (before)
+  // CHECK-DAG:                           InvokeStaticOrDirect
+
+  // CHECK-START: void Main.invokeStaticNotInlined() liveness (before)
+  // CHECK-NOT:                           LoadClass
+  // CHECK-NOT:                           ClinitCheck
+
+  static void invokeStaticNotInlined() {
+    ClassWithClinit2.staticMethod();
+  }
+
+  static class ClassWithClinit2 {
+    static {
+      System.out.println("Main$ClassWithClinit2's static initializer");
+    }
+
+    static boolean doThrow = false;
+
+    static void staticMethod() {
+      if (doThrow) {
+        // Try defeating inlining.
+        throw new Error();
+      }
+    }
+  }
+
+  /*
+   * Ensure an inlined call to a static method whose declaring class
+   * is statically known to have been initialized does not require an
+   * explicit clinit check.
+   */
+
+  // CHECK-START: void Main$ClassWithClinit3.invokeStaticInlined() builder (after)
+  // CHECK-DAG:                           InvokeStaticOrDirect
+
+  // CHECK-START: void Main$ClassWithClinit3.invokeStaticInlined() builder (after)
+  // CHECK-NOT:                           LoadClass
+  // CHECK-NOT:                           ClinitCheck
+
+  // CHECK-START: void Main$ClassWithClinit3.invokeStaticInlined() inliner (after)
+  // CHECK-NOT:                           LoadClass
+  // CHECK-NOT:                           ClinitCheck
+  // CHECK-NOT:                           InvokeStaticOrDirect
+
+  static class ClassWithClinit3 {
+    static void invokeStaticInlined() {
+      // The invocation of invokeStaticInlined triggers the
+      // initialization of ClassWithClinit3, meaning that the
+      // hereinbelow call to $opt$inline$StaticMethod does not need a
+      // clinit check.
+      $opt$inline$StaticMethod();
+    }
+
+    static {
+      System.out.println("Main$ClassWithClinit3's static initializer");
+    }
+
+    static void $opt$inline$StaticMethod() {
+    }
+  }
+
+  /*
+   * Ensure an non-inlined call to a static method whose declaring
+   * class is statically known to have been initialized does not
+   * require an explicit clinit check.
+   */
+
+  // CHECK-START: void Main$ClassWithClinit4.invokeStaticNotInlined() builder (after)
+  // CHECK-DAG:                           InvokeStaticOrDirect
+
+  // CHECK-START: void Main$ClassWithClinit4.invokeStaticNotInlined() builder (after)
+  // CHECK-NOT:                           LoadClass
+  // CHECK-NOT:                           ClinitCheck
+
+  // CHECK-START: void Main$ClassWithClinit4.invokeStaticNotInlined() inliner (after)
+  // CHECK-DAG:                           InvokeStaticOrDirect
+
+  // CHECK-START: void Main$ClassWithClinit4.invokeStaticNotInlined() inliner (after)
+  // CHECK-NOT:                           LoadClass
+  // CHECK-NOT:                           ClinitCheck
+
+  static class ClassWithClinit4 {
+    static void invokeStaticNotInlined() {
+      // The invocation of invokeStaticNotInlined triggers the
+      // initialization of ClassWithClinit4, meaning that the
+      // hereinbelow call to staticMethod does not need a clinit
+      // check.
+      staticMethod();
+    }
+
+    static {
+      System.out.println("Main$ClassWithClinit4's static initializer");
+    }
+
+    static boolean doThrow = false;
+
+    static void staticMethod() {
+      if (doThrow) {
+        // Try defeating inlining.
+        throw new Error();
+      }
+    }
+  }
+
+  // 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
+  // art::HGraphBuilder::BuildInvoke).
+
+  public static void main(String[] args) {
+    invokeStaticInlined();
+    invokeStaticNotInlined();
+    ClassWithClinit3.invokeStaticInlined();
+    ClassWithClinit4.invokeStaticNotInlined();
+  }
+}