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