xref: /aosp_15_r20/art/test/2042-checker-dce-always-throw/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     // Basic test
20     assertEquals(0, $noinline$testSimplifyThrow(1));
21 
22     // Basic test for non-trivial blocks (i.e. not just an invoke and a Goto)
23     assertEquals(0, $noinline$testSimplifyThrowAndPrint(1));
24     assertEquals(0, $noinline$testSimplifyTwoThrows(1));
25     assertEquals(0, $noinline$testSimplifyWithArgument(1));
26 
27     // Try catch tests
28     assertEquals(0, $noinline$testDoNotSimplifyInTry(1));
29     assertEquals(0, $noinline$testSimplifyInCatch(1));
30     assertEquals(0, $noinline$testDoNotSimplifyInCatchInOuterTry(1, 1));
31 
32     // Test that we update the phis correctly after simplifying an always throwing method, and
33     // recomputing dominance.
34     assertEquals(0, $noinline$testUpdatePhisCorrectly(1));
35     assertEquals(0, $noinline$testDeleteAllUsesBeforeDeletingInstruction(1));
36 
37     // SimplifyAlwaysThrows for blocks without a goto at the end
38     assertEquals(0, $noinline$testEndsWithIf(1, 1));
39     assertEquals(0, $noinline$testEndsWithReturn(1));
40     // Since we cannot use `assertEquals`, not throwing would be the success.
41     $noinline$testEndsWithReturnVoid(1);
42     assertEquals(0, $noinline$testEndsWithSwitch(1, 1));
43     assertEquals(0, $noinline$testEndsWithThrow(1));
44     assertEquals(0, $noinline$testEndsWithTryBoundary(1));
45 
46     // SimplifyAlwaysThrows for invokes in catch blocks
47     assertEquals(0, $noinline$testInsideCatch(1));
48   }
49 
alwaysThrows()50   private static void alwaysThrows() throws Error {
51     throw new Error("");
52   }
53 
54   /// CHECK-START: int Main.$noinline$testSimplifyThrow(int) dead_code_elimination$after_inlining (before)
55   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
56   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
57   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<TargetBlock:B\d+>>
58   /// CHECK-EVAL:  "<<ExitBlock>>" != "<<TargetBlock>>"
59 
60   /// CHECK-START: int Main.$noinline$testSimplifyThrow(int) dead_code_elimination$after_inlining (after)
61   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
62   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
63   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<ExitBlock>>
64 
65   // Tests that we simplify the always throwing branch directly to the exit.
$noinline$testSimplifyThrow(int num)66   private static int $noinline$testSimplifyThrow(int num) {
67     if (num == 0) {
68       alwaysThrows();
69     }
70     return 0;
71   }
72 
73   /// CHECK-START: int Main.$noinline$testSimplifyThrowAndPrint(int) dead_code_elimination$after_inlining (before)
74   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
75   /// CHECK-DAG:   InvokeVirtual method_name:java.io.PrintStream.println
76   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
77   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<TargetBlock:B\d+>>
78   /// CHECK-EVAL:  "<<ExitBlock>>" != "<<TargetBlock>>"
79 
80   /// CHECK-START: int Main.$noinline$testSimplifyThrowAndPrint(int) dead_code_elimination$after_inlining (after)
81   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
82   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
83   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<ExitBlock>>
84 
85   /// CHECK-START: int Main.$noinline$testSimplifyThrowAndPrint(int) dead_code_elimination$after_inlining (after)
86   /// CHECK-NOT:   InvokeVirtual method_name:java.io.PrintStream.println
$noinline$testSimplifyThrowAndPrint(int num)87   private static int $noinline$testSimplifyThrowAndPrint(int num) {
88     if (num == 0) {
89       alwaysThrows();
90       System.out.println("I am unrechable!");
91     }
92     return 0;
93   }
94 
95   /// CHECK-START: int Main.$noinline$testSimplifyTwoThrows(int) dead_code_elimination$after_inlining (before)
96   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
97   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock>> method_name:Main.alwaysThrows always_throws:true
98   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
99   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<TargetBlock:B\d+>>
100   /// CHECK-EVAL:  "<<ExitBlock>>" != "<<TargetBlock>>"
101 
102   /// CHECK-START: int Main.$noinline$testSimplifyTwoThrows(int) dead_code_elimination$after_inlining (after)
103   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
104   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
105   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<ExitBlock>>
106 
107   // Check that the second `alwaysThrows` gets removed.
108   /// CHECK-START: int Main.$noinline$testSimplifyTwoThrows(int) dead_code_elimination$after_inlining (after)
109   /// CHECK:       InvokeStaticOrDirect method_name:Main.alwaysThrows always_throws:true
110   /// CHECK-NOT:   InvokeStaticOrDirect method_name:Main.alwaysThrows always_throws:true
111 
112   // Tests that we simplify the always throwing branch directly to the exit, even with blocks that
113   // are not just the throwing instruction and a Goto.
$noinline$testSimplifyTwoThrows(int num)114   private static int $noinline$testSimplifyTwoThrows(int num) {
115     if (num == 0) {
116       alwaysThrows();
117       alwaysThrows();
118     }
119     return 0;
120   }
121 
throwIfZero(int num)122   private static int throwIfZero(int num) {
123     if (num == 0) {
124       throw new Error("num is 0!");
125     }
126     return num / num;
127   }
128 
129   /// CHECK-START: int Main.$noinline$testSimplifyWithArgument(int) dead_code_elimination$after_inlining (before)
130   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.throwIfZero always_throws:true
131   /// CHECK-DAG:   InvokeVirtual method_name:java.io.PrintStream.println
132   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
133   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<TargetBlock:B\d+>>
134   /// CHECK-EVAL:  "<<ExitBlock>>" != "<<TargetBlock>>"
135 
136   /// CHECK-START: int Main.$noinline$testSimplifyWithArgument(int) dead_code_elimination$after_inlining (after)
137   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.throwIfZero always_throws:true
138   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
139   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<ExitBlock>>
140 
141   /// CHECK-START: int Main.$noinline$testSimplifyWithArgument(int) dead_code_elimination$after_inlining (after)
142   /// CHECK-NOT:   InvokeVirtual method_name:java.io.PrintStream.println
$noinline$testSimplifyWithArgument(int num)143   private static int $noinline$testSimplifyWithArgument(int num) {
144     if (num == 0) {
145       throwIfZero(0);
146       System.out.println("I am unrechable!");
147     }
148     return 0;
149   }
150 
151   /// CHECK-START: int Main.$noinline$testSimplifyThrowWithTryCatch(int) dead_code_elimination$after_inlining (before)
152   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
153   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
154   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<TargetBlock:B\d+>>
155   /// CHECK-EVAL:  "<<ExitBlock>>" != "<<TargetBlock>>"
156 
157   /// CHECK-START: int Main.$noinline$testSimplifyThrowWithTryCatch(int) dead_code_elimination$after_inlining (after)
158   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
159   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
160   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<ExitBlock>>
161 
162   // Consistency check to make sure we have the try catches in the graph at this stage.
163   /// CHECK-START: int Main.$noinline$testSimplifyThrowWithTryCatch(int) dead_code_elimination$after_inlining (before)
164   /// CHECK:       TryBoundary kind:entry
165   /// CHECK:       TryBoundary kind:entry
166 
167   // Tests that we simplify the always throwing branch directly to the exit, with non-blocking try
168   // catches in the graph.
$noinline$testSimplifyThrowWithTryCatch(int num)169   private static int $noinline$testSimplifyThrowWithTryCatch(int num) {
170     try {
171       if (num == 123) {
172         throw new Error();
173       }
174     } catch (Error e) {
175       return 123;
176     }
177 
178     if (num == 0) {
179       alwaysThrows();
180     }
181 
182     try {
183       if (num == 456) {
184         throw new Error();
185       }
186     } catch (Error e) {
187       return 456;
188     }
189     return 0;
190   }
191 
$inline$testDoNotSimplifyInner(int num)192   private static void $inline$testDoNotSimplifyInner(int num) {
193     alwaysThrows();
194     while (num == 0) {
195       // We should never hit this since we are always throwing.
196       System.out.println(num);
197     }
198   }
199 
200   /// CHECK-START: int Main.$noinline$testDoNotSimplifyInTry(int) dead_code_elimination$after_inlining (before)
201   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
202   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
203   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<TargetBlock:B\d+>>
204   /// CHECK-EVAL:  "<<ExitBlock>>" != "<<TargetBlock>>"
205 
206   /// CHECK-START: int Main.$noinline$testDoNotSimplifyInTry(int) dead_code_elimination$after_inlining (after)
207   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
208   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
209   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<TargetBlock:B\d+>>
210   /// CHECK-EVAL:  "<<ExitBlock>>" != "<<TargetBlock>>"
211 
212   // Consistency check to make sure we have the try catch in the graph at this stage.
213   /// CHECK-START: int Main.$noinline$testDoNotSimplifyInTry(int) dead_code_elimination$after_inlining (before)
214   /// CHECK:       TryBoundary kind:entry
215 
216   // Consistency check to that we do not simplify it by the last DCE pass either
217   /// CHECK-START: int Main.$noinline$testDoNotSimplifyInTry(int) dead_code_elimination$before_codegen (after)
218   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
219   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
220   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<TargetBlock:B\d+>>
221   /// CHECK-EVAL:  "<<ExitBlock>>" != "<<TargetBlock>>"
222 
223   // Tests that we have the necessary conditions for us to simplify the always throwing instruction
224   // (e.g. InvokeStaticOrDirect followed by a Goto) but we are blocking this due to being in a try.
225   // Changing the Goto here for the exit would be wrong since we want to flow to the catch rather
226   // than the Exit. The preconditions are tricky to do with just one function (since we will have an
227   // invoke followed by a TryBoundary rather than a Goto) but we can do so with the help of the
228   // inliner.
$noinline$testDoNotSimplifyInTry(int num)229   private static int $noinline$testDoNotSimplifyInTry(int num) {
230     try {
231       $inline$testDoNotSimplifyInner(num);
232     } catch (Error e) {
233       return 0;
234     }
235     return 123;
236   }
237 
238   /// CHECK-START: int Main.$noinline$testSimplifyInCatch(int) dead_code_elimination$after_inlining (before)
239   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
240   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
241   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<TargetBlock:B\d+>>
242   /// CHECK-EVAL:  "<<ExitBlock>>" != "<<TargetBlock>>"
243 
244   /// CHECK-START: int Main.$noinline$testSimplifyInCatch(int) dead_code_elimination$after_inlining (after)
245   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
246   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
247   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<ExitBlock>>
248 
249   // Consistency check to make sure we have the try catch in the graph at this stage.
250   /// CHECK-START: int Main.$noinline$testSimplifyInCatch(int) dead_code_elimination$after_inlining (before)
251   /// CHECK:       TryBoundary kind:entry
252 
253   // We are able to simplify the `alwaysThrows` even though we are inside of the catch { ... } since
254   // the if makes it so that we are not the first block of the catch and therefore not in the
255   // "catch_block".
$noinline$testSimplifyInCatch(int num)256   private static int $noinline$testSimplifyInCatch(int num) {
257     try {
258       throw new Error();
259     } catch (Error e) {
260       if (num == 0) {
261         alwaysThrows();
262       }
263       return 0;
264     }
265   }
266 
267   /// CHECK-START: int Main.$noinline$testDoNotSimplifyInCatchInOuterTry(int, int) dead_code_elimination$after_inlining (before)
268   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
269   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
270   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<TargetBlock:B\d+>>
271   /// CHECK-EVAL:  "<<ExitBlock>>" != "<<TargetBlock>>"
272 
273   /// CHECK-START: int Main.$noinline$testDoNotSimplifyInCatchInOuterTry(int, int) dead_code_elimination$after_inlining (after)
274   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
275   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
276   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<TargetBlock:B\d+>>
277   /// CHECK-EVAL:  "<<ExitBlock>>" != "<<TargetBlock>>"
278 
279   // Consistency check to make sure we have the try catches in the graph at this stage.
280   /// CHECK-START: int Main.$noinline$testDoNotSimplifyInCatchInOuterTry(int, int) dead_code_elimination$after_inlining (before)
281   /// CHECK-DAG:   TryBoundary kind:entry
282   /// CHECK-DAG:   TryBoundary kind:entry
283 
284   // Consistency check to that we do not simplify it by the last DCE pass either
285   /// CHECK-START: int Main.$noinline$testDoNotSimplifyInCatchInOuterTry(int, int) dead_code_elimination$before_codegen (after)
286   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
287   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
288   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<TargetBlock:B\d+>>
289   /// CHECK-EVAL:  "<<ExitBlock>>" != "<<TargetBlock>>"
290 
291   // Similar to testSimplifyInCatch, but now the throw is in an outer try and we shouldn't simplify
292   // it. Like in testDoNotSimplifyInTry, we need the help of the inliner to have an invoke followed
293   // by a Goto.
$noinline$testDoNotSimplifyInCatchInOuterTry(int num, int other_num)294   private static int $noinline$testDoNotSimplifyInCatchInOuterTry(int num, int other_num) {
295     try {
296       try {
297         throw new Error();
298       } catch (Error e) {
299         if (num == 0) {
300           // We use `other_num` here because otherwise we propagate the knowledge that `num` equals
301           // zero.
302           $inline$testDoNotSimplifyInner(other_num);
303         }
304         return 0;
305       }
306     } catch (Error e) {
307       return 123;
308     }
309   }
310 
311   // Check that when we perform SimplifyAlwaysThrows, that the phi for `phi_value` exists, and that
312   // we correctly update it after running DCE.
313 
314   /// CHECK-START: int Main.$noinline$testUpdatePhisCorrectly(int) dead_code_elimination$after_inlining (before)
315   /// CHECK-DAG:   <<Const0:i\d+>> IntConstant 0
316   /// CHECK-DAG:   <<Const5:i\d+>> IntConstant 5
317   /// CHECK-DAG:   <<ReturnValue:i\d+>> Phi [<<Const0>>,<<Const5>>]
318   /// CHECK-DAG:   Return [<<ReturnValue>>]
319   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
320   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
321   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<TargetBlock:B\d+>>
322   /// CHECK-EVAL:  "<<ExitBlock>>" != "<<TargetBlock>>"
323 
324   /// CHECK-START: int Main.$noinline$testUpdatePhisCorrectly(int) dead_code_elimination$after_inlining (after)
325   /// CHECK-DAG:   <<Const0:i\d+>> IntConstant 0
326   /// CHECK-DAG:   Return [<<Const0>>]
327   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
328   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
329   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<ExitBlock>>
330 
331   /// CHECK-START: int Main.$noinline$testUpdatePhisCorrectly(int) dead_code_elimination$after_inlining (after)
332   /// CHECK-NOT:   Phi
$noinline$testUpdatePhisCorrectly(int num)333   private static int $noinline$testUpdatePhisCorrectly(int num) {
334     int phi_value = 0;
335     if (num == 0) {
336       alwaysThrows();
337       phi_value = 5;
338     }
339     return phi_value;
340   }
341 
342   // Test to check that we delete all uses before the instruction.
$noinline$foo(int num)343   private static int $noinline$foo(int num) {
344     return num;
345   }
346 
347   /// CHECK-START: int Main.$noinline$testDeleteAllUsesBeforeDeletingInstruction(int) dead_code_elimination$after_inlining (before)
348   /// CHECK-DAG:   <<Const0:i\d+>> IntConstant 0
349   /// CHECK-DAG:   <<Invoke:i\d+>> InvokeStaticOrDirect method_name:Main.$noinline$foo
350   /// CHECK-DAG:   <<ReturnValue:i\d+>> Phi [<<Const0>>,<<Invoke>>]
351   /// CHECK-DAG:   Return [<<ReturnValue>>]
352   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
353   /// CHECK-DAG:   TryBoundary block:<<InvokeBlock>>
354   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
355 
356   /// CHECK-START: int Main.$noinline$testDeleteAllUsesBeforeDeletingInstruction(int) dead_code_elimination$after_inlining (after)
357   /// CHECK-DAG:   <<Const0:i\d+>> IntConstant 0
358   /// CHECK-DAG:   Return [<<Const0>>]
359   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
360   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
361   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<ExitBlock>>
362 
363   /// CHECK-START: int Main.$noinline$testDeleteAllUsesBeforeDeletingInstruction(int) dead_code_elimination$after_inlining (after)
364   /// CHECK-NOT:   Phi
$noinline$testDeleteAllUsesBeforeDeletingInstruction(int num)365   private static int $noinline$testDeleteAllUsesBeforeDeletingInstruction(int num) {
366     int phi_value = 0;
367     if (num == 0) {
368       alwaysThrows();
369       try {
370         phi_value = $noinline$foo(2);
371       } catch (Error e) {
372         throw new Error("We shouldn't hit this");
373       }
374     }
375     return phi_value;
376   }
377 
378   /// CHECK-START: int Main.$noinline$testEndsWithIf(int, int) dead_code_elimination$after_inlining (before)
379   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
380   /// CHECK-DAG:   If block:<<InvokeBlock>>
381   /// CHECK-DAG:   InvokeVirtual method_name:java.io.PrintStream.println
382   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
383 
384   /// CHECK-START: int Main.$noinline$testEndsWithIf(int, int) dead_code_elimination$after_inlining (after)
385   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
386   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
387   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<ExitBlock>>
388 
389   /// CHECK-START: int Main.$noinline$testEndsWithIf(int, int) dead_code_elimination$after_inlining (after)
390   /// CHECK-NOT:   InvokeVirtual method_name:java.io.PrintStream.println
$noinline$testEndsWithIf(int num, int other_num)391   private static int $noinline$testEndsWithIf(int num, int other_num) {
392     if (num == 0) {
393       alwaysThrows();
394       if (other_num == 0) {
395         System.out.println("I am unrechable!");
396       }
397     }
398     return 0;
399   }
400 
401   /// CHECK-START: int Main.$noinline$testEndsWithReturn(int) dead_code_elimination$after_inlining (before)
402   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
403   /// CHECK-DAG:   Return block:<<InvokeBlock>>
404   /// CHECK-DAG:   InvokeVirtual method_name:java.io.PrintStream.println
405   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
406 
407   /// CHECK-START: int Main.$noinline$testEndsWithReturn(int) dead_code_elimination$after_inlining (after)
408   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
409   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
410   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<ExitBlock>>
411 
412   /// CHECK-START: int Main.$noinline$testEndsWithReturn(int) dead_code_elimination$after_inlining (after)
413   /// CHECK-NOT:   InvokeVirtual method_name:java.io.PrintStream.println
$noinline$testEndsWithReturn(int num)414   private static int $noinline$testEndsWithReturn(int num) {
415     if (num == 0) {
416       alwaysThrows();
417       System.out.println("I am unrechable!");
418       return 1;
419     }
420     return 0;
421   }
422 
423   /// CHECK-START: void Main.$noinline$testEndsWithReturnVoid(int) dead_code_elimination$after_inlining (before)
424   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
425   /// CHECK-DAG:   ReturnVoid block:<<InvokeBlock>>
426   /// CHECK-DAG:   InvokeVirtual method_name:java.io.PrintStream.println
427   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
428 
429   /// CHECK-START: void Main.$noinline$testEndsWithReturnVoid(int) dead_code_elimination$after_inlining (after)
430   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
431   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
432   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<ExitBlock>>
433 
434   /// CHECK-START: void Main.$noinline$testEndsWithReturnVoid(int) dead_code_elimination$after_inlining (after)
435   /// CHECK-NOT:   InvokeVirtual method_name:java.io.PrintStream.println
$noinline$testEndsWithReturnVoid(int num)436   private static void $noinline$testEndsWithReturnVoid(int num) {
437     if (num == 0) {
438       alwaysThrows();
439       System.out.println("I am unrechable!");
440       return;
441     }
442     return;
443   }
444 
445   /// CHECK-START: int Main.$noinline$testEndsWithSwitch(int, int) dead_code_elimination$after_inlining (before)
446   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
447   /// CHECK-DAG:   PackedSwitch block:<<InvokeBlock>>
448   /// CHECK-DAG:   InvokeVirtual method_name:java.io.PrintStream.println
449   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
450 
451   /// CHECK-START: int Main.$noinline$testEndsWithSwitch(int, int) dead_code_elimination$after_inlining (after)
452   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
453   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
454   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<ExitBlock>>
455 
456   /// CHECK-START: int Main.$noinline$testEndsWithSwitch(int, int) dead_code_elimination$after_inlining (after)
457   /// CHECK-NOT:   InvokeVirtual method_name:java.io.PrintStream.println
$noinline$testEndsWithSwitch(int num, int other_num)458   private static int $noinline$testEndsWithSwitch(int num, int other_num) {
459     if (num == 0) {
460       alwaysThrows();
461       System.out.println("I am unrechable!");
462       int result = 10;
463       switch (other_num) {
464         case 1:
465           result = 100;
466           break;
467         case 2:
468           result = 300;
469           break;
470         case 3:
471           result = 500;
472           break;
473         case 4:
474           result = 700;
475           break;
476       }
477       return result;
478     }
479     return 0;
480   }
481 
482   /// CHECK-START: int Main.$noinline$testEndsWithThrow(int) dead_code_elimination$after_inlining (before)
483   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
484   /// CHECK-DAG:   Throw block:<<InvokeBlock>>
485   /// CHECK-DAG:   InvokeVirtual method_name:java.io.PrintStream.println
486   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
487 
488   /// CHECK-START: int Main.$noinline$testEndsWithThrow(int) dead_code_elimination$after_inlining (after)
489   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
490   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
491   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<ExitBlock>>
492 
493   /// CHECK-START: int Main.$noinline$testEndsWithThrow(int) dead_code_elimination$after_inlining (after)
494   /// CHECK-NOT:   InvokeVirtual method_name:java.io.PrintStream.println
$noinline$testEndsWithThrow(int num)495   private static int $noinline$testEndsWithThrow(int num) {
496     if (num == 0) {
497       alwaysThrows();
498       System.out.println("I am unrechable!");
499       throw new Error("Other error");
500     }
501     return 0;
502   }
503 
504   /// CHECK-START: int Main.$noinline$testEndsWithTryBoundary(int) dead_code_elimination$after_inlining (before)
505   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
506   /// CHECK-DAG:   TryBoundary block:<<InvokeBlock>>
507   /// CHECK-DAG:   InvokeVirtual method_name:java.io.PrintStream.println
508   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
509 
510   /// CHECK-START: int Main.$noinline$testEndsWithTryBoundary(int) dead_code_elimination$after_inlining (after)
511   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
512   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
513   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<ExitBlock>>
514 
515   /// CHECK-START: int Main.$noinline$testEndsWithTryBoundary(int) dead_code_elimination$after_inlining (after)
516   /// CHECK-NOT:   InvokeVirtual method_name:java.io.PrintStream.println
$noinline$testEndsWithTryBoundary(int num)517   private static int $noinline$testEndsWithTryBoundary(int num) {
518     if (num == 0) {
519       alwaysThrows();
520       System.out.println("I am unrechable!");
521       try {
522         alwaysThrows();
523       } catch (Error e) {
524         return 1;
525       }
526     }
527     return 0;
528   }
529 
530   // Empty method to forbid the try from disappearing
$noinline$emptyMethod()531   private static void $noinline$emptyMethod() {}
532 
533   /// CHECK-START: int Main.$noinline$testInsideCatch(int) dead_code_elimination$after_inlining (before)
534   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
535   /// CHECK-DAG:   Return block:<<InvokeBlock>>
536   /// CHECK-DAG:   InvokeVirtual method_name:java.io.PrintStream.println
537   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
538 
539   /// CHECK-START: int Main.$noinline$testInsideCatch(int) dead_code_elimination$after_inlining (after)
540   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
541   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
542   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<ExitBlock>>
543 
544   /// CHECK-START: int Main.$noinline$testInsideCatch(int) dead_code_elimination$after_inlining (after)
545   /// CHECK-NOT:   InvokeVirtual method_name:java.io.PrintStream.println
$noinline$testInsideCatch(int num)546   private static int $noinline$testInsideCatch(int num) {
547     if (num == 0) {
548       try {
549         $noinline$emptyMethod();
550       } catch (Error e) {
551         alwaysThrows();
552         System.out.println("I am unrechable!");
553         return 1;
554       }
555     }
556     return 0;
557   }
558 
assertEquals(int expected, int actual)559   static void assertEquals(int expected, int actual) {
560     if (expected != actual) {
561       throw new AssertionError("Expected " + expected + " got " + actual);
562     }
563   }
564 }
565