1 /* 2 * Copyright (C) 2017 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 import java.lang.reflect.Array; 18 import java.lang.reflect.Field; 19 import java.lang.reflect.Method; 20 import java.lang.reflect.Modifier; 21 22 // Baseline class. This has no final fields, so there are no additional freezes 23 // in its constructor. 24 // 25 // The new-instance itself always has 1 freeze for the happens-before on the object header 26 // write (i.e. [obj.class = X] happens-before any access to obj). 27 // 28 // Total freezes for "new Base()": 1. 29 class Base { 30 int w0; 31 int w1; 32 int w2; 33 int w3; 34 Base()35 Base() { 36 // Prevent inliner from matching the code pattern when calling this constructor 37 // to test the normal inlining that builds and inserts the callee graph. 38 // (Pattern matching can merge or eliminate constructor barriers.) 39 $inline$nop(); 40 } 41 42 @Override toString()43 public String toString() { 44 return getClass().getName() + "(" + baseString() + ")"; 45 } 46 baseString()47 protected String baseString() { 48 return String.format("w0: %d, w1: %d, w2: %d, w3: %d", w0, w1, w2, w3); 49 } 50 $inline$nop()51 private void $inline$nop() {} 52 } 53 54 // This has a final field in its constructor, so there must be a field freeze 55 // at the end of <init>. 56 // 57 // Total freezes for "new OneFinal()": 2. 58 class OneFinal extends Base { 59 final int x; OneFinal(int x)60 OneFinal(int x) { 61 this.x = x; 62 } 63 64 @Override baseString()65 protected String baseString() { 66 return String.format("%s, x: %d", super.baseString(), x); 67 } 68 } 69 70 class Assert { stringEquals(String expected, Object actual)71 public static void stringEquals(String expected, Object actual) { 72 stringEquals$noinline$(expected, actual); 73 } 74 75 // Forbid compiler from inlining this to avoid overly clever optimizations. stringEquals$noinline$(String expected, Object actual)76 private static void stringEquals$noinline$(String expected, Object actual) { 77 String actualStr = Main.valueToString(actual); 78 if (!expected.equals(actualStr)) { 79 throw new AssertionError("Expected: " + expected + ", actual: " + actualStr); 80 } 81 } 82 } 83 84 interface Test { exercise()85 public void exercise(); check()86 public void check(); 87 } 88 89 class TestOneFinal implements Test { 90 // Initialize at least once before actual test. 91 public static Object external; 92 93 /// CHECK-START: void TestOneFinal.exercise() constructor_fence_redundancy_elimination (before) 94 /// CHECK: <<NewInstance:l\d+>> NewInstance 95 /// CHECK-DAG: ConstructorFence [<<NewInstance>>] 96 /// CHECK-DAG: ConstructorFence [<<NewInstance>>] 97 /// CHECK-NOT: ConstructorFence 98 /// CHECK-DAG: StaticFieldSet [<<External:l\d+>>,<<NewInstance>>] 99 100 /// CHECK-START: void TestOneFinal.exercise() constructor_fence_redundancy_elimination (after) 101 /// CHECK: <<NewInstance:l\d+>> NewInstance 102 /// CHECK-DAG: ConstructorFence [<<NewInstance>>] 103 /// CHECK-NOT: ConstructorFence 104 /// CHECK-DAG: StaticFieldSet [<<External:l\d+>>,<<NewInstance>>] 105 @Override exercise()106 public void exercise() { 107 Base b = new OneFinal(1); 108 // 1 store, 2 freezes. 109 110 // Stores to 'b' do not escape b. 111 b.w0 = 1; 112 b.w1 = 2; 113 b.w2 = 3; 114 115 // Publish the result to a global so that it is not LSE-eliminated. 116 external = b; 117 } 118 119 @Override check()120 public void check() { 121 Assert.stringEquals("OneFinal(w0: 1, w1: 2, w2: 3, w3: 0, x: 1)", external); 122 } 123 } 124 125 // This has a final field in its constructor, so there must be a field freeze 126 // at the end of <init>. The previous base class's freezes accumulate on top 127 // of this one. 128 // 129 // Total freezes for "new TwoFinal()": 3. 130 class TwoFinal extends OneFinal { 131 final int y; TwoFinal(int x, int y)132 TwoFinal(int x, int y) { 133 super(x); 134 this.y = y; 135 } 136 137 @Override baseString()138 protected String baseString() { 139 return String.format("%s, y: %d", super.baseString(), y); 140 } 141 } 142 143 // This has a final field in its constructor, so there must be a field freeze 144 // at the end of <init>. The previous base class's freezes accumulate on top 145 // of this one. 146 // 147 // Total freezes for "new ThreeFinal()": 4. 148 class ThreeFinal extends TwoFinal { 149 final int z; ThreeFinal(int x, int y, int z)150 ThreeFinal(int x, int y, int z) { 151 super(x, y); 152 this.z = z; 153 } 154 155 @Override baseString()156 protected String baseString() { 157 return String.format("%s, z: %d", super.baseString(), z); 158 } 159 } 160 161 class TestThreeFinal implements Test { 162 // Initialize at least once before actual test. 163 public static Object external; 164 165 /// CHECK-START: void TestThreeFinal.exercise() constructor_fence_redundancy_elimination (before) 166 /// CHECK: <<NewInstance:l\d+>> NewInstance 167 /// CHECK-DAG: ConstructorFence [<<NewInstance>>] 168 /// CHECK-DAG: ConstructorFence [<<NewInstance>>] 169 /// CHECK-DAG: ConstructorFence [<<NewInstance>>] 170 /// CHECK-DAG: ConstructorFence [<<NewInstance>>] 171 /// CHECK-NOT: ConstructorFence 172 /// CHECK-DAG: StaticFieldSet [<<External:l\d+>>,<<NewInstance>>] 173 174 /// CHECK-START: void TestThreeFinal.exercise() constructor_fence_redundancy_elimination (after) 175 /// CHECK: <<NewInstance:l\d+>> NewInstance 176 /// CHECK-DAG: ConstructorFence [<<NewInstance>>] 177 /// CHECK-NOT: ConstructorFence 178 /// CHECK-DAG: StaticFieldSet [<<External:l\d+>>,<<NewInstance>>] 179 @Override exercise()180 public void exercise() { 181 Base b = new ThreeFinal(1, 1, 2); 182 // 3 store, 4 freezes. 183 184 // Stores to 'b' do not escape b. 185 b.w0 = 3; 186 187 // Publish the result to a global so that it is not LSE-eliminated. 188 external = b; 189 } 190 191 @Override check()192 public void check() { 193 Assert.stringEquals("ThreeFinal(w0: 3, w1: 0, w2: 0, w3: 0, x: 1, y: 1, z: 2)", external); 194 } 195 } 196 197 // Ensure "freezes" between multiple new-instances are optimized out. 198 class TestMultiAlloc implements Test { 199 public static Object external; 200 public static Object external2; 201 202 /// CHECK-START: void TestMultiAlloc.exercise() constructor_fence_redundancy_elimination (before) 203 /// CHECK: <<NewInstance:l\d+>> NewInstance 204 /// CHECK-DAG: ConstructorFence [<<NewInstance>>] 205 /// CHECK: <<NewInstance2:l\d+>> NewInstance 206 /// CHECK-DAG: ConstructorFence [<<NewInstance2>>] 207 /// CHECK-NOT: ConstructorFence 208 /// CHECK-DAG: StaticFieldSet [<<External:l\d+>>,<<NewInstance>>] 209 /// CHECK-DAG: StaticFieldSet [<<External2:l\d+>>,<<NewInstance2>>] 210 211 /// CHECK-START: void TestMultiAlloc.exercise() constructor_fence_redundancy_elimination (after) 212 /// CHECK: <<NewInstance:l\d+>> NewInstance 213 /// CHECK: <<NewInstance2:l\d+>> NewInstance 214 /// CHECK-DAG: ConstructorFence [<<NewInstance2>>,<<NewInstance>>] 215 /// CHECK-NOT: ConstructorFence 216 /// CHECK-DAG: StaticFieldSet [<<External:l\d+>>,<<NewInstance>>] 217 /// CHECK-DAG: StaticFieldSet [<<External2:l\d+>>,<<NewInstance2>>] 218 @Override exercise()219 public void exercise() { 220 // 1 freeze 221 Base b = new Base(); 222 // 1 freeze 223 Base b2 = new Base(); 224 225 // Merge 2 freezes above into 1 constructor fence. 226 external = b; 227 external2 = b2; 228 } 229 230 @Override check()231 public void check() { 232 Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external); 233 Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external2); 234 } 235 } 236 237 // Ensure "freezes" between multiple new-instances are optimized out. 238 class TestThreeFinalTwice implements Test { 239 // Initialize at least once before actual test. 240 public static Object external; 241 public static Object external2; 242 243 /// CHECK-START: void TestThreeFinalTwice.exercise() constructor_fence_redundancy_elimination (before) 244 /// CHECK: <<NewInstance:l\d+>> NewInstance 245 /// CHECK-DAG: ConstructorFence [<<NewInstance>>] 246 /// CHECK-DAG: ConstructorFence [<<NewInstance>>] 247 /// CHECK-DAG: ConstructorFence [<<NewInstance>>] 248 /// CHECK-DAG: ConstructorFence [<<NewInstance>>] 249 /// CHECK: <<NewInstance2:l\d+>> NewInstance 250 /// CHECK-DAG: ConstructorFence [<<NewInstance2>>] 251 /// CHECK-DAG: ConstructorFence [<<NewInstance2>>] 252 /// CHECK-DAG: ConstructorFence [<<NewInstance2>>] 253 /// CHECK-DAG: ConstructorFence [<<NewInstance2>>] 254 /// CHECK-NOT: ConstructorFence 255 /// CHECK-DAG: StaticFieldSet [<<External:l\d+>>,<<NewInstance>>] 256 /// CHECK-DAG: StaticFieldSet [<<External2:l\d+>>,<<NewInstance2>>] 257 258 /// CHECK-START: void TestThreeFinalTwice.exercise() constructor_fence_redundancy_elimination (after) 259 /// CHECK: <<NewInstance:l\d+>> NewInstance 260 /// CHECK: <<NewInstance2:l\d+>> NewInstance 261 /// CHECK-DAG: ConstructorFence [<<NewInstance2>>,<<NewInstance>>] 262 /// CHECK-NOT: ConstructorFence 263 /// CHECK-DAG: StaticFieldSet [<<External:l\d+>>,<<NewInstance>>] 264 /// CHECK-DAG: StaticFieldSet [<<External2:l\d+>>,<<NewInstance2>>] 265 @Override exercise()266 public void exercise() { 267 Base b = new ThreeFinal(1, 1, 2); 268 // 3 store, 4 freezes. 269 270 // Stores to 'b' do not escape b. 271 b.w0 = 3; 272 273 Base b2 = new ThreeFinal(4, 5, 6); 274 // 3 store, 4 freezes. 275 276 // Stores to 'b2' do not escape b2. 277 b2.w0 = 7; 278 279 // Publish the result to a global so that it is not LSE-eliminated. 280 // Publishing is done at the end to give freezes above a chance to merge. 281 external = b; 282 external2 = b2; 283 } 284 285 @Override check()286 public void check() { 287 Assert.stringEquals("ThreeFinal(w0: 3, w1: 0, w2: 0, w3: 0, x: 1, y: 1, z: 2)", external); 288 Assert.stringEquals("ThreeFinal(w0: 7, w1: 0, w2: 0, w3: 0, x: 4, y: 5, z: 6)", external2); 289 } 290 } 291 292 class TestNonEscaping { 293 // Prevent constant folding. 294 static boolean test; 295 296 static Object external; 297 static Object external2; 298 static Object external3; 299 static Object external4; 300 301 static class Invoke implements Test { 302 /// CHECK-START: void TestNonEscaping$Invoke.exercise() constructor_fence_redundancy_elimination (before) 303 /// CHECK: <<NewInstance:l\d+>> NewInstance 304 /// CHECK: ConstructorFence [<<NewInstance>>] 305 /// CHECK: InvokeStaticOrDirect 306 /// CHECK: <<NewInstance2:l\d+>> NewInstance 307 /// CHECK-DAG: ConstructorFence [<<NewInstance2>>] 308 /// CHECK-NOT: ConstructorFence 309 310 /// CHECK-START: void TestNonEscaping$Invoke.exercise() constructor_fence_redundancy_elimination (after) 311 /// CHECK: <<NewInstance:l\d+>> NewInstance 312 /// CHECK: InvokeStaticOrDirect 313 /// CHECK: <<NewInstance2:l\d+>> NewInstance 314 /// CHECK-DAG: ConstructorFence [<<NewInstance2>>,<<NewInstance>>] 315 /// CHECK-NOT: ConstructorFence 316 @Override exercise()317 public void exercise() { 318 Base b = new Base(); 319 320 // b cannot possibly escape into this invoke because it hasn't escaped onto the heap earlier, 321 // and the invoke doesn't take it as a parameter. 322 noEscape$noinline$(); 323 324 // Remove the Constructor Fence for b, merging into the fence for b2. 325 Base b2 = new Base(); 326 327 // Do not LSE-eliminate b,b2 328 external = b; 329 external2 = b2; 330 } 331 332 @Override check()333 public void check() { 334 Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external); 335 Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external2); 336 } 337 } 338 339 public static int[] array = new int[1]; 340 static Base base = new Base(); 341 342 static class Store implements Test { 343 /// CHECK-START: void TestNonEscaping$Store.exercise() constructor_fence_redundancy_elimination (before) 344 /// CHECK: <<NewInstance:l\d+>> NewInstance 345 /// CHECK: ConstructorFence [<<NewInstance>>] 346 /// CHECK-DAG: ArraySet 347 /// CHECK-DAG: StaticFieldSet 348 /// CHECK-DAG: InstanceFieldSet 349 /// CHECK: <<NewInstance2:l\d+>> NewInstance 350 /// CHECK-DAG: ConstructorFence [<<NewInstance2>>] 351 /// CHECK-NOT: ConstructorFence 352 353 /// CHECK-START: void TestNonEscaping$Store.exercise() constructor_fence_redundancy_elimination (after) 354 /// CHECK-DAG: <<NewInstance:l\d+>> NewInstance 355 /// CHECK-DAG: <<NewInstance2:l\d+>> NewInstance 356 /// CHECK-DAG: ConstructorFence [<<NewInstance2>>,<<NewInstance>>] 357 /// CHECK-NOT: ConstructorFence 358 @Override exercise()359 public void exercise() { 360 Base b = new Base(); 361 362 // Stores of inputs other than the fence target do not publish 'b'. 363 array[0] = b.w0; // aput 364 external = array; // sput 365 base.w0 = b.w0; // iput 366 367 // Remove the Constructor Fence for b, merging into the fence for b2. 368 Base b2 = new Base(); 369 370 // Do not LSE-eliminate b,b2 371 external3 = b; 372 external4 = b2; 373 } 374 375 @Override check()376 public void check() { 377 Assert.stringEquals("[0]", array); 378 Assert.stringEquals("[0]", external); 379 Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", base); 380 Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external3); 381 Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external4); 382 } 383 } 384 noEscape$noinline$()385 private static void noEscape$noinline$() { 386 } 387 } 388 389 class TestDontOptimizeAcrossBlocks implements Test { 390 // Prevent constant folding. 391 static boolean test; 392 393 static Object external; 394 static Object external3; 395 396 /// CHECK-START: void TestDontOptimizeAcrossBlocks.exercise() constructor_fence_redundancy_elimination (before) 397 /// CHECK: <<NewInstance:l\d+>> NewInstance 398 /// CHECK: ConstructorFence [<<NewInstance>>] 399 /// CHECK: <<NewInstance2:l\d+>> NewInstance 400 /// CHECK-DAG: ConstructorFence [<<NewInstance2>>] 401 /// CHECK-NOT: ConstructorFence 402 /// CHECK-DAG: StaticFieldSet [<<External:l\d+>>,<<NewInstance>>] 403 /// CHECK-DAG: StaticFieldSet [<<External2:l\d+>>,<<NewInstance2>>] 404 405 /// CHECK-START: void TestDontOptimizeAcrossBlocks.exercise() constructor_fence_redundancy_elimination (after) 406 /// CHECK: <<NewInstance:l\d+>> NewInstance 407 /// CHECK: ConstructorFence [<<NewInstance>>] 408 /// CHECK: <<NewInstance2:l\d+>> NewInstance 409 /// CHECK-DAG: ConstructorFence [<<NewInstance2>>] 410 /// CHECK-NOT: ConstructorFence 411 /// CHECK-DAG: StaticFieldSet [<<External:l\d+>>,<<NewInstance>>] 412 /// CHECK-DAG: StaticFieldSet [<<External2:l\d+>>,<<NewInstance2>>] 413 @Override exercise()414 public void exercise() { 415 Base b = new Base(); 416 417 // Do not move constructor fence across this block, even though 'b' is not published yet. 418 if (test) { 419 external = null; 420 } 421 422 Base b2 = new Base(); 423 external = b2; 424 external3 = b; 425 } 426 427 @Override check()428 public void check() { 429 Assert.stringEquals("false", test); 430 Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external); 431 Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external3); 432 } 433 } 434 435 class TestDontOptimizeAcrossEscape { 436 // Prevent constant folding. 437 static boolean test; 438 439 static Object external; 440 static Object external2; 441 static Object external3; 442 static Object external4; 443 444 static class Invoke implements Test { 445 /// CHECK-START: void TestDontOptimizeAcrossEscape$Invoke.exercise() constructor_fence_redundancy_elimination (before) 446 /// CHECK: <<NewInstance:l\d+>> NewInstance 447 /// CHECK: ConstructorFence [<<NewInstance>>] 448 /// CHECK: InvokeStaticOrDirect 449 /// CHECK: <<NewInstance2:l\d+>> NewInstance 450 /// CHECK-DAG: ConstructorFence [<<NewInstance2>>] 451 /// CHECK-NOT: ConstructorFence 452 453 /// CHECK-START: void TestDontOptimizeAcrossEscape$Invoke.exercise() constructor_fence_redundancy_elimination (after) 454 /// CHECK: <<NewInstance:l\d+>> NewInstance 455 /// CHECK: ConstructorFence [<<NewInstance>>] 456 /// CHECK: InvokeStaticOrDirect 457 /// CHECK: <<NewInstance2:l\d+>> NewInstance 458 /// CHECK-DAG: ConstructorFence [<<NewInstance2>>] 459 /// CHECK-NOT: ConstructorFence 460 @Override exercise()461 public void exercise() { 462 Base b = new Base(); 463 // Do not optimize across invokes into which the fence target escapes. 464 invoke$noinline$(b); 465 466 Base b2 = new Base(); 467 468 // Do not LSE-eliminate b,b2 469 external = b; 470 external2 = b2; 471 } 472 invoke$noinline$(Object b)473 private static void invoke$noinline$(Object b) { 474 // Even though 'b' does not escape this method, we conservatively assume all parameters 475 // of an invoke escape. 476 } 477 478 @Override check()479 public void check() { 480 Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external); 481 Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external2); 482 } 483 } 484 485 public static Object[] array = new Object[3]; 486 static Base base = new Base(); 487 488 static class InstanceEscaper { 489 public Object holder; 490 491 @Override toString()492 public String toString() { 493 return getClass().getName() + "(" + baseString() + ")"; 494 } 495 baseString()496 protected String baseString() { 497 return String.format("holder: %s", Main.valueToString(holder)); 498 } 499 } 500 501 static InstanceEscaper instanceEscaper = new InstanceEscaper(); 502 503 static class StoreIput implements Test { 504 /// CHECK-START: void TestDontOptimizeAcrossEscape$StoreIput.exercise() constructor_fence_redundancy_elimination (before) 505 /// CHECK: <<NewInstance:l\d+>> NewInstance 506 /// CHECK: ConstructorFence [<<NewInstance>>] 507 /// CHECK-DAG: InstanceFieldSet 508 /// CHECK: <<NewInstance2:l\d+>> NewInstance 509 /// CHECK-DAG: ConstructorFence [<<NewInstance2>>] 510 /// CHECK-NOT: ConstructorFence 511 512 /// CHECK-START: void TestDontOptimizeAcrossEscape$StoreIput.exercise() constructor_fence_redundancy_elimination (after) 513 /// CHECK-DAG: <<NewInstance:l\d+>> NewInstance 514 /// CHECK: ConstructorFence [<<NewInstance>>] 515 /// CHECK-DAG: <<NewInstance2:l\d+>> NewInstance 516 /// CHECK-DAG: ConstructorFence [<<NewInstance2>>] 517 /// CHECK-NOT: ConstructorFence 518 @Override exercise()519 public void exercise() { 520 Base b = new Base(); 521 522 // A store of 'b' into another instance will publish 'b'. 523 instanceEscaper.holder = b; 524 525 // Do not remove any constructor fences above. 526 Base b2 = new Base(); 527 528 // Do not LSE-eliminate b,b2 529 external3 = b; 530 external4 = b2; 531 } 532 533 @Override check()534 public void check() { 535 Assert.stringEquals( 536 "TestDontOptimizeAcrossEscape$InstanceEscaper(holder: Base(w0: 0, w1: 0, w2: 0, w3: 0))", 537 instanceEscaper); 538 Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external3); 539 Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external4); 540 } 541 } 542 543 static class StoreAput implements Test { 544 /// CHECK-START: void TestDontOptimizeAcrossEscape$StoreAput.exercise() constructor_fence_redundancy_elimination (before) 545 /// CHECK: <<NewInstance:l\d+>> NewInstance 546 /// CHECK: ConstructorFence [<<NewInstance>>] 547 /// CHECK-DAG: ArraySet 548 /// CHECK: <<NewInstance2:l\d+>> NewInstance 549 /// CHECK-DAG: ConstructorFence [<<NewInstance2>>] 550 /// CHECK-NOT: ConstructorFence 551 552 /// CHECK-START: void TestDontOptimizeAcrossEscape$StoreAput.exercise() constructor_fence_redundancy_elimination (after) 553 /// CHECK-DAG: <<NewInstance:l\d+>> NewInstance 554 /// CHECK: ConstructorFence [<<NewInstance>>] 555 /// CHECK-DAG: <<NewInstance2:l\d+>> NewInstance 556 /// CHECK-DAG: ConstructorFence [<<NewInstance2>>] 557 /// CHECK-NOT: ConstructorFence 558 @Override exercise()559 public void exercise() { 560 Base b = new Base(); 561 562 // A store of 'b' into another array will publish 'b'. 563 array[0] = b; // aput 564 565 // Do not remove any constructor fences above. 566 Base b2 = new Base(); 567 568 // Do not LSE-eliminate b,b2 569 external3 = b; 570 external4 = b2; 571 } 572 573 @Override check()574 public void check() { 575 Assert.stringEquals("[Base(w0: 0, w1: 0, w2: 0, w3: 0),<null>,<null>]", array); 576 Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external3); 577 Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external4); 578 } 579 } 580 581 static class StoreSput implements Test { 582 /// CHECK-START: void TestDontOptimizeAcrossEscape$StoreSput.exercise() constructor_fence_redundancy_elimination (before) 583 /// CHECK: <<NewInstance:l\d+>> NewInstance 584 /// CHECK: ConstructorFence [<<NewInstance>>] 585 /// CHECK-DAG: StaticFieldSet 586 /// CHECK: <<NewInstance2:l\d+>> NewInstance 587 /// CHECK-DAG: ConstructorFence [<<NewInstance2>>] 588 /// CHECK-NOT: ConstructorFence 589 590 /// CHECK-START: void TestDontOptimizeAcrossEscape$StoreSput.exercise() constructor_fence_redundancy_elimination (after) 591 /// CHECK-DAG: <<NewInstance:l\d+>> NewInstance 592 /// CHECK: ConstructorFence [<<NewInstance>>] 593 /// CHECK-DAG: <<NewInstance2:l\d+>> NewInstance 594 /// CHECK-DAG: ConstructorFence [<<NewInstance2>>] 595 /// CHECK-NOT: ConstructorFence 596 @Override exercise()597 public void exercise() { 598 Base b = new Base(); 599 600 // A store of 'b' into a static will publish 'b'. 601 external = b; 602 603 // Do not remove any constructor fences above. 604 Base b2 = new Base(); 605 606 // Do not LSE-eliminate b,b2 607 external3 = b; 608 external4 = b2; 609 } 610 611 @Override check()612 public void check() { 613 Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external); 614 Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external3); 615 Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external4); 616 } 617 } 618 619 static class Deopt implements Test { 620 /// CHECK-START: void TestDontOptimizeAcrossEscape$Deopt.exercise() constructor_fence_redundancy_elimination (before) 621 /// CHECK: <<NewInstance:l\d+>> NewInstance 622 /// CHECK: ConstructorFence [<<NewInstance>>] 623 /// CHECK-DAG: Deoptimize 624 /// CHECK: <<NewInstance2:l\d+>> NewInstance 625 /// CHECK-DAG: ConstructorFence [<<NewInstance2>>] 626 /// CHECK-NOT: ConstructorFence 627 628 /// CHECK-START: void TestDontOptimizeAcrossEscape$Deopt.exercise() constructor_fence_redundancy_elimination (after) 629 /// CHECK-DAG: <<NewInstance:l\d+>> NewInstance 630 /// CHECK: ConstructorFence [<<NewInstance>>] 631 /// CHECK-DAG: <<NewInstance2:l\d+>> NewInstance 632 /// CHECK-DAG: ConstructorFence [<<NewInstance2>>] 633 /// CHECK-NOT: ConstructorFence 634 @Override exercise()635 public void exercise() { 636 Base b = new Base(); 637 638 // An array access generates a Deopt to avoid doing bounds check. 639 array[0] = external; // aput 640 array[1] = external; // aput 641 array[2] = external; // aput 642 643 // Do not remove any constructor fences above. 644 Base b2 = new Base(); 645 646 // Do not LSE-eliminate b,b2 647 external3 = b; 648 external4 = b2; 649 } 650 651 @Override check()652 public void check() { 653 Assert.stringEquals("[Base(w0: 0, w1: 0, w2: 0, w3: 0)," 654 + "Base(w0: 0, w1: 0, w2: 0, w3: 0)," 655 + "Base(w0: 0, w1: 0, w2: 0, w3: 0)]", 656 array); 657 Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external3); 658 Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external4); 659 } 660 } 661 662 static class Select implements Test { 663 /// CHECK-START: void TestDontOptimizeAcrossEscape$Select.exercise() constructor_fence_redundancy_elimination (before) 664 /// CHECK: <<NewInstance:l\d+>> NewInstance 665 /// CHECK: ConstructorFence [<<NewInstance>>] 666 /// CHECK-DAG: Select 667 /// CHECK: <<NewInstance2:l\d+>> NewInstance 668 /// CHECK-DAG: ConstructorFence [<<NewInstance2>>] 669 /// CHECK-NOT: ConstructorFence 670 671 /// CHECK-START: void TestDontOptimizeAcrossEscape$Select.exercise() constructor_fence_redundancy_elimination (after) 672 /// CHECK-DAG: <<NewInstance:l\d+>> NewInstance 673 /// CHECK: ConstructorFence [<<NewInstance>>] 674 /// CHECK-DAG: <<NewInstance2:l\d+>> NewInstance 675 /// CHECK-DAG: ConstructorFence [<<NewInstance2>>] 676 /// CHECK-NOT: ConstructorFence 677 @Override exercise()678 public void exercise() { 679 Base b = new Base(); 680 681 boolean localTest = test; 682 Object localExternal = external3; 683 684 // Selecting 'b' creates an alias, which we conservatively assume escapes immediately. 685 external = localTest ? b : localExternal; 686 687 // Do not remove any constructor fences above. 688 Base b2 = new Base(); 689 690 // Do not LSE-eliminate b,b2 691 external3 = b; 692 external4 = b2; 693 } 694 695 @Override check()696 public void check() { 697 Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external); 698 Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external3); 699 Assert.stringEquals("Base(w0: 0, w1: 0, w2: 0, w3: 0)", external4); 700 } 701 } 702 703 static class MakeBoundTypeTest implements Test { 704 public static Object makeBoundType; 705 public static Object makeBoundTypeSub; 706 707 @Override exercise()708 public void exercise() { 709 // Note: MakeBoundType is special and we have to call the constructor directly 710 // to prevent inlining it. 711 try { 712 makeBoundType = exerciseNewInstance(MakeBoundType.class, 123); 713 makeBoundTypeSub = exerciseNewInstance(MakeBoundTypeSub.class, 123); 714 } catch (Exception e) { 715 throw new RuntimeException(e); 716 } 717 } 718 719 @Override check()720 public void check() { 721 Assert.stringEquals( 722 "TestDontOptimizeAcrossEscape$MakeBoundTypeTest$MakeBoundType(abcdefgh: 123, x: 2)", 723 makeBoundType); 724 Assert.stringEquals( 725 "TestDontOptimizeAcrossEscape$MakeBoundTypeTest$MakeBoundTypeSub(abcdefgh: 123, x: 1)", 726 makeBoundTypeSub); 727 } 728 729 // Make a new instance of 'klass'. exerciseNewInstance(Class<T> klass, int params)730 private static <T> T exerciseNewInstance(Class<T> klass, int params) throws Exception { 731 return klass.cast(klass.getDeclaredConstructor(int.class).newInstance(params)); 732 } 733 734 /// CHECK-START: void TestDontOptimizeAcrossEscape$MakeBoundTypeTest$MakeBoundType.<init>(int) constructor_fence_redundancy_elimination (before) 735 /// CHECK-DAG: <<This:l\d+>> ParameterValue 736 /// CHECK-DAG: <<NewInstance:l\d+>> NewInstance 737 /// CHECK: ConstructorFence [<<NewInstance>>] 738 /// CHECK-DAG: BoundType 739 /// CHECK-DAG: ConstructorFence [<<This>>] 740 /// CHECK-NOT: ConstructorFence 741 742 /// CHECK-START: void TestDontOptimizeAcrossEscape$MakeBoundTypeTest$MakeBoundType.<init>(int) constructor_fence_redundancy_elimination (after) 743 /// CHECK-DAG: <<This:l\d+>> ParameterValue 744 /// CHECK-DAG: <<NewInstance:l\d+>> NewInstance 745 /// CHECK: ConstructorFence [<<NewInstance>>] 746 /// CHECK-DAG: BoundType 747 /// CHECK-DAG: ConstructorFence [<<This>>] 748 /// CHECK-NOT: ConstructorFence 749 static class MakeBoundType { 750 final int abcdefgh; 751 int x; 752 MakeBoundType(int param)753 MakeBoundType(int param) { 754 abcdefgh = param; 755 756 Base b = new Base(); 757 // constructor-fence(b) 758 759 if (this instanceof MakeBoundTypeSub) { 760 // Create a "BoundType(this)" which prevents 761 // a merged constructor-fence(this, b) 762 x = 1; 763 } else { 764 x = 2; 765 } 766 767 // publish(b). 768 external = b; 769 770 // constructor-fence(this) 771 } 772 773 @Override toString()774 public String toString() { 775 return getClass().getName() + "(" + baseString() + ")"; 776 } 777 baseString()778 protected String baseString() { 779 return String.format("abcdefgh: %d, x: %d", abcdefgh, x); 780 } 781 } 782 783 static class MakeBoundTypeSub extends MakeBoundType { MakeBoundTypeSub(int xyz)784 MakeBoundTypeSub(int xyz) { 785 super(xyz); 786 } 787 } 788 } 789 } 790 791 public class Main { main(String[] args)792 public static void main(String[] args) throws Exception { 793 // Ensure that all of this code does not get optimized out into a no-op 794 // by actually running the code with reflection, then validating 795 // the result by asserting it against a string. 796 Class<? extends Test>[] testClasses = new Class[] { 797 TestOneFinal.class, 798 TestThreeFinal.class, 799 TestMultiAlloc.class, 800 TestThreeFinalTwice.class, 801 TestNonEscaping.Invoke.class, 802 TestNonEscaping.Store.class, 803 TestDontOptimizeAcrossBlocks.class, 804 TestDontOptimizeAcrossEscape.Invoke.class, 805 TestDontOptimizeAcrossEscape.StoreIput.class, 806 TestDontOptimizeAcrossEscape.StoreAput.class, 807 TestDontOptimizeAcrossEscape.StoreSput.class, 808 TestDontOptimizeAcrossEscape.Deopt.class, 809 TestDontOptimizeAcrossEscape.Select.class, 810 TestDontOptimizeAcrossEscape.MakeBoundTypeTest.class, 811 }; 812 813 for (Class<? extends Test> klass : testClasses) { 814 exerciseTestClass(klass); 815 } 816 } 817 818 /** 819 * Invoke Test#exercise(), then Test#check(). 820 * @throws AssertionError if test fails. 821 */ exerciseTestClass(Class<? extends Test> klass)822 private static void exerciseTestClass(Class<? extends Test> klass) throws Exception { 823 Test instance = klass.cast(klass.getDeclaredConstructor().newInstance()); 824 825 // Use reflection as a best-effort to avoid compiler optimizations (e.g. inlining). 826 instance.getClass().getDeclaredMethod("exercise").invoke(instance); 827 instance.getClass().getDeclaredMethod("check").invoke(instance); 828 } 829 830 // Print an object, with special handling for array and null. valueToString(Object val)831 public static String valueToString(Object val) { 832 if (val == null) { 833 return "<null>"; 834 } 835 if (val.getClass().isArray()) { 836 String fmt = "["; 837 int length = Array.getLength(val); 838 for (int i = 0; i < length; ++i) { 839 Object arrayElement = Array.get(val, i); 840 fmt += valueToString(arrayElement); 841 842 if (i != length - 1) { 843 fmt += ","; 844 } 845 } 846 fmt += "]"; 847 848 return fmt; 849 } 850 851 return val.toString(); 852 } 853 } 854