• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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       if (doThrow) {
107         // Try defeating inlining.
108         throw new Error();
109       }
110     }
111   }
112 
113   /*
114    * Ensure an inlined call to a static method whose declaring class
115    * is statically known to have been initialized does not require an
116    * explicit clinit check.
117    */
118 
119   /// CHECK-START: void Main$ClassWithClinit3.invokeStaticInlined() builder (after)
120   /// CHECK-DAG:                           InvokeStaticOrDirect
121 
122   /// CHECK-START: void Main$ClassWithClinit3.invokeStaticInlined() builder (after)
123   /// CHECK-NOT:                           LoadClass
124   /// CHECK-NOT:                           ClinitCheck
125 
126   /// CHECK-START: void Main$ClassWithClinit3.invokeStaticInlined() inliner (after)
127   /// CHECK-NOT:                           LoadClass
128   /// CHECK-NOT:                           ClinitCheck
129   /// CHECK-NOT:                           InvokeStaticOrDirect
130 
131   static class ClassWithClinit3 {
invokeStaticInlined()132     static void invokeStaticInlined() {
133       // The invocation of invokeStaticInlined triggers the
134       // initialization of ClassWithClinit3, meaning that the
135       // hereinbelow call to $opt$inline$StaticMethod does not need a
136       // clinit check.
137       $opt$inline$StaticMethod();
138     }
139 
140     static {
141       System.out.println("Main$ClassWithClinit3's static initializer");
142     }
143 
$opt$inline$StaticMethod()144     static void $opt$inline$StaticMethod() {
145     }
146   }
147 
148   /*
149    * Ensure an non-inlined call to a static method whose declaring
150    * class is statically known to have been initialized does not
151    * require an explicit clinit check.
152    */
153 
154   /// CHECK-START: void Main$ClassWithClinit4.invokeStaticNotInlined() builder (after)
155   /// CHECK-DAG:                           InvokeStaticOrDirect
156 
157   /// CHECK-START: void Main$ClassWithClinit4.invokeStaticNotInlined() builder (after)
158   /// CHECK-NOT:                           LoadClass
159   /// CHECK-NOT:                           ClinitCheck
160 
161   /// CHECK-START: void Main$ClassWithClinit4.invokeStaticNotInlined() inliner (after)
162   /// CHECK-DAG:                           InvokeStaticOrDirect
163 
164   /// CHECK-START: void Main$ClassWithClinit4.invokeStaticNotInlined() inliner (after)
165   /// CHECK-NOT:                           LoadClass
166   /// CHECK-NOT:                           ClinitCheck
167 
168   static class ClassWithClinit4 {
invokeStaticNotInlined()169     static void invokeStaticNotInlined() {
170       // The invocation of invokeStaticNotInlined triggers the
171       // initialization of ClassWithClinit4, meaning that the
172       // call to staticMethod below does not need a clinit
173       // check.
174       $noinline$staticMethod();
175     }
176 
177     static {
178       System.out.println("Main$ClassWithClinit4's static initializer");
179     }
180 
181     static boolean doThrow = false;
182 
$noinline$staticMethod()183     static void $noinline$staticMethod() {
184       if (doThrow) {
185         // Try defeating inlining.
186         throw new Error();
187       }
188     }
189   }
190 
191   /*
192    * Ensure an inlined call to a static method whose declaring class
193    * is a super class of the caller's class does not require an
194    * explicit clinit check.
195    */
196 
197   /// CHECK-START: void Main$SubClassOfClassWithClinit5.invokeStaticInlined() builder (after)
198   /// CHECK-DAG:                           InvokeStaticOrDirect
199 
200   /// CHECK-START: void Main$SubClassOfClassWithClinit5.invokeStaticInlined() builder (after)
201   /// CHECK-NOT:                           LoadClass
202   /// CHECK-NOT:                           ClinitCheck
203 
204   /// CHECK-START: void Main$SubClassOfClassWithClinit5.invokeStaticInlined() inliner (after)
205   /// CHECK-NOT:                           LoadClass
206   /// CHECK-NOT:                           ClinitCheck
207   /// CHECK-NOT:                           InvokeStaticOrDirect
208 
209   static class ClassWithClinit5 {
$opt$inline$StaticMethod()210     static void $opt$inline$StaticMethod() {
211     }
212 
213     static {
214       System.out.println("Main$ClassWithClinit5's static initializer");
215     }
216   }
217 
218   static class SubClassOfClassWithClinit5 extends ClassWithClinit5 {
invokeStaticInlined()219     static void invokeStaticInlined() {
220       ClassWithClinit5.$opt$inline$StaticMethod();
221     }
222   }
223 
224   /*
225    * Ensure an non-inlined call to a static method whose declaring
226    * class is a super class of the caller's class does not require an
227    * explicit clinit check.
228    */
229 
230   /// CHECK-START: void Main$SubClassOfClassWithClinit6.invokeStaticNotInlined() builder (after)
231   /// CHECK-DAG:                           InvokeStaticOrDirect
232 
233   /// CHECK-START: void Main$SubClassOfClassWithClinit6.invokeStaticNotInlined() builder (after)
234   /// CHECK-NOT:                           LoadClass
235   /// CHECK-NOT:                           ClinitCheck
236 
237   /// CHECK-START: void Main$SubClassOfClassWithClinit6.invokeStaticNotInlined() inliner (after)
238   /// CHECK-DAG:                           InvokeStaticOrDirect
239 
240   /// CHECK-START: void Main$SubClassOfClassWithClinit6.invokeStaticNotInlined() inliner (after)
241   /// CHECK-NOT:                           LoadClass
242   /// CHECK-NOT:                           ClinitCheck
243 
244   static class ClassWithClinit6 {
245     static boolean doThrow = false;
246 
$noinline$staticMethod()247     static void $noinline$staticMethod() {
248       if (doThrow) {
249         // Try defeating inlining.
250         throw new Error();
251       }
252     }
253 
254     static {
255       System.out.println("Main$ClassWithClinit6's static initializer");
256     }
257   }
258 
259   static class SubClassOfClassWithClinit6 extends ClassWithClinit6 {
invokeStaticNotInlined()260     static void invokeStaticNotInlined() {
261       ClassWithClinit6.$noinline$staticMethod();
262     }
263   }
264 
265 
266   /*
267    * Verify that if we have a static call immediately after the load class
268    * we don't do generate a clinit check.
269    */
270 
271   /// CHECK-START: void Main.noClinitBecauseOfInvokeStatic() liveness (before)
272   /// CHECK-DAG:     <<IntConstant:i\d+>>  IntConstant 0
273   /// CHECK-DAG:     <<LoadClass:l\d+>>    LoadClass gen_clinit_check:false
274   /// CHECK-DAG:                           InvokeStaticOrDirect clinit_check:implicit
275   /// CHECK-DAG:                           StaticFieldSet [<<LoadClass>>,<<IntConstant>>]
276 
277   /// CHECK-START: void Main.noClinitBecauseOfInvokeStatic() liveness (before)
278   /// CHECK-NOT:                           ClinitCheck
279 
noClinitBecauseOfInvokeStatic()280   static void noClinitBecauseOfInvokeStatic() {
281     ClassWithClinit2.$noinline$staticMethod();
282     ClassWithClinit2.doThrow = false;
283   }
284 
285   /*
286    * Verify that if the static call is after a field access, the load class
287    * will generate a clinit check.
288    */
289 
290   /// CHECK-START: void Main.clinitBecauseOfFieldAccess() liveness (before)
291   /// CHECK-DAG:     <<IntConstant:i\d+>>  IntConstant 0
292   /// CHECK-DAG:     <<LoadClass:l\d+>>    LoadClass gen_clinit_check:true
293   /// CHECK-DAG:                           StaticFieldSet [<<LoadClass>>,<<IntConstant>>]
294   /// CHECK-DAG:                           InvokeStaticOrDirect clinit_check:none
295 
296   /// CHECK-START: void Main.clinitBecauseOfFieldAccess() liveness (before)
297   /// CHECK-NOT:                           ClinitCheck
clinitBecauseOfFieldAccess()298   static void clinitBecauseOfFieldAccess() {
299     ClassWithClinit2.doThrow = false;
300     ClassWithClinit2.$noinline$staticMethod();
301   }
302 
303   /*
304    * Verify that LoadClass from const-class is not merged with
305    * later invoke-static (or it's ClinitCheck).
306    */
307 
308   /// CHECK-START: void Main.constClassAndInvokeStatic(java.lang.Iterable) liveness (before)
309   /// CHECK:                               LoadClass gen_clinit_check:false
310   /// CHECK:                               InvokeStaticOrDirect clinit_check:implicit
311 
312   /// CHECK-START: void Main.constClassAndInvokeStatic(java.lang.Iterable) liveness (before)
313   /// CHECK-NOT:                           ClinitCheck
314 
constClassAndInvokeStatic(Iterable<?> it)315   static void constClassAndInvokeStatic(Iterable<?> it) {
316     $opt$inline$ignoreClass(ClassWithClinit7.class);
317     ClassWithClinit7.someStaticMethod(it);
318   }
319 
$opt$inline$ignoreClass(Class<?> c)320   static void $opt$inline$ignoreClass(Class<?> c) {
321   }
322 
323   static class ClassWithClinit7 {
324     static {
325       System.out.println("Main$ClassWithClinit7's static initializer");
326     }
327 
328     // Note: not inlined from constClassAndInvokeStatic() but fully inlined from main().
someStaticMethod(Iterable<?> it)329     static void someStaticMethod(Iterable<?> it) {
330       // We're not inlining invoke-interface at the moment.
331       it.iterator();
332     }
333   }
334 
335   /*
336    * Verify that LoadClass from sget is not merged with later invoke-static.
337    */
338 
339   /// CHECK-START: void Main.sgetAndInvokeStatic(java.lang.Iterable) liveness (before)
340   /// CHECK:                               LoadClass gen_clinit_check:true
341   /// CHECK:                               InvokeStaticOrDirect clinit_check:none
342 
343   /// CHECK-START: void Main.sgetAndInvokeStatic(java.lang.Iterable) liveness (before)
344   /// CHECK-NOT:                           ClinitCheck
345 
sgetAndInvokeStatic(Iterable<?> it)346   static void sgetAndInvokeStatic(Iterable<?> it) {
347     $opt$inline$ignoreInt(ClassWithClinit8.value);
348     ClassWithClinit8.someStaticMethod(it);
349   }
350 
$opt$inline$ignoreInt(int i)351   static void $opt$inline$ignoreInt(int i) {
352   }
353 
354   static class ClassWithClinit8 {
355     public static int value = 0;
356     static {
357       System.out.println("Main$ClassWithClinit8's static initializer");
358     }
359 
360     // Note: not inlined from sgetAndInvokeStatic() but fully inlined from main().
someStaticMethod(Iterable<?> it)361     static void someStaticMethod(Iterable<?> it) {
362       // We're not inlining invoke-interface at the moment.
363       it.iterator();
364     }
365   }
366 
367   /*
368    * Verify that LoadClass from const-class, ClinitCheck from sget and
369    * InvokeStaticOrDirect from invoke-static are not merged.
370    */
371 
372   /// CHECK-START: void Main.constClassSgetAndInvokeStatic(java.lang.Iterable) liveness (before)
373   /// CHECK:                               LoadClass gen_clinit_check:false
374   /// CHECK:                               ClinitCheck
375   /// CHECK:                               InvokeStaticOrDirect clinit_check:none
376 
constClassSgetAndInvokeStatic(Iterable<?> it)377   static void constClassSgetAndInvokeStatic(Iterable<?> it) {
378     $opt$inline$ignoreClass(ClassWithClinit9.class);
379     $opt$inline$ignoreInt(ClassWithClinit9.value);
380     ClassWithClinit9.someStaticMethod(it);
381   }
382 
383   static class ClassWithClinit9 {
384     public static int value = 0;
385     static {
386       System.out.println("Main$ClassWithClinit9's static initializer");
387     }
388 
389     // Note: not inlined from constClassSgetAndInvokeStatic() but fully inlined from main().
someStaticMethod(Iterable<?> it)390     static void someStaticMethod(Iterable<?> it) {
391       // We're not inlining invoke-interface at the moment.
392       it.iterator();
393     }
394   }
395 
396   /*
397    * Verify that LoadClass from a fully-inlined invoke-static is not merged
398    * with InvokeStaticOrDirect from a later invoke-static to the same method.
399    */
400 
401   /// CHECK-START: void Main.inlinedInvokeStaticViaNonStatic(java.lang.Iterable) liveness (before)
402   /// CHECK:                               LoadClass gen_clinit_check:true
403   /// CHECK:                               InvokeStaticOrDirect clinit_check:none
404 
405   /// CHECK-START: void Main.inlinedInvokeStaticViaNonStatic(java.lang.Iterable) liveness (before)
406   /// CHECK-NOT:                           ClinitCheck
407 
inlinedInvokeStaticViaNonStatic(Iterable<?> it)408   static void inlinedInvokeStaticViaNonStatic(Iterable<?> it) {
409     inlinedInvokeStaticViaNonStaticHelper(null);
410     inlinedInvokeStaticViaNonStaticHelper(it);
411   }
412 
inlinedInvokeStaticViaNonStaticHelper(Iterable<?> it)413   static void inlinedInvokeStaticViaNonStaticHelper(Iterable<?> it) {
414     ClassWithClinit10.inlinedForNull(it);
415   }
416 
417   static class ClassWithClinit10 {
418     public static int value = 0;
419     static {
420       System.out.println("Main$ClassWithClinit10's static initializer");
421     }
422 
inlinedForNull(Iterable<?> it)423     static void inlinedForNull(Iterable<?> it) {
424       if (it != null) {
425         // We're not inlining invoke-interface at the moment.
426         it.iterator();
427       }
428     }
429   }
430 
431   /*
432    * Check that the LoadClass from an invoke-static C.foo() doesn't get merged with
433    * an invoke-static inside C.foo(). This would mess up the stack walk in the
434    * resolution trampoline where we would have to load C (if C isn't loaded yet)
435    * which is not permitted there.
436    *
437    * Note: In case of failure, we would get an failed assertion during compilation,
438    * so we wouldn't really get to the checker tests below.
439    */
440 
441   /// CHECK-START: void Main.inlinedInvokeStaticViaStatic(java.lang.Iterable) liveness (before)
442   /// CHECK:                               LoadClass gen_clinit_check:true
443   /// CHECK:                               InvokeStaticOrDirect clinit_check:none
444 
445   /// CHECK-START: void Main.inlinedInvokeStaticViaStatic(java.lang.Iterable) liveness (before)
446   /// CHECK-NOT:                           ClinitCheck
447 
inlinedInvokeStaticViaStatic(Iterable<?> it)448   static void inlinedInvokeStaticViaStatic(Iterable<?> it) {
449     ClassWithClinit11.callInlinedForNull(it);
450   }
451 
452   static class ClassWithClinit11 {
453     public static int value = 0;
454     static {
455       System.out.println("Main$ClassWithClinit11's static initializer");
456     }
457 
callInlinedForNull(Iterable<?> it)458     static void callInlinedForNull(Iterable<?> it) {
459       inlinedForNull(it);
460     }
461 
inlinedForNull(Iterable<?> it)462     static void inlinedForNull(Iterable<?> it) {
463       // We're not inlining invoke-interface at the moment.
464       it.iterator();
465     }
466   }
467 
468   /*
469    * A test similar to inlinedInvokeStaticViaStatic() but doing the indirect invoke
470    * twice with the first one to be fully inlined.
471    */
472 
473   /// CHECK-START: void Main.inlinedInvokeStaticViaStaticTwice(java.lang.Iterable) liveness (before)
474   /// CHECK:                               LoadClass gen_clinit_check:true
475   /// CHECK:                               InvokeStaticOrDirect clinit_check:none
476 
477   /// CHECK-START: void Main.inlinedInvokeStaticViaStaticTwice(java.lang.Iterable) liveness (before)
478   /// CHECK-NOT:                           ClinitCheck
479 
inlinedInvokeStaticViaStaticTwice(Iterable<?> it)480   static void inlinedInvokeStaticViaStaticTwice(Iterable<?> it) {
481     ClassWithClinit12.callInlinedForNull(null);
482     ClassWithClinit12.callInlinedForNull(it);
483   }
484 
485   static class ClassWithClinit12 {
486     public static int value = 0;
487     static {
488       System.out.println("Main$ClassWithClinit12's static initializer");
489     }
490 
callInlinedForNull(Iterable<?> it)491     static void callInlinedForNull(Iterable<?> it) {
492       inlinedForNull(it);
493     }
494 
inlinedForNull(Iterable<?> it)495     static void inlinedForNull(Iterable<?> it) {
496       if (it != null) {
497         // We're not inlining invoke-interface at the moment.
498         it.iterator();
499       }
500     }
501   }
502 
503   static class ClassWithClinit13 {
504     static {
505       System.out.println("Main$ClassWithClinit13's static initializer");
506     }
507 
$inline$forwardToGetIterator(Iterable<?> it)508     public static void $inline$forwardToGetIterator(Iterable<?> it) {
509       $noinline$getIterator(it);
510     }
511 
$noinline$getIterator(Iterable<?> it)512     public static void $noinline$getIterator(Iterable<?> it) {
513       // We're not inlining invoke-interface at the moment.
514       it.iterator();
515     }
516   }
517 
518   // TODO: Write checker statements.
$noinline$testInliningAndNewInstance(Iterable<?> it)519   static Object $noinline$testInliningAndNewInstance(Iterable<?> it) {
520     if (doThrow) { throw new Error(); }
521     ClassWithClinit13.$inline$forwardToGetIterator(it);
522     return new ClassWithClinit13();
523   }
524 
525   // TODO: Add a test for the case of a static method whose declaring
526   // class type index is not available (i.e. when `storage_index`
527   // equals `DexFile::kDexNoIndex` in
528   // art::HGraphBuilder::BuildInvoke).
529 
main(String[] args)530   public static void main(String[] args) {
531     invokeStaticInlined();
532     invokeStaticNotInlined();
533     ClassWithClinit3.invokeStaticInlined();
534     ClassWithClinit4.invokeStaticNotInlined();
535     SubClassOfClassWithClinit5.invokeStaticInlined();
536     SubClassOfClassWithClinit6.invokeStaticNotInlined();
537     Iterable it = new Iterable() { public java.util.Iterator iterator() { return null; } };
538     constClassAndInvokeStatic(it);
539     sgetAndInvokeStatic(it);
540     constClassSgetAndInvokeStatic(it);
541     inlinedInvokeStaticViaNonStatic(it);
542     inlinedInvokeStaticViaStatic(it);
543     inlinedInvokeStaticViaStaticTwice(it);
544     $noinline$testInliningAndNewInstance(it);
545   }
546 }
547