1 /* 2 * Copyright (c) 2016, 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 // TODO add bugid and summary 25 26 /* 27 * @test 28 * @library /testlibrary /test/lib /compiler/whitebox / 29 * @build compiler.valhalla.valuetypes.ValueTypeTestBench 30 * @run main ClassFileInstaller sun.hotspot.WhiteBox 31 * @run main ClassFileInstaller jdk.test.lib.Platform 32 * @run main/othervm -ea -noverify -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 33 * compiler.valhalla.valuetypes.ValueTypeTestBench 34 * @run main/othervm -ea -noverify -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 35 * -XX:+UnlockExperimentalVMOptions -XX:-ValueTypePassFieldsAsArgs 36 * compiler.valhalla.valuetypes.ValueTypeTestBench 37 */ 38 39 package compiler.valhalla.valuetypes; 40 41 import compiler.whitebox.CompilerWhiteBoxTest; 42 import jdk.internal.misc.Unsafe; 43 import jdk.test.lib.Asserts; 44 import jdk.test.lib.Platform; 45 import jdk.test.lib.ProcessTools; 46 import jdk.test.lib.OutputAnalyzer; 47 import jdk.test.lib.Utils; 48 import sun.hotspot.WhiteBox; 49 50 import java.lang.annotation.Retention; 51 import java.lang.annotation.RetentionPolicy; 52 import java.lang.annotation.Repeatable; 53 import java.lang.reflect.Method; 54 import java.util.ArrayList; 55 import java.util.Arrays; 56 import java.util.Hashtable; 57 import java.util.List; 58 import java.util.regex.Matcher; 59 import java.util.regex.Pattern; 60 61 // Test value types 62 __ByValue final class MyValue1 { 63 final int x; 64 final long y; 65 final MyValue2 v1; 66 final MyValue2 v2; 67 final int c; 68 69 private MyValue1(int x, long y, MyValue2 v1, MyValue2 v2, int c) { 70 this.x = x; 71 this.y = y; 72 this.v1 = v1; 73 this.v2 = v2; 74 this.c = c; 75 } 76 77 @DontInline 78 public static MyValue1 createDontInline(int x, long y) { 79 return __Make MyValue1(x, y, MyValue2.createInline(x, x < y), MyValue2.createInline(x, x > y), ValueTypeTestBench.rI); 80 } 81 82 @ForceInline 83 public static MyValue1 createInline(int x, long y) { 84 return __Make MyValue1(x, y, MyValue2.createInline(x, x < y), MyValue2.createInline(x, x > y), ValueTypeTestBench.rI); 85 } 86 87 @ForceInline 88 public long hash() { 89 return x + y + c + v1.hash() + v2.hash(); 90 } 91 92 @DontCompile 93 public long hashInterpreted() { 94 return x + y + c + v1.hashInterpreted() + v2.hashInterpreted(); 95 } 96 } 97 98 __ByValue final class MyValue2 { 99 final int x; 100 final boolean b; 101 final long c; 102 103 private MyValue2(int x, boolean b, long c) { 104 this.x = x; 105 this.b = b; 106 this.c = c; 107 } 108 109 @ForceInline 110 public static MyValue2 createInline(int x, boolean b) { 111 return __Make MyValue2(x, b, ValueTypeTestBench.rL); 112 } 113 114 @ForceInline 115 public long hash() { 116 return x + (b ? 0 : 1) + c; 117 } 118 119 @DontInline 120 public long hashInterpreted() { 121 return x + (b ? 0 : 1) + c; 122 } 123 } 124 125 public class ValueTypeTestBench { 126 // Print ideal graph after execution of each test 127 private static final boolean PRINT_GRAPH = true; 128 129 // ========== Helper methods ========== 130 131 public long hash() { 132 return hash(rI, rL); 133 } 134 135 public long hash(int x, long y) { 136 return MyValue1.createInline(x, y).hash(); 137 } 138 139 // ========== Test definitions ========== 140 141 // Receive value type through call to interpreter 142 @Test(failOn = ALLOC + STORE + TRAP) 143 public long test1() { 144 MyValue1 v = MyValue1.createDontInline(rI, rL); 145 return v.hash(); 146 } 147 148 @DontCompile 149 public void test1_verifier(boolean warmup) { 150 long result = test1(); 151 Asserts.assertEQ(result, hash()); 152 } 153 154 // Receive value type from interpreter via parameter 155 @Test(failOn = ALLOC + STORE + TRAP) 156 public long test2(MyValue1 v) { 157 return v.hash(); 158 } 159 160 @DontCompile 161 public void test2_verifier(boolean warmup) { 162 MyValue1 v = MyValue1.createDontInline(rI, rL); 163 long result = test2(v); 164 Asserts.assertEQ(result, hash()); 165 } 166 167 // Return incoming value type without accessing fields 168 @Test(valid = ValueTypePassFieldsAsArgsOn, match = {ALLOC, STORE}, matchCount = {1, 9}, failOn = LOAD + TRAP) 169 @Test(valid = ValueTypePassFieldsAsArgsOff, failOn = ALLOC + LOAD + STORE + TRAP) 170 public MyValue1 test3(MyValue1 v) { 171 return v; 172 } 173 174 @DontCompile 175 public void test3_verifier(boolean warmup) { 176 MyValue1 v1 = MyValue1.createDontInline(rI, rL); 177 MyValue1 v2 = test3(v1); 178 Asserts.assertEQ(v1.x, v2.x); 179 Asserts.assertEQ(v1.y, v2.y); 180 } 181 182 // Create a value type in compiled code and only use fields. 183 // Allocation should go away because value type does not escape. 184 @Test(failOn = ALLOC + LOAD + STORE + TRAP) 185 public long test4() { 186 MyValue1 v = MyValue1.createInline(rI, rL); 187 return v.hash(); 188 } 189 190 @DontCompile 191 public void test4_verifier(boolean warmup) { 192 long result = test4(); 193 Asserts.assertEQ(result, hash()); 194 } 195 196 // Create a value type in compiled code and pass it to 197 // an inlined compiled method via a call. 198 @Test(failOn = ALLOC + LOAD + STORE + TRAP) 199 public long test5() { 200 MyValue1 v = MyValue1.createInline(rI, rL); 201 return test5Inline(v); 202 } 203 204 @ForceInline 205 public long test5Inline(MyValue1 v) { 206 return v.hash(); 207 } 208 209 @DontCompile 210 public void test5_verifier(boolean warmup) { 211 long result = test5(); 212 Asserts.assertEQ(result, hash()); 213 } 214 215 // Create a value type in compiled code and pass it to 216 // the interpreter via a call. 217 @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = LOAD + TRAP + ALLOC) 218 @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP) 219 public long test6() { 220 MyValue1 v = MyValue1.createInline(rI, rL); 221 // Pass to interpreter 222 return v.hashInterpreted(); 223 } 224 225 @DontCompile 226 public void test6_verifier(boolean warmup) { 227 long result = test6(); 228 Asserts.assertEQ(result, hash()); 229 } 230 231 // Create a value type in compiled code and pass it to 232 // the interpreter by returning. 233 @Test(match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP) 234 public MyValue1 test7(int x, long y) { 235 return MyValue1.createInline(x, y); 236 } 237 238 @DontCompile 239 public void test7_verifier(boolean warmup) { 240 MyValue1 v = test7(rI, rL); 241 Asserts.assertEQ(v.hash(), hash()); 242 } 243 244 // Merge value types created from two branches 245 @Test(failOn = ALLOC + STORE + TRAP) 246 public long test8(boolean b) { 247 MyValue1 v; 248 if (b) { 249 v = MyValue1.createInline(rI, rL); 250 } else { 251 v = MyValue1.createDontInline(rI + 1, rL + 1); 252 } 253 return v.hash(); 254 } 255 256 @DontCompile 257 public void test8_verifier(boolean warmup) { 258 Asserts.assertEQ(test8(true), hash()); 259 Asserts.assertEQ(test8(false), hash(rI + 1, rL + 1)); 260 } 261 262 // Merge value types created from two branches 263 @Test(valid = ValueTypePassFieldsAsArgsOn, match = {LOAD}, matchCount = {9}, failOn = TRAP + ALLOC + STORE) 264 @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC, STORE}, matchCount = {1, 9}, failOn = LOAD + TRAP) 265 public MyValue1 test9(boolean b) { 266 MyValue1 v; 267 if (b) { 268 // Value type is not allocated 269 v = MyValue1.createInline(rI, rL); 270 } else { 271 // Value type is allocated by the callee 272 v = MyValue1.createDontInline(rI + 1, rL + 1); 273 } 274 // Need to allocate value type if 'b' is true 275 long sum = v.hashInterpreted(); 276 if (b) { 277 v = MyValue1.createDontInline(rI, sum); 278 } else { 279 v = MyValue1.createDontInline(rI, sum + 1); 280 } 281 // Don't need to allocate value type because both branches allocate 282 return v; 283 } 284 285 @DontCompile 286 public void test9_verifier(boolean warmup) { 287 MyValue1 v = test9(true); 288 Asserts.assertEQ(v.x, rI); 289 Asserts.assertEQ(v.y, hash()); 290 v = test9(false); 291 Asserts.assertEQ(v.x, rI); 292 Asserts.assertEQ(v.y, hash(rI + 1, rL + 1) + 1); 293 } 294 295 // Merge value types created in a loop (not inlined) 296 @Test(failOn = ALLOC + STORE + TRAP) 297 public long test10(int x, long y) { 298 MyValue1 v = MyValue1.createDontInline(x, y); 299 for (int i = 0; i < 10; ++i) { 300 v = MyValue1.createDontInline(v.x + 1, v.y + 1); 301 } 302 return v.hash(); 303 } 304 305 @DontCompile 306 public void test10_verifier(boolean warmup) { 307 long result = test10(rI, rL); 308 Asserts.assertEQ(result, hash(rI + 10, rL + 10)); 309 } 310 311 // Merge value types created in a loop (inlined) 312 @Test(failOn = ALLOC + LOAD + STORE + LOOP + TRAP) 313 public long test11(int x, long y) { 314 MyValue1 v = MyValue1.createInline(x, y); 315 for (int i = 0; i < 10; ++i) { 316 v = MyValue1.createInline(v.x + 1, v.y + 1); 317 } 318 return v.hash(); 319 } 320 321 @DontCompile 322 public void test11_verifier(boolean warmup) { 323 long result = test11(rI, rL); 324 Asserts.assertEQ(result, hash(rI + 10, rL + 10)); 325 } 326 327 // Test loop with uncommon trap referencing a value type 328 @Test(match = {TRAP, SCOBJ}, matchCount = {1, 1}, failOn = ALLOC + LOAD + STORE) 329 public long test12(boolean b) { 330 MyValue1 v = MyValue1.createInline(rI, rL); 331 long result = 42; 332 for (int i = 0; i < 1000; ++i) { 333 if (b) { 334 result += v.x; 335 } else { 336 // Uncommon trap referencing v. We delegate allocation to the 337 // interpreter by adding a SafePointScalarObjectNode. 338 result = v.hashInterpreted(); 339 } 340 } 341 return result; 342 } 343 344 @DontCompile 345 public void test12_verifier(boolean warmup) { 346 long result = test12(warmup); 347 Asserts.assertEQ(result, warmup ? 42 + (1000*rI) : hash()); 348 } 349 350 // Test loop with uncommon trap referencing a value type 351 @Test(match = {TRAP, LOAD}, matchCount = {1, 1}, failOn = ALLOC + STORE + SCOBJ) 352 public long test13(boolean b) { 353 MyValue1 v = MyValue1.createDontInline(rI, rL); 354 long result = 42; 355 for (int i = 0; i < 1000; ++i) { 356 if (b) { 357 result += v.x; 358 } else { 359 // Uncommon trap referencing v. Should not allocate 360 // but just pass the existing oop to the uncommon trap. 361 result = v.hashInterpreted(); 362 } 363 } 364 return result; 365 } 366 367 @DontCompile 368 public void test13_verifier(boolean warmup) { 369 long result = test13(warmup); 370 Asserts.assertEQ(result, warmup ? 42 + (1000*rI) : hash()); 371 } 372 373 // Create a value type in a non-inlined method and then call a 374 // non-inlined method on that value type. 375 @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = (ALLOC + STORE + TRAP), match = {LOAD}, matchCount = {9}) 376 @Test(valid = ValueTypePassFieldsAsArgsOff, failOn = (ALLOC + LOAD + STORE + TRAP)) 377 public long test14() { 378 MyValue1 v = MyValue1.createDontInline(rI, rL); 379 return v.hashInterpreted(); 380 } 381 382 @DontCompile 383 public void test14_verifier(boolean b) { 384 long result = test14(); 385 Asserts.assertEQ(result, hash()); 386 } 387 388 // Create a value type in an inlined method and then call a 389 // non-inlined method on that value type. 390 @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = (LOAD + TRAP + ALLOC)) 391 @Test(valid = ValueTypePassFieldsAsArgsOff, failOn = (LOAD + TRAP), match = {ALLOC}, matchCount = {1}) 392 public long test15() { 393 MyValue1 v = MyValue1.createInline(rI, rL); 394 return v.hashInterpreted(); 395 } 396 397 @DontCompile 398 public void test15_verifier(boolean b) { 399 long result = test15(); 400 Asserts.assertEQ(result, hash()); 401 } 402 403 // Create a value type in a non-inlined method and then call an 404 // inlined method on that value type. 405 @Test(failOn = (ALLOC + STORE + TRAP)) 406 public long test16() { 407 MyValue1 v = MyValue1.createDontInline(rI, rL); 408 return v.hash(); 409 } 410 411 @DontCompile 412 public void test16_verifier(boolean b) { 413 long result = test16(); 414 Asserts.assertEQ(result, hash()); 415 } 416 417 // Create a value type in an inlined method and then call an 418 // inlined method on that value type. 419 @Test(failOn = (ALLOC + LOAD + STORE + TRAP)) 420 public long test17() { 421 MyValue1 v = MyValue1.createInline(rI, rL); 422 return v.hash(); 423 } 424 425 @DontCompile 426 public void test17_verifier(boolean b) { 427 long result = test17(); 428 Asserts.assertEQ(result, hash()); 429 } 430 431 // Create a value type in compiled code and pass it to the 432 // interpreter via a call. The value is live at the first call so 433 // debug info should include a reference to all its fields. 434 @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = ALLOC + LOAD + TRAP) 435 @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP) 436 public long test18() { 437 MyValue1 v = MyValue1.createInline(rI, rL); 438 v.hashInterpreted(); 439 return v.hashInterpreted(); 440 } 441 442 @DontCompile 443 public void test18_verifier(boolean warmup) { 444 long result = test18(); 445 Asserts.assertEQ(result, hash()); 446 } 447 448 // Create a value type in compiled code and pass it to the 449 // interpreter via a call. The value type is passed twice but 450 // should only be allocated once. 451 @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = ALLOC + LOAD + TRAP) 452 @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP) 453 public long test19() { 454 MyValue1 v = MyValue1.createInline(rI, rL); 455 return sumValue(v, v); 456 } 457 458 @DontCompile 459 public long sumValue(MyValue1 v, MyValue1 dummy) { 460 return v.hash(); 461 } 462 463 @DontCompile 464 public void test19_verifier(boolean warmup) { 465 long result = test19(); 466 Asserts.assertEQ(result, hash()); 467 } 468 469 // Create a value type in compiled code and pass it to the 470 // interpreter via a call. The value type is live at the uncommon 471 // trap: verify that deoptimization causes the value type to be 472 // correctly allocated. 473 @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = LOAD + ALLOC + STORE) 474 @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC}, matchCount = {1}, failOn = LOAD) 475 public long test20(boolean flag) { 476 MyValue1 v = MyValue1.createInline(rI, rL); 477 if (flag) { 478 // uncommon trap 479 WHITE_BOX.deoptimizeMethod(tests.get("ValueTypeTestBench::test20")); 480 } 481 return v.hashInterpreted(); 482 } 483 484 @DontCompile 485 public void test20_verifier(boolean warmup) { 486 long result = test20(false); 487 Asserts.assertEQ(result, hash()); 488 if (!warmup) { 489 result = test20(true); 490 Asserts.assertEQ(result, hash()); 491 } 492 } 493 494 // Value type fields in regular object 495 MyValue1 val1; 496 MyValue2 val2; 497 498 // Test value type fields in objects 499 @Test(failOn = (ALLOC + TRAP)) 500 public long test21(int x, long y) { 501 // Compute hash of value type fields 502 long result = val1.hash() + val2.hash(); 503 // Update fields 504 val1 = MyValue1.createInline(x, y); 505 val2 = MyValue2.createInline(x, true); 506 return result; 507 } 508 509 @DontCompile 510 public void test21_verifier(boolean warmup) { 511 // Check if hash computed by test18 is correct 512 val1 = MyValue1.createInline(rI, rL); 513 val2 = val1.v2; 514 long hash = val1.hash() + val2.hash(); 515 long result = test21(rI + 1, rL + 1); 516 Asserts.assertEQ(result, hash); 517 // Check if value type fields were updated 518 Asserts.assertEQ(val1.hash(), hash(rI + 1, rL + 1)); 519 Asserts.assertEQ(val2.hash(), MyValue2.createInline(rI + 1, true).hash()); 520 } 521 522 // Test interpreter to compiled code with various signatures 523 @Test(failOn = ALLOC + STORE + TRAP) 524 public long test22(MyValue2 v) { 525 return v.hash(); 526 } 527 528 @DontCompile 529 public void test22_verifier(boolean warmup) { 530 MyValue2 v = MyValue2.createInline(rI, true); 531 long result = test22(v); 532 Asserts.assertEQ(result, v.hashInterpreted()); 533 } 534 535 @Test(failOn = ALLOC + STORE + TRAP) 536 public long test23(int i1, MyValue2 v, int i2) { 537 return v.hash() + i1 - i2; 538 } 539 540 @DontCompile 541 public void test23_verifier(boolean warmup) { 542 MyValue2 v = MyValue2.createInline(rI, true); 543 long result = test23(rI, v, 2*rI); 544 Asserts.assertEQ(result, v.hashInterpreted() - rI); 545 } 546 547 @Test(failOn = ALLOC + STORE + TRAP) 548 public long test24(long l1, MyValue2 v, long l2) { 549 return v.hash() + l1 - l2; 550 } 551 552 @DontCompile 553 public void test24_verifier(boolean warmup) { 554 MyValue2 v = MyValue2.createInline(rI, true); 555 long result = test24(rL, v, 2*rL); 556 Asserts.assertEQ(result, v.hashInterpreted() - rL); 557 } 558 559 @Test(failOn = ALLOC + STORE + TRAP) 560 public long test25(int i, MyValue2 v, long l) { 561 return v.hash() + i + l; 562 } 563 564 @DontCompile 565 public void test25_verifier(boolean warmup) { 566 MyValue2 v = MyValue2.createInline(rI, true); 567 long result = test25(rI, v, rL); 568 Asserts.assertEQ(result, v.hashInterpreted() + rL + rI); 569 } 570 571 @Test(failOn = ALLOC + STORE + TRAP) 572 public long test26(long l, MyValue2 v, int i) { 573 return v.hash() + i + l; 574 } 575 576 @DontCompile 577 public void test26_verifier(boolean warmup) { 578 MyValue2 v = MyValue2.createInline(rI, true); 579 long result = test26(rL, v, rI); 580 Asserts.assertEQ(result, v.hashInterpreted() + rL + rI); 581 } 582 583 @Test(failOn = ALLOC + STORE + TRAP) 584 public long test27(long l, MyValue1 v1, int i, MyValue2 v2) { 585 return v1.hash() + i + l + v2.hash(); 586 } 587 588 @DontCompile 589 public void test27_verifier(boolean warmup) { 590 MyValue1 v1 = MyValue1.createDontInline(rI, rL); 591 MyValue2 v2 = MyValue2.createInline(rI, true); 592 long result = test27(rL, v1, rI, v2); 593 Asserts.assertEQ(result, v1.hashInterpreted() + rL + rI + v2.hashInterpreted()); 594 } 595 596 // Test compiled code to interpreter with various signatures 597 @DontCompile 598 public long test28_interp(MyValue2 v) { 599 return v.hash(); 600 } 601 602 @Test(failOn = ALLOC + STORE + TRAP) 603 public long test28(MyValue2 v) { 604 return test28_interp(v); 605 } 606 607 @DontCompile 608 public void test28_verifier(boolean warmup) { 609 MyValue2 v = MyValue2.createInline(rI, true); 610 long result = test28(v); 611 Asserts.assertEQ(result, v.hashInterpreted()); 612 } 613 614 @DontCompile 615 public long test29_interp(int i1, MyValue2 v, int i2) { 616 return v.hash() + i1 - i2; 617 } 618 619 @Test(failOn = ALLOC + STORE + TRAP) 620 public long test29(int i1, MyValue2 v, int i2) { 621 return test29_interp(i1, v, i2); 622 } 623 624 @DontCompile 625 public void test29_verifier(boolean warmup) { 626 MyValue2 v = MyValue2.createInline(rI, true); 627 long result = test29(rI, v, 2*rI); 628 Asserts.assertEQ(result, v.hashInterpreted() - rI); 629 } 630 631 @DontCompile 632 public long test30_interp(long l1, MyValue2 v, long l2) { 633 return v.hash() + l1 - l2; 634 } 635 636 @Test(failOn = ALLOC + STORE + TRAP) 637 public long test30(long l1, MyValue2 v, long l2) { 638 return test30_interp(l1, v, l2); 639 } 640 641 @DontCompile 642 public void test30_verifier(boolean warmup) { 643 MyValue2 v = MyValue2.createInline(rI, true); 644 long result = test30(rL, v, 2*rL); 645 Asserts.assertEQ(result, v.hashInterpreted() - rL); 646 } 647 648 @DontCompile 649 public long test31_interp(int i, MyValue2 v, long l) { 650 return v.hash() + i + l; 651 } 652 653 @Test(failOn = ALLOC + STORE + TRAP) 654 public long test31(int i, MyValue2 v, long l) { 655 return test31_interp(i, v, l); 656 } 657 658 @DontCompile 659 public void test31_verifier(boolean warmup) { 660 MyValue2 v = MyValue2.createInline(rI, true); 661 long result = test31(rI, v, rL); 662 Asserts.assertEQ(result, v.hashInterpreted() + rL + rI); 663 } 664 665 @DontCompile 666 public long test32_interp(long l, MyValue2 v, int i) { 667 return v.hash() + i + l; 668 } 669 670 @Test(failOn = ALLOC + STORE + TRAP) 671 public long test32(long l, MyValue2 v, int i) { 672 return test32_interp(l, v, i); 673 } 674 675 @DontCompile 676 public void test32_verifier(boolean warmup) { 677 MyValue2 v = MyValue2.createInline(rI, true); 678 long result = test32(rL, v, rI); 679 Asserts.assertEQ(result, v.hashInterpreted() + rL + rI); 680 } 681 682 @DontCompile 683 public long test33_interp(long l, MyValue1 v1, int i, MyValue2 v2) { 684 return v1.hash() + i + l + v2.hash(); 685 } 686 687 @Test(failOn = ALLOC + STORE + TRAP) 688 public long test33(long l, MyValue1 v1, int i, MyValue2 v2) { 689 return test33_interp(l, v1, i, v2); 690 } 691 692 @DontCompile 693 public void test33_verifier(boolean warmup) { 694 MyValue1 v1 = MyValue1.createDontInline(rI, rL); 695 MyValue2 v2 = MyValue2.createInline(rI, true); 696 long result = test33(rL, v1, rI, v2); 697 Asserts.assertEQ(result, v1.hashInterpreted() + rL + rI + v2.hashInterpreted()); 698 } 699 700 // test that debug info at a call is correct 701 @DontCompile 702 public long test34_interp(MyValue2 v, boolean flag) { 703 if (flag) { 704 // uncommon trap 705 WHITE_BOX.deoptimizeMethod(tests.get("ValueTypeTestBench::test34")); 706 } 707 return v.hash(); 708 } 709 710 @Test(failOn = ALLOC + STORE + TRAP) 711 public long test34(MyValue2 v, boolean flag, long l) { 712 return test34_interp(v, flag) + l; 713 } 714 715 @DontCompile 716 public void test34_verifier(boolean warmup) { 717 MyValue2 v = MyValue2.createInline(rI, true); 718 long result = test34(v, false, rL); 719 Asserts.assertEQ(result, v.hashInterpreted() + rL); 720 if (!warmup) { 721 result = test34(v, true, rL); 722 Asserts.assertEQ(result, v.hashInterpreted() + rL); 723 } 724 } 725 726 // ========== Test infrastructure ========== 727 728 private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); 729 private static final int ValueTypePassFieldsAsArgsOn = 0x1; 730 private static final int ValueTypePassFieldsAsArgsOff = 0x2; 731 static final int AllFlags = ValueTypePassFieldsAsArgsOn | ValueTypePassFieldsAsArgsOff; 732 private static final boolean ValueTypePassFieldsAsArgs = (Boolean)WHITE_BOX.getVMFlag("ValueTypePassFieldsAsArgs"); 733 private static final int COMP_LEVEL_ANY = -1; 734 private static final int COMP_LEVEL_FULL_OPTIMIZATION = 4; 735 private static final Hashtable<String, Method> tests = new Hashtable<String, Method>(); 736 private static final int WARMUP = 10; 737 private static boolean USE_COMPILER = WHITE_BOX.getBooleanVMFlag("UseCompiler"); 738 private static boolean PRINT_IDEAL = WHITE_BOX.getBooleanVMFlag("PrintIdeal"); 739 740 // Regular expressions used to match nodes in the PrintIdeal output 741 private static final String START = "(\\d+\\t(.*"; 742 private static final String MID = ".*)+\\t===.*"; 743 private static final String END = ")|"; 744 private static final String ALLOC = START + "CallStaticJava" + MID + "_new_instance_Java" + END; 745 private static final String LOAD = START + "Load" + MID + "valuetype\\*" + END; 746 private static final String STORE = START + "Store" + MID + "valuetype\\*" + END; 747 private static final String LOOP = START + "Loop" + MID + "" + END; 748 private static final String TRAP = START + "CallStaticJava" + MID + "uncommon_trap" + END; 749 // TODO: match field values of scalar replaced object 750 private static final String SCOBJ = "(.*# ScObj.*" + END; 751 752 // Random test values 753 public static final int rI = Utils.getRandomInstance().nextInt() % 1000; 754 public static final long rL = Utils.getRandomInstance().nextLong() % 1000; 755 756 static { 757 // Gather all test methods and put them in Hashtable 758 for (Method m : ValueTypeTestBench.class.getDeclaredMethods()) { 759 Test[] annos = m.getAnnotationsByType(Test.class); 760 if (annos.length != 0) { 761 tests.put("ValueTypeTestBench::" + m.getName(), m); 762 } 763 } 764 } 765 766 public static void main(String[] args) throws Throwable { 767 if (args.length == 0) { 768 ArrayList<String> all_args = new ArrayList(List.of( 769 "-noverify", 770 "-XX:+UnlockDiagnosticVMOptions", "-Xbootclasspath/a:.", "-XX:+WhiteBoxAPI", 771 "-XX:-TieredCompilation", "-XX:-BackgroundCompilation", "-XX:-UseOnStackReplacement", 772 "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+PrintCompilation", "-XX:+PrintIdeal", "-XX:+PrintOptoAssembly", 773 "-XX:CompileCommand=quiet", "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.ValueTypeTestBench::*", 774 "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue1::*", 775 "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue2::*" 776 )); 777 // Run tests in own process and verify output 778 all_args.add("-XX:+UnlockExperimentalVMOptions"); 779 if ((Boolean)WHITE_BOX.getVMFlag("ValueTypePassFieldsAsArgs")) { 780 all_args.add("-XX:+ValueTypePassFieldsAsArgs"); 781 } else { 782 all_args.add("-XX:-ValueTypePassFieldsAsArgs"); 783 } 784 all_args.add(ValueTypeTestBench.class.getName()); 785 all_args.add("run"); 786 OutputAnalyzer oa = ProcessTools.executeTestJvm(all_args.toArray(new String[0])); 787 // If ideal graph printing is enabled/supported, verify output 788 String output = oa.getOutput(); 789 oa.shouldHaveExitValue(0); 790 if (output.contains("PrintIdeal enabled")) { 791 parseOutput(output); 792 } else { 793 System.out.println("WARNING: test methods not compiled or PrintIdeal disabled! Running with -Xint or release build?"); 794 } 795 } else { 796 if (USE_COMPILER && PRINT_IDEAL) { 797 System.out.println("PrintIdeal enabled"); 798 } 799 // Execute tests 800 ValueTypeTestBench bench = new ValueTypeTestBench(); 801 bench.run(); 802 } 803 } 804 805 public static void parseOutput(String output) throws Exception { 806 String split = "b compiler.valhalla.valuetypes."; 807 String[] compilations = output.split(split); 808 // Print header 809 System.out.println(compilations[0]); 810 // Iterate over compilation output 811 for (String graph : compilations) { 812 String[] lines = graph.split("\\n"); 813 String testName = lines[0].split(" ")[0]; 814 Method test = tests.get(testName); 815 if (test == null) { 816 // Skip helper methods 817 continue; 818 } 819 if (PRINT_GRAPH) { 820 System.out.println("\nGraph for " + graph); 821 } 822 // Parse graph using regular expressions to determine if it contains forbidden nodes 823 Test[] annos = test.getAnnotationsByType(Test.class); 824 Test anno = null; 825 for (Test a : annos) { 826 if ((a.valid() & ValueTypePassFieldsAsArgsOn) != 0 && ValueTypePassFieldsAsArgs) { 827 assert anno == null; 828 anno = a; 829 } else if ((a.valid() & ValueTypePassFieldsAsArgsOff) != 0 && !ValueTypePassFieldsAsArgs) { 830 assert anno == null; 831 anno = a; 832 } 833 } 834 assert anno != null; 835 String regexFail = anno.failOn(); 836 if (!regexFail.isEmpty()) { 837 Pattern pattern = Pattern.compile(regexFail.substring(0, regexFail.length()-1)); 838 Matcher matcher = pattern.matcher(graph); 839 boolean fail = false; 840 while (matcher.find()) { 841 System.out.println("Graph for '" + testName + "' contains forbidden node:"); 842 System.out.println(matcher.group()); 843 fail = true; 844 } 845 Asserts.assertFalse(fail, "Graph for '" + testName + "' contains forbidden nodes"); 846 } 847 String[] regexMatch = anno.match(); 848 int[] matchCount = anno.matchCount(); 849 for (int i = 0; i < regexMatch.length; ++i) { 850 Pattern pattern = Pattern.compile(regexMatch[i].substring(0, regexMatch[i].length()-1)); 851 Matcher matcher = pattern.matcher(graph); 852 int count = 0; 853 String nodes = ""; 854 while (matcher.find()) { 855 count++; 856 nodes += matcher.group() + "\n"; 857 } 858 if (matchCount[i] != count) { 859 System.out.println("Graph for '" + testName + "' contains different number of match nodes:"); 860 System.out.println(nodes); 861 } 862 Asserts.assertEQ(matchCount[i], count, "Graph for '" + testName + "' contains different number of match nodes"); 863 } 864 tests.remove(testName); 865 System.out.println(testName + " passed"); 866 } 867 // Check if all tests were compiled 868 if (tests.size() != 0) { 869 for (String name : tests.keySet()) { 870 System.out.println("Test '" + name + "' not compiled!"); 871 } 872 throw new RuntimeException("Not all tests were compiled"); 873 } 874 } 875 876 public void setup(Method[] methods) { 877 for (Method m : methods) { 878 if (m.isAnnotationPresent(Test.class)) { 879 // Don't inline tests 880 WHITE_BOX.testSetDontInlineMethod(m, true); 881 } 882 if (m.isAnnotationPresent(DontCompile.class)) { 883 WHITE_BOX.makeMethodNotCompilable(m, COMP_LEVEL_ANY, true); 884 WHITE_BOX.makeMethodNotCompilable(m, COMP_LEVEL_ANY, false); 885 } 886 if (m.isAnnotationPresent(ForceInline.class)) { 887 WHITE_BOX.testSetForceInlineMethod(m, true); 888 } else if (m.isAnnotationPresent(DontInline.class)) { 889 WHITE_BOX.testSetDontInlineMethod(m, true); 890 } 891 } 892 } 893 894 public void run() throws Exception { 895 System.out.format("rI = %d, rL = %d\n", rI, rL); 896 setup(this.getClass().getDeclaredMethods()); 897 setup(MyValue1.class.getDeclaredMethods()); 898 setup(MyValue2.class.getDeclaredMethods()); 899 900 // Execute tests 901 for (Method test : tests.values()) { 902 Method verifier = getClass().getDeclaredMethod(test.getName() + "_verifier", boolean.class); 903 // Warmup using verifier method 904 for (int i = 0; i < WARMUP; ++i) { 905 verifier.invoke(this, true); 906 } 907 // Trigger compilation 908 WHITE_BOX.enqueueMethodForCompilation(test, COMP_LEVEL_FULL_OPTIMIZATION); 909 Asserts.assertTrue(!USE_COMPILER || WHITE_BOX.isMethodCompiled(test, false), test + " not compiled"); 910 // Check result 911 verifier.invoke(this, false); 912 } 913 } 914 } 915 916 // Mark method as test 917 @Retention(RetentionPolicy.RUNTIME) 918 @Repeatable(Tests.class) 919 @interface Test { 920 // Regular expression used to match forbidden IR nodes 921 // in the C2 IR emitted for this test. 922 String failOn() default ""; 923 // Regular expressions used to match and count IR nodes. 924 String[] match() default { }; 925 int[] matchCount() default { }; 926 int valid() default ValueTypeTestBench.AllFlags; 927 } 928 929 @Retention(RetentionPolicy.RUNTIME) 930 @interface Tests { 931 Test[] value(); 932 } 933 934 // Force method inlining during compilation 935 @Retention(RetentionPolicy.RUNTIME) 936 @interface ForceInline { } 937 938 // Prevent method inlining during compilation 939 @Retention(RetentionPolicy.RUNTIME) 940 @interface DontInline { } 941 942 // Prevent method compilation 943 @Retention(RetentionPolicy.RUNTIME) 944 @interface DontCompile { }