xref: /aosp_15_r20/art/test/2244-checker-remove-try-boundary/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 public class Main {
main(String[] args)18   public static void main(String[] args) throws Exception {
19     assertEquals(2, $noinline$testDivideOverTen(20));
20     assertEquals(-2, $noinline$testDivideOverTen(-20));
21     assertEquals(0, $noinline$testSimpleDivisionInLoop(0));
22     assertEquals(1, $noinline$testSimpleDivisionInLoop(81));
23     assertEquals(10, $noinline$testOptimizeSeparateBranches(60, true));
24     assertEquals(10, $noinline$testOptimizeSeparateBranches(80, false));
25     assertEquals(1, $noinline$testDoNotOptimizeOneBranchThrows(81, false));
26     assertEquals(-1000, $noinline$testDoNotOptimizeOneBranchThrows(81, true));
27     assertEquals(1, $noinline$testOptimizeAfterOneBranchDisappears(81, false));
28     assertEquals(10, $noinline$testRemoveTryBoundaryNested(60));
29     assertEquals(-2000, $noinline$testRemoveTryBoundaryNestedButNotCatch(60, true));
30     assertEquals(30, $noinline$testRemoveTryBoundaryNestedButNotCatch(60, false));
31     assertEquals(30, $noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(60, false));
32   }
33 
assertEquals(int expected, int result)34   public static void assertEquals(int expected, int result) {
35     if (expected != result) {
36       throw new Error("Expected: " + expected + ", found: " + result);
37     }
38   }
39 
40   // Check that this version cannot remove the TryBoundary instructions since we may throw.
41 
42   /// CHECK-START: int Main.$inline$division(int, int) register (after)
43   /// CHECK:     TryBoundary
44   /// CHECK:     TryBoundary
45   /// CHECK-NOT: TryBoundary
46 
47   /// CHECK-START: int Main.$inline$division(int, int) register (after)
48   /// CHECK:     flags "catch_block"
$inline$division(int a, int b)49   private static int $inline$division(int a, int b) {
50     try {
51       return a / b;
52     } catch (Error unexpected) {
53       return -1000;
54     }
55   }
56 
57   // Check that we can remove the TryBoundary afer inlining since we know we can't throw.
58 
59   /// CHECK-START: int Main.$noinline$testDivideOverTen(int) inliner (after)
60   /// CHECK-NOT: TryBoundary
61 
62   /// CHECK-START: int Main.$noinline$testDivideOverTen(int) inliner (after)
63   /// CHECK-NOT: flags "catch_block"
$noinline$testDivideOverTen(int a)64   private static int $noinline$testDivideOverTen(int a) {
65     return $inline$division(a, 10);
66   }
67 
68   /// CHECK-START: int Main.$noinline$testSimpleDivisionInLoop(int) dead_code_elimination$initial (before)
69   /// CHECK:     TryBoundary
70   /// CHECK:     TryBoundary
71   /// CHECK-NOT: TryBoundary
72 
73   /// CHECK-START: int Main.$noinline$testSimpleDivisionInLoop(int) dead_code_elimination$initial (before)
74   /// CHECK:     flags "catch_block"
75 
76   /// CHECK-START: int Main.$noinline$testSimpleDivisionInLoop(int) dead_code_elimination$initial (after)
77   /// CHECK-NOT: TryBoundary
78 
79   /// CHECK-START: int Main.$noinline$testSimpleDivisionInLoop(int) dead_code_elimination$initial (after)
80   /// CHECK-NOT: flags "catch_block"
$noinline$testSimpleDivisionInLoop(int a)81   private static int $noinline$testSimpleDivisionInLoop(int a) {
82     try {
83       for (int i = 0; i < 4; i++) {
84         a /= 3;
85       }
86     } catch (Error unexpected) {
87       return -1000;
88     }
89     return a;
90   }
91 
92   // Even though the `TryBoundary`s are split, we can remove them as nothing in the try can throw.
93 
94   /// CHECK-START: int Main.$noinline$testOptimizeSeparateBranches(int, boolean) dead_code_elimination$initial (before)
95   /// CHECK:     TryBoundary
96   /// CHECK:     TryBoundary
97   /// CHECK:     TryBoundary
98   /// CHECK-NOT: TryBoundary
99 
100   /// CHECK-START: int Main.$noinline$testOptimizeSeparateBranches(int, boolean) dead_code_elimination$initial (before)
101   /// CHECK:     flags "catch_block"
102   /// CHECK-NOT: flags "catch_block"
103 
104   /// CHECK-START: int Main.$noinline$testOptimizeSeparateBranches(int, boolean) dead_code_elimination$initial (after)
105   /// CHECK-NOT: TryBoundary
106 
107   /// CHECK-START: int Main.$noinline$testOptimizeSeparateBranches(int, boolean) dead_code_elimination$initial (after)
108   /// CHECK-NOT: flags "catch_block"
$noinline$testOptimizeSeparateBranches(int a, boolean val)109   private static int $noinline$testOptimizeSeparateBranches(int a, boolean val) {
110     try {
111       if (val) {
112         // TryBoundary kind:entry
113         a /= 3;
114       } else {
115         // TryBoundary kind:entry
116         a /= 4;
117       }
118       a /= 2;
119       // TryBoundary kind:exit
120     } catch (Error unexpected) {
121       return -1000;
122     }
123     return a;
124   }
125 
126   // Even though the `a /= 3;` can't throw, we don't eliminate any `TryBoundary` instructions. This
127   // is because we have the `throw new Error();` in the try as well. We could potentially support
128   // removing some `TryBoundary` instructions and not all in the try, but this would complicate the
129   // code and wouldn't bring code size reductions since we would be unable to remove the catch
130   // block.
131 
132   /// CHECK-START: int Main.$noinline$testDoNotOptimizeOneBranchThrows(int, boolean) register (after)
133   /// CHECK:     TryBoundary
134   /// CHECK:     TryBoundary
135   /// CHECK:     TryBoundary
136   /// CHECK:     TryBoundary
137   /// CHECK-NOT: TryBoundary
138 
139   /// CHECK-START: int Main.$noinline$testDoNotOptimizeOneBranchThrows(int, boolean) register (after)
140   /// CHECK:     flags "catch_block"
$noinline$testDoNotOptimizeOneBranchThrows(int a, boolean val)141   public static int $noinline$testDoNotOptimizeOneBranchThrows(int a, boolean val) {
142     try {
143       for (int i = 0; i < 4; i++) {
144         // TryBoundary kind:entry
145         a /= 3;
146         // TryBoundary kind:exit
147       }
148 
149       if (val) {
150         // TryBoundary kind:entry
151         throw new Error();
152         // TryBoundary kind:exit
153       }
154     } catch (Error e) {
155       return -1000;
156     }
157     return a;
158   }
159 
160   // The throw gets eliminated by `SimplifyIfs` in DCE, so we can detect that nothing can throw in
161   // the graph and eliminate the `TryBoundary` instructions.
162 
163   /// CHECK-START: int Main.$noinline$testOptimizeAfterOneBranchDisappears(int, boolean) dead_code_elimination$initial (before)
164   /// CHECK:     Throw
165 
166   /// CHECK-START: int Main.$noinline$testOptimizeAfterOneBranchDisappears(int, boolean) dead_code_elimination$initial (before)
167   /// CHECK:     TryBoundary
168   /// CHECK:     TryBoundary
169   /// CHECK:     TryBoundary
170   /// CHECK:     TryBoundary
171   /// CHECK-NOT: TryBoundary
172 
173   /// CHECK-START: int Main.$noinline$testOptimizeAfterOneBranchDisappears(int, boolean) dead_code_elimination$initial (before)
174   /// CHECK:     flags "catch_block"
175   /// CHECK-NOT: flags "catch_block"
176 
177   /// CHECK-START: int Main.$noinline$testOptimizeAfterOneBranchDisappears(int, boolean) dead_code_elimination$initial (after)
178   /// CHECK-NOT: Throw
179 
180   /// CHECK-START: int Main.$noinline$testOptimizeAfterOneBranchDisappears(int, boolean) dead_code_elimination$initial (after)
181   /// CHECK-NOT: TryBoundary
182 
183   /// CHECK-START: int Main.$noinline$testOptimizeAfterOneBranchDisappears(int, boolean) dead_code_elimination$initial (after)
184   /// CHECK-NOT: flags "catch_block"
$noinline$testOptimizeAfterOneBranchDisappears(int a, boolean val)185   public static int $noinline$testOptimizeAfterOneBranchDisappears(int a, boolean val) {
186     try {
187       for (int i = 0; i < 4; i++) {
188         // TryBoundary kind:entry
189         a /= 3;
190         // TryBoundary kind:exit
191       }
192 
193       if (val && !val) {
194         // TryBoundary kind:entry
195         throw new Error();
196         // TryBoundary kind:exit
197       }
198     } catch (Error e) {
199       return -1000;
200     }
201     return a;
202   }
203 
204   /// CHECK-START: int Main.$noinline$testRemoveTryBoundaryNested(int) dead_code_elimination$initial (before)
205   /// CHECK:     TryBoundary
206   /// CHECK:     TryBoundary
207   /// CHECK:     TryBoundary
208   /// CHECK:     TryBoundary
209   /// CHECK-NOT: TryBoundary
210 
211   /// CHECK-START: int Main.$noinline$testRemoveTryBoundaryNested(int) dead_code_elimination$initial (before)
212   /// CHECK:     flags "catch_block"
213   /// CHECK:     flags "catch_block"
214   /// CHECK-NOT: flags "catch_block"
215 
216   /// CHECK-START: int Main.$noinline$testRemoveTryBoundaryNested(int) dead_code_elimination$initial (after)
217   /// CHECK-NOT: TryBoundary
218 
219   /// CHECK-START: int Main.$noinline$testRemoveTryBoundaryNested(int) dead_code_elimination$initial (after)
220   /// CHECK-NOT: flags "catch_block"
$noinline$testRemoveTryBoundaryNested(int a)221   public static int $noinline$testRemoveTryBoundaryNested(int a) {
222     try {
223       // TryBoundary kind:entry
224       a /= 2;
225       // TryBoundary kind:exit
226       try {
227         // TryBoundary kind:entry
228         a /= 3;
229         // TryBoundary kind:exit
230       } catch (Error e) {
231         return -2000;
232       }
233     } catch (Exception e) {
234       return -1000;
235     }
236     return a;
237   }
238 
239   // We can remove the `TryBoundary` instructions surrounding `a /= 2;` but since the inner try can
240   // throw, we must keep both the inner and outer catches as they are catch handlers of the inner
241   // try.
242 
243   /// CHECK-START: int Main.$noinline$testRemoveTryBoundaryNestedButNotCatch(int, boolean) dead_code_elimination$initial (before)
244   /// CHECK:     TryBoundary
245   /// CHECK:     TryBoundary
246   /// CHECK:     TryBoundary
247   /// CHECK:     TryBoundary
248   /// CHECK-NOT: TryBoundary
249 
250   /// CHECK-START: int Main.$noinline$testRemoveTryBoundaryNestedButNotCatch(int, boolean) dead_code_elimination$initial (before)
251   /// CHECK:     flags "catch_block"
252   /// CHECK:     flags "catch_block"
253   /// CHECK-NOT: flags "catch_block"
254 
255   /// CHECK-START: int Main.$noinline$testRemoveTryBoundaryNestedButNotCatch(int, boolean) dead_code_elimination$initial (after)
256   /// CHECK:     TryBoundary
257   /// CHECK:     TryBoundary
258   /// CHECK-NOT: TryBoundary
259 
260   /// CHECK-START: int Main.$noinline$testRemoveTryBoundaryNestedButNotCatch(int, boolean) dead_code_elimination$initial (after)
261   /// CHECK:     flags "catch_block"
262   /// CHECK:     flags "catch_block"
263   /// CHECK-NOT: flags "catch_block"
$noinline$testRemoveTryBoundaryNestedButNotCatch(int a, boolean val)264   public static int $noinline$testRemoveTryBoundaryNestedButNotCatch(int a, boolean val) {
265     try {
266       // TryBoundary kind:entry
267       a /= 2;
268       // TryBoundary kind:exit
269       try {
270         if (val) {
271           // TryBoundary kind:entry
272           throw new Error();
273           // TryBoundary kind:exit
274         }
275         // TryBoundary kind:exit
276       } catch (Error e) {
277         return -2000;
278       }
279     } catch (Exception e) {
280       return -1000;
281     }
282     return a;
283   }
284 
285   // We eliminate the return -1000 catch block which is outside of the loop in
286   // dead_code_elimination$initial. We can do so since we eliminated the TryBoundary of `a /= 2;`.
287 
288   /// CHECK-START: int Main.$noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(int, boolean) dead_code_elimination$initial (before)
289   /// CHECK:     TryBoundary
290   /// CHECK:     TryBoundary
291   /// CHECK:     TryBoundary
292   /// CHECK:     TryBoundary
293   /// CHECK-NOT: TryBoundary
294 
295   /// CHECK-START: int Main.$noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(int, boolean) dead_code_elimination$initial (before)
296   /// CHECK:     flags "catch_block"
297   /// CHECK:     flags "catch_block"
298   /// CHECK:     flags "catch_block"
299   /// CHECK-NOT: flags "catch_block"
300 
301   /// CHECK-START: int Main.$noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(int, boolean) dead_code_elimination$initial (before)
302   /// CHECK:     IntConstant -1000
303 
304   /// CHECK-START: int Main.$noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(int, boolean) dead_code_elimination$initial (after)
305   /// CHECK:     TryBoundary
306   /// CHECK:     TryBoundary
307   /// CHECK-NOT: TryBoundary
308 
309   /// CHECK-START: int Main.$noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(int, boolean) dead_code_elimination$initial (after)
310   /// CHECK:     flags "catch_block"
311   /// CHECK:     flags "catch_block"
312   /// CHECK-NOT: flags "catch_block"
313 
314   /// CHECK-START: int Main.$noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(int, boolean) dead_code_elimination$initial (after)
315   /// CHECK-NOT: IntConstant -1000
316 
317   // When removing that block, we are removing a block outside of a loop but we still need to update
318   // the loop information in the graph since we removed TryBoundary instructions inside of a loop
319   // and now `a /= 2;` is not considered part of a loop (Cannot throw so it will not `continue` and
320   // will always return).
321 
322   /// CHECK-START: int Main.$noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(int, boolean) dead_code_elimination$initial (before)
323   /// CHECK:     Div loop:B2
324 
325   /// CHECK-START: int Main.$noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(int, boolean) dead_code_elimination$initial (after)
326   /// CHECK-NOT:  Div loop:B2
327 
328   /// CHECK-START: int Main.$noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(int, boolean) dead_code_elimination$initial (after)
329   /// CHECK:      Div
330   /// CHECK-NOT:  Div
$noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop( int a, boolean val)331   public static int $noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(
332           int a, boolean val) {
333     try {
334       for (int i = 0; i < 4; ++i) {
335         try {
336           try {
337             if (val) {
338               // TryBoundary kind:entry
339               throw new Error();
340               // TryBoundary kind:exit
341             }
342             // TryBoundary kind:exit
343           } catch (Exception e) {
344               continue;
345           }
346           // TryBoundary kind:entry
347           a /= 2;
348           // TryBoundary kind:exit
349           return a;
350         } catch (Error e) {
351           continue;
352         }
353       }
354     } catch (Exception e) {
355       return -1000;
356     }
357     return a;
358   }
359 }
360