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