• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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   static class ValueHolder {
getValue()19     int getValue() {
20       return 1;
21     }
22   }
23 
main(String[] args)24   public static void main(String[] args) throws Exception {
25     testSimpleUse();
26     testTwoUses();
27     testFieldStores(doThrow);
28     testFieldStoreCycle();
29     testArrayStores();
30     testOnlyStoreUses();
31     testNoUse();
32     testPhiInput();
33     testVolatileStore();
34     testCatchBlock();
35     $noinline$testTwoThrowingPathsAndStringBuilderAppend();
36     try {
37       $noinline$testSinkNewInstanceWithClinitCheck();
38       throw new Exception("Unreachable");
39     } catch (Error e) {
40       // expected
41     }
42     $noinline$testMethodEndsWithTryBoundary();
43     doThrow = true;
44     try {
45       testInstanceSideEffects();
46     } catch (Error e) {
47       // expected
48       System.out.println(e.getMessage());
49     }
50     try {
51       testStaticSideEffects();
52     } catch (Error e) {
53       // expected
54       System.out.println(e.getMessage());
55     }
56 
57     try {
58       testStoreStore(doThrow);
59     } catch (Error e) {
60       // expected
61       System.out.println(e.getMessage());
62     }
63   }
64 
65   /// CHECK-START: void Main.testSimpleUse() code_sinking (before)
66   /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:java.lang.Object
67   /// CHECK: <<New:l\d+>>       NewInstance [<<LoadClass>>]
68   /// CHECK:                    ConstructorFence [<<New>>]
69   /// CHECK:                    If
70   /// CHECK:                    begin_block
71   /// CHECK:                    Throw
72 
73   /// CHECK-START: void Main.testSimpleUse() code_sinking (after)
74   /// CHECK-NOT:                NewInstance
75   /// CHECK:                    If
76   /// CHECK:                    begin_block
77   /// CHECK: <<Error:l\d+>>     LoadClass class_name:java.lang.Error
78   /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:java.lang.Object
79   /// CHECK-NOT:                begin_block
80   /// CHECK: <<New:l\d+>>       NewInstance [<<LoadClass>>]
81   /// CHECK:                    ConstructorFence [<<New>>]
82   /// CHECK-NOT:                begin_block
83   /// CHECK:                    NewInstance [<<Error>>]
84   /// CHECK:                    Throw
testSimpleUse()85   public static void testSimpleUse() {
86     Object o = new Object();
87     if (doThrow) {
88       throw new Error(o.toString());
89     }
90   }
91 
92   /// CHECK-START: void Main.testTwoUses() code_sinking (before)
93   /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:java.lang.Object
94   /// CHECK:                    NewInstance [<<LoadClass>>]
95   /// CHECK:                    If
96   /// CHECK:                    begin_block
97   /// CHECK:                    Throw
98 
99   /// CHECK-START: void Main.testTwoUses() code_sinking (after)
100   /// CHECK-NOT:                NewInstance
101   /// CHECK:                    If
102   /// CHECK:                    begin_block
103   /// CHECK: <<Error:l\d+>>     LoadClass class_name:java.lang.Error
104   /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:java.lang.Object
105   /// CHECK-NOT:                begin_block
106   /// CHECK:                    NewInstance [<<LoadClass>>]
107   /// CHECK-NOT:                begin_block
108   /// CHECK:                    NewInstance [<<Error>>]
109   /// CHECK:                    Throw
testTwoUses()110   public static void testTwoUses() {
111     Object o = new Object();
112     if (doThrow) {
113       throw new Error(o.toString() + o.toString());
114     }
115   }
116 
117   // NB It might seem that we'd move the allocation and ifield-set but those are
118   // already moved into the throw block by a combo of partial-LSE and DCE.
119   // Instead all that is actually moved is the LoadClass. Also note the
120   // LoadClass can only be moved since it refers to the 'Main' class itself,
121   // meaning there's no need for any clinit/actual loading.
122   //
123   /// CHECK-START: void Main.testFieldStores(boolean) code_sinking (before)
124   /// CHECK: <<Int42:i\d+>>       IntConstant 42
125   /// CHECK:                      begin_block
126   /// CHECK: <<LoadClass:l\d+>>   LoadClass class_name:Main
127   /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>]
128   /// CHECK:                      InstanceFieldSet [<<NewInstance>>,<<Int42>>]
129   /// CHECK:                      Throw
130 
131   /// CHECK-START: void Main.testFieldStores(boolean) code_sinking (after)
132   /// CHECK: <<Int42:i\d+>>       IntConstant 42
133   /// CHECK-NOT:                  NewInstance
134   /// CHECK:                      If
135   /// CHECK:                      begin_block
136   /// CHECK: <<Error:l\d+>>       LoadClass class_name:java.lang.Error
137   /// CHECK-NOT:                  begin_block
138   /// CHECK: <<LoadClass:l\d+>>   LoadClass class_name:Main
139   /// CHECK-NOT:                  begin_block
140   /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>]
141   /// CHECK-NOT:                  begin_block
142   /// CHECK:                      InstanceFieldSet [<<NewInstance>>,<<Int42>>]
143   /// CHECK-NOT:                  begin_block
144   /// CHECK: <<Throw:l\d+>>       NewInstance [<<Error>>]
145   /// CHECK-NOT:                  begin_block
146   /// CHECK:                      Throw [<<Throw>>]
testFieldStores(boolean doThrow)147   public static void testFieldStores(boolean doThrow) {
148     Main m = new Main();
149     m.intField = 42;
150     if (doThrow) {
151       throw new Error(m.toString());
152     }
153   }
154 
155   /// CHECK-START: void Main.testFieldStoreCycle() code_sinking (before)
156   /// CHECK: <<LoadClass:l\d+>>    LoadClass class_name:Main
157   /// CHECK: <<NewInstance1:l\d+>> NewInstance [<<LoadClass>>]
158   /// CHECK: <<NewInstance2:l\d+>> NewInstance [<<LoadClass>>]
159   /// CHECK:                       InstanceFieldSet [<<NewInstance1>>,<<NewInstance2>>]
160   /// CHECK:                       InstanceFieldSet [<<NewInstance2>>,<<NewInstance1>>]
161   /// CHECK:                       If
162   /// CHECK:                       begin_block
163   /// CHECK:                       Throw
164 
165   // TODO(ngeoffray): Handle allocation/store cycles.
166   /// CHECK-START: void Main.testFieldStoreCycle() code_sinking (after)
167   /// CHECK: begin_block
168   /// CHECK: <<LoadClass:l\d+>>    LoadClass class_name:Main
169   /// CHECK: <<NewInstance1:l\d+>> NewInstance [<<LoadClass>>]
170   /// CHECK: <<NewInstance2:l\d+>> NewInstance [<<LoadClass>>]
171   /// CHECK:                       InstanceFieldSet [<<NewInstance1>>,<<NewInstance2>>]
172   /// CHECK:                       InstanceFieldSet [<<NewInstance2>>,<<NewInstance1>>]
173   /// CHECK:                       If
174   /// CHECK:                       begin_block
175   /// CHECK:                       Throw
testFieldStoreCycle()176   public static void testFieldStoreCycle() {
177     Main m1 = new Main();
178     Main m2 = new Main();
179     m1.objectField = m2;
180     m2.objectField = m1;
181     if (doThrow) {
182       throw new Error(m1.toString() + m2.toString());
183     }
184   }
185 
186   /// CHECK-START: void Main.testArrayStores() code_sinking (before)
187   /// CHECK: <<Int1:i\d+>>        IntConstant 1
188   /// CHECK: <<Int0:i\d+>>        IntConstant 0
189   /// CHECK: <<LoadClass:l\d+>>   LoadClass class_name:java.lang.Object[]
190   /// CHECK: <<NewArray:l\d+>>    NewArray [<<LoadClass>>,<<Int1>>]
191   /// CHECK:                      ArraySet [<<NewArray>>,<<Int0>>,<<NewArray>>]
192   /// CHECK:                      If
193   /// CHECK:                      begin_block
194   /// CHECK:                      Throw
195 
196   /// CHECK-START: void Main.testArrayStores() code_sinking (after)
197   /// CHECK: <<Int1:i\d+>>        IntConstant 1
198   /// CHECK: <<Int0:i\d+>>        IntConstant 0
199   /// CHECK-NOT:                  NewArray
200   /// CHECK:                      If
201   /// CHECK:                      begin_block
202   /// CHECK: <<Error:l\d+>>       LoadClass class_name:java.lang.Error
203   /// CHECK: <<LoadClass:l\d+>>   LoadClass class_name:java.lang.Object[]
204   /// CHECK-NOT:                  begin_block
205   /// CHECK: <<NewArray:l\d+>>    NewArray [<<LoadClass>>,<<Int1>>]
206   /// CHECK-NOT:                  begin_block
207   /// CHECK:                      ArraySet [<<NewArray>>,<<Int0>>,<<NewArray>>]
208   /// CHECK-NOT:                  begin_block
209   /// CHECK:                      NewInstance [<<Error>>]
210   /// CHECK:                      Throw
testArrayStores()211   public static void testArrayStores() {
212     Object[] o = new Object[1];
213     o[0] = o;
214     if (doThrow) {
215       throw new Error(o.toString());
216     }
217   }
218 
219   // Make sure code sinking does not crash on dead allocations.
testOnlyStoreUses()220   public static void testOnlyStoreUses() {
221     Main m = new Main();
222     Object[] o = new Object[1];  // dead allocation, should eventually be removed b/35634932.
223     o[0] = m;
224     o = null;  // Avoid environment uses for the array allocation.
225     if (doThrow) {
226       throw new Error(m.toString());
227     }
228   }
229 
230   // Make sure code sinking does not crash on dead code.
testNoUse()231   public static void testNoUse() {
232     Main m = new Main();
233     boolean load = Main.doLoop;  // dead code, not removed because of environment use.
234     // Ensure one environment use for the static field
235     $opt$noinline$foo();
236     load = false;
237     if (doThrow) {
238       throw new Error(m.toString());
239     }
240   }
241 
242   // Make sure we can move code only used by a phi.
243   /// CHECK-START: void Main.testPhiInput() code_sinking (before)
244   /// CHECK: <<Null:l\d+>>        NullConstant
245   /// CHECK: <<LoadClass:l\d+>>   LoadClass class_name:java.lang.Object
246   /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>]
247   /// CHECK:                      If
248   /// CHECK:                      begin_block
249   /// CHECK:                      Phi [<<Null>>,<<NewInstance>>]
250   /// CHECK:                      Throw
251 
252   /// CHECK-START: void Main.testPhiInput() code_sinking (after)
253   /// CHECK: <<Null:l\d+>>        NullConstant
254   /// CHECK-NOT:                  NewInstance
255   /// CHECK:                      If
256   /// CHECK:                      begin_block
257   /// CHECK: <<LoadClass:l\d+>>   LoadClass class_name:java.lang.Object
258   /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>]
259   /// CHECK:                      begin_block
260   /// CHECK:                      Phi [<<Null>>,<<NewInstance>>]
261   /// CHECK: <<Error:l\d+>>       LoadClass class_name:java.lang.Error
262   /// CHECK:                      NewInstance [<<Error>>]
263   /// CHECK:                      Throw
testPhiInput()264   public static void testPhiInput() {
265     Object f = new Object();
266     if (doThrow) {
267       Object o = null;
268       int i = 2;
269       if (doLoop) {
270         o = f;
271         i = 42;
272       }
273       throw new Error(o.toString() + i);
274     }
275   }
276 
$opt$noinline$foo()277   static void $opt$noinline$foo() {}
278 
279   // Check that we do not move volatile stores.
280   /// CHECK-START: void Main.testVolatileStore() code_sinking (before)
281   /// CHECK: <<Int42:i\d+>>        IntConstant 42
282   /// CHECK: <<LoadClass:l\d+>>    LoadClass class_name:Main
283   /// CHECK: <<NewInstance:l\d+>>  NewInstance [<<LoadClass>>]
284   /// CHECK:                       InstanceFieldSet [<<NewInstance>>,<<Int42>>]
285   /// CHECK:                       If
286   /// CHECK:                       begin_block
287   /// CHECK:                       Throw
288 
289   /// CHECK-START: void Main.testVolatileStore() code_sinking (after)
290   /// CHECK: <<Int42:i\d+>>        IntConstant 42
291   /// CHECK: <<LoadClass:l\d+>>    LoadClass class_name:Main
292   /// CHECK: <<NewInstance:l\d+>>  NewInstance [<<LoadClass>>]
293   /// CHECK:                       InstanceFieldSet [<<NewInstance>>,<<Int42>>]
294   /// CHECK:                       If
295   /// CHECK:                       begin_block
296   /// CHECK:                       Throw
testVolatileStore()297   public static void testVolatileStore() {
298     Main m = new Main();
299     m.volatileField = 42;
300     if (doThrow) {
301       throw new Error(m.toString());
302     }
303   }
304 
$noinline$testMethodEndsWithTryBoundary()305   private static void $noinline$testMethodEndsWithTryBoundary() throws Exception {
306     assertEquals(0, $noinline$testDontSinkToReturnBranch(0, 0, false, new Object()));
307     assertEquals(1, $noinline$testSinkToThrowBranch(0, 0, true, new Object()));
308     try {
309       $noinline$testSinkToThrowBranch(0, 0, false, new Object());
310       throw new Exception("Unreachable");
311     } catch (Error expected) {
312     }
313   }
314 
315   // Consistency check: only one add
316   /// CHECK-START: int Main.$noinline$testDontSinkToReturnBranch(int, int, boolean, java.lang.Object) code_sinking (before)
317   /// CHECK:     Add
318   /// CHECK-NOT: Add
319 
320   /// CHECK-START: int Main.$noinline$testDontSinkToReturnBranch(int, int, boolean, java.lang.Object) code_sinking (before)
321   /// CHECK:      Add
322   /// CHECK-NEXT: If
323 
324   /// CHECK-START: int Main.$noinline$testDontSinkToReturnBranch(int, int, boolean, java.lang.Object) code_sinking (after)
325   /// CHECK:      Add
326   /// CHECK-NEXT: If
$noinline$testDontSinkToReturnBranch(int a, int b, boolean flag, Object obj)327   private static int $noinline$testDontSinkToReturnBranch(int a, int b, boolean flag, Object obj) {
328     int c = a + b;
329     if (flag) {
330       return 1;
331     }
332 
333     synchronized (obj) {
334       return $noinline$returnSameValue(c);
335     }
336   }
337 
$noinline$returnSameValue(int value)338   private static int $noinline$returnSameValue(int value) {
339     return value;
340   }
341 
342   // Consistency check: only one add
343   /// CHECK-START: int Main.$noinline$testSinkToThrowBranch(int, int, boolean, java.lang.Object) code_sinking (before)
344   /// CHECK:     Add
345   /// CHECK-NOT: Add
346 
347   /// CHECK-START: int Main.$noinline$testSinkToThrowBranch(int, int, boolean, java.lang.Object) code_sinking (before)
348   /// CHECK:      Add
349   /// CHECK:      If
350 
351   /// CHECK-START: int Main.$noinline$testSinkToThrowBranch(int, int, boolean, java.lang.Object) code_sinking (after)
352   /// CHECK:      If
353   /// CHECK:      Add
$noinline$testSinkToThrowBranch(int a, int b, boolean flag, Object obj)354   private static int $noinline$testSinkToThrowBranch(int a, int b, boolean flag, Object obj) {
355     int c = a + b;
356     if (flag) {
357       return 1;
358     }
359 
360     synchronized (obj) {
361       throw new Error(Integer.toString(c));
362     }
363   }
364 
testInstanceSideEffects()365   public static void testInstanceSideEffects() {
366     int a = mainField.intField;
367     $noinline$changeIntField();
368     if (doThrow) {
369       throw new Error("" + a);
370     }
371   }
372 
$noinline$changeIntField()373   static void $noinline$changeIntField() {
374     mainField.intField = 42;
375   }
376 
testStaticSideEffects()377   public static void testStaticSideEffects() {
378     Object o = obj;
379     $noinline$changeStaticObjectField();
380     if (doThrow) {
381       throw new Error(o.getClass().toString());
382     }
383   }
384 
$noinline$changeStaticObjectField()385   static void $noinline$changeStaticObjectField() {
386     obj = new Main();
387   }
388 
389   // Test that we preserve the order of stores.
390   // NB It might seem that we'd move the allocation and ifield-set but those are
391   // already moved into the throw block by a combo of partial-LSE and DCE.
392   // Instead all that is actually moved is the LoadClass. Also note the
393   // LoadClass can only be moved since it refers to the 'Main' class itself,
394   // meaning there's no need for any clinit/actual loading.
395   //
396   /// CHECK-START: void Main.testStoreStore(boolean) code_sinking (before)
397   /// CHECK: <<Int42:i\d+>>       IntConstant 42
398   /// CHECK: <<Int43:i\d+>>       IntConstant 43
399   /// CHECK: <<LoadClass:l\d+>>   LoadClass class_name:Main
400   /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>]
401   /// CHECK-DAG:                  InstanceFieldSet [<<NewInstance>>,<<Int42>>]
402   /// CHECK-DAG:                  InstanceFieldSet [<<NewInstance>>,<<Int43>>]
403   /// CHECK:                      Throw
404   /// CHECK-NOT:                  InstanceFieldSet
405 
406   /// CHECK-START: void Main.testStoreStore(boolean) code_sinking (after)
407   /// CHECK: <<Int42:i\d+>>       IntConstant 42
408   /// CHECK: <<Int43:i\d+>>       IntConstant 43
409   /// CHECK-NOT:                  NewInstance
410   /// CHECK:                      If
411   /// CHECK:                      begin_block
412   /// CHECK: <<Error:l\d+>>       LoadClass class_name:java.lang.Error
413   /// CHECK-NOT:                  begin_block
414   /// CHECK: <<LoadClass:l\d+>>   LoadClass class_name:Main
415   /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>]
416   /// CHECK-NOT:                  begin_block
417   /// CHECK-DAG:                  InstanceFieldSet [<<NewInstance>>,<<Int42>>]
418   /// CHECK-DAG:                  InstanceFieldSet [<<NewInstance>>,<<Int43>>]
419   /// CHECK-NOT:                  begin_block
420   /// CHECK:                      NewInstance [<<Error>>]
421   /// CHECK:                      Throw
422   /// CHECK-NOT:                  InstanceFieldSet
testStoreStore(boolean doThrow)423   public static void testStoreStore(boolean doThrow) {
424     Main m = new Main();
425     m.intField = 42;
426     m.intField2 = 43;
427     if (doThrow) {
428       throw new Error(m.$opt$noinline$toString());
429     }
430   }
431 
doStaticNativeCallLiveVreg()432   static native void doStaticNativeCallLiveVreg();
433 
434   //  Test ensures that 'o' has been moved into the if despite the InvokeStaticOrDirect.
435   //
436   /// CHECK-START: void Main.testSinkingOverInvoke() code_sinking (before)
437   /// CHECK: <<Int1:i\d+>>        IntConstant 1
438   /// CHECK: <<Int0:i\d+>>        IntConstant 0
439   /// CHECK: <<LoadClass:l\d+>>   LoadClass class_name:java.lang.Object[]
440   /// CHECK-NOT:                  begin_block
441   /// CHECK:                      NewArray [<<LoadClass>>,<<Int1>>]
442   /// CHECK:                      If
443   /// CHECK:                      begin_block
444   /// CHECK:                      Throw
445 
446   /// CHECK-START: void Main.testSinkingOverInvoke() code_sinking (after)
447   /// CHECK: <<Int1:i\d+>>        IntConstant 1
448   /// CHECK: <<Int0:i\d+>>        IntConstant 0
449   /// CHECK:                      If
450   /// CHECK:                      begin_block
451   /// CHECK: <<LoadClass:l\d+>>   LoadClass class_name:java.lang.Object[]
452   /// CHECK:                      NewArray [<<LoadClass>>,<<Int1>>]
453   /// CHECK:                      Throw
testSinkingOverInvoke()454   static void testSinkingOverInvoke() {
455     Object[] o = new Object[1];
456     o[0] = o;
457     doStaticNativeCallLiveVreg();
458     if (doThrow) {
459       throw new Error(o.toString());
460     }
461   }
462 
$opt$noinline$toString()463   public String $opt$noinline$toString() {
464     return "" + intField;
465   }
466 
testCatchBlock()467   private static void testCatchBlock() {
468     assertEquals(456, testDoNotSinkToTry());
469     assertEquals(456, testSinkWithinTryBlock());
470     assertEquals(456, testSinkRightBeforeTryBlock());
471     assertEquals(456, testDoNotSinkToCatchInsideTryWithMoreThings(false, false));
472     assertEquals(456, DoNotSinkWithOOMThrow());
473   }
474 
475   /// CHECK-START: int Main.testDoNotSinkToTry() code_sinking (before)
476   /// CHECK: <<ObjLoadClass:l\d+>>   LoadClass class_name:java.lang.Object
477   /// CHECK:                         NewInstance [<<ObjLoadClass>>]
478   /// CHECK:                         TryBoundary kind:entry
479 
480   /// CHECK-START: int Main.testDoNotSinkToTry() code_sinking (after)
481   /// CHECK: <<ObjLoadClass:l\d+>>   LoadClass class_name:java.lang.Object
482   /// CHECK:                         NewInstance [<<ObjLoadClass>>]
483   /// CHECK:                         TryBoundary kind:entry
484 
485   // Consistency check to make sure there's only one entry TryBoundary.
486   /// CHECK-START: int Main.testDoNotSinkToTry() code_sinking (after)
487   /// CHECK:                         TryBoundary kind:entry
488   /// CHECK-NOT:                     TryBoundary kind:entry
489 
490   // Tests that we don't sink the Object creation into the try.
testDoNotSinkToTry()491   private static int testDoNotSinkToTry() {
492     Object o = new Object();
493     try {
494       if (doEarlyReturn) {
495         throw new Error(o.toString());
496       }
497     } catch (Error e) {
498       throw new Error();
499     }
500     return 456;
501   }
502 
503   /// CHECK-START: int Main.testSinkWithinTryBlock() code_sinking (before)
504   /// CHECK: <<ObjLoadClass:l\d+>>   LoadClass class_name:java.lang.Object
505   /// CHECK:                         NewInstance [<<ObjLoadClass>>]
506   /// CHECK:                         If
507 
508   /// CHECK-START: int Main.testSinkWithinTryBlock() code_sinking (after)
509   /// CHECK:                         If
510   /// CHECK: <<ObjLoadClass:l\d+>>   LoadClass class_name:java.lang.Object
511   /// CHECK:                         NewInstance [<<ObjLoadClass>>]
testSinkWithinTryBlock()512   private static int testSinkWithinTryBlock() {
513     try {
514       Object o = new Object();
515       if (doEarlyReturn) {
516         throw new Error(o.toString());
517       }
518     } catch (Error e) {
519       return 123;
520     }
521     return 456;
522   }
523 
524   /// CHECK-START: int Main.testSinkRightBeforeTryBlock() code_sinking (before)
525   /// CHECK: <<ObjLoadClass:l\d+>>   LoadClass class_name:java.lang.Object
526   /// CHECK:                         NewInstance [<<ObjLoadClass>>]
527   /// CHECK:                         If
528   /// CHECK:                         TryBoundary kind:entry
529 
530   /// CHECK-START: int Main.testSinkRightBeforeTryBlock() code_sinking (after)
531   /// CHECK:                         If
532   /// CHECK: <<ObjLoadClass:l\d+>>   LoadClass class_name:java.lang.Object
533   /// CHECK:                         NewInstance [<<ObjLoadClass>>]
534   /// CHECK:                         TryBoundary kind:entry
testSinkRightBeforeTryBlock()535   private static int testSinkRightBeforeTryBlock() {
536     Object o = new Object();
537     if (doEarlyReturn) {
538       try {
539         throw new Error(o.toString());
540       } catch (Error e) {
541         return 123;
542       }
543     }
544     return 456;
545   }
546 
547   /// CHECK-START: int Main.testDoNotSinkToCatchInsideTryWithMoreThings(boolean, boolean) code_sinking (before)
548   /// CHECK-NOT:                     TryBoundary kind:entry
549   /// CHECK: <<ObjLoadClass:l\d+>>   LoadClass class_name:java.lang.Object
550   /// CHECK:                         NewInstance [<<ObjLoadClass>>]
551 
552   /// CHECK-START: int Main.testDoNotSinkToCatchInsideTryWithMoreThings(boolean, boolean) code_sinking (after)
553   /// CHECK-NOT:                     TryBoundary kind:entry
554   /// CHECK: <<ObjLoadClass:l\d+>>   LoadClass class_name:java.lang.Object
555   /// CHECK:                         NewInstance [<<ObjLoadClass>>]
556 
557   // Tests that we don't sink the Object creation into a catch handler surrounded by try/catch, even
558   // when that inner catch is not at the boundary of the outer try catch.
testDoNotSinkToCatchInsideTryWithMoreThings(boolean a, boolean b)559   private static int testDoNotSinkToCatchInsideTryWithMoreThings(boolean a, boolean b) {
560     Object o = new Object();
561     try {
562       if (a) {
563         System.out.println(a);
564       }
565       try {
566         if (doEarlyReturn) {
567           return 123;
568         }
569       } catch (Error e) {
570         throw new Error(o.toString());
571       }
572       if (b) {
573         System.out.println(b);
574       }
575     } catch (Error e) {
576       throw new Error();
577     }
578     return 456;
579   }
580 
581   private static class ObjectWithInt {
582     int x;
583   }
584 
585   /// CHECK-START: int Main.DoNotSinkWithOOMThrow() code_sinking (before)
586   /// CHECK: <<LoadClass:l\d+>>      LoadClass class_name:Main$ObjectWithInt
587   /// CHECK: <<Clinit:l\d+>>         ClinitCheck [<<LoadClass>>]
588   /// CHECK:                         NewInstance [<<Clinit>>]
589   /// CHECK:                         TryBoundary kind:entry
590 
591   /// CHECK-START: int Main.DoNotSinkWithOOMThrow() code_sinking (after)
592   /// CHECK: <<LoadClass:l\d+>>      LoadClass class_name:Main$ObjectWithInt
593   /// CHECK: <<Clinit:l\d+>>         ClinitCheck [<<LoadClass>>]
594   /// CHECK:                         NewInstance [<<Clinit>>]
595   /// CHECK:                         TryBoundary kind:entry
596 
597   // Consistency check to make sure there's only one entry TryBoundary.
598   /// CHECK-START: int Main.DoNotSinkWithOOMThrow() code_sinking (after)
599   /// CHECK:                         TryBoundary kind:entry
600   /// CHECK-NOT:                     TryBoundary kind:entry
DoNotSinkWithOOMThrow()601   private static int DoNotSinkWithOOMThrow() throws OutOfMemoryError {
602     int x = 0;
603     ObjectWithInt obj = new ObjectWithInt();
604     try {
605       // We want an if/else here so that the catch block will have a catch phi.
606       if (doThrow) {
607         x = 1;
608         // Doesn't really matter what we throw we just want it to not be caught by the
609         // NullPointerException below.
610         throw new OutOfMemoryError(Integer.toString(obj.x));
611       } else {
612         x = 456;
613       }
614     } catch (NullPointerException e) {
615     }
616 
617     // We want to use obj over here so that it doesn't get deleted by LSE.
618     if (obj.x == 123) {
619       return 123;
620     }
621     return x;
622   }
623 
$noinline$testTwoThrowingPathsAndStringBuilderAppend()624   private static void $noinline$testTwoThrowingPathsAndStringBuilderAppend() {
625     try {
626       $noinline$twoThrowingPathsAndStringBuilderAppend(null);
627       throw new Error("Unreachable");
628     } catch (Error expected) {
629       assertEquals("Object is null", expected.getMessage());
630     }
631     try {
632       $noinline$twoThrowingPathsAndStringBuilderAppend(new Object());
633       throw new Error("Unreachable");
634     } catch (Error expected) {
635       assertEquals("s1s2", expected.getMessage());
636     }
637   }
638 
639   // Consistency check: only one ClinitCheck
640   /// CHECK-START: void Main.$noinline$testSinkNewInstanceWithClinitCheck() code_sinking (before)
641   /// CHECK:                ClinitCheck
642   /// CHECK-NOT:            ClinitCheck
643 
644   /// CHECK-START: void Main.$noinline$testSinkNewInstanceWithClinitCheck() code_sinking (before)
645   /// CHECK: <<Check:l\d+>> ClinitCheck
646   /// CHECK:                NewInstance [<<Check>>]
647   /// CHECK:                NewInstance
648   /// CHECK:                If
649 
650   /// CHECK-START: void Main.$noinline$testSinkNewInstanceWithClinitCheck() code_sinking (after)
651   /// CHECK: <<Check:l\d+>> ClinitCheck
652   /// CHECK:                If
653   /// CHECK:                NewInstance
654   /// CHECK:                NewInstance [<<Check>>]
655 
656   /// CHECK-START: void Main.$noinline$testSinkNewInstanceWithClinitCheck() prepare_for_register_allocation (before)
657   /// CHECK-NOT:            If
658 
659   // We have an instruction that can throw between the ClinitCheck and its NewInstance.
660 
661   /// CHECK-START: void Main.$noinline$testSinkNewInstanceWithClinitCheck() prepare_for_register_allocation (before)
662   /// CHECK: <<Check:l\d+>> ClinitCheck
663   /// CHECK:                NewInstance
664   /// CHECK:                NewInstance [<<Check>>]
665 
666   // We can remove the ClinitCheck by merging it with the LoadClass right before.
667 
668   /// CHECK-START: void Main.$noinline$testSinkNewInstanceWithClinitCheck() prepare_for_register_allocation (after)
669   /// CHECK-NOT: ClinitCheck
$noinline$testSinkNewInstanceWithClinitCheck()670   private static void $noinline$testSinkNewInstanceWithClinitCheck() {
671     ValueHolder vh = new ValueHolder();
672     Object o = new Object();
673 
674     // The if will always be true but we don't know this after LSE. Code sinking will sink code
675     // since this is an uncommon branch, but then we will have everything in one block before
676     // prepare_for_register_allocation for the crash to appear.
677     staticIntField = 1;
678     int value = staticIntField;
679     if (value == 1) {
680       throw new Error(Integer.toString(vh.getValue()) + o.toString());
681     }
682   }
683 
684   // We currently do not inline the `StringBuilder` constructor.
685   // When we did, the `StringBuilderAppend` pattern recognition was looking for
686   // the inlined `NewArray` (and its associated `LoadClass`) and checked in
687   // debug build that the `StringBuilder` has an environment use from this
688   // `NewArray` (and maybe from `LoadClass`). However, code sinking was pruning
689   // the environment of the `NewArray`, leading to a crash when compiling the
690   // code below on the device (we do not inline `core-oj` on host). b/252799691
691 
692   // We currently have a heuristic that disallows inlining methods if their basic blocks end with a
693   // throw. We could add code so that `requireNonNull`'s block doesn't end with a throw but that
694   // would mean that the string builder optimization wouldn't fire as it requires all uses to be in
695   // the same block. If `requireNonNull` is inlined at some point, we need to re-mark it as $inline$
696   // so that the test is operational again.
697 
698   /// CHECK-START: void Main.$noinline$twoThrowingPathsAndStringBuilderAppend(java.lang.Object) inliner (before)
699   /// CHECK: InvokeStaticOrDirect method_name:Main.requireNonNull
700 
701   /// CHECK-START: void Main.$noinline$twoThrowingPathsAndStringBuilderAppend(java.lang.Object) inliner (after)
702   /// CHECK: InvokeStaticOrDirect method_name:Main.requireNonNull
$noinline$twoThrowingPathsAndStringBuilderAppend(Object o)703   private static void $noinline$twoThrowingPathsAndStringBuilderAppend(Object o) {
704     String s1 = "s1";
705     String s2 = "s2";
706     StringBuilder sb = new StringBuilder();
707 
708     // Before inlining, the environment use from this invoke prevents the
709     // `StringBuilderAppend` pattern recognition. After inlining, we end up
710     // with two paths ending with a `Throw` and we could sink the `sb`
711     // instructions from above down to those below, enabling the
712     // `StringBuilderAppend` pattern recognition.
713     // (But that does not happen when the `StringBuilder` constructor is
714     // not inlined, see above.)
715     requireNonNull(o);
716 
717     String s1s2 = sb.append(s1).append(s2).toString();
718     sb = null;
719     throw new Error(s1s2);
720   }
721 
requireNonNull(Object o)722   private static void requireNonNull(Object o) {
723     if (o == null) {
724       throw new Error("Object is null");
725     }
726   }
727 
assertEquals(int expected, int actual)728   private static void assertEquals(int expected, int actual) {
729     if (expected != actual) {
730       throw new AssertionError("Expected: " + expected + ", Actual: " + actual);
731     }
732   }
733 
assertEquals(String expected, String actual)734   private static void assertEquals(String expected, String actual) {
735     if (!expected.equals(actual)) {
736       throw new AssertionError("Expected: " + expected + ", Actual: " + actual);
737     }
738   }
739 
740   volatile int volatileField;
741   int intField;
742   int intField2;
743   Object objectField;
744   static boolean doThrow;
745   static boolean doLoop;
746   static boolean doEarlyReturn;
747   static boolean doOtherEarlyReturn;
748   static int staticIntField;
749   static Main mainField = new Main();
750   static Object obj = new Object();
751 }
752