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 * @requires os.simpleArch == "x64" 30 * @build compiler.valhalla.valuetypes.ValueTypeTestBench 31 * @run main ClassFileInstaller sun.hotspot.WhiteBox 32 * @run main ClassFileInstaller jdk.test.lib.Platform 33 * @run main/othervm -ea -noverify -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 34 * -XX:+UnlockExperimentalVMOptions -XX:+ValueTypePassFieldsAsArgs -XX:+ValueArrayFlatten 35 * -XX:-TieredCompilation compiler.valhalla.valuetypes.ValueTypeTestBench 36 * @run main/othervm -ea -noverify -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 37 * -XX:+UnlockExperimentalVMOptions -XX:-ValueTypePassFieldsAsArgs -XX:-ValueArrayFlatten 38 * -XX:-TieredCompilation compiler.valhalla.valuetypes.ValueTypeTestBench 39 * @run main/othervm -ea -noverify -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 40 * -XX:+UnlockExperimentalVMOptions -XX:+AlwaysIncrementalInline -XX:+ValueArrayFlatten 41 * -XX:-TieredCompilation compiler.valhalla.valuetypes.ValueTypeTestBench 42 */ 43 44 package compiler.valhalla.valuetypes; 45 46 import compiler.whitebox.CompilerWhiteBoxTest; 47 import jdk.internal.misc.Unsafe; 48 import jdk.test.lib.Asserts; 49 import jdk.test.lib.Platform; 50 import jdk.test.lib.ProcessTools; 51 import jdk.test.lib.OutputAnalyzer; 52 import jdk.test.lib.Utils; 53 import sun.hotspot.WhiteBox; 54 55 import java.lang.annotation.Retention; 56 import java.lang.annotation.RetentionPolicy; 57 import java.lang.annotation.Repeatable; 58 import java.lang.invoke.*; 59 import java.lang.reflect.Method; 60 import java.util.ArrayList; 61 import java.util.Arrays; 62 import java.util.Hashtable; 63 import java.util.List; 64 import java.util.regex.Matcher; 65 import java.util.regex.Pattern; 66 import jdk.experimental.value.*; 67 68 // Test value types 69 __ByValue final class MyValue1 { 70 static int s; 71 static final long sf = ValueTypeTestBench.rL; 72 final int x; 73 final long y; 74 final MyValue2 v1; 75 final MyValue2 v2; 76 static final MyValue2 v3 = MyValue2.createInline(ValueTypeTestBench.rI, true); 77 final int c; 78 79 private MyValue1(int x, long y, MyValue2 v1, MyValue2 v2, int c) { 80 s = x; 81 this.x = x; 82 this.y = y; 83 this.v1 = v1; 84 this.v2 = v2; 85 this.c = c; 86 } 87 88 private MyValue1() { 89 s = 0; 90 this.x = 0; 91 this.y = 0; 92 this.v1 = MyValue2.createDefaultInline(); 93 this.v2 = MyValue2.createDefaultInline(); 94 this.c = 0; 95 } 96 97 @DontInline 98 public static MyValue1 createDontInline(int x, long y) { 99 return __Make MyValue1(x, y, MyValue2.createInline(x, true), MyValue2.createInline(x, false), ValueTypeTestBench.rI); 100 } 101 102 @ForceInline 103 public static MyValue1 createInline(int x, long y) { 104 return __Make MyValue1(x, y, MyValue2.createInline(x, true), MyValue2.createInline(x, false), ValueTypeTestBench.rI); 105 } 106 107 @DontInline 108 __ValueFactory static MyValue1 createDefaultDontInline() { 109 return __MakeDefault MyValue1(); 110 } 111 112 @ForceInline 113 __ValueFactory static MyValue1 createDefaultInline() { 114 return __MakeDefault MyValue1(); 115 } 116 117 @DontInline 118 static MyValue1 createWithFieldsDontInline(int x, long y) { 119 MyValue1 v = createDefaultInline(); 120 v = setX(v, x); 121 v = setY(v, y); 122 v = setV1(v, MyValue2.createWithFieldsInline(x, x < y)); 123 v = setV2(v, MyValue2.createWithFieldsInline(x, x > y)); 124 v = setC(v, ValueTypeTestBench.rI); 125 return v; 126 } 127 128 @ForceInline 129 static MyValue1 createWithFieldsInline(int x, long y) { 130 MyValue1 v = createDefaultInline(); 131 v = setX(v, x); 132 v = setY(v, y); 133 v = setV1(v, MyValue2.createWithFieldsInline(x, x < y)); 134 v = setV2(v, MyValue2.createWithFieldsInline(x, x > y)); 135 v = setC(v, ValueTypeTestBench.rI); 136 return v; 137 } 138 139 @ForceInline 140 public long hash() { 141 return s + sf + x + y + c + v1.hash() + v2.hash() + v3.hash(); 142 } 143 144 @DontCompile 145 public long hashInterpreted() { 146 return s + sf + x + y + c + v1.hashInterpreted() + v2.hashInterpreted() + v3.hashInterpreted(); 147 } 148 149 @ForceInline 150 public void print() { 151 System.out.print("s=" + s + ", sf=" + sf + ", x=" + x + ", y=" + y + ", v1["); 152 v1.print(); 153 System.out.print("], v2["); 154 v2.print(); 155 System.out.print("], v3["); 156 v3.print(); 157 System.out.print("], c=" + c); 158 } 159 160 @ForceInline 161 __ValueFactory static MyValue1 setX(MyValue1 v, int x) { 162 v.x = x; 163 return v; 164 } 165 166 @ForceInline 167 __ValueFactory static MyValue1 setY(MyValue1 v, long y) { 168 v.y = y; 169 return v; 170 } 171 172 @ForceInline 173 __ValueFactory static MyValue1 setC(MyValue1 v, int c) { 174 v.c = c; 175 return v; 176 } 177 178 @ForceInline 179 __ValueFactory static MyValue1 setV1(MyValue1 v, MyValue2 v1) { 180 v.v1 = v1; 181 return v; 182 } 183 184 @ForceInline 185 __ValueFactory static MyValue1 setV2(MyValue1 v, MyValue2 v2) { 186 v.v2 = v2; 187 return v; 188 } 189 } 190 191 __ByValue final class MyValue2 { 192 final int x; 193 final boolean b; 194 final long c; 195 196 private MyValue2(int x, boolean b, long c) { 197 this.x = x; 198 this.b = b; 199 this.c = c; 200 } 201 202 private MyValue2() { 203 this.x = 0; 204 this.b = false; 205 this.c = 0; 206 } 207 208 @ForceInline 209 public static MyValue2 createInline(int x, boolean b) { 210 return __Make MyValue2(x, b, ValueTypeTestBench.rL); 211 } 212 213 @ForceInline 214 __ValueFactory public static MyValue2 createDefaultInline() { 215 return __MakeDefault MyValue2(); 216 } 217 218 @ForceInline 219 public static MyValue2 createWithFieldsInline(int x, boolean b) { 220 MyValue2 v = createDefaultInline(); 221 v = setX(v, x); 222 v = setB(v, b); 223 v = setC(v, ValueTypeTestBench.rL); 224 return v; 225 } 226 227 @ForceInline 228 public long hash() { 229 return x + (b ? 0 : 1) + c; 230 } 231 232 @DontInline 233 public long hashInterpreted() { 234 return x + (b ? 0 : 1) + c; 235 } 236 237 @ForceInline 238 public void print() { 239 System.out.print("x=" + x + ", b=" + b + ", c=" + c); 240 } 241 242 @ForceInline 243 __ValueFactory static MyValue2 setX(MyValue2 v, int x) { 244 v.x = x; 245 return v; 246 } 247 248 @ForceInline 249 __ValueFactory static MyValue2 setC(MyValue2 v, long c) { 250 v.c = c; 251 return v; 252 } 253 254 @ForceInline 255 __ValueFactory static MyValue2 setB(MyValue2 v, boolean b) { 256 v.b = b; 257 return v; 258 } 259 } 260 261 public class ValueTypeTestBench { 262 // Print ideal graph after execution of each test 263 private static final boolean PRINT_GRAPH = true; 264 265 // Random test values 266 public static final int rI = Utils.getRandomInstance().nextInt() % 1000; 267 public static final long rL = Utils.getRandomInstance().nextLong() % 1000; 268 269 public ValueTypeTestBench() { 270 val3 = MyValue1.createInline(rI, rL); 271 } 272 273 // MethodHandles and value-capable class instance needed for testing vbox/vunbox 274 private static final MethodHandle vccUnboxLoadLongMH = generateVCCUnboxLoadLongMH(); 275 private static final MethodHandle vccUnboxLoadIntMH = generateVCCUnboxLoadIntMH(); 276 private static final MethodHandle vccUnboxBoxMH = generateVCCUnboxBoxMH(); 277 private static final MethodHandle vccUnboxBoxLoadIntMH = generateVCCUnboxBoxLoadIntMH(); 278 279 private static final ValueCapableClass1 vcc = ValueCapableClass1.create(rL, rI, (short)rI, (short)rI); 280 281 // ========== Helper methods ========== 282 283 public long hash() { 284 return hash(rI, rL); 285 } 286 287 public long hash(int x, long y) { 288 return MyValue1.createInline(x, y).hash(); 289 } 290 291 // ========== Test definitions ========== 292 293 // Receive value type through call to interpreter 294 @Test(failOn = ALLOC + STORE + TRAP) 295 public long test1() { 296 MyValue1 v = MyValue1.createDontInline(rI, rL); 297 return v.hash(); 298 } 299 300 @DontCompile 301 public void test1_verifier(boolean warmup) { 302 long result = test1(); 303 Asserts.assertEQ(result, hash()); 304 } 305 306 // Receive value type from interpreter via parameter 307 @Test(failOn = ALLOC + STORE + TRAP) 308 public long test2(MyValue1 v) { 309 return v.hash(); 310 } 311 312 @DontCompile 313 public void test2_verifier(boolean warmup) { 314 MyValue1 v = MyValue1.createDontInline(rI, rL); 315 long result = test2(v); 316 Asserts.assertEQ(result, hash()); 317 } 318 319 // Return incoming value type without accessing fields 320 @Test(valid = ValueTypePassFieldsAsArgsOn, match = {ALLOC, STORE}, matchCount = {1, 9}, failOn = LOAD + TRAP) 321 @Test(valid = ValueTypePassFieldsAsArgsOff, failOn = ALLOC + LOAD + STORE + TRAP) 322 public MyValue1 test3(MyValue1 v) { 323 return v; 324 } 325 326 @DontCompile 327 public void test3_verifier(boolean warmup) { 328 MyValue1 v1 = MyValue1.createDontInline(rI, rL); 329 MyValue1 v2 = test3(v1); 330 Asserts.assertEQ(v1.x, v2.x); 331 Asserts.assertEQ(v1.y, v2.y); 332 } 333 334 // Create a value type in compiled code and only use fields. 335 // Allocation should go away because value type does not escape. 336 @Test(failOn = ALLOC + LOAD + STORE + TRAP) 337 public long test4() { 338 MyValue1 v = MyValue1.createInline(rI, rL); 339 return v.hash(); 340 } 341 342 @DontCompile 343 public void test4_verifier(boolean warmup) { 344 long result = test4(); 345 Asserts.assertEQ(result, hash()); 346 } 347 348 // Create a value type in compiled code and pass it to 349 // an inlined compiled method via a call. 350 @Test(failOn = ALLOC + LOAD + STORE + TRAP) 351 public long test5() { 352 MyValue1 v = MyValue1.createInline(rI, rL); 353 return test5Inline(v); 354 } 355 356 @ForceInline 357 public long test5Inline(MyValue1 v) { 358 return v.hash(); 359 } 360 361 @DontCompile 362 public void test5_verifier(boolean warmup) { 363 long result = test5(); 364 Asserts.assertEQ(result, hash()); 365 } 366 367 // Create a value type in compiled code and pass it to 368 // the interpreter via a call. 369 @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = LOAD + TRAP + ALLOC) 370 @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP) 371 public long test6() { 372 MyValue1 v = MyValue1.createInline(rI, rL); 373 // Pass to interpreter 374 return v.hashInterpreted(); 375 } 376 377 @DontCompile 378 public void test6_verifier(boolean warmup) { 379 long result = test6(); 380 Asserts.assertEQ(result, hash()); 381 } 382 383 // Create a value type in compiled code and pass it to 384 // the interpreter by returning. 385 @Test(match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP) 386 public MyValue1 test7(int x, long y) { 387 return MyValue1.createInline(x, y); 388 } 389 390 @DontCompile 391 public void test7_verifier(boolean warmup) { 392 MyValue1 v = test7(rI, rL); 393 Asserts.assertEQ(v.hash(), hash()); 394 } 395 396 // Merge value types created from two branches 397 @Test(failOn = ALLOC + STORE + TRAP) 398 public long test8(boolean b) { 399 MyValue1 v; 400 if (b) { 401 v = MyValue1.createInline(rI, rL); 402 } else { 403 v = MyValue1.createDontInline(rI + 1, rL + 1); 404 } 405 return v.hash(); 406 } 407 408 @DontCompile 409 public void test8_verifier(boolean warmup) { 410 Asserts.assertEQ(test8(true), hash()); 411 Asserts.assertEQ(test8(false), hash(rI + 1, rL + 1)); 412 } 413 414 // Merge value types created from two branches 415 @Test(valid = ValueTypePassFieldsAsArgsOn, match = {LOAD}, matchCount = {7}, failOn = TRAP + ALLOC + STORE) 416 // FIXME 417 //@Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC, STORE}, matchCount = {1, 9}, failOn = LOAD + TRAP) 418 @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC, STORE}, matchCount = {1, 3}, failOn = LOAD + TRAP) 419 public MyValue1 test9(boolean b) { 420 MyValue1 v; 421 if (b) { 422 // Value type is not allocated 423 v = MyValue1.createInline(rI, rL); 424 } else { 425 // Value type is allocated by the callee 426 v = MyValue1.createDontInline(rI + 1, rL + 1); 427 } 428 // Need to allocate value type if 'b' is true 429 long sum = v.hashInterpreted(); 430 if (b) { 431 v = MyValue1.createDontInline(rI, sum); 432 } else { 433 v = MyValue1.createDontInline(rI, sum + 1); 434 } 435 // Don't need to allocate value type because both branches allocate 436 return v; 437 } 438 439 @DontCompile 440 public void test9_verifier(boolean warmup) { 441 MyValue1 v = test9(true); 442 Asserts.assertEQ(v.x, rI); 443 Asserts.assertEQ(v.y, hash()); 444 v = test9(false); 445 Asserts.assertEQ(v.x, rI); 446 Asserts.assertEQ(v.y, hash(rI + 1, rL + 1) + 1); 447 } 448 449 // Merge value types created in a loop (not inlined) 450 @Test(failOn = ALLOC + STORE + TRAP) 451 public long test10(int x, long y) { 452 MyValue1 v = MyValue1.createDontInline(x, y); 453 for (int i = 0; i < 10; ++i) { 454 v = MyValue1.createDontInline(v.x + 1, v.y + 1); 455 } 456 return v.hash(); 457 } 458 459 @DontCompile 460 public void test10_verifier(boolean warmup) { 461 long result = test10(rI, rL); 462 Asserts.assertEQ(result, hash(rI + 10, rL + 10)); 463 } 464 465 // Merge value types created in a loop (inlined) 466 @Test(failOn = ALLOC + LOAD + STORE + LOOP + TRAP) 467 public long test11(int x, long y) { 468 MyValue1 v = MyValue1.createInline(x, y); 469 for (int i = 0; i < 10; ++i) { 470 v = MyValue1.createInline(v.x + 1, v.y + 1); 471 } 472 return v.hash(); 473 } 474 475 @DontCompile 476 public void test11_verifier(boolean warmup) { 477 long result = test11(rI, rL); 478 Asserts.assertEQ(result, hash(rI + 10, rL + 10)); 479 } 480 481 // Test loop with uncommon trap referencing a value type 482 @Test(match = {TRAP, SCOBJ}, matchCount = {1, -1 /* at least 1 */}, failOn = LOAD) 483 public long test12(boolean b) { 484 MyValue1 v = MyValue1.createInline(rI, rL); 485 MyValue1[] va = new MyValue1[Math.abs(rI) % 10]; 486 for (int i = 0; i < va.length; ++i) { 487 va[i] = MyValue1.createInline(rI, rL); 488 } 489 long result = rL; 490 for (int i = 0; i < 1000; ++i) { 491 if (b) { 492 result += v.x; 493 } else { 494 // Uncommon trap referencing v. We delegate allocation to the 495 // interpreter by adding a SafePointScalarObjectNode. 496 result = v.hashInterpreted(); 497 for (int j = 0; j < va.length; ++j) { 498 result += va[j].hash(); 499 } 500 } 501 } 502 return result; 503 } 504 505 @DontCompile 506 public void test12_verifier(boolean warmup) { 507 long result = test12(warmup); 508 Asserts.assertEQ(result, warmup ? rL + (1000 * rI) : ((Math.abs(rI) % 10) + 1) * hash()); 509 } 510 511 // Test loop with uncommon trap referencing a value type 512 @Test(match = {TRAP}, matchCount = {1}) 513 public long test13(boolean b) { 514 MyValue1 v = MyValue1.createDontInline(rI, rL); 515 MyValue1[] va = new MyValue1[Math.abs(rI) % 10]; 516 for (int i = 0; i < va.length; ++i) { 517 va[i] = MyValue1.createDontInline(rI, rL); 518 } 519 long result = rL; 520 for (int i = 0; i < 1000; ++i) { 521 if (b) { 522 result += v.x; 523 } else { 524 // Uncommon trap referencing v. Should not allocate 525 // but just pass the existing oop to the uncommon trap. 526 result = v.hashInterpreted(); 527 for (int j = 0; j < va.length; ++j) { 528 result += va[j].hashInterpreted(); 529 } 530 } 531 } 532 return result; 533 } 534 535 @DontCompile 536 public void test13_verifier(boolean warmup) { 537 long result = test13(warmup); 538 Asserts.assertEQ(result, warmup ? rL + (1000 * rI) : ((Math.abs(rI) % 10) + 1) * hash()); 539 } 540 541 // Create a value type in a non-inlined method and then call a 542 // non-inlined method on that value type. 543 @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = (ALLOC + STORE + TRAP), match = {LOAD}, matchCount = {7}) 544 @Test(valid = ValueTypePassFieldsAsArgsOff, failOn = (ALLOC + LOAD + STORE + TRAP)) 545 public long test14() { 546 MyValue1 v = MyValue1.createDontInline(rI, rL); 547 return v.hashInterpreted(); 548 } 549 550 @DontCompile 551 public void test14_verifier(boolean b) { 552 long result = test14(); 553 Asserts.assertEQ(result, hash()); 554 } 555 556 // Create a value type in an inlined method and then call a 557 // non-inlined method on that value type. 558 @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = (LOAD + TRAP + ALLOC)) 559 @Test(valid = ValueTypePassFieldsAsArgsOff, failOn = (LOAD + TRAP), match = {ALLOC}, matchCount = {1}) 560 public long test15() { 561 MyValue1 v = MyValue1.createInline(rI, rL); 562 return v.hashInterpreted(); 563 } 564 565 @DontCompile 566 public void test15_verifier(boolean b) { 567 long result = test15(); 568 Asserts.assertEQ(result, hash()); 569 } 570 571 // Create a value type in a non-inlined method and then call an 572 // inlined method on that value type. 573 @Test(failOn = (ALLOC + STORE + TRAP)) 574 public long test16() { 575 MyValue1 v = MyValue1.createDontInline(rI, rL); 576 return v.hash(); 577 } 578 579 @DontCompile 580 public void test16_verifier(boolean b) { 581 long result = test16(); 582 Asserts.assertEQ(result, hash()); 583 } 584 585 // Create a value type in an inlined method and then call an 586 // inlined method on that value type. 587 @Test(failOn = (ALLOC + LOAD + STORE + TRAP)) 588 public long test17() { 589 MyValue1 v = MyValue1.createInline(rI, rL); 590 return v.hash(); 591 } 592 593 @DontCompile 594 public void test17_verifier(boolean b) { 595 long result = test17(); 596 Asserts.assertEQ(result, hash()); 597 } 598 599 // Create a value type in compiled code and pass it to the 600 // interpreter via a call. The value is live at the first call so 601 // debug info should include a reference to all its fields. 602 @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = ALLOC + LOAD + TRAP) 603 @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP) 604 public long test18() { 605 MyValue1 v = MyValue1.createInline(rI, rL); 606 v.hashInterpreted(); 607 return v.hashInterpreted(); 608 } 609 610 @DontCompile 611 public void test18_verifier(boolean warmup) { 612 long result = test18(); 613 Asserts.assertEQ(result, hash()); 614 } 615 616 // Create a value type in compiled code and pass it to the 617 // interpreter via a call. The value type is passed twice but 618 // should only be allocated once. 619 @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = ALLOC + LOAD + TRAP) 620 @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP) 621 public long test19() { 622 MyValue1 v = MyValue1.createInline(rI, rL); 623 return sumValue(v, v); 624 } 625 626 @DontCompile 627 public long sumValue(MyValue1 v, MyValue1 dummy) { 628 return v.hash(); 629 } 630 631 @DontCompile 632 public void test19_verifier(boolean warmup) { 633 long result = test19(); 634 Asserts.assertEQ(result, hash()); 635 } 636 637 // Create a value type (array) in compiled code and pass it to the 638 // interpreter via a call. The value type is live at the uncommon 639 // trap: verify that deoptimization causes the value type to be 640 // correctly allocated. 641 @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = LOAD + ALLOC + STORE) 642 @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC}, matchCount = {2}, failOn = LOAD) 643 public long test20(boolean deopt) { 644 MyValue1 v = MyValue1.createInline(rI, rL); 645 MyValue1[] va = new MyValue1[3]; 646 if (deopt) { 647 // uncommon trap 648 WHITE_BOX.deoptimizeMethod(tests.get("ValueTypeTestBench::test20")); 649 } 650 return v.hashInterpreted() + va[0].hashInterpreted() + 651 va[1].hashInterpreted() + va[2].hashInterpreted(); 652 } 653 654 @DontCompile 655 public void test20_verifier(boolean warmup) { 656 MyValue1[] va = new MyValue1[42]; 657 long result = test20(!warmup); 658 Asserts.assertEQ(result, hash() + va[0].hash() + va[1].hash() + va[2].hash()); 659 } 660 661 // Value type fields in regular object 662 MyValue1 val1; 663 MyValue2 val2; 664 final MyValue1 val3; 665 static MyValue1 val4; 666 static final MyValue1 val5 = MyValue1.createInline(rI, rL); 667 668 // Test value type fields in objects 669 @Test(match = {ALLOC}, matchCount = {1}, failOn = (TRAP)) 670 public long test21(int x, long y) { 671 // Compute hash of value type fields 672 long result = val1.hash() + val2.hash() + val3.hash() + val4.hash() + val5.hash(); 673 // Update fields 674 val1 = MyValue1.createInline(x, y); 675 val2 = MyValue2.createInline(x, true); 676 val4 = MyValue1.createInline(x, y); 677 return result; 678 } 679 680 @DontCompile 681 public void test21_verifier(boolean warmup) { 682 // Check if hash computed by test18 is correct 683 val1 = MyValue1.createInline(rI, rL); 684 val2 = val1.v2; 685 // val3 is initialized in the constructor 686 val4 = val1; 687 // val5 is initialized in the static initializer 688 long hash = val1.hash() + val2.hash() + val3.hash() + val4.hash() + val5.hash(); 689 long result = test21(rI + 1, rL + 1); 690 Asserts.assertEQ(result, hash); 691 // Check if value type fields were updated 692 Asserts.assertEQ(val1.hash(), hash(rI + 1, rL + 1)); 693 Asserts.assertEQ(val2.hash(), MyValue2.createInline(rI + 1, true).hash()); 694 Asserts.assertEQ(val4.hash(), hash(rI + 1, rL + 1)); 695 } 696 697 // Test folding of constant value type fields 698 @Test(failOn = ALLOC + LOAD + STORE + LOOP + TRAP) 699 public long test22() { 700 // This should be constant folded 701 return val5.hash() + val5.v3.hash(); 702 } 703 704 @DontCompile 705 public void test22_verifier(boolean warmup) { 706 long result = test22(); 707 Asserts.assertEQ(result, val5.hash() + val5.v3.hash()); 708 } 709 710 // Test OSR compilation 711 @Test() 712 public long test23() { 713 MyValue1 v = MyValue1.createInline(rI, rL); 714 MyValue1[] va = new MyValue1[Math.abs(rI) % 3]; 715 for (int i = 0; i < va.length; ++i) { 716 va[i] = MyValue1.createInline(rI, rL); 717 } 718 long result = 0; 719 // Long loop to trigger OSR compilation 720 for (int i = 0 ; i < 100_000; ++i) { 721 // Reference local value type in interpreter state 722 result = v.hash(); 723 for (int j = 0; j < va.length; ++j) { 724 result += va[j].hash(); 725 } 726 } 727 return result; 728 } 729 730 @DontCompile 731 public void test23_verifier(boolean warmup) { 732 long result = test23(); 733 Asserts.assertEQ(result, ((Math.abs(rI) % 3) + 1) * hash()); 734 } 735 736 // Test interpreter to compiled code with various signatures 737 @Test(failOn = ALLOC + STORE + TRAP) 738 public long test24(MyValue2 v) { 739 return v.hash(); 740 } 741 742 @DontCompile 743 public void test24_verifier(boolean warmup) { 744 MyValue2 v = MyValue2.createInline(rI, true); 745 long result = test24(v); 746 Asserts.assertEQ(result, v.hashInterpreted()); 747 } 748 749 @Test(failOn = ALLOC + STORE + TRAP) 750 public long test25(int i1, MyValue2 v, int i2) { 751 return v.hash() + i1 - i2; 752 } 753 754 @DontCompile 755 public void test25_verifier(boolean warmup) { 756 MyValue2 v = MyValue2.createInline(rI, true); 757 long result = test25(rI, v, 2*rI); 758 Asserts.assertEQ(result, v.hashInterpreted() - rI); 759 } 760 761 @Test(failOn = ALLOC + STORE + TRAP) 762 public long test26(long l1, MyValue2 v, long l2) { 763 return v.hash() + l1 - l2; 764 } 765 766 @DontCompile 767 public void test26_verifier(boolean warmup) { 768 MyValue2 v = MyValue2.createInline(rI, true); 769 long result = test26(rL, v, 2*rL); 770 Asserts.assertEQ(result, v.hashInterpreted() - rL); 771 } 772 773 @Test(failOn = ALLOC + STORE + TRAP) 774 public long test27(int i, MyValue2 v, long l) { 775 return v.hash() + i + l; 776 } 777 778 @DontCompile 779 public void test27_verifier(boolean warmup) { 780 MyValue2 v = MyValue2.createInline(rI, true); 781 long result = test27(rI, v, rL); 782 Asserts.assertEQ(result, v.hashInterpreted() + rL + rI); 783 } 784 785 @Test(failOn = ALLOC + STORE + TRAP) 786 public long test28(long l, MyValue2 v, int i) { 787 return v.hash() + i + l; 788 } 789 790 @DontCompile 791 public void test28_verifier(boolean warmup) { 792 MyValue2 v = MyValue2.createInline(rI, true); 793 long result = test28(rL, v, rI); 794 Asserts.assertEQ(result, v.hashInterpreted() + rL + rI); 795 } 796 797 @Test(failOn = ALLOC + STORE + TRAP) 798 public long test29(long l, MyValue1 v1, int i, MyValue2 v2) { 799 return v1.hash() + i + l + v2.hash(); 800 } 801 802 @DontCompile 803 public void test29_verifier(boolean warmup) { 804 MyValue1 v1 = MyValue1.createDontInline(rI, rL); 805 MyValue2 v2 = MyValue2.createInline(rI, true); 806 long result = test29(rL, v1, rI, v2); 807 Asserts.assertEQ(result, v1.hashInterpreted() + rL + rI + v2.hashInterpreted()); 808 } 809 810 // Test compiled code to interpreter with various signatures 811 @DontCompile 812 public long test30_interp(MyValue2 v) { 813 return v.hash(); 814 } 815 816 @Test(failOn = ALLOC + STORE + TRAP) 817 public long test30(MyValue2 v) { 818 return test30_interp(v); 819 } 820 821 @DontCompile 822 public void test30_verifier(boolean warmup) { 823 MyValue2 v = MyValue2.createInline(rI, true); 824 long result = test30(v); 825 Asserts.assertEQ(result, v.hashInterpreted()); 826 } 827 828 @DontCompile 829 public long test31_interp(int i1, MyValue2 v, int i2) { 830 return v.hash() + i1 - i2; 831 } 832 833 @Test(failOn = ALLOC + STORE + TRAP) 834 public long test31(int i1, MyValue2 v, int i2) { 835 return test31_interp(i1, v, i2); 836 } 837 838 @DontCompile 839 public void test31_verifier(boolean warmup) { 840 MyValue2 v = MyValue2.createInline(rI, true); 841 long result = test31(rI, v, 2*rI); 842 Asserts.assertEQ(result, v.hashInterpreted() - rI); 843 } 844 845 @DontCompile 846 public long test32_interp(long l1, MyValue2 v, long l2) { 847 return v.hash() + l1 - l2; 848 } 849 850 @Test(failOn = ALLOC + STORE + TRAP) 851 public long test32(long l1, MyValue2 v, long l2) { 852 return test32_interp(l1, v, l2); 853 } 854 855 @DontCompile 856 public void test32_verifier(boolean warmup) { 857 MyValue2 v = MyValue2.createInline(rI, true); 858 long result = test32(rL, v, 2*rL); 859 Asserts.assertEQ(result, v.hashInterpreted() - rL); 860 } 861 862 @DontCompile 863 public long test33_interp(int i, MyValue2 v, long l) { 864 return v.hash() + i + l; 865 } 866 867 @Test(failOn = ALLOC + STORE + TRAP) 868 public long test33(int i, MyValue2 v, long l) { 869 return test33_interp(i, v, l); 870 } 871 872 @DontCompile 873 public void test33_verifier(boolean warmup) { 874 MyValue2 v = MyValue2.createInline(rI, true); 875 long result = test33(rI, v, rL); 876 Asserts.assertEQ(result, v.hashInterpreted() + rL + rI); 877 } 878 879 @DontCompile 880 public long test34_interp(long l, MyValue2 v, int i) { 881 return v.hash() + i + l; 882 } 883 884 @Test(failOn = ALLOC + STORE + TRAP) 885 public long test34(long l, MyValue2 v, int i) { 886 return test34_interp(l, v, i); 887 } 888 889 @DontCompile 890 public void test34_verifier(boolean warmup) { 891 MyValue2 v = MyValue2.createInline(rI, true); 892 long result = test34(rL, v, rI); 893 Asserts.assertEQ(result, v.hashInterpreted() + rL + rI); 894 } 895 896 @DontCompile 897 public long test35_interp(long l, MyValue1 v1, int i, MyValue2 v2) { 898 return v1.hash() + i + l + v2.hash(); 899 } 900 901 @Test(failOn = ALLOC + STORE + TRAP) 902 public long test35(long l, MyValue1 v1, int i, MyValue2 v2) { 903 return test35_interp(l, v1, i, v2); 904 } 905 906 @DontCompile 907 public void test35_verifier(boolean warmup) { 908 MyValue1 v1 = MyValue1.createDontInline(rI, rL); 909 MyValue2 v2 = MyValue2.createInline(rI, true); 910 long result = test35(rL, v1, rI, v2); 911 Asserts.assertEQ(result, v1.hashInterpreted() + rL + rI + v2.hashInterpreted()); 912 } 913 914 // test that debug info at a call is correct 915 @DontCompile 916 public long test36_interp(MyValue2 v, MyValue1[] va, boolean deopt) { 917 if (deopt) { 918 // uncommon trap 919 WHITE_BOX.deoptimizeMethod(tests.get("ValueTypeTestBench::test36")); 920 } 921 return v.hash() + va[0].hash() + va[1].hash(); 922 } 923 924 @Test(failOn = ALLOC + STORE + TRAP) 925 public long test36(MyValue2 v, MyValue1[] va, boolean flag, long l) { 926 return test36_interp(v, va, flag) + l; 927 } 928 929 @DontCompile 930 public void test36_verifier(boolean warmup) { 931 MyValue2 v = MyValue2.createInline(rI, true); 932 MyValue1[] va = new MyValue1[2]; 933 va[0] = MyValue1.createDontInline(rI, rL); 934 va[1] = MyValue1.createDontInline(rI, rL); 935 long result = test36(v, va, !warmup, rL); 936 Asserts.assertEQ(result, v.hashInterpreted() + va[0].hash() + va[1].hash() + rL); 937 } 938 939 // Test vbox and vunbox 940 @Test 941 public long test37() throws Throwable { 942 return (long)vccUnboxLoadLongMH.invokeExact(vcc); 943 } 944 945 @DontCompile 946 public void test37_verifier(boolean warmup) { 947 try { 948 long result = test37(); 949 Asserts.assertEQ(vcc.t, result, "Field t of input and result must be equal."); 950 } catch (Throwable t) { 951 throw new RuntimeException("test 37 failed", t); 952 } 953 } 954 955 // Generate a MethodHandle that obtains field t of the 956 // derived value type 957 private static MethodHandle generateVCCUnboxLoadLongMH() { 958 return MethodHandleBuilder.loadCode(MethodHandles.lookup(), 959 "vccUnboxLoadLong", 960 MethodType.methodType(long.class, ValueCapableClass1.class), 961 CODE -> { 962 CODE. 963 aload_0(). 964 vunbox(ValueType.forClass(ValueCapableClass1.class).valueClass()). 965 vgetfield(ValueType.forClass(ValueCapableClass1.class).valueClass(), "t", "J"). 966 lreturn(); 967 } 968 ); 969 } 970 971 @Test 972 public int test38() throws Throwable { 973 return (int)vccUnboxLoadIntMH.invokeExact(vcc); 974 } 975 976 @DontCompile 977 public void test38_verifier(boolean warmup) { 978 try { 979 int result = test38(); 980 Asserts.assertEQ(vcc.x, result, "Field x of input and result must be equal."); 981 } catch (Throwable t) { 982 throw new RuntimeException("test 38 failed", t); 983 } 984 } 985 986 // Generate a MethodHandle that obtains field x of the 987 // derived value type 988 private static MethodHandle generateVCCUnboxLoadIntMH() { 989 return MethodHandleBuilder.loadCode(MethodHandles.lookup(), 990 "vccUnboxLoadInt", 991 MethodType.methodType(int.class, ValueCapableClass1.class), 992 CODE -> { 993 CODE. 994 aload_0(). 995 vunbox(ValueType.forClass(ValueCapableClass1.class).valueClass()). 996 vgetfield(ValueType.forClass(ValueCapableClass1.class).valueClass(), "x", "I"). 997 ireturn(); 998 } 999 ); 1000 } 1001 1002 @Test 1003 public ValueCapableClass1 test39() throws Throwable { 1004 return (ValueCapableClass1)vccUnboxBoxMH.invokeExact(vcc); 1005 } 1006 1007 @DontCompile 1008 public void test39_verifier(boolean warmup) { 1009 try { 1010 ValueCapableClass1 result = test39(); 1011 Asserts.assertEQ(vcc.value(), result.value(), "Value of VCC and returned VCC must be equal"); 1012 } catch (Throwable t) { 1013 throw new RuntimeException("test 39 failed", t); 1014 } 1015 } 1016 1017 // Generate a MethodHandle that takes a value-capable class, 1018 // unboxes it, then boxes it again and returns it. 1019 private static MethodHandle generateVCCUnboxBoxMH() { 1020 return MethodHandleBuilder.loadCode(MethodHandles.lookup(), 1021 "vccUnboxBox", 1022 MethodType.methodType(ValueCapableClass1.class, ValueCapableClass1.class), 1023 CODE -> { 1024 CODE. 1025 aload_0(). 1026 vunbox(ValueType.forClass(ValueCapableClass1.class).valueClass()). 1027 vbox(ValueCapableClass1.class). 1028 areturn(); 1029 } 1030 ); 1031 } 1032 1033 @Test 1034 public int test40() throws Throwable { 1035 return (int)vccUnboxBoxLoadIntMH.invokeExact(vcc); 1036 } 1037 1038 @DontCompile 1039 public void test40_verifier(boolean warmup) { 1040 try { 1041 int result = test40(); 1042 Asserts.assertEQ(vcc.x, result, "Field x of VCC and result must be equal"); 1043 } catch (Throwable t) { 1044 throw new RuntimeException("Test failed in the interpeter", t); 1045 } 1046 } 1047 1048 // Generate a MethodHandle that takes a value-capable class, 1049 // unboxes it, boxes it, reads a field from it, and returns the 1050 // field. 1051 private static MethodHandle generateVCCUnboxBoxLoadIntMH() { 1052 return MethodHandleBuilder.loadCode(MethodHandles.lookup(), 1053 "vccUnboxBoxLoadInt", 1054 MethodType.methodType(int.class, ValueCapableClass1.class), 1055 CODE -> { 1056 CODE. 1057 aload_0(). 1058 vunbox(ValueType.forClass(ValueCapableClass1.class).valueClass()). 1059 vbox(ValueCapableClass1.class). 1060 getfield(ValueCapableClass1.class, "x", "I"). 1061 ireturn(); 1062 } 1063 ); 1064 1065 } 1066 1067 // Test value type array creation and initialization 1068 @Test(valid = ValueTypeArrayFlattenOff, failOn = (LOAD)) 1069 @Test(valid = ValueTypeArrayFlattenOn) 1070 public MyValue1[] test41(int len) { 1071 MyValue1[] va = new MyValue1[len]; 1072 for (int i = 0; i < len; ++i) { 1073 va[i] = MyValue1.createDontInline(rI, rL); 1074 } 1075 return va; 1076 } 1077 1078 @DontCompile 1079 public void test41_verifier(boolean warmup) { 1080 int len = Math.abs(rI % 10); 1081 MyValue1[] va = test41(len); 1082 for (int i = 0; i < len; ++i) { 1083 Asserts.assertEQ(va[i].hash(), hash()); 1084 } 1085 } 1086 1087 // Test creation of a value type array and element access 1088 @Test(valid = ValueTypeArrayFlattenOff, failOn = (LOOP + LOAD + TRAP)) 1089 @Test(valid = ValueTypeArrayFlattenOn, failOn = (ALLOC + ALLOCA + LOOP + LOAD + LOADP + STORE + TRAP)) 1090 public long test42() { 1091 MyValue1[] va = new MyValue1[1]; 1092 va[0] = MyValue1.createInline(rI, rL); 1093 return va[0].hash(); 1094 } 1095 1096 @DontCompile 1097 public void test42_verifier(boolean warmup) { 1098 long result = test42(); 1099 Asserts.assertEQ(result, hash()); 1100 } 1101 1102 // Test receiving a value type array from the interpreter, 1103 // updating its elements in a loop and computing a hash. 1104 @Test(failOn = (ALLOCA)) 1105 public long test43(MyValue1[] va) { 1106 long result = 0; 1107 for (int i = 0; i < 10; ++i) { 1108 result += va[i].hash(); 1109 va[i] = MyValue1.createInline(rI + 1, rL + 1); 1110 } 1111 return result; 1112 } 1113 1114 @DontCompile 1115 public void test43_verifier(boolean warmup) { 1116 MyValue1[] va = new MyValue1[10]; 1117 long expected = 0; 1118 for (int i = 0; i < 10; ++i) { 1119 va[i] = MyValue1.createDontInline(rI + i, rL + i); 1120 expected += va[i].hash(); 1121 } 1122 long result = test43(va); 1123 Asserts.assertEQ(expected, result); 1124 for (int i = 0; i < 10; ++i) { 1125 if (va[i].hash() != hash(rI + 1, rL + 1)) { 1126 Asserts.assertEQ(va[i].hash(), hash(rI + 1, rL + 1)); 1127 } 1128 } 1129 } 1130 1131 // Test returning a value type array received from the interpreter 1132 @Test(failOn = ALLOC + ALLOCA + LOAD + LOADP + STORE + LOOP + TRAP) 1133 public MyValue1[] test44(MyValue1[] va) { 1134 return va; 1135 } 1136 1137 @DontCompile 1138 public void test44_verifier(boolean warmup) { 1139 MyValue1[] va = new MyValue1[10]; 1140 for (int i = 0; i < 10; ++i) { 1141 va[i] = MyValue1.createDontInline(rI + i, rL + i); 1142 } 1143 va = test44(va); 1144 for (int i = 0; i < 10; ++i) { 1145 Asserts.assertEQ(va[i].hash(), hash(rI + i, rL + i)); 1146 } 1147 } 1148 1149 @Test(failOn = (TRAP)) 1150 public MyValue1[] test45(boolean b) { 1151 MyValue1[] va; 1152 if (b) { 1153 va = new MyValue1[5]; 1154 for (int i = 0; i < 5; ++i) { 1155 va[i] = MyValue1.createInline(rI, rL); 1156 } 1157 } else { 1158 va = new MyValue1[10]; 1159 for (int i = 0; i < 10; ++i) { 1160 va[i] = MyValue1.createInline(rI + i, rL + i); 1161 } 1162 } 1163 long sum = va[0].hashInterpreted(); 1164 if (b) { 1165 va[0] = MyValue1.createDontInline(rI, sum); 1166 } else { 1167 va[0] = MyValue1.createDontInline(rI + 1, sum + 1); 1168 } 1169 return va; 1170 } 1171 1172 @DontCompile 1173 public void test45_verifier(boolean warmup) { 1174 MyValue1[] va = test45(true); 1175 Asserts.assertEQ(va.length, 5); 1176 Asserts.assertEQ(va[0].hash(), hash(rI, hash())); 1177 for (int i = 1; i < 5; ++i) { 1178 Asserts.assertEQ(va[i].hash(), hash()); 1179 } 1180 va = test45(false); 1181 Asserts.assertEQ(va.length, 10); 1182 Asserts.assertEQ(va[0].hash(), hash(rI + 1, hash(rI, rL) + 1)); 1183 for (int i = 1; i < 10; ++i) { 1184 Asserts.assertEQ(va[i].hash(), hash(rI + i, rL + i)); 1185 } 1186 } 1187 1188 // Test creation of value type array with single element 1189 @Test(valid = ValueTypeArrayFlattenOff, failOn = (LOAD + LOOP + TRAP)) 1190 @Test(valid = ValueTypeArrayFlattenOn, failOn = (ALLOCA + LOAD + LOOP + TRAP)) 1191 public MyValue1 test46() { 1192 MyValue1[] va = new MyValue1[1]; 1193 return va[0]; 1194 } 1195 1196 @DontCompile 1197 public void test46_verifier(boolean warmup) { 1198 MyValue1[] va = new MyValue1[1]; 1199 MyValue1 v = test46(); 1200 Asserts.assertEQ(v.hash(), va[0].hash()); 1201 } 1202 1203 // Test default initialization of value type arrays 1204 @Test(failOn = LOAD) 1205 public MyValue1[] test47(int len) { 1206 return new MyValue1[len]; 1207 } 1208 1209 @DontCompile 1210 public void test47_verifier(boolean warmup) { 1211 int len = Math.abs(rI % 10); 1212 MyValue1[] va = new MyValue1[len]; 1213 MyValue1[] var = test47(len); 1214 for (int i = 0; i < len; ++i) { 1215 Asserts.assertEQ(va[i].hash(), var[i].hash()); 1216 } 1217 } 1218 1219 // Test creation of value type array with zero length 1220 @Test(failOn = ALLOC + LOAD + STORE + LOOP + TRAP) 1221 public MyValue1[] test48() { 1222 return new MyValue1[0]; 1223 } 1224 1225 @DontCompile 1226 public void test48_verifier(boolean warmup) { 1227 MyValue1[] va = test48(); 1228 Asserts.assertEQ(va.length, 0); 1229 } 1230 1231 static MyValue1[] test49_va; 1232 1233 // Test that value type array loaded from field has correct type 1234 @Test(failOn = (LOOP)) 1235 public long test49() { 1236 return test49_va[0].hash(); 1237 } 1238 1239 @DontCompile 1240 public void test49_verifier(boolean warmup) { 1241 test49_va = new MyValue1[1]; 1242 test49_va[0] = MyValue1.createInline(rI, rL); 1243 long result = test49(); 1244 Asserts.assertEQ(result, hash()); 1245 } 1246 1247 // test vdefault 1248 @Test(failOn = ALLOC + LOAD + LOADP + STORE + LOOP + TRAP) 1249 public long test50() { 1250 MyValue2 v = MyValue2.createDefaultInline(); 1251 return v.hash(); 1252 } 1253 1254 @DontCompile 1255 public void test50_verifier(boolean warmup) { 1256 long result = test50(); 1257 Asserts.assertEQ(result, MyValue2.createDefaultInline().hash()); 1258 } 1259 1260 // test vdefault 1261 @Test(failOn = ALLOC + STORE + LOOP + TRAP) 1262 public long test51() { 1263 MyValue1 v1 = MyValue1.createDefaultInline(); 1264 MyValue1 v2 = MyValue1.createDefaultDontInline(); 1265 return v1.hash() + v2.hash(); 1266 } 1267 1268 @DontCompile 1269 public void test51_verifier(boolean warmup) { 1270 long result = test51(); 1271 Asserts.assertEQ(result, 2 * MyValue1.createDefaultInline().hash()); 1272 } 1273 1274 // test vwithfield 1275 @Test(failOn = ALLOC + LOAD + LOADP + STORE + LOOP + TRAP) 1276 public long test52() { 1277 MyValue2 v = MyValue2.createWithFieldsInline(rI, true); 1278 return v.hash(); 1279 } 1280 1281 @DontCompile 1282 public void test52_verifier(boolean warmup) { 1283 long result = test52(); 1284 Asserts.assertEQ(result, MyValue2.createWithFieldsInline(rI, true).hash()); 1285 } 1286 1287 // test vwithfield 1288 @Test(failOn = ALLOC + STORE + LOOP + TRAP) 1289 public long test53() { 1290 MyValue1 v1 = MyValue1.createWithFieldsInline(rI, rL); 1291 MyValue1 v2 = MyValue1.createWithFieldsDontInline(rI, rL); 1292 return v1.hash() + v2.hash(); 1293 } 1294 1295 @DontCompile 1296 public void test53_verifier(boolean warmup) { 1297 long result = test53(); 1298 Asserts.assertEQ(result, 2 * hash()); 1299 } 1300 1301 // multi-dimensional arrays 1302 @Test 1303 public MyValue1[][][] test54(int len1, int len2, int len3) { 1304 MyValue1[][][] arr = new MyValue1[len1][len2][len3]; 1305 for (int i = 0; i < len1; i++) { 1306 for (int j = 0; j < len2; j++) { 1307 for (int k = 0; k < len3; k++) { 1308 arr[i][j][k] = MyValue1.createDontInline(rI + i , rL + j + k); 1309 } 1310 } 1311 } 1312 return arr; 1313 } 1314 1315 @DontCompile 1316 public void test54_verifier(boolean warmup) { 1317 MyValue1[][][] arr = test54(2, 3, 4); 1318 for (int i = 0; i < 2; i++) { 1319 for (int j = 0; j < 3; j++) { 1320 for (int k = 0; k < 4; k++) { 1321 Asserts.assertEQ(arr[i][j][k].hash(), MyValue1.createDontInline(rI + i , rL + j + k).hash()); 1322 } 1323 } 1324 } 1325 } 1326 1327 @Test 1328 public void test55(MyValue1[][][] arr, long[] res) { 1329 int l = 0; 1330 for (int i = 0; i < arr.length; i++) { 1331 for (int j = 0; j < arr[i].length; j++) { 1332 for (int k = 0; k < arr[i][j].length; k++) { 1333 res[l] = arr[i][j][k].hash(); 1334 l++; 1335 } 1336 } 1337 } 1338 } 1339 1340 @DontCompile 1341 public void test55_verifier(boolean warmup) { 1342 MyValue1[][][] arr = new MyValue1[2][3][4]; 1343 long[] res = new long[2*3*4]; 1344 long[] verif = new long[2*3*4]; 1345 int l = 0; 1346 for (int i = 0; i < 2; i++) { 1347 for (int j = 0; j < 3; j++) { 1348 for (int k = 0; k < 4; k++) { 1349 arr[i][j][k] = MyValue1.createDontInline(rI + i, rL + j + k); 1350 verif[l] = arr[i][j][k].hash(); 1351 l++; 1352 } 1353 } 1354 } 1355 test55(arr, res); 1356 for (int i = 0; i < verif.length; i++) { 1357 Asserts.assertEQ(res[i], verif[i]); 1358 } 1359 } 1360 1361 class TestClass56 { 1362 public MyValue1 v; 1363 } 1364 1365 // Test allocation elimination of unused object with initialized value type field 1366 @Test(failOn = ALLOC + LOAD + STORE + LOOP) 1367 public void test56(boolean deopt) { 1368 TestClass56 unused = new TestClass56(); 1369 MyValue1 v = MyValue1.createInline(rI, rL); 1370 unused.v = v; 1371 if (deopt) { 1372 // uncommon trap 1373 WHITE_BOX.deoptimizeMethod(tests.get("ValueTypeTestBench::test56")); 1374 } 1375 } 1376 1377 @DontCompile 1378 public void test56_verifier(boolean warmup) { 1379 test56(!warmup); 1380 } 1381 1382 // Test loop peeling 1383 @Test(failOn = ALLOC + LOAD + STORE) 1384 public void test57() { 1385 MyValue1 v = MyValue1.createInline(0, 1); 1386 // Trigger OSR compilation and loop peeling 1387 for (int i = 0; i < 100_000; ++i) { 1388 if (v.x != i || v.y != i + 1) { 1389 // Uncommon trap 1390 throw new RuntimeException("test57 failed"); 1391 } 1392 v = MyValue1.createInline(i + 1, i + 2); 1393 } 1394 } 1395 1396 @DontCompile 1397 public void test57_verifier(boolean warmup) { 1398 test57(); 1399 } 1400 1401 // Test loop peeling and unrolling 1402 @Test() 1403 public void test58() { 1404 MyValue1 v1 = MyValue1.createInline(0, 0); 1405 MyValue1 v2 = MyValue1.createInline(1, 1); 1406 // Trigger OSR compilation and loop peeling 1407 for (int i = 0; i < 100_000; ++i) { 1408 if (v1.x != 2*i || v2.x != i+1 || v2.y != i+1) { 1409 // Uncommon trap 1410 throw new RuntimeException("test58 failed"); 1411 } 1412 v1 = MyValue1.createInline(2*(i+1), 0); 1413 v2 = MyValue1.createInline(i+2, i+2); 1414 } 1415 } 1416 1417 @DontCompile 1418 public void test58_verifier(boolean warmup) { 1419 test58(); 1420 } 1421 1422 // When calling a method on __Value, passing fields as arguments is impossible 1423 @Test(valid = ValueTypePassFieldsAsArgsOn, match = {ALLOC, STORE}, matchCount={1, 9}) 1424 @Test(valid = ValueTypePassFieldsAsArgsOff, failOn = ALLOC + STORE + LOAD) 1425 public String test59(MyValue1 v) { 1426 return v.toString(); 1427 } 1428 1429 @DontCompile 1430 public void test59_verifier(boolean warmup) { 1431 boolean failed = false; 1432 try { 1433 test59(val1); 1434 failed = true; 1435 } catch (UnsupportedOperationException uoe) { 1436 } 1437 Asserts.assertFalse(failed); 1438 } 1439 1440 // Same as above, but the method on __Value is inlined 1441 // hashCode allocates an exception so can't really check the graph shape 1442 @Test() 1443 public int test60(MyValue1 v) { 1444 return v.hashCode(); 1445 } 1446 1447 @DontCompile 1448 public void test60_verifier(boolean warmup) { 1449 boolean failed = false; 1450 try { 1451 test60(val1); 1452 failed = true; 1453 } catch (UnsupportedOperationException uoe) { 1454 } 1455 Asserts.assertFalse(failed); 1456 } 1457 1458 // ========== Test infrastructure ========== 1459 1460 private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); 1461 private static final int ValueTypePassFieldsAsArgsOn = 0x1; 1462 private static final int ValueTypePassFieldsAsArgsOff = 0x2; 1463 private static final int ValueTypeArrayFlattenOn = 0x4; 1464 private static final int ValueTypeArrayFlattenOff = 0x8; 1465 static final int AllFlags = ValueTypePassFieldsAsArgsOn | ValueTypePassFieldsAsArgsOff | ValueTypeArrayFlattenOn | ValueTypeArrayFlattenOff; 1466 private static final boolean ValueTypePassFieldsAsArgs = (Boolean)WHITE_BOX.getVMFlag("ValueTypePassFieldsAsArgs"); 1467 private static final boolean ValueTypeArrayFlatten = (Boolean)WHITE_BOX.getVMFlag("ValueArrayFlatten"); 1468 private static final int COMP_LEVEL_ANY = -1; 1469 private static final int COMP_LEVEL_FULL_OPTIMIZATION = 4; 1470 private static final Hashtable<String, Method> tests = new Hashtable<String, Method>(); 1471 private static final int WARMUP = 250; 1472 private static boolean USE_COMPILER = WHITE_BOX.getBooleanVMFlag("UseCompiler"); 1473 private static boolean PRINT_IDEAL = WHITE_BOX.getBooleanVMFlag("PrintIdeal"); 1474 // TODO use Platform.isComp() after merge with JDK 9 1475 private static boolean XCOMP = System.getProperty("java.vm.info").startsWith("compiled "); 1476 1477 // Regular expressions used to match nodes in the PrintIdeal output 1478 private static final String START = "(\\d+\\t(.*"; 1479 private static final String MID = ".*)+\\t===.*"; 1480 private static final String END = ")|"; 1481 private static final String ALLOC = START + "CallStaticJava" + MID + "_new_instance_Java" + END; 1482 private static final String ALLOCA = START + "CallStaticJava" + MID + "_new_array_Java" + END; 1483 private static final String LOAD = START + "Load(B|S|I|L|F|D)" + MID + "valuetype\\*" + END; 1484 private static final String LOADP = START + "Load(P|N)" + MID + "valuetype\\*" + END; 1485 private static final String STORE = START + "Store(B|S|I|L|F|D)" + MID + "valuetype\\*" + END; 1486 private static final String STOREP = START + "Store(P|N)" + MID + "valuetype\\*" + END; 1487 private static final String LOOP = START + "Loop" + MID + "" + END; 1488 private static final String TRAP = START + "CallStaticJava" + MID + "uncommon_trap" + END; 1489 // TODO: match field values of scalar replaced object 1490 private static final String SCOBJ = "(.*# ScObj.*" + END; 1491 1492 static { 1493 // Gather all test methods and put them in Hashtable 1494 for (Method m : ValueTypeTestBench.class.getDeclaredMethods()) { 1495 Test[] annos = m.getAnnotationsByType(Test.class); 1496 if (annos.length != 0) { 1497 tests.put("ValueTypeTestBench::" + m.getName(), m); 1498 } 1499 } 1500 } 1501 1502 private static void execute_vm(String... args) throws Throwable { 1503 Asserts.assertFalse(tests.isEmpty(), "no tests to execute"); 1504 ArrayList<String> all_args = new ArrayList(List.of(args)); 1505 // Run tests in own process and verify output 1506 all_args.add(ValueTypeTestBench.class.getName()); 1507 all_args.add("run"); 1508 // Spawn process with default JVM options from the test's run command 1509 OutputAnalyzer oa = ProcessTools.executeTestJvmAllArgs(all_args.toArray(new String[all_args.size()])); 1510 // If ideal graph printing is enabled/supported, verify output 1511 String output = oa.getOutput(); 1512 oa.shouldHaveExitValue(0); 1513 boolean verifyIR = output.contains("PrintIdeal enabled") && 1514 !output.contains("ValueTypePassFieldsAsArgs is not supported on this platform"); 1515 if (verifyIR) { 1516 parseOutput(output); 1517 } else { 1518 System.out.println("WARNING: IR verification disabled! Running with -Xint, -Xcomp or release build?"); 1519 } 1520 } 1521 1522 public static void main(String[] args) throws Throwable { 1523 //tests.values().removeIf(p -> !p.getName().equals("test58")); // Run single test 1524 if (args.length == 0) { 1525 execute_vm("-XX:+IgnoreUnrecognizedVMOptions", "-XX:-BackgroundCompilation", 1526 "-XX:+PrintCompilation", "-XX:+PrintIdeal", "-XX:+PrintOptoAssembly", 1527 "-XX:CompileCommand=quiet", "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.ValueTypeTestBench::*", 1528 "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue1::*", 1529 "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue2::*", 1530 "-XX:CompileCommand=compileonly,java.lang.Object::<init>", 1531 "-XX:CompileCommand=inline,java.lang.__Value::hashCode"); 1532 } else { 1533 // Execute tests 1534 ValueTypeTestBench bench = new ValueTypeTestBench(); 1535 bench.run(); 1536 } 1537 } 1538 1539 public static void parseOutput(String output) throws Exception { 1540 String split = "b compiler.valhalla.valuetypes."; 1541 String[] compilations = output.split(split); 1542 // Print header 1543 System.out.println(compilations[0]); 1544 // Iterate over compilation output 1545 for (String graph : compilations) { 1546 String[] lines = graph.split("\\n"); 1547 if (lines[0].contains("@")) { 1548 continue; // Ignore OSR compilations 1549 } 1550 String testName = lines[0].split(" ")[0]; 1551 Method test = tests.get(testName); 1552 if (test == null) { 1553 // Skip helper methods 1554 continue; 1555 } 1556 if (PRINT_GRAPH) { 1557 System.out.println("\nGraph for " + graph); 1558 } 1559 // Parse graph using regular expressions to determine if it contains forbidden nodes 1560 Test[] annos = test.getAnnotationsByType(Test.class); 1561 Test anno = null; 1562 for (Test a : annos) { 1563 if ((a.valid() & ValueTypePassFieldsAsArgsOn) != 0 && ValueTypePassFieldsAsArgs) { 1564 assert anno == null; 1565 anno = a; 1566 } else if ((a.valid() & ValueTypePassFieldsAsArgsOff) != 0 && !ValueTypePassFieldsAsArgs) { 1567 assert anno == null; 1568 anno = a; 1569 } else if ((a.valid() & ValueTypeArrayFlattenOn) != 0 && ValueTypeArrayFlatten) { 1570 assert anno == null; 1571 anno = a; 1572 } else if ((a.valid() & ValueTypeArrayFlattenOff) != 0 && !ValueTypeArrayFlatten) { 1573 assert anno == null; 1574 anno = a; 1575 } 1576 } 1577 assert anno != null; 1578 String regexFail = anno.failOn(); 1579 if (!regexFail.isEmpty()) { 1580 Pattern pattern = Pattern.compile(regexFail.substring(0, regexFail.length()-1)); 1581 Matcher matcher = pattern.matcher(graph); 1582 boolean found = matcher.find(); 1583 Asserts.assertFalse(found, "Graph for '" + testName + "' contains forbidden node:\n" + (found ? matcher.group() : "")); 1584 } 1585 String[] regexMatch = anno.match(); 1586 int[] matchCount = anno.matchCount(); 1587 for (int i = 0; i < regexMatch.length; ++i) { 1588 Pattern pattern = Pattern.compile(regexMatch[i].substring(0, regexMatch[i].length()-1)); 1589 Matcher matcher = pattern.matcher(graph); 1590 int count = 0; 1591 String nodes = ""; 1592 while (matcher.find()) { 1593 count++; 1594 nodes += matcher.group() + "\n"; 1595 } 1596 if (matchCount[i] < 0) { 1597 Asserts.assertLTE(Math.abs(matchCount[i]), count, "Graph for '" + testName + "' contains different number of match nodes:\n" + nodes); 1598 } else { 1599 Asserts.assertEQ(matchCount[i], count, "Graph for '" + testName + "' contains different number of match nodes:\n" + nodes); 1600 } 1601 } 1602 tests.remove(testName); 1603 System.out.println(testName + " passed"); 1604 } 1605 // Check if all tests were compiled 1606 if (tests.size() != 0) { 1607 for (String name : tests.keySet()) { 1608 System.out.println("Test '" + name + "' not compiled!"); 1609 } 1610 throw new RuntimeException("Not all tests were compiled"); 1611 } 1612 } 1613 1614 public void setup(Method[] methods) { 1615 for (Method m : methods) { 1616 if (m.isAnnotationPresent(Test.class)) { 1617 // Don't inline tests 1618 WHITE_BOX.testSetDontInlineMethod(m, true); 1619 } 1620 if (m.isAnnotationPresent(DontCompile.class)) { 1621 WHITE_BOX.makeMethodNotCompilable(m, COMP_LEVEL_ANY, true); 1622 WHITE_BOX.makeMethodNotCompilable(m, COMP_LEVEL_ANY, false); 1623 } 1624 if (m.isAnnotationPresent(ForceInline.class)) { 1625 WHITE_BOX.testSetForceInlineMethod(m, true); 1626 } else if (m.isAnnotationPresent(DontInline.class)) { 1627 WHITE_BOX.testSetDontInlineMethod(m, true); 1628 } 1629 } 1630 } 1631 1632 public void run() throws Exception { 1633 if (USE_COMPILER && PRINT_IDEAL && !XCOMP) { 1634 System.out.println("PrintIdeal enabled"); 1635 } 1636 System.out.format("rI = %d, rL = %d\n", rI, rL); 1637 setup(this.getClass().getDeclaredMethods()); 1638 setup(MyValue1.class.getDeclaredMethods()); 1639 setup(MyValue2.class.getDeclaredMethods()); 1640 1641 // TODO enable this after JDK 9 merge and verify that it's compiled 1642 //WHITE_BOX.enqueueInitializerForCompilation(this.getClass(), COMP_LEVEL_FULL_OPTIMIZATION); 1643 1644 // Execute tests 1645 for (Method test : tests.values()) { 1646 Method verifier = getClass().getDeclaredMethod(test.getName() + "_verifier", boolean.class); 1647 // Warmup using verifier method 1648 for (int i = 0; i < WARMUP; ++i) { 1649 verifier.invoke(this, true); 1650 } 1651 // Trigger compilation 1652 WHITE_BOX.enqueueMethodForCompilation(test, COMP_LEVEL_FULL_OPTIMIZATION); 1653 Asserts.assertTrue(!USE_COMPILER || WHITE_BOX.isMethodCompiled(test, false), test + " not compiled"); 1654 // Check result 1655 verifier.invoke(this, false); 1656 } 1657 } 1658 } 1659 1660 // Mark method as test 1661 @Retention(RetentionPolicy.RUNTIME) 1662 @Repeatable(Tests.class) 1663 @interface Test { 1664 // Regular expression used to match forbidden IR nodes 1665 // in the C2 IR emitted for this test. 1666 String failOn() default ""; 1667 // Regular expressions used to match and count IR nodes. 1668 String[] match() default { }; 1669 int[] matchCount() default { }; 1670 int valid() default ValueTypeTestBench.AllFlags; 1671 } 1672 1673 @Retention(RetentionPolicy.RUNTIME) 1674 @interface Tests { 1675 Test[] value(); 1676 } 1677 1678 // Force method inlining during compilation 1679 @Retention(RetentionPolicy.RUNTIME) 1680 @interface ForceInline { } 1681 1682 // Prevent method inlining during compilation 1683 @Retention(RetentionPolicy.RUNTIME) 1684 @interface DontInline { } 1685 1686 // Prevent method compilation 1687 @Retention(RetentionPolicy.RUNTIME) 1688 @interface DontCompile { }