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 class Point { 18 int x; 19 int y; 20 } 21 22 public class Main { main(String[] args)23 public static void main(String[] args) { 24 final boolean boolean_throw = false; 25 final boolean boolean_other_throw = false; 26 assertEquals(3, 27 $noinline$testDifferentFields( 28 new Point(), new Point(), boolean_throw, boolean_other_throw)); 29 assertEquals(1, $noinline$testRedundantStore(new Point(), boolean_throw, boolean_other_throw)); 30 assertEquals(1, $noinline$testTryCatchBlocking(new Point(), boolean_throw)); 31 assertEquals(1, $noinline$testTryCatchPhi(new Point(), boolean_throw)); 32 assertEquals(2, $noinline$testTryCatchPhiWithTwoCatches(new Point(), new int[0])); 33 assertEquals(1, $noinline$testKeepStoreInsideTry()); 34 assertEquals(10, $noinline$testDontKeepStoreInsideCatch(new int[]{10})); 35 assertEquals(30, $noinline$testDontKeepStoreInsideCatch(new int[]{})); 36 assertEquals(10, $noinline$testKeepStoreInsideCatchWithOuterTry(new int[]{10})); 37 assertEquals(30, $noinline$testKeepStoreInsideCatchWithOuterTry(new int[]{})); 38 assertEquals(40, $noinline$testDontKeepStoreInsideFinally(new int[]{10})); 39 try { 40 assertEquals(30, $noinline$testDontKeepStoreInsideFinally(new int[]{})); 41 throw new Error("Unreachable"); 42 } catch (ArrayIndexOutOfBoundsException expected) { 43 } 44 assertEquals(10, $noinline$testDontKeepStoreInsideOuterCatch(new int[]{10})); 45 assertEquals(100030, $noinline$testDontKeepStoreInsideOuterCatch(new int[]{})); 46 assertEquals(150, $noinline$test40()); 47 } 48 49 /// CHECK-START: int Main.$noinline$testDifferentFields(Point, Point, boolean, boolean) load_store_elimination (before) 50 /// CHECK-DAG: InstanceFieldSet field_name:Point.x 51 /// CHECK-DAG: InstanceFieldSet field_name:Point.y 52 /// CHECK-DAG: InstanceFieldGet field_name:Point.x 53 /// CHECK-DAG: InstanceFieldGet field_name:Point.y 54 55 /// CHECK-START: int Main.$noinline$testDifferentFields(Point, Point, boolean, boolean) load_store_elimination (after) 56 /// CHECK-DAG: InstanceFieldSet field_name:Point.x 57 /// CHECK-DAG: InstanceFieldSet field_name:Point.y 58 59 /// CHECK-START: int Main.$noinline$testDifferentFields(Point, Point, boolean, boolean) load_store_elimination (after) 60 /// CHECK-NOT: InstanceFieldGet field_name:Point.x 61 /// CHECK-NOT: InstanceFieldGet field_name:Point.y 62 63 // Consistency check to make sure the try/catches weren't removed by an earlier pass. 64 /// CHECK-START: int Main.$noinline$testDifferentFields(Point, Point, boolean, boolean) load_store_elimination (after) 65 /// CHECK: TryBoundary kind:entry 66 /// CHECK: TryBoundary kind:entry 67 68 // Different fields shouldn't alias. $noinline$testDifferentFields( Point obj1, Point obj2, boolean boolean_throw, boolean boolean_other_throw)69 private static int $noinline$testDifferentFields( 70 Point obj1, Point obj2, boolean boolean_throw, boolean boolean_other_throw) { 71 try { 72 if (boolean_throw) { 73 throw new Error(); 74 } 75 } catch (Error e) { 76 return 0; 77 } 78 obj1.x = 1; 79 obj2.y = 2; 80 int result = obj1.x + obj2.y; 81 try { 82 if (boolean_other_throw) { 83 throw new Error(); 84 } 85 } catch (Error e) { 86 return 0; 87 } 88 return result; 89 } 90 91 /// CHECK-START: int Main.$noinline$testRedundantStore(Point, boolean, boolean) load_store_elimination (before) 92 /// CHECK-DAG: InstanceFieldSet field_name:Point.y 93 /// CHECK-DAG: InstanceFieldSet field_name:Point.y 94 /// CHECK-DAG: InstanceFieldGet field_name:Point.y 95 96 /// CHECK-START: int Main.$noinline$testRedundantStore(Point, boolean, boolean) load_store_elimination (after) 97 /// CHECK: InstanceFieldSet field_name:Point.y 98 /// CHECK-NOT: InstanceFieldSet field_name:Point.y 99 100 /// CHECK-START: int Main.$noinline$testRedundantStore(Point, boolean, boolean) load_store_elimination (after) 101 /// CHECK-NOT: InstanceFieldGet field_name:Point.y 102 103 // Consistency check to make sure the try/catches weren't removed by an earlier pass. 104 /// CHECK-START: int Main.$noinline$testRedundantStore(Point, boolean, boolean) load_store_elimination (after) 105 /// CHECK-DAG: TryBoundary kind:entry 106 /// CHECK-DAG: TryBoundary kind:entry 107 108 // Redundant store of the same value. $noinline$testRedundantStore( Point obj, boolean boolean_throw, boolean boolean_other_throw)109 private static int $noinline$testRedundantStore( 110 Point obj, boolean boolean_throw, boolean boolean_other_throw) { 111 try { 112 if (boolean_throw) { 113 throw new Error(); 114 } 115 } catch (Error e) { 116 return 0; 117 } 118 obj.y = 1; 119 obj.y = 1; 120 try { 121 if (boolean_other_throw) { 122 throw new Error(); 123 } 124 } catch (Error e) { 125 return 0; 126 } 127 return obj.y; 128 } 129 130 /// CHECK-START: int Main.$noinline$testTryCatchBlocking(Point, boolean) load_store_elimination (before) 131 /// CHECK: InstanceFieldSet field_name:Point.y 132 /// CHECK: InstanceFieldGet field_name:Point.y 133 134 /// CHECK-START: int Main.$noinline$testTryCatchBlocking(Point, boolean) load_store_elimination (after) 135 /// CHECK: InstanceFieldSet field_name:Point.y 136 /// CHECK: InstanceFieldGet field_name:Point.y 137 138 // Consistency check to make sure the try/catch wasn't removed by an earlier pass. 139 /// CHECK-START: int Main.$noinline$testTryCatchBlocking(Point, boolean) load_store_elimination (after) 140 /// CHECK: TryBoundary kind:entry 141 142 // We cannot remove the Get since we might have thrown. $noinline$testTryCatchBlocking(Point obj, boolean boolean_throw)143 private static int $noinline$testTryCatchBlocking(Point obj, boolean boolean_throw) { 144 obj.y = 1; 145 try { 146 if (boolean_throw) { 147 throw new Error(); 148 } 149 } catch (Error e) { 150 } 151 return obj.y; 152 } 153 154 /// CHECK-START: int Main.$noinline$testTryCatchPhi(Point, boolean) load_store_elimination (before) 155 /// CHECK-DAG: InstanceFieldSet field_name:Point.y 156 /// CHECK-DAG: InstanceFieldSet field_name:Point.y 157 /// CHECK-DAG: InstanceFieldGet field_name:Point.y 158 159 /// CHECK-START: int Main.$noinline$testTryCatchPhi(Point, boolean) load_store_elimination (after) 160 /// CHECK-DAG: InstanceFieldSet field_name:Point.y 161 /// CHECK-DAG: InstanceFieldSet field_name:Point.y 162 163 /// CHECK-START: int Main.$noinline$testTryCatchPhi(Point, boolean) load_store_elimination (after) 164 /// CHECK-NOT: InstanceFieldGet field_name:Point.y 165 166 // Consistency check to make sure the try/catch wasn't removed by an earlier pass. 167 /// CHECK-START: int Main.$noinline$testTryCatchPhi(Point, boolean) load_store_elimination (after) 168 /// CHECK-DAG: TryBoundary kind:entry 169 170 // We either threw and we set the value in the catch, or we didn't throw and we set the value 171 // before the catch. We can solve that with a Phi and skip the get. $noinline$testTryCatchPhi(Point obj, boolean boolean_throw)172 private static int $noinline$testTryCatchPhi(Point obj, boolean boolean_throw) { 173 obj.y = 1; 174 try { 175 if (boolean_throw) { 176 throw new Error(); 177 } 178 } catch (Error e) { 179 obj.y = 2; 180 } 181 return obj.y; 182 } 183 184 185 /// CHECK-START: int Main.$noinline$testTryCatchPhiWithTwoCatches(Point, int[]) load_store_elimination (before) 186 /// CHECK-DAG: InstanceFieldSet field_name:Point.y 187 /// CHECK-DAG: InstanceFieldSet field_name:Point.y 188 /// CHECK-DAG: InstanceFieldSet field_name:Point.y 189 /// CHECK-DAG: InstanceFieldGet field_name:Point.y 190 191 /// CHECK-START: int Main.$noinline$testTryCatchPhiWithTwoCatches(Point, int[]) load_store_elimination (after) 192 /// CHECK-DAG: InstanceFieldSet field_name:Point.y 193 /// CHECK-DAG: InstanceFieldSet field_name:Point.y 194 /// CHECK-DAG: InstanceFieldSet field_name:Point.y 195 196 /// CHECK-START: int Main.$noinline$testTryCatchPhiWithTwoCatches(Point, int[]) load_store_elimination (after) 197 /// CHECK-NOT: InstanceFieldGet field_name:Point.y 198 199 // Consistency check to make sure the try/catch wasn't removed by an earlier pass. 200 /// CHECK-START: int Main.$noinline$testTryCatchPhiWithTwoCatches(Point, int[]) load_store_elimination (after) 201 /// CHECK-DAG: TryBoundary kind:entry $noinline$testTryCatchPhiWithTwoCatches(Point obj, int[] numbers)202 private static int $noinline$testTryCatchPhiWithTwoCatches(Point obj, int[] numbers) { 203 obj.y = 1; 204 try { 205 if (numbers[0] == 1) { 206 throw new Error(); 207 } 208 } catch (ArrayIndexOutOfBoundsException e) { 209 obj.y = 2; 210 } catch (Error e) { 211 obj.y = 3; 212 } 213 return obj.y; 214 } 215 216 // Check that we don't eliminate the first store to `main.sumForKeepStoreInsideTryCatch` since it 217 // is observable. 218 219 // Consistency check to make sure the try/catch wasn't removed by an earlier pass. 220 /// CHECK-START: int Main.$noinline$testKeepStoreInsideTry() load_store_elimination (after) 221 /// CHECK-DAG: TryBoundary kind:entry 222 223 /// CHECK-START: int Main.$noinline$testKeepStoreInsideTry() load_store_elimination (before) 224 /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch 225 /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch 226 227 /// CHECK-START: int Main.$noinline$testKeepStoreInsideTry() load_store_elimination (after) 228 /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch 229 /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch $noinline$testKeepStoreInsideTry()230 private static int $noinline$testKeepStoreInsideTry() { 231 Main main = new Main(); 232 main.sumForKeepStoreInsideTryCatch = 0; 233 try { 234 int[] array = {1}; 235 main.sumForKeepStoreInsideTryCatch += array[0]; 236 main.sumForKeepStoreInsideTryCatch += array[1]; 237 throw new RuntimeException("Unreachable"); 238 } catch (ArrayIndexOutOfBoundsException e) { 239 return main.sumForKeepStoreInsideTryCatch; 240 } 241 } 242 $noinline$returnValue(int value)243 private static int $noinline$returnValue(int value) { 244 return value; 245 } 246 247 /// CHECK-START: int Main.$noinline$testDontKeepStoreInsideCatch(int[]) load_store_elimination (before) 248 /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch 249 /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch 250 /// CHECK-NOT: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch 251 252 /// CHECK-START: int Main.$noinline$testDontKeepStoreInsideCatch(int[]) load_store_elimination (after) 253 /// CHECK-NOT: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch $noinline$testDontKeepStoreInsideCatch(int[] array)254 private static int $noinline$testDontKeepStoreInsideCatch(int[] array) { 255 Main main = new Main(); 256 int value = 0; 257 try { 258 value = array[0]; 259 } catch (Exception e) { 260 // These sets can be eliminated even though we have invokes since this catch is not part of an 261 // outer try. 262 main.sumForKeepStoreInsideTryCatch += $noinline$returnValue(10); 263 main.sumForKeepStoreInsideTryCatch += $noinline$returnValue(20); 264 } 265 return main.sumForKeepStoreInsideTryCatch + value; 266 } 267 268 /// CHECK-START: int Main.$noinline$testKeepStoreInsideCatchWithOuterTry(int[]) load_store_elimination (before) 269 /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch 270 /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch 271 /// CHECK-NOT: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch 272 273 /// CHECK-START: int Main.$noinline$testKeepStoreInsideCatchWithOuterTry(int[]) load_store_elimination (after) 274 /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch 275 /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch 276 /// CHECK-NOT: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch $noinline$testKeepStoreInsideCatchWithOuterTry(int[] array)277 private static int $noinline$testKeepStoreInsideCatchWithOuterTry(int[] array) { 278 Main main = new Main(); 279 int value = 0; 280 try { 281 try { 282 value = array[0]; 283 } catch (Exception e) { 284 // These sets can't be eliminated since this catch is part of a outer try. 285 main.sumForKeepStoreInsideTryCatch += $noinline$returnValue(10); 286 main.sumForKeepStoreInsideTryCatch += $noinline$returnValue(20); 287 } 288 } catch (Exception e) { 289 value = 100000; 290 } 291 292 return main.sumForKeepStoreInsideTryCatch + value; 293 } 294 295 // Note that there are four `InstanceFieldSet` instead of two since we split the `finally` block 296 // into the normal path, and the exceptional path. 297 298 /// CHECK-START: int Main.$noinline$testDontKeepStoreInsideFinally(int[]) load_store_elimination (before) 299 /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch 300 /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch 301 /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch 302 /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch 303 /// CHECK-NOT: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch 304 305 /// CHECK-START: int Main.$noinline$testDontKeepStoreInsideFinally(int[]) load_store_elimination (after) 306 /// CHECK-NOT: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch $noinline$testDontKeepStoreInsideFinally(int[] array)307 private static int $noinline$testDontKeepStoreInsideFinally(int[] array) { 308 Main main = new Main(); 309 int value = 0; 310 try { 311 value = array[0]; 312 } finally { 313 // These sets can be eliminated even though we have invokes since this catch is not part of an 314 // outer try. 315 main.sumForKeepStoreInsideTryCatch += $noinline$returnValue(10); 316 main.sumForKeepStoreInsideTryCatch += $noinline$returnValue(20); 317 } 318 return main.sumForKeepStoreInsideTryCatch + value; 319 } 320 321 // Checks that we are able to do LSE inside of catches which are outside of try blocks. 322 323 /// CHECK-START: int Main.$noinline$testDontKeepStoreInsideOuterCatch(int[]) load_store_elimination (before) 324 /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch 325 /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch 326 /// CHECK-NOT: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch 327 328 // This store potentially can be eliminated too, but our phi creation logic doesn't realize it can 329 // create a Phi for `main.sumForKeepStoreInsideTryCatch` and skip a store+load. 330 331 /// CHECK-START: int Main.$noinline$testDontKeepStoreInsideOuterCatch(int[]) load_store_elimination (after) 332 /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch 333 /// CHECK-NOT: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch 334 $noinline$testDontKeepStoreInsideOuterCatch(int[] array)335 private static int $noinline$testDontKeepStoreInsideOuterCatch(int[] array) { 336 Main main = new Main(); 337 int value = 0; 338 try { 339 value = array[0]; 340 } catch (ArrayIndexOutOfBoundsException expected) { 341 // These sets and gets are not considered to be part of a try so they are free to be 342 // eliminated. 343 main.sumForKeepStoreInsideTryCatch += $noinline$returnValue(10); 344 main.sumForKeepStoreInsideTryCatch += $noinline$returnValue(20); 345 try { 346 value = array[0]; 347 } catch (ArrayIndexOutOfBoundsException expectedToo) { 348 value = 100000; 349 } 350 } 351 352 return main.sumForKeepStoreInsideTryCatch + value; 353 } 354 355 /// CHECK-START: int Main.$noinline$test40() load_store_elimination (before) 356 /// CHECK: ArraySet 357 /// CHECK: DivZeroCheck 358 /// CHECK: ArraySet 359 /// CHECK: ArraySet 360 // 361 /// CHECK: ArraySet 362 /// CHECK: ArraySet 363 /// CHECK: DivZeroCheck 364 /// CHECK: ArraySet 365 // 366 /// CHECK: ArraySet 367 /// CHECK: ArraySet 368 /// CHECK: ArraySet 369 /// CHECK-NOT: ArraySet 370 371 /// CHECK-START: int Main.$noinline$test40() load_store_elimination (after) 372 /// CHECK: ArraySet 373 /// CHECK: DivZeroCheck 374 /// CHECK: ArraySet 375 // 376 /// CHECK: ArraySet 377 /// CHECK: DivZeroCheck 378 /// CHECK: ArraySet 379 // 380 /// CHECK-NOT: ArraySet 381 382 // Like `test40` from 530-checker-lse but with $inline$ for the inner method so we check that we 383 // have the array set inside try catches too. 384 // Since we are inlining, we know the parameters and are able to elimnate (some) of the 385 // `ArraySet`s. $noinline$test40()386 private static int $noinline$test40() { 387 int[] array = new int[1]; 388 try { 389 $inline$fillArrayTest40(array, 100, 0); 390 System.out.println("UNREACHABLE"); 391 } catch (Throwable expected) { 392 } 393 assertEquals(1, array[0]); 394 try { 395 $inline$fillArrayTest40(array, 100, 1); 396 System.out.println("UNREACHABLE"); 397 } catch (Throwable expected) { 398 } 399 assertEquals(2, array[0]); 400 $inline$fillArrayTest40(array, 100, 2); 401 assertEquals(150, array[0]); 402 return array[0]; 403 } 404 405 /// CHECK-START: void Main.$inline$fillArrayTest40(int[], int, int) load_store_elimination (before) 406 /// CHECK: ArraySet 407 /// CHECK: DivZeroCheck 408 /// CHECK: ArraySet 409 /// CHECK: DivZeroCheck 410 /// CHECK: ArraySet 411 /// CHECK-NOT: ArraySet 412 413 /// CHECK-START: void Main.$inline$fillArrayTest40(int[], int, int) load_store_elimination (after) 414 /// CHECK: ArraySet 415 /// CHECK: DivZeroCheck 416 /// CHECK: ArraySet 417 /// CHECK: DivZeroCheck 418 /// CHECK: ArraySet 419 /// CHECK-NOT: ArraySet 420 421 // Check that the stores to array[0] are not eliminated since we can throw in between the stores. $inline$fillArrayTest40(int[] array, int a, int b)422 private static void $inline$fillArrayTest40(int[] array, int a, int b) { 423 array[0] = 1; 424 int x = a / b; 425 array[0] = 2; 426 int y = a / (b - 1); 427 array[0] = x + y; 428 } 429 assertEquals(int expected, int actual)430 private static void assertEquals(int expected, int actual) { 431 if (expected != actual) { 432 throw new AssertionError("Expected: " + expected + ", Actual: " + actual); 433 } 434 } 435 436 int sumForKeepStoreInsideTryCatch; 437 } 438