1 /* 2 * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 25 package org.graalvm.compiler.hotspot.test; 26 27 import java.util.List; 28 29 import jdk.internal.vm.compiler.collections.EconomicMap; 30 import org.graalvm.compiler.debug.DebugCloseable; 31 import org.graalvm.compiler.debug.DebugContext; 32 import org.graalvm.compiler.debug.DebugContext.Scope; 33 import org.graalvm.compiler.debug.DebugDumpScope; 34 import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig; 35 import org.graalvm.compiler.hotspot.gc.g1.G1ArrayRangePostWriteBarrier; 36 import org.graalvm.compiler.hotspot.gc.g1.G1ArrayRangePreWriteBarrier; 37 import org.graalvm.compiler.hotspot.gc.g1.G1PostWriteBarrier; 38 import org.graalvm.compiler.hotspot.gc.g1.G1PreWriteBarrier; 39 import org.graalvm.compiler.hotspot.gc.shared.SerialArrayRangeWriteBarrier; 40 import org.graalvm.compiler.hotspot.gc.shared.SerialWriteBarrier; 41 import org.graalvm.compiler.hotspot.phases.WriteBarrierAdditionPhase; 42 import org.graalvm.compiler.hotspot.phases.WriteBarrierVerificationPhase; 43 import org.graalvm.compiler.nodes.AbstractBeginNode; 44 import org.graalvm.compiler.nodes.AbstractMergeNode; 45 import org.graalvm.compiler.nodes.FieldLocationIdentity; 46 import org.graalvm.compiler.nodes.FixedNode; 47 import org.graalvm.compiler.nodes.FixedWithNextNode; 48 import org.graalvm.compiler.nodes.LoopBeginNode; 49 import org.graalvm.compiler.nodes.LoopExitNode; 50 import org.graalvm.compiler.nodes.StructuredGraph; 51 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions; 52 import org.graalvm.compiler.nodes.memory.WriteNode; 53 import org.graalvm.compiler.nodes.spi.LoweringTool; 54 import org.graalvm.compiler.phases.OptimisticOptimizations; 55 import org.graalvm.compiler.phases.common.CanonicalizerPhase; 56 import org.graalvm.compiler.phases.common.GuardLoweringPhase; 57 import org.graalvm.compiler.phases.common.LoopSafepointInsertionPhase; 58 import org.graalvm.compiler.phases.common.LoweringPhase; 59 import org.graalvm.compiler.phases.graph.ReentrantNodeIterator; 60 import org.graalvm.compiler.phases.graph.ReentrantNodeIterator.NodeIteratorClosure; 61 import org.graalvm.compiler.phases.tiers.HighTierContext; 62 import org.graalvm.compiler.phases.tiers.MidTierContext; 63 import jdk.internal.vm.compiler.word.LocationIdentity; 64 import org.junit.Assert; 65 import org.junit.Assume; 66 import org.junit.Before; 67 import org.junit.Test; 68 69 import jdk.vm.ci.meta.ResolvedJavaField; 70 71 /** 72 * The following tests validate the write barrier verification phase. For every tested snippet, an 73 * array of write barrier indices and the total write barrier number are passed as parameters. The 74 * indices denote the barriers that will be manually removed. The write barrier verification phase 75 * runs after the write barrier removal and depending on the result an assertion might be generated. 76 * The tests anticipate the presence or not of an assertion generated by the verification phase. 77 */ 78 public class WriteBarrierVerificationTest extends HotSpotGraalCompilerTest { 79 80 public static int barrierIndex; 81 82 private final GraalHotSpotVMConfig config = runtime().getVMConfig(); 83 84 public static class Container { 85 86 public Container a; 87 public Container b; 88 } 89 90 private static native void safepoint(); 91 92 public static void test1Snippet(Container main) { 93 Container temp1 = new Container(); 94 Container temp2 = new Container(); 95 barrierIndex = 0; 96 safepoint(); 97 barrierIndex = 1; 98 main.a = temp1; 99 safepoint(); 100 barrierIndex = 2; 101 main.b = temp2; 102 safepoint(); 103 } 104 105 @Before 106 public void filterNoBarriers() { 107 // Epsilon does not produce barriers, therefore the test makes little sense 108 // for it. Some test cases expect AssertionErrors to be thrown on manual barrier 109 // removals, but there are no Epsilon barriers to begin with. 110 Assume.assumeTrue(!config.useEpsilonGC); 111 } 112 113 @Test(expected = AssertionError.class) 114 public void test1() { 115 test("test1Snippet", 2, new int[]{1}); 116 } 117 118 @Test(expected = AssertionError.class) 119 public void test2() { 120 test("test1Snippet", 2, new int[]{2}); 121 } 122 123 public static void test2Snippet(Container main) { 124 Container temp1 = new Container(); 125 Container temp2 = new Container(); 126 barrierIndex = 0; 127 safepoint(); 128 barrierIndex = 1; 129 main.a = temp1; 130 barrierIndex = 2; 131 main.b = temp2; 132 safepoint(); 133 } 134 135 @Test(expected = AssertionError.class) 136 public void test3() { 137 test("test2Snippet", 2, new int[]{1}); 138 } 139 140 @Test 141 public void test4() { 142 test("test2Snippet", 2, new int[]{2}); 143 } 144 145 public static void test3Snippet(Container main, boolean test) { 146 Container temp1 = new Container(); 147 Container temp2 = new Container(); 148 barrierIndex = 0; 149 safepoint(); 150 for (int i = 0; i < 10; i++) { 151 if (test) { 152 barrierIndex = 1; 153 main.a = temp1; 154 barrierIndex = 2; 155 main.b = temp2; 156 } else { 157 barrierIndex = 3; 158 main.a = temp1; 159 barrierIndex = 4; 160 main.b = temp2; 161 } 162 } 163 } 164 165 @Test(expected = AssertionError.class) 166 public void test5() { 167 test("test3Snippet", 4, new int[]{1, 2}); 168 } 169 170 @Test(expected = AssertionError.class) 171 public void test6() { 172 test("test3Snippet", 4, new int[]{3, 4}); 173 } 174 175 @Test(expected = AssertionError.class) 176 public void test7() { 177 test("test3Snippet", 4, new int[]{1}); 178 } 179 180 @Test 181 public void test8() { 182 test("test3Snippet", 4, new int[]{2}); 183 } 184 185 @Test(expected = AssertionError.class) 186 public void test9() { 187 test("test3Snippet", 4, new int[]{3}); 188 } 189 190 @Test 191 public void test10() { 192 test("test3Snippet", 4, new int[]{4}); 193 } 194 195 public static void test4Snippet(Container main, boolean test) { 196 Container temp1 = new Container(); 197 Container temp2 = new Container(); 198 safepoint(); 199 barrierIndex = 1; 200 main.a = temp1; 201 for (int i = 0; i < 10; i++) { 202 if (test) { 203 barrierIndex = 2; 204 main.a = temp1; 205 barrierIndex = 3; 206 main.b = temp2; 207 } else { 208 barrierIndex = 4; 209 main.a = temp2; 210 barrierIndex = 5; 211 main.b = temp1; 212 } 213 } 214 } 215 216 @Test(expected = AssertionError.class) 217 public void test11() { 218 test("test4Snippet", 5, new int[]{2, 3}); 219 } 220 221 @Test(expected = AssertionError.class) 222 public void test12() { 223 test("test4Snippet", 5, new int[]{4, 5}); 224 } 225 226 @Test(expected = AssertionError.class) 227 public void test13() { 228 test("test4Snippet", 5, new int[]{1}); 229 } 230 231 public static void test5Snippet(Container main) { 232 Container temp1 = new Container(); 233 Container temp2 = new Container(); 234 safepoint(); 235 barrierIndex = 1; 236 main.a = temp1; 237 if (main.a == main.b) { 238 barrierIndex = 2; 239 main.a = temp1; 240 barrierIndex = 3; 241 main.b = temp2; 242 } else { 243 barrierIndex = 4; 244 main.a = temp2; 245 barrierIndex = 5; 246 main.b = temp1; 247 } 248 safepoint(); 249 } 250 251 @Test(expected = AssertionError.class) 252 public void test14() { 253 test("test5Snippet", 5, new int[]{1}); 254 } 255 256 @Test 257 public void test15() { 258 test("test5Snippet", 5, new int[]{2}); 259 } 260 261 @Test 262 public void test16() { 263 test("test5Snippet", 5, new int[]{4}); 264 } 265 266 @Test 267 public void test17() { 268 test("test5Snippet", 5, new int[]{3}); 269 } 270 271 @Test 272 public void test18() { 273 test("test5Snippet", 5, new int[]{5}); 274 } 275 276 @Test 277 public void test19() { 278 test("test5Snippet", 5, new int[]{2, 3}); 279 } 280 281 @Test 282 public void test20() { 283 test("test5Snippet", 5, new int[]{4, 5}); 284 } 285 286 public static void test6Snippet(Container main, boolean test) { 287 Container temp1 = new Container(); 288 Container temp2 = new Container(); 289 safepoint(); 290 barrierIndex = 1; 291 main.a = temp1; 292 if (test) { 293 barrierIndex = 2; 294 main.a = temp1; 295 barrierIndex = 3; 296 main.b = temp1.a.a; 297 } else { 298 barrierIndex = 4; 299 main.a = temp2; 300 barrierIndex = 5; 301 main.b = temp2.a.a; 302 } 303 safepoint(); 304 } 305 306 @Test(expected = AssertionError.class) 307 public void test21() { 308 test("test6Snippet", 5, new int[]{1}); 309 } 310 311 @Test(expected = AssertionError.class) 312 public void test22() { 313 test("test6Snippet", 5, new int[]{1, 2}); 314 } 315 316 @Test 317 public void test23() { 318 test("test6Snippet", 5, new int[]{3}); 319 } 320 321 @Test 322 public void test24() { 323 test("test6Snippet", 5, new int[]{4}); 324 } 325 326 public static void test7Snippet(Container main, boolean test) { 327 Container temp1 = new Container(); 328 Container temp2 = new Container(); 329 safepoint(); 330 barrierIndex = 1; 331 main.a = temp1; 332 if (test) { 333 barrierIndex = 2; 334 main.a = temp1; 335 } 336 barrierIndex = 3; 337 main.b = temp2; 338 safepoint(); 339 } 340 341 @Test 342 public void test25() { 343 test("test7Snippet", 3, new int[]{2}); 344 } 345 346 @Test 347 public void test26() { 348 test("test7Snippet", 3, new int[]{3}); 349 } 350 351 @Test 352 public void test27() { 353 test("test7Snippet", 3, new int[]{2, 3}); 354 } 355 356 @Test(expected = AssertionError.class) 357 public void test28() { 358 test("test7Snippet", 3, new int[]{1}); 359 } 360 361 public static void test8Snippet(Container main, boolean test) { 362 Container temp1 = new Container(); 363 Container temp2 = new Container(); 364 safepoint(); 365 if (test) { 366 barrierIndex = 1; 367 main.a = temp1; 368 } 369 barrierIndex = 2; 370 main.b = temp2; 371 safepoint(); 372 } 373 374 @Test(expected = AssertionError.class) 375 public void test29() { 376 test("test8Snippet", 2, new int[]{1}); 377 } 378 379 @Test(expected = AssertionError.class) 380 public void test30() { 381 test("test8Snippet", 2, new int[]{2}); 382 } 383 384 @Test(expected = AssertionError.class) 385 public void test31() { 386 test("test8Snippet", 2, new int[]{1, 2}); 387 } 388 389 public static void test9Snippet(Container main1, Container main2, boolean test) { 390 Container temp1 = new Container(); 391 Container temp2 = new Container(); 392 safepoint(); 393 if (test) { 394 barrierIndex = 1; 395 main1.a = temp1; 396 } else { 397 barrierIndex = 2; 398 main2.a = temp1; 399 } 400 barrierIndex = 3; 401 main1.b = temp2; 402 barrierIndex = 4; 403 main2.b = temp2; 404 safepoint(); 405 } 406 407 @Test(expected = AssertionError.class) 408 public void test32() { 409 test("test9Snippet", 4, new int[]{1}); 410 } 411 412 @Test(expected = AssertionError.class) 413 public void test33() { 414 test("test9Snippet", 4, new int[]{2}); 415 } 416 417 @Test(expected = AssertionError.class) 418 public void test34() { 419 test("test9Snippet", 4, new int[]{3}); 420 } 421 422 @Test(expected = AssertionError.class) 423 public void test35() { 424 test("test9Snippet", 4, new int[]{4}); 425 } 426 427 @Test(expected = AssertionError.class) 428 public void test36() { 429 test("test9Snippet", 4, new int[]{1, 2}); 430 } 431 432 @Test(expected = AssertionError.class) 433 public void test37() { 434 test("test9Snippet", 4, new int[]{3, 4}); 435 } 436 437 public static void test10Snippet(Container main1, Container main2, boolean test) { 438 Container temp1 = new Container(); 439 Container temp2 = new Container(); 440 safepoint(); 441 if (test) { 442 barrierIndex = 1; 443 main1.a = temp1; 444 barrierIndex = 2; 445 main2.a = temp2; 446 } else { 447 barrierIndex = 3; 448 main2.a = temp1; 449 } 450 barrierIndex = 4; 451 main1.b = temp2; 452 barrierIndex = 5; 453 main2.b = temp2; 454 safepoint(); 455 } 456 457 @Test(expected = AssertionError.class) 458 public void test38() { 459 test("test10Snippet", 5, new int[]{1}); 460 } 461 462 @Test(expected = AssertionError.class) 463 public void test39() { 464 test("test10Snippet", 5, new int[]{2}); 465 } 466 467 @Test(expected = AssertionError.class) 468 public void test40() { 469 test("test10Snippet", 5, new int[]{3}); 470 } 471 472 @Test(expected = AssertionError.class) 473 public void test41() { 474 test("test10Snippet", 5, new int[]{4}); 475 } 476 477 @Test 478 public void test42() { 479 test("test10Snippet", 5, new int[]{5}); 480 } 481 482 @Test(expected = AssertionError.class) 483 public void test43() { 484 test("test10Snippet", 5, new int[]{1, 2}); 485 } 486 487 @Test(expected = AssertionError.class) 488 public void test44() { 489 test("test10Snippet", 5, new int[]{1, 2, 3}); 490 } 491 492 @Test(expected = AssertionError.class) 493 public void test45() { 494 test("test10Snippet", 5, new int[]{3, 4}); 495 } 496 497 public static void test11Snippet(Container main1, Container main2, Container main3, boolean test) { 498 Container temp1 = new Container(); 499 Container temp2 = new Container(); 500 safepoint(); 501 if (test) { 502 barrierIndex = 1; 503 main1.a = temp1; 504 barrierIndex = 2; 505 main3.a = temp1; 506 if (!test) { 507 barrierIndex = 3; 508 main2.a = temp2; 509 } else { 510 barrierIndex = 4; 511 main1.a = temp2; 512 barrierIndex = 5; 513 main3.a = temp2; 514 } 515 } else { 516 barrierIndex = 6; 517 main1.b = temp2; 518 for (int i = 0; i < 10; i++) { 519 barrierIndex = 7; 520 main3.a = temp1; 521 } 522 barrierIndex = 8; 523 main3.b = temp2; 524 } 525 barrierIndex = 9; 526 main1.b = temp2; 527 barrierIndex = 10; 528 main2.b = temp2; 529 barrierIndex = 11; 530 main3.b = temp2; 531 safepoint(); 532 } 533 534 @Test(expected = AssertionError.class) 535 public void test46() { 536 test("test11Snippet", 11, new int[]{1}); 537 } 538 539 @Test(expected = AssertionError.class) 540 public void test47() { 541 test("test11Snippet", 11, new int[]{2}); 542 } 543 544 @Test(expected = AssertionError.class) 545 public void test48() { 546 test("test11Snippet", 11, new int[]{3}); 547 } 548 549 @Test(expected = AssertionError.class) 550 public void test49() { 551 test("test11Snippet", 11, new int[]{6}); 552 } 553 554 @Test(expected = AssertionError.class) 555 public void test50() { 556 test("test11Snippet", 11, new int[]{7}); 557 } 558 559 @Test(expected = AssertionError.class) 560 public void test51() { 561 test("test11Snippet", 11, new int[]{8}); 562 } 563 564 @Test(expected = AssertionError.class) 565 public void test52() { 566 test("test11Snippet", 11, new int[]{9}); 567 } 568 569 @Test(expected = AssertionError.class) 570 public void test53() { 571 test("test11Snippet", 11, new int[]{10}); 572 } 573 574 @Test 575 public void test54() { 576 test("test11Snippet", 11, new int[]{4}); 577 } 578 579 @Test 580 public void test55() { 581 test("test11Snippet", 11, new int[]{5}); 582 } 583 584 @Test 585 public void test56() { 586 test("test11Snippet", 11, new int[]{11}); 587 } 588 589 public static void test12Snippet(Container main, Container main1, boolean test) { 590 Container temp1 = new Container(); 591 Container temp2 = new Container(); 592 barrierIndex = 0; 593 safepoint(); 594 barrierIndex = 7; 595 main1.a = temp1; 596 for (int i = 0; i < 10; i++) { 597 if (test) { 598 barrierIndex = 1; 599 main.a = temp1; 600 barrierIndex = 2; 601 main.b = temp2; 602 } else { 603 barrierIndex = 3; 604 main.a = temp1; 605 barrierIndex = 4; 606 main.b = temp2; 607 } 608 } 609 barrierIndex = 5; 610 main.a = temp1; 611 barrierIndex = 6; 612 main.b = temp1; 613 barrierIndex = 8; 614 main1.b = temp1; 615 safepoint(); 616 } 617 618 @Test(expected = AssertionError.class) 619 public void test57() { 620 test("test12Snippet", 8, new int[]{5}); 621 } 622 623 @Test 624 public void test58() { 625 test("test12Snippet", 8, new int[]{6}); 626 } 627 628 @Test(expected = AssertionError.class) 629 public void test59() { 630 test("test12Snippet", 8, new int[]{7}); 631 } 632 633 @Test(expected = AssertionError.class) 634 public void test60() { 635 test("test12Snippet", 8, new int[]{8}); 636 } 637 638 public static void test13Snippet(Object[] a, Object[] b) { 639 System.arraycopy(a, 0, b, 0, a.length); 640 } 641 642 private interface GraphPredicate { 643 int apply(StructuredGraph graph); 644 } 645 646 private void test(final String snippet, final int expectedBarriers, final int... removedBarrierIndices) { 647 GraphPredicate noCheck = noArg -> expectedBarriers; 648 testPredicate(snippet, noCheck, removedBarrierIndices); 649 } 650 651 @SuppressWarnings("try") 652 private void testPredicate(final String snippet, final GraphPredicate expectedBarriers, final int... removedBarrierIndices) { 653 DebugContext debug = getDebugContext(); 654 try (DebugCloseable d = debug.disableIntercept(); DebugContext.Scope s = debug.scope("WriteBarrierVerificationTest", new DebugDumpScope(snippet))) { 655 final StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES, debug); 656 HighTierContext highTierContext = getDefaultHighTierContext(); 657 createInliningPhase().apply(graph, highTierContext); 658 659 MidTierContext midTierContext = new MidTierContext(getProviders(), getTargetProvider(), OptimisticOptimizations.ALL, graph.getProfilingInfo()); 660 661 new LoweringPhase(new CanonicalizerPhase(), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, highTierContext); 662 new GuardLoweringPhase().apply(graph, midTierContext); 663 new LoopSafepointInsertionPhase().apply(graph); 664 new LoweringPhase(new CanonicalizerPhase(), LoweringTool.StandardLoweringStage.MID_TIER).apply(graph, highTierContext); 665 666 new WriteBarrierAdditionPhase(config).apply(graph); 667 668 int barriers = 0; 669 // First, the total number of expected barriers is checked. 670 if (config.useG1GC) { 671 barriers = graph.getNodes().filter(G1PreWriteBarrier.class).count() + graph.getNodes().filter(G1PostWriteBarrier.class).count() + 672 graph.getNodes().filter(G1ArrayRangePreWriteBarrier.class).count() + graph.getNodes().filter(G1ArrayRangePostWriteBarrier.class).count(); 673 Assert.assertTrue(expectedBarriers.apply(graph) * 2 == barriers); 674 } else { 675 barriers = graph.getNodes().filter(SerialWriteBarrier.class).count() + graph.getNodes().filter(SerialArrayRangeWriteBarrier.class).count(); 676 Assert.assertTrue(expectedBarriers.apply(graph) == barriers); 677 } 678 ResolvedJavaField barrierIndexField = getMetaAccess().lookupJavaField(WriteBarrierVerificationTest.class.getDeclaredField("barrierIndex")); 679 LocationIdentity barrierIdentity = new FieldLocationIdentity(barrierIndexField); 680 // Iterate over all write nodes and remove barriers according to input indices. 681 NodeIteratorClosure<Boolean> closure = new NodeIteratorClosure<Boolean>() { 682 683 @Override 684 protected Boolean processNode(FixedNode node, Boolean currentState) { 685 if (node instanceof WriteNode) { 686 WriteNode write = (WriteNode) node; 687 LocationIdentity obj = write.getLocationIdentity(); 688 if (obj.equals(barrierIdentity)) { 689 /* 690 * A "barrierIndex" variable was found and is checked against the input 691 * barrier array. 692 */ 693 if (eliminateBarrier(write.value().asJavaConstant().asInt(), removedBarrierIndices)) { 694 return true; 695 } 696 } 697 } else if (node instanceof SerialWriteBarrier || node instanceof G1PostWriteBarrier) { 698 // Remove flagged write barriers. 699 if (currentState) { 700 graph.removeFixed(((FixedWithNextNode) node)); 701 return false; 702 } 703 } 704 return currentState; 705 } 706 707 private boolean eliminateBarrier(int index, int[] map) { 708 for (int i = 0; i < map.length; i++) { 709 if (map[i] == index) { 710 return true; 711 } 712 } 713 return false; 714 } 715 716 @Override 717 protected EconomicMap<LoopExitNode, Boolean> processLoop(LoopBeginNode loop, Boolean initialState) { 718 return ReentrantNodeIterator.processLoop(this, loop, initialState).exitStates; 719 } 720 721 @Override 722 protected Boolean merge(AbstractMergeNode merge, List<Boolean> states) { 723 return false; 724 } 725 726 @Override 727 protected Boolean afterSplit(AbstractBeginNode node, Boolean oldState) { 728 return false; 729 } 730 }; 731 732 try (Scope disabled = debug.disable()) { 733 ReentrantNodeIterator.apply(closure, graph.start(), false); 734 new WriteBarrierVerificationPhase(config).apply(graph); 735 } catch (AssertionError error) { 736 /* 737 * Catch assertion, test for expected one and re-throw in order to validate unit 738 * test. 739 */ 740 Assert.assertTrue(error.getMessage().contains("Write barrier must be present")); 741 throw error; 742 } 743 } catch (Throwable e) { 744 throw debug.handle(e); 745 } 746 } 747 }