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