• 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     // 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$testSimplifyTwoThrows(1));
24 
25     // Try catch tests
26     assertEquals(0, $noinline$testDoNotSimplifyInTry(1));
27     assertEquals(0, $noinline$testSimplifyInCatch(1));
28     assertEquals(0, $noinline$testDoNotSimplifyInCatchInOuterTry(1));
29   }
30 
alwaysThrows()31   private static void alwaysThrows() throws Error {
32     throw new Error("");
33   }
34 
35   /// CHECK-START: int Main.$noinline$testSimplifyThrow(int) dead_code_elimination$after_inlining (before)
36   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
37   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
38   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<TargetBlock:B\d+>>
39   /// CHECK-EVAL:  "<<ExitBlock>>" != "<<TargetBlock>>"
40 
41   /// CHECK-START: int Main.$noinline$testSimplifyThrow(int) dead_code_elimination$after_inlining (after)
42   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
43   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
44   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<ExitBlock>>
45 
46   // Tests that we simplify the always throwing branch directly to the exit.
$noinline$testSimplifyThrow(int num)47   private static int $noinline$testSimplifyThrow(int num) {
48     if (num == 0) {
49       alwaysThrows();
50     }
51     return 0;
52   }
53 
54   /// CHECK-START: int Main.$noinline$testSimplifyTwoThrows(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:   InvokeStaticOrDirect block:<<InvokeBlock>> method_name:Main.alwaysThrows always_throws:true
57   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
58   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<TargetBlock:B\d+>>
59   /// CHECK-EVAL:  "<<ExitBlock>>" != "<<TargetBlock>>"
60 
61   /// CHECK-START: int Main.$noinline$testSimplifyTwoThrows(int) dead_code_elimination$after_inlining (after)
62   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
63   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock>> method_name:Main.alwaysThrows always_throws:true
64   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
65   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<ExitBlock>>
66 
67   // Tests that we simplify the always throwing branch directly to the exit, even with blocks that
68   // are not just the throwing instruction and a Goto.
$noinline$testSimplifyTwoThrows(int num)69   private static int $noinline$testSimplifyTwoThrows(int num) {
70     if (num == 0) {
71       alwaysThrows();
72       alwaysThrows();
73     }
74     return 0;
75   }
76 
77   /// CHECK-START: int Main.$noinline$testSimplifyThrowWithTryCatch(int) dead_code_elimination$after_inlining (before)
78   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
79   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
80   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<TargetBlock:B\d+>>
81   /// CHECK-EVAL:  "<<ExitBlock>>" != "<<TargetBlock>>"
82 
83   /// CHECK-START: int Main.$noinline$testSimplifyThrowWithTryCatch(int) dead_code_elimination$after_inlining (after)
84   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
85   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
86   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<ExitBlock>>
87 
88   // Consistency check to make sure we have the try catches in the graph at this stage.
89   /// CHECK-START: int Main.$noinline$testSimplifyThrowWithTryCatch(int) dead_code_elimination$after_inlining (before)
90   /// CHECK:       TryBoundary kind:entry
91   /// CHECK:       TryBoundary kind:entry
92 
93   // Tests that we simplify the always throwing branch directly to the exit, with non-blocking try
94   // catches in the graph.
$noinline$testSimplifyThrowWithTryCatch(int num)95   private static int $noinline$testSimplifyThrowWithTryCatch(int num) {
96     try {
97       if (num == 123) {
98         throw new Error();
99       }
100     } catch (Error e) {
101       return 123;
102     }
103 
104     if (num == 0) {
105       alwaysThrows();
106     }
107 
108     try {
109       if (num == 456) {
110         throw new Error();
111       }
112     } catch (Error e) {
113       return 456;
114     }
115     return 0;
116   }
117 
$inline$testDoNotSimplifyInner(int num)118   private static void $inline$testDoNotSimplifyInner(int num) {
119     alwaysThrows();
120     while (num == 0) {
121       // We should never hit this since we are always throwing.
122       System.out.println(num);
123     }
124   }
125 
126   /// CHECK-START: int Main.$noinline$testDoNotSimplifyInTry(int) dead_code_elimination$after_inlining (before)
127   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
128   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
129   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<TargetBlock:B\d+>>
130   /// CHECK-EVAL:  "<<ExitBlock>>" != "<<TargetBlock>>"
131 
132   /// CHECK-START: int Main.$noinline$testDoNotSimplifyInTry(int) dead_code_elimination$after_inlining (after)
133   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
134   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
135   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<TargetBlock:B\d+>>
136   /// CHECK-EVAL:  "<<ExitBlock>>" != "<<TargetBlock>>"
137 
138   // Consistency check to make sure we have the try catch in the graph at this stage.
139   /// CHECK-START: int Main.$noinline$testDoNotSimplifyInTry(int) dead_code_elimination$after_inlining (before)
140   /// CHECK:       TryBoundary kind:entry
141 
142   // Consistency check to that we do not simplify it by the last DCE pass either
143   /// CHECK-START: int Main.$noinline$testDoNotSimplifyInTry(int) dead_code_elimination$final (after)
144   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
145   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
146   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<TargetBlock:B\d+>>
147   /// CHECK-EVAL:  "<<ExitBlock>>" != "<<TargetBlock>>"
148 
149   // Tests that we have the necessary conditions for us to simplify the always throwing instruction
150   // (e.g. InvokeStaticOrDirect followed by a Goto) but we are blocking this due to being in a try.
151   // Changing the Goto here for the exit would be wrong since we want to flow to the catch rather
152   // than the Exit. The preconditions are tricky to do with just one function (since we will have an
153   // invoke followed by a TryBoundary rather than a Goto) but we can do so with the help of the
154   // inliner.
$noinline$testDoNotSimplifyInTry(int num)155   private static int $noinline$testDoNotSimplifyInTry(int num) {
156     try {
157       $inline$testDoNotSimplifyInner(num);
158     } catch (Error e) {
159       return 0;
160     }
161     return 123;
162   }
163 
164   /// CHECK-START: int Main.$noinline$testSimplifyInCatch(int) dead_code_elimination$after_inlining (before)
165   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
166   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
167   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<TargetBlock:B\d+>>
168   /// CHECK-EVAL:  "<<ExitBlock>>" != "<<TargetBlock>>"
169 
170   /// CHECK-START: int Main.$noinline$testSimplifyInCatch(int) dead_code_elimination$after_inlining (after)
171   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
172   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
173   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<ExitBlock>>
174 
175   // Consistency check to make sure we have the try catch in the graph at this stage.
176   /// CHECK-START: int Main.$noinline$testSimplifyInCatch(int) dead_code_elimination$after_inlining (before)
177   /// CHECK:       TryBoundary kind:entry
178 
179   // We are able to simplify the `alwaysThrows` even though we are inside of the catch { ... } since
180   // the if makes it so that we are not the first block of the catch and therefore not in the
181   // "catch_block".
$noinline$testSimplifyInCatch(int num)182   private static int $noinline$testSimplifyInCatch(int num) {
183     try {
184       throw new Error();
185     } catch (Error e) {
186       if (num == 0) {
187         alwaysThrows();
188       }
189       return 0;
190     }
191   }
192 
193   /// CHECK-START: int Main.$noinline$testDoNotSimplifyInCatchInOuterTry(int) dead_code_elimination$after_inlining (before)
194   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
195   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
196   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<TargetBlock:B\d+>>
197   /// CHECK-EVAL:  "<<ExitBlock>>" != "<<TargetBlock>>"
198 
199   /// CHECK-START: int Main.$noinline$testDoNotSimplifyInCatchInOuterTry(int) dead_code_elimination$after_inlining (after)
200   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
201   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
202   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<TargetBlock:B\d+>>
203   /// CHECK-EVAL:  "<<ExitBlock>>" != "<<TargetBlock>>"
204 
205   // Consistency check to make sure we have the try catches in the graph at this stage.
206   /// CHECK-START: int Main.$noinline$testDoNotSimplifyInCatchInOuterTry(int) dead_code_elimination$after_inlining (before)
207   /// CHECK-DAG:   TryBoundary kind:entry
208   /// CHECK-DAG:   TryBoundary kind:entry
209 
210   // Consistency check to that we do not simplify it by the last DCE pass either
211   /// CHECK-START: int Main.$noinline$testDoNotSimplifyInCatchInOuterTry(int) dead_code_elimination$final (after)
212   /// CHECK-DAG:   InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
213   /// CHECK-DAG:   Exit block:<<ExitBlock:B\d+>>
214   /// CHECK-DAG:   Goto block:<<InvokeBlock>> target:<<TargetBlock:B\d+>>
215   /// CHECK-EVAL:  "<<ExitBlock>>" != "<<TargetBlock>>"
216 
217   // Similar to testSimplifyInCatch, but now the throw is in an outer try and we shouldn't simplify
218   // it. Like in testDoNotSimplifyInTry, we need the help of the inliner to have an invoke followed
219   // by a Goto.
$noinline$testDoNotSimplifyInCatchInOuterTry(int num)220   private static int $noinline$testDoNotSimplifyInCatchInOuterTry(int num) {
221     try {
222       try {
223         throw new Error();
224       } catch (Error e) {
225         if (num == 0) {
226           $inline$testDoNotSimplifyInner(num);
227         }
228         return 0;
229       }
230     } catch (Error e) {
231       return 123;
232     }
233   }
234 
assertEquals(int expected, int actual)235   static void assertEquals(int expected, int actual) {
236     if (expected != actual) {
237       throw new AssertionError("Expected " + expected + " got " + actual);
238     }
239   }
240 }
241