• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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. It does so in `after_gvn` since it
162   // requires the VisitIf optimization which happens later in the graph.
163 
164   /// CHECK-START: int Main.$noinline$testOptimizeAfterOneBranchDisappears(int, boolean) dead_code_elimination$after_gvn (before)
165   /// CHECK:     Throw
166 
167   /// CHECK-START: int Main.$noinline$testOptimizeAfterOneBranchDisappears(int, boolean) dead_code_elimination$after_gvn (before)
168   /// CHECK:     TryBoundary
169   /// CHECK:     TryBoundary
170   /// CHECK:     TryBoundary
171   /// CHECK:     TryBoundary
172   /// CHECK-NOT: TryBoundary
173 
174   /// CHECK-START: int Main.$noinline$testOptimizeAfterOneBranchDisappears(int, boolean) dead_code_elimination$after_gvn (before)
175   /// CHECK:     flags "catch_block"
176   /// CHECK-NOT: flags "catch_block"
177 
178   /// CHECK-START: int Main.$noinline$testOptimizeAfterOneBranchDisappears(int, boolean) dead_code_elimination$after_gvn (after)
179   /// CHECK-NOT: Throw
180 
181   /// CHECK-START: int Main.$noinline$testOptimizeAfterOneBranchDisappears(int, boolean) dead_code_elimination$after_gvn (after)
182   /// CHECK-NOT: TryBoundary
183 
184   /// CHECK-START: int Main.$noinline$testOptimizeAfterOneBranchDisappears(int, boolean) dead_code_elimination$after_gvn (after)
185   /// CHECK-NOT: flags "catch_block"
$noinline$testOptimizeAfterOneBranchDisappears(int a, boolean val)186   public static int $noinline$testOptimizeAfterOneBranchDisappears(int a, boolean val) {
187     try {
188       for (int i = 0; i < 4; i++) {
189         // TryBoundary kind:entry
190         a /= 3;
191         // TryBoundary kind:exit
192       }
193 
194       if (val && !val) {
195         // TryBoundary kind:entry
196         throw new Error();
197         // TryBoundary kind:exit
198       }
199     } catch (Error e) {
200       return -1000;
201     }
202     return a;
203   }
204 
205   /// CHECK-START: int Main.$noinline$testRemoveTryBoundaryNested(int) dead_code_elimination$initial (before)
206   /// CHECK:     TryBoundary
207   /// CHECK:     TryBoundary
208   /// CHECK:     TryBoundary
209   /// CHECK:     TryBoundary
210   /// CHECK-NOT: TryBoundary
211 
212   /// CHECK-START: int Main.$noinline$testRemoveTryBoundaryNested(int) dead_code_elimination$initial (before)
213   /// CHECK:     flags "catch_block"
214   /// CHECK:     flags "catch_block"
215   /// CHECK-NOT: flags "catch_block"
216 
217   /// CHECK-START: int Main.$noinline$testRemoveTryBoundaryNested(int) dead_code_elimination$initial (after)
218   /// CHECK-NOT: TryBoundary
219 
220   /// CHECK-START: int Main.$noinline$testRemoveTryBoundaryNested(int) dead_code_elimination$initial (after)
221   /// CHECK-NOT: flags "catch_block"
$noinline$testRemoveTryBoundaryNested(int a)222   public static int $noinline$testRemoveTryBoundaryNested(int a) {
223     try {
224       // TryBoundary kind:entry
225       a /= 2;
226       // TryBoundary kind:exit
227       try {
228         // TryBoundary kind:entry
229         a /= 3;
230         // TryBoundary kind:exit
231       } catch (Error e) {
232         return -2000;
233       }
234     } catch (Exception e) {
235       return -1000;
236     }
237     return a;
238   }
239 
240   // We can remove the `TryBoundary` instructions surrounding `a /= 2;` but since the inner try can
241   // throw, we must keep both the inner and outer catches as they are catch handlers of the inner
242   // try.
243 
244   /// CHECK-START: int Main.$noinline$testRemoveTryBoundaryNestedButNotCatch(int, boolean) dead_code_elimination$initial (before)
245   /// CHECK:     TryBoundary
246   /// CHECK:     TryBoundary
247   /// CHECK:     TryBoundary
248   /// CHECK:     TryBoundary
249   /// CHECK-NOT: TryBoundary
250 
251   /// CHECK-START: int Main.$noinline$testRemoveTryBoundaryNestedButNotCatch(int, boolean) dead_code_elimination$initial (before)
252   /// CHECK:     flags "catch_block"
253   /// CHECK:     flags "catch_block"
254   /// CHECK-NOT: flags "catch_block"
255 
256   /// CHECK-START: int Main.$noinline$testRemoveTryBoundaryNestedButNotCatch(int, boolean) dead_code_elimination$initial (after)
257   /// CHECK:     TryBoundary
258   /// CHECK:     TryBoundary
259   /// CHECK-NOT: TryBoundary
260 
261   /// CHECK-START: int Main.$noinline$testRemoveTryBoundaryNestedButNotCatch(int, boolean) dead_code_elimination$initial (after)
262   /// CHECK:     flags "catch_block"
263   /// CHECK:     flags "catch_block"
264   /// CHECK-NOT: flags "catch_block"
$noinline$testRemoveTryBoundaryNestedButNotCatch(int a, boolean val)265   public static int $noinline$testRemoveTryBoundaryNestedButNotCatch(int a, boolean val) {
266     try {
267       // TryBoundary kind:entry
268       a /= 2;
269       // TryBoundary kind:exit
270       try {
271         if (val) {
272           // TryBoundary kind:entry
273           throw new Error();
274           // TryBoundary kind:exit
275         }
276         // TryBoundary kind:exit
277       } catch (Error e) {
278         return -2000;
279       }
280     } catch (Exception e) {
281       return -1000;
282     }
283     return a;
284   }
285 
286   // We eliminate the return -1000 catch block which is outside of the loop in
287   // dead_code_elimination$initial. We can do so since we eliminated the TryBoundary of `a /= 2;`.
288 
289   /// CHECK-START: int Main.$noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(int, boolean) dead_code_elimination$initial (before)
290   /// CHECK:     TryBoundary
291   /// CHECK:     TryBoundary
292   /// CHECK:     TryBoundary
293   /// CHECK:     TryBoundary
294   /// CHECK-NOT: TryBoundary
295 
296   /// CHECK-START: int Main.$noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(int, boolean) dead_code_elimination$initial (before)
297   /// CHECK:     flags "catch_block"
298   /// CHECK:     flags "catch_block"
299   /// CHECK:     flags "catch_block"
300   /// CHECK-NOT: flags "catch_block"
301 
302   /// CHECK-START: int Main.$noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(int, boolean) dead_code_elimination$initial (before)
303   /// CHECK:     IntConstant -1000
304 
305   /// CHECK-START: int Main.$noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(int, boolean) dead_code_elimination$initial (after)
306   /// CHECK:     TryBoundary
307   /// CHECK:     TryBoundary
308   /// CHECK-NOT: TryBoundary
309 
310   /// CHECK-START: int Main.$noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(int, boolean) dead_code_elimination$initial (after)
311   /// CHECK:     flags "catch_block"
312   /// CHECK:     flags "catch_block"
313   /// CHECK-NOT: flags "catch_block"
314 
315   /// CHECK-START: int Main.$noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(int, boolean) dead_code_elimination$initial (after)
316   /// CHECK-NOT: IntConstant -1000
317 
318   // When removing that block, we are removing a block outside of a loop but we still need to update
319   // the loop information in the graph since we removed TryBoundary instructions inside of a loop
320   // and now `a /= 2;` is not considered part of a loop (Cannot throw so it will not `continue` and
321   // will always return).
322 
323   /// CHECK-START: int Main.$noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(int, boolean) dead_code_elimination$initial (before)
324   /// CHECK:     Div loop:B2
325 
326   /// CHECK-START: int Main.$noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(int, boolean) dead_code_elimination$initial (after)
327   /// CHECK-NOT:  Div loop:B2
328 
329   /// CHECK-START: int Main.$noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(int, boolean) dead_code_elimination$initial (after)
330   /// CHECK:      Div
331   /// CHECK-NOT:  Div
$noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop( int a, boolean val)332   public static int $noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(
333           int a, boolean val) {
334     try {
335       for (int i = 0; i < 4; ++i) {
336         try {
337           try {
338             if (val) {
339               // TryBoundary kind:entry
340               throw new Error();
341               // TryBoundary kind:exit
342             }
343             // TryBoundary kind:exit
344           } catch (Exception e) {
345               continue;
346           }
347           // TryBoundary kind:entry
348           a /= 2;
349           // TryBoundary kind:exit
350           return a;
351         } catch (Error e) {
352           continue;
353         }
354       }
355     } catch (Exception e) {
356       return -1000;
357     }
358     return a;
359   }
360 }
361