1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 public class Main {
18 
19   static boolean doThrow = false;
20 
21   /*
22    * Ensure an inlined static invoke explicitly triggers the
23    * initialization check of the called method's declaring class, and
24    * that the corresponding load class instruction does not get
25    * removed before register allocation & code generation.
26    */
27 
28   /// CHECK-START: void Main.invokeStaticInlined() builder (after)
29   /// CHECK-DAG:     <<LoadClass:l\d+>>    LoadClass gen_clinit_check:false
30   /// CHECK-DAG:     <<ClinitCheck:l\d+>>  ClinitCheck [<<LoadClass>>]
31   /// CHECK-DAG:                           InvokeStaticOrDirect [{{[ij]\d+}},<<ClinitCheck>>]
32 
33   /// CHECK-START: void Main.invokeStaticInlined() inliner (after)
34   /// CHECK-DAG:     <<LoadClass:l\d+>>    LoadClass gen_clinit_check:false
35   /// CHECK-DAG:     <<ClinitCheck:l\d+>>  ClinitCheck [<<LoadClass>>]
36 
37   /// CHECK-START: void Main.invokeStaticInlined() inliner (after)
38   /// CHECK-NOT:                           InvokeStaticOrDirect
39 
40   // The following checks ensure the clinit check instruction added by
41   // the builder is pruned by the PrepareForRegisterAllocation, while
42   // the load class instruction is preserved.  As the control flow
43   // graph is not dumped after (nor before) this step, we check the
44   // CFG as it is before the next pass (liveness analysis) instead.
45 
46   /// CHECK-START: void Main.invokeStaticInlined() liveness (before)
47   /// CHECK-DAG:                           LoadClass gen_clinit_check:true
48 
49   /// CHECK-START: void Main.invokeStaticInlined() liveness (before)
50   /// CHECK-NOT:                           ClinitCheck
51   /// CHECK-NOT:                           InvokeStaticOrDirect
52 
invokeStaticInlined()53   static void invokeStaticInlined() {
54     ClassWithClinit1.$opt$inline$StaticMethod();
55   }
56 
57   static class ClassWithClinit1 {
58     static {
59       System.out.println("Main$ClassWithClinit1's static initializer");
60     }
61 
$opt$inline$StaticMethod()62     static void $opt$inline$StaticMethod() {
63     }
64   }
65 
66   /*
67    * Ensure a non-inlined static invoke eventually has an implicit
68    * initialization check of the called method's declaring class.
69    */
70 
71   /// CHECK-START: void Main.invokeStaticNotInlined() builder (after)
72   /// CHECK:         <<LoadClass:l\d+>>    LoadClass gen_clinit_check:false
73   /// CHECK:         <<ClinitCheck:l\d+>>  ClinitCheck [<<LoadClass>>]
74   /// CHECK:                               InvokeStaticOrDirect [{{[ij]\d+}},<<ClinitCheck>>]
75 
76   /// CHECK-START: void Main.invokeStaticNotInlined() inliner (after)
77   /// CHECK:         <<LoadClass:l\d+>>    LoadClass gen_clinit_check:false
78   /// CHECK:         <<ClinitCheck:l\d+>>  ClinitCheck [<<LoadClass>>]
79   /// CHECK:                               InvokeStaticOrDirect [{{([ij]\d+,)?}}<<ClinitCheck>>]
80 
81   // The following checks ensure the clinit check and load class
82   // instructions added by the builder are pruned by the
83   // PrepareForRegisterAllocation.  As the control flow graph is not
84   // dumped after (nor before) this step, we check the CFG as it is
85   // before the next pass (liveness analysis) instead.
86 
87   /// CHECK-START: void Main.invokeStaticNotInlined() liveness (before)
88   /// CHECK:                               InvokeStaticOrDirect clinit_check:implicit
89 
90   /// CHECK-START: void Main.invokeStaticNotInlined() liveness (before)
91   /// CHECK-NOT:                           LoadClass
92   /// CHECK-NOT:                           ClinitCheck
93 
invokeStaticNotInlined()94   static void invokeStaticNotInlined() {
95     ClassWithClinit2.$noinline$staticMethod();
96   }
97 
98   static class ClassWithClinit2 {
99     static {
100       System.out.println("Main$ClassWithClinit2's static initializer");
101     }
102 
103     static boolean doThrow = false;
104 
$noinline$staticMethod()105     static void $noinline$staticMethod() {
106       // Try defeating inlining.
107       if (doThrow) { throw new Error(); }
108     }
109   }
110 
111   /*
112    * Ensure an inlined call to a static method whose declaring class
113    * is statically known to have been initialized does not require an
114    * explicit clinit check.
115    */
116 
117   /// CHECK-START: void Main$ClassWithClinit3.invokeStaticInlined() builder (after)
118   /// CHECK-DAG:                           InvokeStaticOrDirect
119 
120   /// CHECK-START: void Main$ClassWithClinit3.invokeStaticInlined() builder (after)
121   /// CHECK-NOT:                           LoadClass
122   /// CHECK-NOT:                           ClinitCheck
123 
124   /// CHECK-START: void Main$ClassWithClinit3.invokeStaticInlined() inliner (after)
125   /// CHECK-NOT:                           LoadClass
126   /// CHECK-NOT:                           ClinitCheck
127   /// CHECK-NOT:                           InvokeStaticOrDirect
128 
129   static class ClassWithClinit3 {
invokeStaticInlined()130     static void invokeStaticInlined() {
131       // The invocation of invokeStaticInlined triggers the
132       // initialization of ClassWithClinit3, meaning that the
133       // hereinbelow call to $opt$inline$StaticMethod does not need a
134       // clinit check.
135       $opt$inline$StaticMethod();
136     }
137 
138     static {
139       System.out.println("Main$ClassWithClinit3's static initializer");
140     }
141 
$opt$inline$StaticMethod()142     static void $opt$inline$StaticMethod() {
143     }
144   }
145 
146   /*
147    * Ensure an non-inlined call to a static method whose declaring
148    * class is statically known to have been initialized does not
149    * require an explicit clinit check.
150    */
151 
152   /// CHECK-START: void Main$ClassWithClinit4.invokeStaticNotInlined() builder (after)
153   /// CHECK-DAG:                           InvokeStaticOrDirect
154 
155   /// CHECK-START: void Main$ClassWithClinit4.invokeStaticNotInlined() builder (after)
156   /// CHECK-NOT:                           LoadClass
157   /// CHECK-NOT:                           ClinitCheck
158 
159   /// CHECK-START: void Main$ClassWithClinit4.invokeStaticNotInlined() inliner (after)
160   /// CHECK-DAG:                           InvokeStaticOrDirect
161 
162   /// CHECK-START: void Main$ClassWithClinit4.invokeStaticNotInlined() inliner (after)
163   /// CHECK-NOT:                           LoadClass
164   /// CHECK-NOT:                           ClinitCheck
165 
166   static class ClassWithClinit4 {
invokeStaticNotInlined()167     static void invokeStaticNotInlined() {
168       // The invocation of invokeStaticNotInlined triggers the
169       // initialization of ClassWithClinit4, meaning that the
170       // call to staticMethod below does not need a clinit
171       // check.
172       $noinline$staticMethod();
173     }
174 
175     static {
176       System.out.println("Main$ClassWithClinit4's static initializer");
177     }
178 
179     static boolean doThrow = false;
180 
$noinline$staticMethod()181     static void $noinline$staticMethod() {
182         // Try defeating inlining.
183       if (doThrow) { throw new Error(); }
184     }
185   }
186 
187   /*
188    * Ensure an inlined call to a static method whose declaring class
189    * is a super class of the caller's class does not require an
190    * explicit clinit check.
191    */
192 
193   /// CHECK-START: void Main$SubClassOfClassWithClinit5.invokeStaticInlined() builder (after)
194   /// CHECK-DAG:                           InvokeStaticOrDirect
195 
196   /// CHECK-START: void Main$SubClassOfClassWithClinit5.invokeStaticInlined() builder (after)
197   /// CHECK-NOT:                           LoadClass
198   /// CHECK-NOT:                           ClinitCheck
199 
200   /// CHECK-START: void Main$SubClassOfClassWithClinit5.invokeStaticInlined() inliner (after)
201   /// CHECK-NOT:                           LoadClass
202   /// CHECK-NOT:                           ClinitCheck
203   /// CHECK-NOT:                           InvokeStaticOrDirect
204 
205   static class ClassWithClinit5 {
$opt$inline$StaticMethod()206     static void $opt$inline$StaticMethod() {
207     }
208 
209     static {
210       System.out.println("Main$ClassWithClinit5's static initializer");
211     }
212   }
213 
214   static class SubClassOfClassWithClinit5 extends ClassWithClinit5 {
invokeStaticInlined()215     static void invokeStaticInlined() {
216       ClassWithClinit5.$opt$inline$StaticMethod();
217     }
218   }
219 
220   /*
221    * Ensure an non-inlined call to a static method whose declaring
222    * class is a super class of the caller's class does not require an
223    * explicit clinit check.
224    */
225 
226   /// CHECK-START: void Main$SubClassOfClassWithClinit6.invokeStaticNotInlined() builder (after)
227   /// CHECK-DAG:                           InvokeStaticOrDirect
228 
229   /// CHECK-START: void Main$SubClassOfClassWithClinit6.invokeStaticNotInlined() builder (after)
230   /// CHECK-NOT:                           LoadClass
231   /// CHECK-NOT:                           ClinitCheck
232 
233   /// CHECK-START: void Main$SubClassOfClassWithClinit6.invokeStaticNotInlined() inliner (after)
234   /// CHECK-DAG:                           InvokeStaticOrDirect
235 
236   /// CHECK-START: void Main$SubClassOfClassWithClinit6.invokeStaticNotInlined() inliner (after)
237   /// CHECK-NOT:                           LoadClass
238   /// CHECK-NOT:                           ClinitCheck
239 
240   static class ClassWithClinit6 {
241     static boolean doThrow = false;
242 
$noinline$staticMethod()243     static void $noinline$staticMethod() {
244         // Try defeating inlining.
245       if (doThrow) { throw new Error(); }
246     }
247 
248     static {
249       System.out.println("Main$ClassWithClinit6's static initializer");
250     }
251   }
252 
253   static class SubClassOfClassWithClinit6 extends ClassWithClinit6 {
invokeStaticNotInlined()254     static void invokeStaticNotInlined() {
255       ClassWithClinit6.$noinline$staticMethod();
256     }
257   }
258 
259 
260   /*
261    * Verify that if we have a static call immediately after the load class
262    * we don't do generate a clinit check.
263    */
264 
265   /// CHECK-START: void Main.noClinitBecauseOfInvokeStatic() liveness (before)
266   /// CHECK-DAG:     <<IntConstant:i\d+>>  IntConstant 0
267   /// CHECK-DAG:     <<LoadClass:l\d+>>    LoadClass gen_clinit_check:false
268   /// CHECK-DAG:                           InvokeStaticOrDirect clinit_check:implicit
269   /// CHECK-DAG:                           StaticFieldSet [<<LoadClass>>,<<IntConstant>>]
270 
271   /// CHECK-START: void Main.noClinitBecauseOfInvokeStatic() liveness (before)
272   /// CHECK-NOT:                           ClinitCheck
273 
noClinitBecauseOfInvokeStatic()274   static void noClinitBecauseOfInvokeStatic() {
275     ClassWithClinit2.$noinline$staticMethod();
276     ClassWithClinit2.doThrow = false;
277   }
278 
279   /*
280    * Verify that if the static call is after a field access, the load class
281    * will generate a clinit check.
282    */
283 
284   /// CHECK-START: void Main.clinitBecauseOfFieldAccess() liveness (before)
285   /// CHECK-DAG:     <<IntConstant:i\d+>>  IntConstant 0
286   /// CHECK-DAG:     <<LoadClass:l\d+>>    LoadClass gen_clinit_check:true
287   /// CHECK-DAG:                           StaticFieldSet [<<LoadClass>>,<<IntConstant>>]
288   /// CHECK-DAG:                           InvokeStaticOrDirect clinit_check:none
289 
290   /// CHECK-START: void Main.clinitBecauseOfFieldAccess() liveness (before)
291   /// CHECK-NOT:                           ClinitCheck
clinitBecauseOfFieldAccess()292   static void clinitBecauseOfFieldAccess() {
293     ClassWithClinit2.doThrow = false;
294     ClassWithClinit2.$noinline$staticMethod();
295   }
296 
297   /*
298    * Verify that LoadClass from const-class is not merged with
299    * later invoke-static (or it's ClinitCheck).
300    */
301 
302   /// CHECK-START: void Main.constClassAndInvokeStatic(java.lang.Iterable) liveness (before)
303   /// CHECK:                               LoadClass gen_clinit_check:false
304   /// CHECK:                               InvokeStaticOrDirect clinit_check:implicit
305 
306   /// CHECK-START: void Main.constClassAndInvokeStatic(java.lang.Iterable) liveness (before)
307   /// CHECK-NOT:                           ClinitCheck
308 
constClassAndInvokeStatic(Iterable<?> it)309   static void constClassAndInvokeStatic(Iterable<?> it) {
310     $opt$inline$ignoreClass(ClassWithClinit7.class);
311     ClassWithClinit7.$noinline$someStaticMethod(it);
312   }
313 
$opt$inline$ignoreClass(Class<?> c)314   static void $opt$inline$ignoreClass(Class<?> c) {
315   }
316 
317   static class ClassWithClinit7 {
318     static {
319       System.out.println("Main$ClassWithClinit7's static initializer");
320     }
321 
$noinline$someStaticMethod(Iterable<?> it)322     static void $noinline$someStaticMethod(Iterable<?> it) {
323       it.iterator();
324       // We're not inlining throw at the moment.
325       if (doThrow) { throw new Error(""); }
326     }
327   }
328 
329   /*
330    * Verify that LoadClass from sget is not merged with later invoke-static.
331    */
332 
333   /// CHECK-START: void Main.sgetAndInvokeStatic(java.lang.Iterable) liveness (before)
334   /// CHECK:                               LoadClass gen_clinit_check:true
335   /// CHECK:                               InvokeStaticOrDirect clinit_check:none
336 
337   /// CHECK-START: void Main.sgetAndInvokeStatic(java.lang.Iterable) liveness (before)
338   /// CHECK-NOT:                           ClinitCheck
339 
sgetAndInvokeStatic(Iterable<?> it)340   static void sgetAndInvokeStatic(Iterable<?> it) {
341     $opt$inline$ignoreInt(ClassWithClinit8.value);
342     ClassWithClinit8.$noinline$someStaticMethod(it);
343   }
344 
$opt$inline$ignoreInt(int i)345   static void $opt$inline$ignoreInt(int i) {
346   }
347 
348   static class ClassWithClinit8 {
349     public static int value = 0;
350     static {
351       System.out.println("Main$ClassWithClinit8's static initializer");
352     }
353 
$noinline$someStaticMethod(Iterable<?> it)354     static void $noinline$someStaticMethod(Iterable<?> it) {
355       it.iterator();
356       // We're not inlining throw at the moment.
357       if (doThrow) { throw new Error(""); }
358     }
359   }
360 
361   /*
362    * Verify that LoadClass from const-class, ClinitCheck from sget and
363    * InvokeStaticOrDirect from invoke-static are not merged.
364    */
365 
366   /// CHECK-START: void Main.constClassSgetAndInvokeStatic(java.lang.Iterable) liveness (before)
367   /// CHECK:                               LoadClass gen_clinit_check:false
368   /// CHECK:                               ClinitCheck
369   /// CHECK:                               InvokeStaticOrDirect clinit_check:none
370 
constClassSgetAndInvokeStatic(Iterable<?> it)371   static void constClassSgetAndInvokeStatic(Iterable<?> it) {
372     $opt$inline$ignoreClass(ClassWithClinit9.class);
373     $opt$inline$ignoreInt(ClassWithClinit9.value);
374     ClassWithClinit9.$noinline$someStaticMethod(it);
375   }
376 
377   static class ClassWithClinit9 {
378     public static int value = 0;
379     static {
380       System.out.println("Main$ClassWithClinit9's static initializer");
381     }
382 
$noinline$someStaticMethod(Iterable<?> it)383     static void $noinline$someStaticMethod(Iterable<?> it) {
384       it.iterator();
385       // We're not inlining throw at the moment.
386       if (doThrow) { throw new Error(""); }
387     }
388   }
389 
390   /*
391    * Verify that LoadClass from a fully-inlined invoke-static is not merged
392    * with InvokeStaticOrDirect from a later invoke-static to the same method.
393    */
394 
395   /// CHECK-START: void Main.inlinedInvokeStaticViaNonStatic(java.lang.Iterable) liveness (before)
396   /// CHECK:                               LoadClass gen_clinit_check:true
397   /// CHECK:                               InvokeStaticOrDirect clinit_check:none
398 
399   /// CHECK-START: void Main.inlinedInvokeStaticViaNonStatic(java.lang.Iterable) liveness (before)
400   /// CHECK-NOT:                           ClinitCheck
401 
inlinedInvokeStaticViaNonStatic(Iterable<?> it)402   static void inlinedInvokeStaticViaNonStatic(Iterable<?> it) {
403     if (it != null) {
404       inlinedInvokeStaticViaNonStaticHelper(null);
405       inlinedInvokeStaticViaNonStaticHelper(it);
406     }
407   }
408 
inlinedInvokeStaticViaNonStaticHelper(Iterable<?> it)409   static void inlinedInvokeStaticViaNonStaticHelper(Iterable<?> it) {
410     ClassWithClinit10.inlinedForNull(it);
411   }
412 
413   static class ClassWithClinit10 {
414     public static int value = 0;
415     static {
416       System.out.println("Main$ClassWithClinit10's static initializer");
417     }
418 
inlinedForNull(Iterable<?> it)419     static void inlinedForNull(Iterable<?> it) {
420       if (it != null) {
421         it.iterator();
422         // We're not inlining methods that always throw.
423         throw new Error("");
424       }
425     }
426   }
427 
428   /*
429    * Check that the LoadClass from an invoke-static C.foo() doesn't get merged with
430    * an invoke-static inside C.foo(). This would mess up the stack walk in the
431    * resolution trampoline where we would have to load C (if C isn't loaded yet)
432    * which is not permitted there.
433    *
434    * Note: In case of failure, we would get an failed assertion during compilation,
435    * so we wouldn't really get to the checker tests below.
436    */
437 
438   /// CHECK-START: void Main.inlinedInvokeStaticViaStatic(java.lang.Iterable) liveness (before)
439   /// CHECK:                               LoadClass gen_clinit_check:true
440   /// CHECK:                               InvokeStaticOrDirect clinit_check:none
441 
442   /// CHECK-START: void Main.inlinedInvokeStaticViaStatic(java.lang.Iterable) liveness (before)
443   /// CHECK-NOT:                           ClinitCheck
444 
inlinedInvokeStaticViaStatic(Iterable<?> it)445   static void inlinedInvokeStaticViaStatic(Iterable<?> it) {
446     if (it != null) {
447       ClassWithClinit11.callInlinedForNull(it);
448     }
449   }
450 
451   static class ClassWithClinit11 {
452     public static int value = 0;
453     static {
454       System.out.println("Main$ClassWithClinit11's static initializer");
455     }
456 
callInlinedForNull(Iterable<?> it)457     static void callInlinedForNull(Iterable<?> it) {
458       inlinedForNull(it);
459     }
460 
inlinedForNull(Iterable<?> it)461     static void inlinedForNull(Iterable<?> it) {
462       it.iterator();
463       if (it != null) {
464         // We're not inlining methods that always throw.
465         throw new Error("");
466       }
467     }
468   }
469 
470   /*
471    * A test similar to inlinedInvokeStaticViaStatic() but doing the indirect invoke
472    * twice with the first one to be fully inlined.
473    */
474 
475   /// CHECK-START: void Main.inlinedInvokeStaticViaStaticTwice(java.lang.Iterable) liveness (before)
476   /// CHECK:                               LoadClass gen_clinit_check:true
477   /// CHECK:                               InvokeStaticOrDirect clinit_check:none
478 
479   /// CHECK-START: void Main.inlinedInvokeStaticViaStaticTwice(java.lang.Iterable) liveness (before)
480   /// CHECK-NOT:                           ClinitCheck
481 
inlinedInvokeStaticViaStaticTwice(Iterable<?> it)482   static void inlinedInvokeStaticViaStaticTwice(Iterable<?> it) {
483     if (it != null) {
484       ClassWithClinit12.callInlinedForNull(null);
485       ClassWithClinit12.callInlinedForNull(it);
486     }
487   }
488 
489   static class ClassWithClinit12 {
490     public static int value = 0;
491     static {
492       System.out.println("Main$ClassWithClinit12's static initializer");
493     }
494 
callInlinedForNull(Iterable<?> it)495     static void callInlinedForNull(Iterable<?> it) {
496       inlinedForNull(it);
497     }
498 
inlinedForNull(Iterable<?> it)499     static void inlinedForNull(Iterable<?> it) {
500       if (it != null) {
501         // We're not inlining methods that always throw.
502         throw new Error("");
503       }
504     }
505   }
506 
507   static class ClassWithClinit13 {
508     static {
509       System.out.println("Main$ClassWithClinit13's static initializer");
510     }
511 
$inline$forwardToGetIterator(Iterable<?> it)512     public static void $inline$forwardToGetIterator(Iterable<?> it) {
513       $noinline$getIterator(it);
514     }
515 
$noinline$getIterator(Iterable<?> it)516     public static void $noinline$getIterator(Iterable<?> it) {
517       it.iterator();
518       // We're not inlining throw at the moment.
519       if (doThrow) { throw new Error(""); }
520     }
521   }
522 
523   // TODO: Write checker statements.
$noinline$testInliningAndNewInstance(Iterable<?> it)524   static Object $noinline$testInliningAndNewInstance(Iterable<?> it) {
525     if (doThrow) { throw new Error(); }
526     ClassWithClinit13.$inline$forwardToGetIterator(it);
527     return new ClassWithClinit13();
528   }
529 
530   // TODO: Add a test for the case of a static method whose declaring
531   // class type index is not available (i.e. when `storage_index`
532   // equals `dex::kDexNoIndex` in
533   // art::HGraphBuilder::BuildInvoke).
534 
main(String[] args)535   public static void main(String[] args) {
536     invokeStaticInlined();
537     invokeStaticNotInlined();
538     ClassWithClinit3.invokeStaticInlined();
539     ClassWithClinit4.invokeStaticNotInlined();
540     SubClassOfClassWithClinit5.invokeStaticInlined();
541     SubClassOfClassWithClinit6.invokeStaticNotInlined();
542     Iterable it = new Iterable() { public java.util.Iterator iterator() { return null; } };
543     constClassAndInvokeStatic(it);
544     sgetAndInvokeStatic(it);
545     constClassSgetAndInvokeStatic(it);
546     try {
547       inlinedInvokeStaticViaNonStatic(it);
548     } catch (Error e) {
549       // Expected
550     }
551     try {
552       inlinedInvokeStaticViaStatic(it);
553     } catch (Error e) {
554       // Expected
555     }
556     try{
557       inlinedInvokeStaticViaStaticTwice(it);
558     } catch (Error e) {
559       // Expected
560     }
561     $noinline$testInliningAndNewInstance(it);
562   }
563 }
564