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