xref: /aosp_15_r20/art/test/530-checker-lse-try-catch/src/Main.java (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1 /*
2  * Copyright (C) 2022 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 class Point {
18   int x;
19   int y;
20 }
21 
22 public class Main {
main(String[] args)23   public static void main(String[] args) {
24     final boolean boolean_throw = false;
25     final boolean boolean_other_throw = false;
26     assertEquals(3,
27         $noinline$testDifferentFields(
28             new Point(), new Point(), boolean_throw, boolean_other_throw));
29     assertEquals(1, $noinline$testRedundantStore(new Point(), boolean_throw, boolean_other_throw));
30     assertEquals(1, $noinline$testTryCatchBlocking(new Point(), boolean_throw));
31     assertEquals(1, $noinline$testTryCatchPhi(new Point(), boolean_throw));
32     assertEquals(2, $noinline$testTryCatchPhiWithTwoCatches(new Point(), new int[0]));
33     assertEquals(1, $noinline$testKeepStoreInsideTry());
34     assertEquals(10, $noinline$testDontKeepStoreInsideCatch(new int[]{10}));
35     assertEquals(30, $noinline$testDontKeepStoreInsideCatch(new int[]{}));
36     assertEquals(10, $noinline$testKeepStoreInsideCatchWithOuterTry(new int[]{10}));
37     assertEquals(30, $noinline$testKeepStoreInsideCatchWithOuterTry(new int[]{}));
38     assertEquals(40, $noinline$testDontKeepStoreInsideFinally(new int[]{10}));
39     try {
40       assertEquals(30, $noinline$testDontKeepStoreInsideFinally(new int[]{}));
41       throw new Error("Unreachable");
42     } catch (ArrayIndexOutOfBoundsException expected) {
43     }
44     assertEquals(10, $noinline$testDontKeepStoreInsideOuterCatch(new int[]{10}));
45     assertEquals(100030, $noinline$testDontKeepStoreInsideOuterCatch(new int[]{}));
46     assertEquals(150, $noinline$test40());
47   }
48 
49   /// CHECK-START: int Main.$noinline$testDifferentFields(Point, Point, boolean, boolean) load_store_elimination (before)
50   /// CHECK-DAG:     InstanceFieldSet field_name:Point.x
51   /// CHECK-DAG:     InstanceFieldSet field_name:Point.y
52   /// CHECK-DAG:     InstanceFieldGet field_name:Point.x
53   /// CHECK-DAG:     InstanceFieldGet field_name:Point.y
54 
55   /// CHECK-START: int Main.$noinline$testDifferentFields(Point, Point, boolean, boolean) load_store_elimination (after)
56   /// CHECK-DAG:     InstanceFieldSet field_name:Point.x
57   /// CHECK-DAG:     InstanceFieldSet field_name:Point.y
58 
59   /// CHECK-START: int Main.$noinline$testDifferentFields(Point, Point, boolean, boolean) load_store_elimination (after)
60   /// CHECK-NOT:     InstanceFieldGet field_name:Point.x
61   /// CHECK-NOT:     InstanceFieldGet field_name:Point.y
62 
63   // Consistency check to make sure the try/catches weren't removed by an earlier pass.
64   /// CHECK-START: int Main.$noinline$testDifferentFields(Point, Point, boolean, boolean) load_store_elimination (after)
65   /// CHECK:         TryBoundary kind:entry
66   /// CHECK:         TryBoundary kind:entry
67 
68   // Different fields shouldn't alias.
$noinline$testDifferentFields( Point obj1, Point obj2, boolean boolean_throw, boolean boolean_other_throw)69   private static int $noinline$testDifferentFields(
70       Point obj1, Point obj2, boolean boolean_throw, boolean boolean_other_throw) {
71     try {
72       if (boolean_throw) {
73         throw new Error();
74       }
75     } catch (Error e) {
76       return 0;
77     }
78     obj1.x = 1;
79     obj2.y = 2;
80     int result = obj1.x + obj2.y;
81     try {
82       if (boolean_other_throw) {
83         throw new Error();
84       }
85     } catch (Error e) {
86       return 0;
87     }
88     return result;
89   }
90 
91   /// CHECK-START: int Main.$noinline$testRedundantStore(Point, boolean, boolean) load_store_elimination (before)
92   /// CHECK-DAG:     InstanceFieldSet field_name:Point.y
93   /// CHECK-DAG:     InstanceFieldSet field_name:Point.y
94   /// CHECK-DAG:     InstanceFieldGet field_name:Point.y
95 
96   /// CHECK-START: int Main.$noinline$testRedundantStore(Point, boolean, boolean) load_store_elimination (after)
97   /// CHECK:         InstanceFieldSet field_name:Point.y
98   /// CHECK-NOT:     InstanceFieldSet field_name:Point.y
99 
100   /// CHECK-START: int Main.$noinline$testRedundantStore(Point, boolean, boolean) load_store_elimination (after)
101   /// CHECK-NOT:     InstanceFieldGet field_name:Point.y
102 
103   // Consistency check to make sure the try/catches weren't removed by an earlier pass.
104   /// CHECK-START: int Main.$noinline$testRedundantStore(Point, boolean, boolean) load_store_elimination (after)
105   /// CHECK-DAG:     TryBoundary kind:entry
106   /// CHECK-DAG:     TryBoundary kind:entry
107 
108   // Redundant store of the same value.
$noinline$testRedundantStore( Point obj, boolean boolean_throw, boolean boolean_other_throw)109   private static int $noinline$testRedundantStore(
110       Point obj, boolean boolean_throw, boolean boolean_other_throw) {
111     try {
112       if (boolean_throw) {
113         throw new Error();
114       }
115     } catch (Error e) {
116       return 0;
117     }
118     obj.y = 1;
119     obj.y = 1;
120     try {
121       if (boolean_other_throw) {
122         throw new Error();
123       }
124     } catch (Error e) {
125       return 0;
126     }
127     return obj.y;
128   }
129 
130   /// CHECK-START: int Main.$noinline$testTryCatchBlocking(Point, boolean) load_store_elimination (before)
131   /// CHECK: InstanceFieldSet field_name:Point.y
132   /// CHECK: InstanceFieldGet field_name:Point.y
133 
134   /// CHECK-START: int Main.$noinline$testTryCatchBlocking(Point, boolean) load_store_elimination (after)
135   /// CHECK: InstanceFieldSet field_name:Point.y
136   /// CHECK: InstanceFieldGet field_name:Point.y
137 
138   // Consistency check to make sure the try/catch wasn't removed by an earlier pass.
139   /// CHECK-START: int Main.$noinline$testTryCatchBlocking(Point, boolean) load_store_elimination (after)
140   /// CHECK: TryBoundary kind:entry
141 
142   // We cannot remove the Get since we might have thrown.
$noinline$testTryCatchBlocking(Point obj, boolean boolean_throw)143   private static int $noinline$testTryCatchBlocking(Point obj, boolean boolean_throw) {
144     obj.y = 1;
145     try {
146       if (boolean_throw) {
147         throw new Error();
148       }
149     } catch (Error e) {
150     }
151     return obj.y;
152   }
153 
154   /// CHECK-START: int Main.$noinline$testTryCatchPhi(Point, boolean) load_store_elimination (before)
155   /// CHECK-DAG:     InstanceFieldSet field_name:Point.y
156   /// CHECK-DAG:     InstanceFieldSet field_name:Point.y
157   /// CHECK-DAG:     InstanceFieldGet field_name:Point.y
158 
159   /// CHECK-START: int Main.$noinline$testTryCatchPhi(Point, boolean) load_store_elimination (after)
160   /// CHECK-DAG:     InstanceFieldSet field_name:Point.y
161   /// CHECK-DAG:     InstanceFieldSet field_name:Point.y
162 
163   /// CHECK-START: int Main.$noinline$testTryCatchPhi(Point, boolean) load_store_elimination (after)
164   /// CHECK-NOT:     InstanceFieldGet field_name:Point.y
165 
166   // Consistency check to make sure the try/catch wasn't removed by an earlier pass.
167   /// CHECK-START: int Main.$noinline$testTryCatchPhi(Point, boolean) load_store_elimination (after)
168   /// CHECK-DAG:     TryBoundary kind:entry
169 
170   // We either threw and we set the value in the catch, or we didn't throw and we set the value
171   // before the catch. We can solve that with a Phi and skip the get.
$noinline$testTryCatchPhi(Point obj, boolean boolean_throw)172   private static int $noinline$testTryCatchPhi(Point obj, boolean boolean_throw) {
173     obj.y = 1;
174     try {
175       if (boolean_throw) {
176         throw new Error();
177       }
178     } catch (Error e) {
179       obj.y = 2;
180     }
181     return obj.y;
182   }
183 
184 
185   /// CHECK-START: int Main.$noinline$testTryCatchPhiWithTwoCatches(Point, int[]) load_store_elimination (before)
186   /// CHECK-DAG:     InstanceFieldSet field_name:Point.y
187   /// CHECK-DAG:     InstanceFieldSet field_name:Point.y
188   /// CHECK-DAG:     InstanceFieldSet field_name:Point.y
189   /// CHECK-DAG:     InstanceFieldGet field_name:Point.y
190 
191   /// CHECK-START: int Main.$noinline$testTryCatchPhiWithTwoCatches(Point, int[]) load_store_elimination (after)
192   /// CHECK-DAG:     InstanceFieldSet field_name:Point.y
193   /// CHECK-DAG:     InstanceFieldSet field_name:Point.y
194   /// CHECK-DAG:     InstanceFieldSet field_name:Point.y
195 
196   /// CHECK-START: int Main.$noinline$testTryCatchPhiWithTwoCatches(Point, int[]) load_store_elimination (after)
197   /// CHECK-NOT: InstanceFieldGet field_name:Point.y
198 
199   // Consistency check to make sure the try/catch wasn't removed by an earlier pass.
200   /// CHECK-START: int Main.$noinline$testTryCatchPhiWithTwoCatches(Point, int[]) load_store_elimination (after)
201   /// CHECK-DAG: TryBoundary kind:entry
$noinline$testTryCatchPhiWithTwoCatches(Point obj, int[] numbers)202   private static int $noinline$testTryCatchPhiWithTwoCatches(Point obj, int[] numbers) {
203     obj.y = 1;
204     try {
205       if (numbers[0] == 1) {
206         throw new Error();
207       }
208     } catch (ArrayIndexOutOfBoundsException e) {
209       obj.y = 2;
210     } catch (Error e) {
211       obj.y = 3;
212     }
213     return obj.y;
214   }
215 
216   // Check that we don't eliminate the first store to `main.sumForKeepStoreInsideTryCatch` since it
217   // is observable.
218 
219   // Consistency check to make sure the try/catch wasn't removed by an earlier pass.
220   /// CHECK-START: int Main.$noinline$testKeepStoreInsideTry() load_store_elimination (after)
221   /// CHECK-DAG: TryBoundary kind:entry
222 
223   /// CHECK-START: int Main.$noinline$testKeepStoreInsideTry() load_store_elimination (before)
224   /// CHECK:     InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
225   /// CHECK:     InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
226 
227   /// CHECK-START: int Main.$noinline$testKeepStoreInsideTry() load_store_elimination (after)
228   /// CHECK:     InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
229   /// CHECK:     InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
$noinline$testKeepStoreInsideTry()230   private static int $noinline$testKeepStoreInsideTry() {
231     Main main = new Main();
232     main.sumForKeepStoreInsideTryCatch = 0;
233     try {
234       int[] array = {1};
235       main.sumForKeepStoreInsideTryCatch += array[0];
236       main.sumForKeepStoreInsideTryCatch += array[1];
237       throw new RuntimeException("Unreachable");
238     } catch (ArrayIndexOutOfBoundsException e) {
239       return main.sumForKeepStoreInsideTryCatch;
240     }
241   }
242 
$noinline$returnValue(int value)243   private static int $noinline$returnValue(int value) {
244     return value;
245   }
246 
247   /// CHECK-START: int Main.$noinline$testDontKeepStoreInsideCatch(int[]) load_store_elimination (before)
248   /// CHECK:     InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
249   /// CHECK:     InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
250   /// CHECK-NOT: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
251 
252   /// CHECK-START: int Main.$noinline$testDontKeepStoreInsideCatch(int[]) load_store_elimination (after)
253   /// CHECK-NOT: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
$noinline$testDontKeepStoreInsideCatch(int[] array)254   private static int $noinline$testDontKeepStoreInsideCatch(int[] array) {
255     Main main = new Main();
256     int value = 0;
257     try {
258       value = array[0];
259     } catch (Exception e) {
260       // These sets can be eliminated even though we have invokes since this catch is not part of an
261       // outer try.
262       main.sumForKeepStoreInsideTryCatch += $noinline$returnValue(10);
263       main.sumForKeepStoreInsideTryCatch += $noinline$returnValue(20);
264     }
265     return main.sumForKeepStoreInsideTryCatch + value;
266   }
267 
268   /// CHECK-START: int Main.$noinline$testKeepStoreInsideCatchWithOuterTry(int[]) load_store_elimination (before)
269   /// CHECK:     InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
270   /// CHECK:     InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
271   /// CHECK-NOT: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
272 
273   /// CHECK-START: int Main.$noinline$testKeepStoreInsideCatchWithOuterTry(int[]) load_store_elimination (after)
274   /// CHECK:     InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
275   /// CHECK:     InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
276   /// CHECK-NOT: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
$noinline$testKeepStoreInsideCatchWithOuterTry(int[] array)277   private static int $noinline$testKeepStoreInsideCatchWithOuterTry(int[] array) {
278     Main main = new Main();
279     int value = 0;
280     try {
281       try {
282         value = array[0];
283       } catch (Exception e) {
284         // These sets can't be eliminated since this catch is part of a outer try.
285         main.sumForKeepStoreInsideTryCatch += $noinline$returnValue(10);
286         main.sumForKeepStoreInsideTryCatch += $noinline$returnValue(20);
287       }
288     } catch (Exception e) {
289       value = 100000;
290     }
291 
292     return main.sumForKeepStoreInsideTryCatch + value;
293   }
294 
295   // Note that there are four `InstanceFieldSet` instead of two since we split the `finally` block
296   // into the normal path, and the exceptional path.
297 
298   /// CHECK-START: int Main.$noinline$testDontKeepStoreInsideFinally(int[]) load_store_elimination (before)
299   /// CHECK:     InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
300   /// CHECK:     InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
301   /// CHECK:     InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
302   /// CHECK:     InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
303   /// CHECK-NOT: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
304 
305   /// CHECK-START: int Main.$noinline$testDontKeepStoreInsideFinally(int[]) load_store_elimination (after)
306   /// CHECK-NOT: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
$noinline$testDontKeepStoreInsideFinally(int[] array)307   private static int $noinline$testDontKeepStoreInsideFinally(int[] array) {
308     Main main = new Main();
309     int value = 0;
310     try {
311       value = array[0];
312     } finally {
313       // These sets can be eliminated even though we have invokes since this catch is not part of an
314       // outer try.
315       main.sumForKeepStoreInsideTryCatch += $noinline$returnValue(10);
316       main.sumForKeepStoreInsideTryCatch += $noinline$returnValue(20);
317     }
318     return main.sumForKeepStoreInsideTryCatch + value;
319   }
320 
321   // Checks that we are able to do LSE inside of catches which are outside of try blocks.
322 
323   /// CHECK-START: int Main.$noinline$testDontKeepStoreInsideOuterCatch(int[]) load_store_elimination (before)
324   /// CHECK:     InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
325   /// CHECK:     InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
326   /// CHECK-NOT: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
327 
328   // This store potentially can be eliminated too, but our phi creation logic doesn't realize it can
329   // create a Phi for `main.sumForKeepStoreInsideTryCatch` and skip a store+load.
330 
331   /// CHECK-START: int Main.$noinline$testDontKeepStoreInsideOuterCatch(int[]) load_store_elimination (after)
332   /// CHECK:     InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
333   /// CHECK-NOT: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
334 
$noinline$testDontKeepStoreInsideOuterCatch(int[] array)335   private static int $noinline$testDontKeepStoreInsideOuterCatch(int[] array) {
336     Main main = new Main();
337     int value = 0;
338     try {
339       value = array[0];
340     } catch (ArrayIndexOutOfBoundsException expected) {
341       // These sets and gets are not considered to be part of a try so they are free to be
342       // eliminated.
343       main.sumForKeepStoreInsideTryCatch += $noinline$returnValue(10);
344       main.sumForKeepStoreInsideTryCatch += $noinline$returnValue(20);
345       try {
346         value = array[0];
347       } catch (ArrayIndexOutOfBoundsException expectedToo) {
348         value = 100000;
349       }
350     }
351 
352     return main.sumForKeepStoreInsideTryCatch + value;
353   }
354 
355   /// CHECK-START: int Main.$noinline$test40() load_store_elimination (before)
356   /// CHECK:                     ArraySet
357   /// CHECK:                     DivZeroCheck
358   /// CHECK:                     ArraySet
359   /// CHECK:                     ArraySet
360   //
361   /// CHECK:                     ArraySet
362   /// CHECK:                     ArraySet
363   /// CHECK:                     DivZeroCheck
364   /// CHECK:                     ArraySet
365   //
366   /// CHECK:                     ArraySet
367   /// CHECK:                     ArraySet
368   /// CHECK:                     ArraySet
369   /// CHECK-NOT:                 ArraySet
370 
371   /// CHECK-START: int Main.$noinline$test40() load_store_elimination (after)
372   /// CHECK:                     ArraySet
373   /// CHECK:                     DivZeroCheck
374   /// CHECK:                     ArraySet
375   //
376   /// CHECK:                     ArraySet
377   /// CHECK:                     DivZeroCheck
378   /// CHECK:                     ArraySet
379   //
380   /// CHECK-NOT:                 ArraySet
381 
382   // Like `test40` from 530-checker-lse but with $inline$ for the inner method so we check that we
383   // have the array set inside try catches too.
384   // Since we are inlining, we know the parameters and are able to elimnate (some) of the
385   // `ArraySet`s.
$noinline$test40()386   private static int $noinline$test40() {
387     int[] array = new int[1];
388     try {
389       $inline$fillArrayTest40(array, 100, 0);
390       System.out.println("UNREACHABLE");
391     } catch (Throwable expected) {
392     }
393     assertEquals(1, array[0]);
394     try {
395       $inline$fillArrayTest40(array, 100, 1);
396       System.out.println("UNREACHABLE");
397     } catch (Throwable expected) {
398     }
399     assertEquals(2, array[0]);
400     $inline$fillArrayTest40(array, 100, 2);
401     assertEquals(150, array[0]);
402     return array[0];
403   }
404 
405   /// CHECK-START: void Main.$inline$fillArrayTest40(int[], int, int) load_store_elimination (before)
406   /// CHECK:                     ArraySet
407   /// CHECK:                     DivZeroCheck
408   /// CHECK:                     ArraySet
409   /// CHECK:                     DivZeroCheck
410   /// CHECK:                     ArraySet
411   /// CHECK-NOT:                 ArraySet
412 
413   /// CHECK-START: void Main.$inline$fillArrayTest40(int[], int, int) load_store_elimination (after)
414   /// CHECK:                     ArraySet
415   /// CHECK:                     DivZeroCheck
416   /// CHECK:                     ArraySet
417   /// CHECK:                     DivZeroCheck
418   /// CHECK:                     ArraySet
419   /// CHECK-NOT:                 ArraySet
420 
421   // Check that the stores to array[0] are not eliminated since we can throw in between the stores.
$inline$fillArrayTest40(int[] array, int a, int b)422   private static void $inline$fillArrayTest40(int[] array, int a, int b) {
423     array[0] = 1;
424     int x = a / b;
425     array[0] = 2;
426     int y = a / (b - 1);
427     array[0] = x + y;
428   }
429 
assertEquals(int expected, int actual)430   private static void assertEquals(int expected, int actual) {
431     if (expected != actual) {
432       throw new AssertionError("Expected: " + expected + ", Actual: " + actual);
433     }
434   }
435 
436   int sumForKeepStoreInsideTryCatch;
437 }
438