/* * 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(); } } } /* * 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 // art::HGraphBuilder::BuildInvoke). public static void main(String[] args) { invokeStaticInlined(); invokeStaticNotInlined(); ClassWithClinit3.invokeStaticInlined(); ClassWithClinit4.invokeStaticNotInlined(); SubClassOfClassWithClinit5.invokeStaticInlined(); SubClassOfClassWithClinit6.invokeStaticNotInlined(); } }