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