1 /* 2 * Copyright (c) 2021, 2025, 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 package compiler.valhalla.inlinetypes; 25 26 import compiler.lib.ir_framework.*; 27 import jdk.test.lib.Asserts; 28 29 import java.lang.reflect.Method; 30 31 import jdk.internal.value.ValueClass; 32 import jdk.internal.vm.annotation.LooselyConsistentValue; 33 import jdk.internal.vm.annotation.NullRestricted; 34 import jdk.internal.vm.annotation.Strict; 35 36 import static compiler.valhalla.inlinetypes.InlineTypeIRNode.*; 37 import static compiler.valhalla.inlinetypes.InlineTypes.*; 38 39 /* 40 * @test 41 * @key randomness 42 * @bug 8327695 43 * @summary Test the basic value class implementation in C2. 44 * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") 45 * @library /test/lib / 46 * @enablePreview 47 * @modules java.base/jdk.internal.value 48 * java.base/jdk.internal.vm.annotation 49 * @run main/othervm/timeout=300 compiler.valhalla.inlinetypes.TestBasicFunctionality 50 */ 51 52 @ForceCompileClassInitializer 53 public class TestBasicFunctionality { 54 55 public static void main(String[] args) { 56 InlineTypes.getFramework() 57 .addScenarios(InlineTypes.DEFAULT_SCENARIOS) 58 .addHelperClasses(MyValue1.class, 59 MyValue2.class, 60 MyValue2Inline.class, 61 MyValue3.class, 62 MyValue3Inline.class) 63 .start(); 64 } 65 66 protected long hash() { 67 return hash(rI, rL); 68 } 69 70 protected long hash(int x, long y) { 71 return MyValue1.createWithFieldsInline(x, y).hash(); 72 } 73 74 @DontInline 75 static void call() {} 76 77 // Receive value class through call to interpreter 78 @Test 79 @IR(failOn = {ALLOC, STORE, TRAP}) 80 public long test1() { 81 MyValue1 v = MyValue1.createWithFieldsDontInline(rI, rL); 82 return v.hash(); 83 } 84 85 @Run(test = "test1") 86 public void test1_verifier() { 87 long result = test1(); 88 Asserts.assertEQ(result, hash()); 89 } 90 91 // Receive value object from interpreter via parameter 92 @Test 93 @IR(failOn = {ALLOC, STORE, TRAP}) 94 public long test2(MyValue1 v) { 95 return v.hash(); 96 } 97 98 @Run(test = "test2") 99 public void test2_verifier() { 100 MyValue1 v = MyValue1.createWithFieldsDontInline(rI, rL); 101 long result = test2(v); 102 Asserts.assertEQ(result, hash()); 103 } 104 105 // Return incoming value object without accessing fields 106 @Test 107 @IR(applyIf = {"InlineTypePassFieldsAsArgs", "true"}, 108 counts = {ALLOC, "= 1", STORE, "= 19"}, 109 failOn = {LOAD, TRAP}) 110 @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"}, 111 failOn = {ALLOC, LOAD, STORE, TRAP}) 112 public MyValue1 test3(MyValue1 v) { 113 return v; 114 } 115 116 @Run(test = "test3") 117 public void test3_verifier() { 118 MyValue1 v1 = MyValue1.createWithFieldsDontInline(rI, rL); 119 MyValue1 v2 = test3(v1); 120 Asserts.assertEQ(v1.x, v2.x); 121 Asserts.assertEQ(v1.y, v2.y); 122 } 123 124 // Create a value object in compiled code and only use fields. 125 // Allocation should go away because value object does not escape. 126 @Test 127 @IR(failOn = {ALLOC, LOAD, STORE, TRAP}) 128 public long test4() { 129 MyValue1 v = MyValue1.createWithFieldsInline(rI, rL); 130 return v.hash(); 131 } 132 133 @Run(test = "test4") 134 public void test4_verifier() { 135 long result = test4(); 136 Asserts.assertEQ(result, hash()); 137 } 138 139 // Create a value object in compiled code and pass it to 140 // an inlined compiled method via a call. 141 @Test 142 @IR(failOn = {ALLOC, LOAD, STORE, TRAP}) 143 public long test5() { 144 MyValue1 v = MyValue1.createWithFieldsInline(rI, rL); 145 return test5Inline(v); 146 } 147 148 @ForceInline 149 public long test5Inline(MyValue1 v) { 150 return v.hash(); 151 } 152 153 @Run(test = "test5") 154 public void test5_verifier() { 155 long result = test5(); 156 Asserts.assertEQ(result, hash()); 157 } 158 159 // Create a value object in compiled code and pass it to 160 // the interpreter via a call. 161 @Test 162 @IR(applyIf = {"InlineTypePassFieldsAsArgs", "true"}, 163 counts = {ALLOC, "<= 1"}, // 1 MyValue2 allocation (if not the all-zero value) 164 failOn = {LOAD, TRAP}) 165 @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"}, 166 counts = {ALLOC, "<= 2"}, // 1 MyValue1 and 1 MyValue2 allocation (if not the all-zero value) 167 failOn = {LOAD, TRAP}) 168 public long test6() { 169 MyValue1 v = MyValue1.createWithFieldsInline(rI, rL); 170 // Pass to interpreter 171 return v.hashInterpreted(); 172 } 173 174 @Run(test = "test6") 175 public void test6_verifier() { 176 long result = test6(); 177 Asserts.assertEQ(result, hash()); 178 } 179 180 // Create a value object in compiled code and pass it to 181 // the interpreter by returning. 182 @Test 183 @IR(counts = {ALLOC, "<= 2"}, 184 failOn = {LOAD, TRAP}) 185 public MyValue1 test7(int x, long y) { 186 return MyValue1.createWithFieldsInline(x, y); 187 } 188 189 @Run(test = "test7") 190 public void test7_verifier() { 191 MyValue1 v = test7(rI, rL); 192 Asserts.assertEQ(v.hash(), hash()); 193 } 194 195 // Merge value objects created from two branches 196 @Test 197 @IR(failOn = {ALLOC, STORE, TRAP}) 198 public long test8(boolean b) { 199 MyValue1 v; 200 if (b) { 201 v = MyValue1.createWithFieldsInline(rI, rL); 202 } else { 203 v = MyValue1.createWithFieldsDontInline(rI + 1, rL + 1); 204 } 205 return v.hash(); 206 } 207 208 @Run(test = "test8") 209 public void test8_verifier() { 210 Asserts.assertEQ(test8(true), hash()); 211 Asserts.assertEQ(test8(false), hash(rI + 1, rL + 1)); 212 } 213 214 static MyValue1 tmp = null; 215 // Merge value objects created from two branches 216 @Test 217 @IR(applyIf = {"InlineTypePassFieldsAsArgs", "true"}, 218 counts = {ALLOC, "= 1", LOAD, "= 19", 219 STORE, "= 3"}, // InitializeNode::coalesce_subword_stores merges stores 220 failOn = {TRAP}) 221 @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"}, 222 counts = {ALLOC, "= 2", STORE, "= 19"}, 223 failOn = {LOAD, TRAP}) 224 public MyValue1 test9(boolean b, int localrI, long localrL) { 225 MyValue1 v; 226 if (b) { 227 // Value object is not allocated 228 // Do not use rI/rL directly here as null values may cause 229 // some redundant null initializations to be optimized out 230 // and matching to fail. 231 v = MyValue1.createWithFieldsInline(localrI, localrL); 232 v.hashInterpreted(); 233 } else { 234 // Value object is allocated by the callee 235 v = MyValue1.createWithFieldsDontInline(rI + 1, rL + 1); 236 } 237 // Need to allocate value object if 'b' is true 238 long sum = v.hashInterpreted(); 239 if (b) { 240 v = MyValue1.createWithFieldsDontInline(rI, sum); 241 } else { 242 v = MyValue1.createWithFieldsDontInline(rI, sum + 1); 243 } 244 // Don't need to allocate value object because both branches allocate 245 return v; 246 } 247 248 @Run(test = "test9") 249 public void test9_verifier() { 250 MyValue1 v = test9(true, rI, rL); 251 Asserts.assertEQ(v.x, rI); 252 Asserts.assertEQ(v.y, hash()); 253 v = test9(false, rI, rL); 254 Asserts.assertEQ(v.x, rI); 255 Asserts.assertEQ(v.y, hash(rI + 1, rL + 1) + 1); 256 } 257 258 // Merge value objects created in a loop (not inlined) 259 @Test 260 @IR(failOn = {ALLOC, STORE, TRAP}) 261 public long test10(int x, long y) { 262 MyValue1 v = MyValue1.createWithFieldsDontInline(x, y); 263 for (int i = 0; i < 10; ++i) { 264 v = MyValue1.createWithFieldsDontInline(v.x + 1, v.y + 1); 265 } 266 return v.hash(); 267 } 268 269 @Run(test = "test10") 270 public void test10_verifier() { 271 long result = test10(rI, rL); 272 Asserts.assertEQ(result, hash(rI + 10, rL + 10)); 273 } 274 275 // Merge value objects created in a loop (inlined) 276 @Test 277 @IR(failOn = {ALLOC, LOAD, STORE, TRAP}) 278 public long test11(int x, long y) { 279 MyValue1 v = MyValue1.createWithFieldsInline(x, y); 280 for (int i = 0; i < 10; ++i) { 281 v = MyValue1.createWithFieldsInline(v.x + 1, v.y + 1); 282 } 283 return v.hash(); 284 } 285 286 @Run(test = "test11") 287 public void test11_verifier() { 288 long result = test11(rI, rL); 289 Asserts.assertEQ(result, hash(rI + 10, rL + 10)); 290 } 291 292 // Test loop with uncommon trap referencing a value object 293 @Test 294 @IR(applyIf = {"UseArrayFlattening", "true"}, 295 counts = {SCOBJ, ">= 1", LOAD, "<= 12"}) // TODO 8227588 (loads should be removed) 296 public long test12(boolean b) { 297 MyValue1 v = MyValue1.createWithFieldsInline(rI, rL); 298 MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedNonAtomicArray(MyValue1.class, Math.abs(rI) % 10, MyValue1.DEFAULT); 299 for (int i = 0; i < va.length; ++i) { 300 va[i] = MyValue1.createWithFieldsInline(rI, rL); 301 } 302 long result = rL; 303 for (int i = 0; i < 1000; ++i) { 304 if (b) { 305 result += v.x; 306 } else { 307 // Uncommon trap referencing v. We delegate allocation to the 308 // interpreter by adding a SafePointScalarObjectNode. 309 result = v.hashInterpreted(); 310 for (int j = 0; j < va.length; ++j) { 311 result += va[j].hash(); 312 } 313 } 314 } 315 return result; 316 } 317 318 @Run(test = "test12") 319 public void test12_verifier(RunInfo info) { 320 long result = test12(info.isWarmUp()); 321 Asserts.assertEQ(result, info.isWarmUp() ? rL + (1000 * rI) : ((Math.abs(rI) % 10) + 1) * hash()); 322 } 323 324 // Test loop with uncommon trap referencing a value object 325 @Test 326 public long test13(boolean b) { 327 MyValue1 v = MyValue1.createWithFieldsDontInline(rI, rL); 328 MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedNonAtomicArray(MyValue1.class, Math.abs(rI) % 10, MyValue1.DEFAULT); 329 for (int i = 0; i < va.length; ++i) { 330 va[i] = MyValue1.createWithFieldsDontInline(rI, rL); 331 } 332 long result = rL; 333 for (int i = 0; i < 1000; ++i) { 334 if (b) { 335 result += v.x; 336 } else { 337 // Uncommon trap referencing v. Should not allocate 338 // but just pass the existing oop to the uncommon trap. 339 result = v.hashInterpreted(); 340 for (int j = 0; j < va.length; ++j) { 341 result += va[j].hashInterpreted(); 342 } 343 } 344 } 345 return result; 346 } 347 348 @Run(test = "test13") 349 public void test13_verifier(RunInfo info) { 350 long result = test13(info.isWarmUp()); 351 Asserts.assertEQ(result, info.isWarmUp() ? rL + (1000 * rI) : ((Math.abs(rI) % 10) + 1) * hash()); 352 } 353 354 // Create a value object in a non-inlined method and then call a 355 // non-inlined method on that value object. 356 @Test 357 @IR(applyIf = {"InlineTypePassFieldsAsArgs", "true"}, 358 failOn = {ALLOC, STORE, TRAP}, 359 counts = {LOAD, "= 19"}) 360 @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"}, 361 failOn = {ALLOC, LOAD, STORE, TRAP}) 362 public long test14() { 363 MyValue1 v = MyValue1.createWithFieldsDontInline(rI, rL); 364 return v.hashInterpreted(); 365 } 366 367 @Run(test = "test14") 368 public void test14_verifier() { 369 long result = test14(); 370 Asserts.assertEQ(result, hash()); 371 } 372 373 // Create a value object in an inlined method and then call a 374 // non-inlined method on that value object. 375 @Test 376 @IR(applyIf = {"InlineTypePassFieldsAsArgs", "true"}, 377 failOn = {LOAD, TRAP}, 378 counts = {ALLOC, "<= 1"}) // 1 MyValue2 allocation (if not the all-zero value) 379 @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"}, 380 failOn = {LOAD, TRAP}, 381 counts = {ALLOC, "<= 2"}) // 1 MyValue1 and 1 MyValue2 allocation (if not the all-zero value) 382 public long test15() { 383 MyValue1 v = MyValue1.createWithFieldsInline(rI, rL); 384 return v.hashInterpreted(); 385 } 386 387 @Run(test = "test15") 388 public void test15_verifier() { 389 long result = test15(); 390 Asserts.assertEQ(result, hash()); 391 } 392 393 // Create a value object in a non-inlined method and then call an 394 // inlined method on that value object. 395 @Test 396 @IR(failOn = {ALLOC, STORE, TRAP}) 397 public long test16() { 398 MyValue1 v = MyValue1.createWithFieldsDontInline(rI, rL); 399 return v.hash(); 400 } 401 402 @Run(test = "test16") 403 public void test16_verifier() { 404 long result = test16(); 405 Asserts.assertEQ(result, hash()); 406 } 407 408 // Create a value object in an inlined method and then call an 409 // inlined method on that value object. 410 @Test 411 @IR(failOn = {ALLOC, LOAD, STORE, TRAP}) 412 public long test17() { 413 MyValue1 v = MyValue1.createWithFieldsInline(rI, rL); 414 return v.hash(); 415 } 416 417 @Run(test = "test17") 418 public void test17_verifier() { 419 long result = test17(); 420 Asserts.assertEQ(result, hash()); 421 } 422 423 // Create a value object in compiled code and pass it to the 424 // interpreter via a call. The value object is live at the first call so 425 // debug info should include a reference to all its fields. 426 @Test 427 @IR(applyIf = {"InlineTypePassFieldsAsArgs", "true"}, 428 counts = {ALLOC, "<= 1"}, // 1 MyValue2 allocation (if not the all-zero value) 429 failOn = {LOAD, TRAP}) 430 @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"}, 431 counts = {ALLOC, "<= 2"}, // 1 MyValue1 and 1 MyValue2 allocation (if not the all-zero value) 432 failOn = {LOAD, TRAP}) 433 public long test18() { 434 MyValue1 v = MyValue1.createWithFieldsInline(rI, rL); 435 v.hashInterpreted(); 436 return v.hashInterpreted(); 437 } 438 439 @Run(test = "test18") 440 public void test18_verifier() { 441 long result = test18(); 442 Asserts.assertEQ(result, hash()); 443 } 444 445 // Create a value object in compiled code and pass it to the 446 // interpreter via a call. The value object is passed twice but 447 // should only be allocated once. 448 @Test 449 @IR(applyIf = {"InlineTypePassFieldsAsArgs", "true"}, 450 counts = {ALLOC, "<= 1"}, // 1 MyValue2 allocation (if not the all-zero value) 451 failOn = {LOAD, TRAP}) 452 @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"}, 453 counts = {ALLOC, "<= 2"}, // 1 MyValue1 and 1 MyValue2 allocation (if not the all-zero value) 454 failOn = {LOAD, TRAP}) 455 public long test19() { 456 MyValue1 v = MyValue1.createWithFieldsInline(rI, rL); 457 return sumValue(v, v); 458 } 459 460 @DontCompile 461 public long sumValue(MyValue1 v, MyValue1 dummy) { 462 return v.hash(); 463 } 464 465 @Run(test = "test19") 466 public void test19_verifier() { 467 long result = test19(); 468 Asserts.assertEQ(result, hash()); 469 } 470 471 // Create a value type (array) in compiled code and pass it to the 472 // interpreter via a call. The value object is live at the uncommon 473 // trap: verify that deoptimization causes the value object to be 474 // correctly allocated. 475 @Test 476 @IR(applyIf = {"InlineTypePassFieldsAsArgs", "true"}, 477 counts = {ALLOC, "<= 1"}, // 1 MyValue2 allocation (if not the all-zero value) 478 failOn = {LOAD}) 479 // TODO 8350865 480 //@IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"}, 481 // counts = {ALLOC, "<= 2"}, // 1 MyValue1 and 1 MyValue2 allocation (if not the all-zero value) 482 // failOn = LOAD) 483 public long test20(boolean deopt, Method m) { 484 MyValue1 v = MyValue1.createWithFieldsInline(rI, rL); 485 MyValue2[] va = (MyValue2[])ValueClass.newNullRestrictedNonAtomicArray(MyValue2.class, 3, MyValue2.DEFAULT); 486 if (deopt) { 487 // uncommon trap 488 TestFramework.deoptimize(m); 489 } 490 491 return v.hashInterpreted() + va[0].hashInterpreted() + 492 va[1].hashInterpreted() + va[2].hashInterpreted(); 493 } 494 495 @Run(test = "test20") 496 public void test20_verifier(RunInfo info) { 497 MyValue2[] va = (MyValue2[])ValueClass.newNullRestrictedNonAtomicArray(MyValue2.class, 42, MyValue2.DEFAULT); 498 long result = test20(!info.isWarmUp(), info.getTest()); 499 Asserts.assertEQ(result, hash() + va[0].hash() + va[1].hash() + va[2].hash()); 500 } 501 502 // Value class fields in regular object 503 MyValue1 val1; 504 MyValue2 val2; 505 @Strict 506 @NullRestricted 507 final MyValue1 val3 = MyValue1.createWithFieldsInline(rI, rL); 508 @Strict 509 @NullRestricted 510 static MyValue1 val4 = MyValue1.DEFAULT; 511 @Strict 512 @NullRestricted 513 static final MyValue1 val5 = MyValue1.createWithFieldsInline(rI, rL); 514 515 // Test value class fields in objects 516 @Test 517 @IR(counts = {ALLOC, "= 4"}, failOn = TRAP) 518 public long test21(int x, long y) { 519 // Compute hash of value class fields 520 long result = val1.hash() + val2.hash() + val3.hash() + val4.hash() + val5.hash(); 521 // Update fields 522 val1 = MyValue1.createWithFieldsInline(x, y); 523 val2 = MyValue2.createWithFieldsInline(x, rD); 524 val4 = MyValue1.createWithFieldsInline(x, y); 525 return result; 526 } 527 528 @Run(test = "test21") 529 public void test21_verifier() { 530 // Check if hash computed by test18 is correct 531 val1 = MyValue1.createWithFieldsInline(rI, rL); 532 val2 = val1.v2; 533 // val3 is initialized in the constructor 534 val4 = val1; 535 // val5 is initialized in the static initializer 536 long hash = val1.hash() + val2.hash() + val3.hash() + val4.hash() + val5.hash(); 537 long result = test21(rI + 1, rL + 1); 538 Asserts.assertEQ(result, hash); 539 // Check if value class fields were updated 540 Asserts.assertEQ(val1.hash(), hash(rI + 1, rL + 1)); 541 Asserts.assertEQ(val2.hash(), MyValue2.createWithFieldsInline(rI + 1, rD).hash()); 542 Asserts.assertEQ(val4.hash(), hash(rI + 1, rL + 1)); 543 } 544 545 // Test folding of constant value class fields 546 @Test 547 @IR(failOn = {ALLOC, LOAD, STORE, LOOP, TRAP}) 548 public long test22() { 549 // This should be constant folded 550 return val5.hash() + val5.v3.hash(); 551 } 552 553 @Run(test = "test22") 554 public void test22_verifier() { 555 long result = test22(); 556 Asserts.assertEQ(result, val5.hash() + val5.v3.hash()); 557 } 558 559 // Test aconst_init 560 @Test 561 @IR(failOn = {ALLOC, LOAD, STORE, LOOP, TRAP}) 562 public long test23() { 563 MyValue2 v = MyValue2.createDefaultInline(); 564 return v.hash(); 565 } 566 567 @Run(test = "test23") 568 public void test23_verifier() { 569 long result = test23(); 570 Asserts.assertEQ(result, MyValue2.createDefaultInline().hash()); 571 } 572 573 // Test aconst_init 574 @Test 575 @IR(failOn = {ALLOC, STORE, LOOP, TRAP}) 576 public long test24() { 577 MyValue1 v1 = MyValue1.createDefaultInline(); 578 MyValue1 v2 = MyValue1.createDefaultDontInline(); 579 return v1.hashPrimitive() + v2.hashPrimitive(); 580 } 581 582 @Run(test = "test24") 583 public void test24_verifier() { 584 long result = test24(); 585 Asserts.assertEQ(result, 2 * MyValue1.createDefaultInline().hashPrimitive()); 586 } 587 588 // Test field initialization 589 @Test 590 @IR(failOn = {ALLOC, LOAD, STORE, LOOP, TRAP}) 591 public long test25() { 592 MyValue2 v = MyValue2.createWithFieldsInline(rI, rD); 593 return v.hash(); 594 } 595 596 @Run(test = "test25") 597 public void test25_verifier() { 598 long result = test25(); 599 Asserts.assertEQ(result, MyValue2.createWithFieldsInline(rI, rD).hash()); 600 } 601 602 // Test field initialization 603 @Test 604 @IR(failOn = {ALLOC, STORE, LOOP, TRAP}) 605 public long test26() { 606 MyValue1 v1 = MyValue1.createWithFieldsInline(rI, rL); 607 MyValue1 v2 = MyValue1.createWithFieldsDontInline(rI, rL); 608 return v1.hash() + v2.hash(); 609 } 610 611 @Run(test = "test26") 612 public void test26_verifier() { 613 long result = test26(); 614 Asserts.assertEQ(result, 2 * hash()); 615 } 616 617 class TestClass27 { 618 @Strict 619 @NullRestricted 620 public MyValue1 v = MyValue1.DEFAULT; 621 } 622 623 // Test allocation elimination of unused object with initialized value class field 624 @Test 625 @IR(failOn = {ALLOC, LOAD, STORE, LOOP}) 626 public void test27(boolean deopt, Method m) { 627 TestClass27 unused = new TestClass27(); 628 MyValue1 v = MyValue1.createWithFieldsInline(rI, rL); 629 unused.v = v; 630 if (deopt) { 631 // uncommon trap 632 TestFramework.deoptimize(m); 633 } 634 } 635 636 @Run(test = "test27") 637 public void test27_verifier(RunInfo info) { 638 test27(!info.isWarmUp(), info.getTest()); 639 } 640 641 @Strict 642 @NullRestricted 643 static MyValue3 staticVal3 = MyValue3.DEFAULT; 644 @Strict 645 @NullRestricted 646 static MyValue3 staticVal3_copy = MyValue3.DEFAULT; 647 648 // Check elimination of redundant value class allocations 649 @Test 650 @IR(counts = {ALLOC, "= 1"}) 651 public MyValue3 test28(MyValue3[] va) { 652 // Create value object and force allocation 653 MyValue3 vt = MyValue3.create(); 654 va[0] = vt; 655 staticVal3 = vt; 656 vt.verify(staticVal3); 657 658 // Value object is now allocated, make a copy and force allocation. 659 // Because copy is equal to vt, C2 should remove this redundant allocation. 660 MyValue3 copy = MyValue3.setC(vt, vt.c); 661 va[0] = copy; 662 staticVal3_copy = copy; 663 copy.verify(staticVal3_copy); 664 return copy; 665 } 666 667 @Run(test = "test28") 668 public void test28_verifier() { 669 MyValue3[] va = (MyValue3[])ValueClass.newNullRestrictedNonAtomicArray(MyValue3.class, 1, MyValue3.DEFAULT); 670 MyValue3 vt = test28(va); 671 staticVal3.verify(vt); 672 staticVal3.verify(va[0]); 673 staticVal3_copy.verify(vt); 674 staticVal3_copy.verify(va[0]); 675 } 676 677 // Verify that only dominating allocations are re-used 678 @Test 679 public MyValue3 test29(boolean warmup) { 680 MyValue3 vt = MyValue3.create(); 681 if (warmup) { 682 staticVal3 = vt; // Force allocation 683 } 684 // Force allocation to verify that above 685 // non-dominating allocation is not re-used 686 MyValue3 copy = MyValue3.setC(vt, vt.c); 687 staticVal3_copy = copy; 688 copy.verify(vt); 689 return copy; 690 } 691 692 @Run(test = "test29") 693 public void test29_verifier(RunInfo info) { 694 MyValue3 vt = test29(info.isWarmUp()); 695 if (info.isWarmUp()) { 696 staticVal3.verify(vt); 697 } 698 } 699 700 // Verify that C2 recognizes value class loads and re-uses the oop to avoid allocations 701 @Test 702 @IR(applyIf = {"UseArrayFlattening", "true"}, 703 failOn = {ALLOC, ALLOCA, STORE}) 704 public MyValue3 test30() { 705 // C2 can re-use the oop of staticVal3 because staticVal3 is equal to copy 706 MyValue3[] va = (MyValue3[])ValueClass.newNullRestrictedNonAtomicArray(MyValue3.class, 1, MyValue3.DEFAULT); 707 MyValue3 copy = MyValue3.copy(staticVal3); 708 va[0] = copy; 709 copy.verify(va[0]); 710 staticVal3 = copy; 711 copy.verify(staticVal3); 712 return copy; 713 } 714 715 @Run(test = "test30") 716 public void test30_verifier() { 717 staticVal3 = MyValue3.create(); 718 MyValue3 vt = test30(); 719 staticVal3.verify(vt); 720 } 721 722 // Verify that C2 recognizes value class loads and re-uses the oop to avoid allocations 723 @Test 724 @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"}, 725 failOn = {ALLOC, ALLOCA, STORE}) 726 public MyValue3 test31() { 727 // C2 can re-use the oop returned by createDontInline() 728 // because the corresponding value object is equal to 'copy'. 729 MyValue3[] va = (MyValue3[])ValueClass.newNullRestrictedNonAtomicArray(MyValue3.class, 1, MyValue3.DEFAULT); 730 MyValue3 copy = MyValue3.copy(MyValue3.createDontInline()); 731 va[0] = copy; 732 copy.verify(va[0]); 733 staticVal3 = copy; 734 copy.verify(staticVal3); 735 return copy; 736 } 737 738 @Run(test = "test31") 739 public void test31_verifier() { 740 MyValue3 vt = test31(); 741 staticVal3.verify(vt); 742 } 743 744 // Verify that C2 recognizes value class loads and re-uses the oop to avoid allocations 745 @Test 746 @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"}, 747 failOn = {ALLOC, ALLOCA, STORE}) 748 public MyValue3 test32(MyValue3 vt) { 749 // C2 can re-use the oop of vt because vt is equal to 'copy'. 750 MyValue3[] va = (MyValue3[])ValueClass.newNullRestrictedNonAtomicArray(MyValue3.class, 1, MyValue3.DEFAULT); 751 MyValue3 copy = MyValue3.copy(vt); 752 va[0] = copy; 753 copy.verify(vt); 754 staticVal3 = copy; 755 copy.verify(staticVal3); 756 return copy; 757 } 758 759 @Run(test = "test32") 760 public void test32_verifier() { 761 MyValue3 vt = MyValue3.create(); 762 MyValue3 result = test32(vt); 763 staticVal3.verify(vt); 764 result.verify(vt); 765 } 766 767 // Test correct identification of value object copies 768 @Test 769 public MyValue3 test33() { 770 MyValue3[] va = (MyValue3[])ValueClass.newNullRestrictedNonAtomicArray(MyValue3.class, 1, MyValue3.DEFAULT); 771 MyValue3 vt = MyValue3.copy(staticVal3); 772 vt = MyValue3.setI(vt, vt.c); 773 // vt is not equal to staticVal3, so C2 should not re-use the oop 774 va[0] = vt; 775 Asserts.assertEQ(va[0].i, (int)vt.c); 776 staticVal3 = vt; 777 vt.verify(staticVal3); 778 return vt; 779 } 780 781 @Run(test = "test33") 782 public void test33_verifier() { 783 staticVal3 = MyValue3.create(); 784 MyValue3 vt = test33(); 785 Asserts.assertEQ(staticVal3.i, (int)staticVal3.c); 786 Asserts.assertEQ(vt.i, (int)staticVal3.c); 787 } 788 789 static final MyValue3[] test34Array = (MyValue3[])ValueClass.newNullRestrictedNonAtomicArray(MyValue3.class, 2, MyValue3.DEFAULT); 790 791 // Verify that the all-zero value class is never allocated. 792 // C2 code should load and use the all-zero oop from the java mirror. 793 @Test 794 // The concept of a pre-allocated "all-zero value" was removed. 795 // @IR(applyIf = {"UseArrayFlattening", "true"}, 796 // failOn = {ALLOC, ALLOCA, LOAD, STORE, LOOP, TRAP}) 797 public MyValue3 test34() { 798 // Explicitly create all-zero value 799 MyValue3 vt = MyValue3.createDefault(); 800 test34Array[0] = vt; 801 staticVal3 = vt; 802 vt.verify(vt); 803 804 // Load all-zero value from uninitialized value class array 805 MyValue3[] dva = (MyValue3[])ValueClass.newNullRestrictedNonAtomicArray(MyValue3.class, 1, MyValue3.DEFAULT); 806 staticVal3_copy = dva[0]; 807 test34Array[1] = dva[0]; 808 dva[0].verify(dva[0]); 809 return vt; 810 } 811 812 @Run(test = "test34") 813 public void test34_verifier() { 814 MyValue3 vt = MyValue3.createDefault(); 815 test34Array[0] = MyValue3.create(); 816 test34Array[1] = MyValue3.create(); 817 MyValue3 res = test34(); 818 res.verify(vt); 819 staticVal3.verify(vt); 820 staticVal3_copy.verify(vt); 821 test34Array[0].verify(vt); 822 test34Array[1].verify(vt); 823 } 824 825 static final MyValue3[] test35Array = (MyValue3[])ValueClass.newNullRestrictedNonAtomicArray(MyValue3.class, 1, MyValue3.DEFAULT); 826 827 // Same as above but manually initialize value class fields to all-zero. 828 @Test 829 // The concept of a pre-allocated "all-zero value" was removed. 830 // @IR(applyIf = {"UseArrayFlattening", "true"}, 831 // failOn = {ALLOC, ALLOCA, LOAD, STORE, LOOP, TRAP}) 832 public MyValue3 test35(MyValue3 vt) { 833 vt = MyValue3.setC(vt, (char)0); 834 vt = MyValue3.setBB(vt, (byte)0); 835 vt = MyValue3.setS(vt, (short)0); 836 vt = MyValue3.setI(vt, 0); 837 vt = MyValue3.setL(vt, 0); 838 vt = MyValue3.setO(vt, null); 839 vt = MyValue3.setF1(vt, 0); 840 vt = MyValue3.setF2(vt, 0); 841 vt = MyValue3.setF3(vt, 0); 842 vt = MyValue3.setF4(vt, 0); 843 vt = MyValue3.setF5(vt, 0); 844 vt = MyValue3.setF6(vt, 0); 845 vt = MyValue3.setV1(vt, MyValue3Inline.createDefault()); 846 test35Array[0] = vt; 847 staticVal3 = vt; 848 vt.verify(vt); 849 return vt; 850 } 851 852 @Run(test = "test35") 853 public void test35_verifier() { 854 MyValue3 vt = MyValue3.createDefault(); 855 test35Array[0] = MyValue3.create(); 856 MyValue3 res = test35(test35Array[0]); 857 res.verify(vt); 858 staticVal3.verify(vt); 859 test35Array[0].verify(vt); 860 } 861 862 // Merge value objects created from two branches 863 864 private Object test36_helper(Object v) { 865 return v; 866 } 867 868 @Test 869 @IR(failOn = {ALLOC, STORE, TRAP}) 870 public long test36(boolean b) { 871 Object o; 872 if (b) { 873 o = test36_helper(MyValue1.createWithFieldsInline(rI, rL)); 874 } else { 875 o = test36_helper(MyValue1.createWithFieldsDontInline(rI + 1, rL + 1)); 876 } 877 MyValue1 v = (MyValue1)o; 878 return v.hash(); 879 } 880 881 @Run(test = "test36") 882 public void test36_verifier() { 883 Asserts.assertEQ(test36(true), hash()); 884 Asserts.assertEQ(test36(false), hash(rI + 1, rL + 1)); 885 } 886 887 // Test correct loading of flattened fields 888 @LooselyConsistentValue 889 value class Test37Value2 { 890 int x = 0; 891 int y = 0; 892 } 893 894 @LooselyConsistentValue 895 value class Test37Value1 { 896 double d = 0; 897 float f = 0; 898 @Strict 899 @NullRestricted 900 Test37Value2 v = new Test37Value2(); 901 } 902 903 @Test 904 public Test37Value1 test37(Test37Value1 vt) { 905 return vt; 906 } 907 908 @Run(test = "test37") 909 public void test37_verifier() { 910 Test37Value1 vt = new Test37Value1(); 911 Asserts.assertEQ(test37(vt), vt); 912 } 913 914 // Test elimination of value class allocations without a unique CheckCastPP 915 @LooselyConsistentValue 916 static value class Test38Value { 917 public int i; 918 public Test38Value(int i) { this.i = i; } 919 } 920 921 @Strict 922 @NullRestricted 923 static Test38Value test38Field = new Test38Value(0); 924 925 @Test 926 public void test38() { 927 for (int i = 3; i < 100; ++i) { 928 int j = 1; 929 while (++j < 11) { 930 try { 931 test38Field = new Test38Value(i); 932 } catch (ArithmeticException ae) { } 933 } 934 } 935 } 936 937 @Run(test = "test38") 938 public void test38_verifier() { 939 test38Field = new Test38Value(0); 940 test38(); 941 Asserts.assertEQ(test38Field, new Test38Value(99)); 942 } 943 944 // Tests split if with value class Phi users 945 @LooselyConsistentValue 946 static value class Test39Value { 947 public int iFld1; 948 public int iFld2; 949 950 public Test39Value(int i1, int i2) { iFld1 = i1; iFld2 = i2; } 951 } 952 953 static int test39A1[][] = new int[400][400]; 954 static double test39A2[] = new double[400]; 955 @Strict 956 @NullRestricted 957 static Test39Value test39Val = new Test39Value(0, 0); 958 959 @DontInline 960 public int[] getArray() { 961 return new int[400]; 962 } 963 964 @Test 965 public int test39() { 966 int result = 0; 967 for (int i = 0; i < 100; ++i) { 968 switch ((i >>> 1) % 3) { 969 case 0: 970 test39A1[i][i] = i; 971 break; 972 case 1: 973 for (int j = 0; j < 100; ++j) { 974 test39A1[i] = getArray(); 975 test39Val = new Test39Value(j, test39Val.iFld2); 976 } 977 break; 978 case 2: 979 for (float f = 142; f > i; f--) { 980 test39A2[i + 1] += 3; 981 } 982 result += test39Val.iFld1; 983 break; 984 } 985 double d1 = 1; 986 while (++d1 < 142) { 987 test39A1[(i >>> 1) % 400][i + 1] = result; 988 test39Val = new Test39Value(i, test39Val.iFld2); 989 } 990 } 991 return result; 992 } 993 994 @Run(test = "test39") 995 @Warmup(10) 996 public void test39_verifier() { 997 int result = test39(); 998 Asserts.assertEQ(result, 1552); 999 } 1000 1001 // Test scalar replacement of value class array containing value class with oop fields 1002 @Test 1003 public long test40(boolean b) { 1004 MyValue1[] va = {MyValue1.createWithFieldsInline(rI, rL)}; 1005 long result = 0; 1006 for (int i = 0; i < 1000; ++i) { 1007 if (!b) { 1008 result = va[0].hash(); 1009 } 1010 } 1011 return result; 1012 } 1013 1014 @Run(test = "test40") 1015 public void test40_verifier(RunInfo info) { 1016 long result = test40(info.isWarmUp()); 1017 Asserts.assertEQ(result, info.isWarmUp() ? 0 : hash()); 1018 } 1019 1020 static value class MyValue41 { 1021 int x; 1022 1023 public MyValue41(int x) { 1024 this.x = x; 1025 } 1026 1027 static MyValue41 make() { 1028 return new MyValue41(0); 1029 } 1030 } 1031 1032 static MyValue41 field41; 1033 1034 // Test detection of value object copies and removal of the MemBarRelease following the value buffer initialization 1035 @Test 1036 @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"}, 1037 failOn = {ALLOC, ALLOCA, STORE}) 1038 public void test41(MyValue41 val) { 1039 field41 = new MyValue41(val.x); 1040 } 1041 1042 @Run(test = "test41") 1043 public void test41_verifier() { 1044 MyValue41 val = new MyValue41(rI); 1045 test41(val); 1046 Asserts.assertEQ(field41, val); 1047 } 1048 1049 @DontInline 1050 public void test42_helper(MyValue41 val) { 1051 Asserts.assertEQ(val, new MyValue41(rI)); 1052 } 1053 1054 // Same as test41 but with call argument requiring buffering 1055 @Test 1056 @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"}, 1057 failOn = {ALLOC, ALLOCA, STORE}) 1058 public void test42(MyValue41 val) { 1059 test42_helper(new MyValue41(val.x)); 1060 } 1061 1062 @Run(test = "test42") 1063 public void test42_verifier() { 1064 MyValue41 val = new MyValue41(rI); 1065 test42(val); 1066 } 1067 1068 static value class MyValue42 { 1069 int x; 1070 1071 @ForceInline 1072 MyValue42(int x) { 1073 this.x = x; 1074 call(); 1075 super(); 1076 } 1077 1078 @ForceInline 1079 static Object make(int x) { 1080 return new MyValue42(x); 1081 } 1082 } 1083 1084 @Test 1085 @IR(failOn = {LOAD}) 1086 public MyValue42 test43(int x) { 1087 return (MyValue42) MyValue42.make(x); 1088 } 1089 1090 @Run(test = "test43") 1091 public void test43_verifier() { 1092 MyValue42 v = test43(rI); 1093 Asserts.assertEQ(rI, v.x); 1094 } 1095 1096 static value class MyValue43 { 1097 int x; 1098 1099 @ForceInline 1100 MyValue43(int x) { 1101 this.x = x; 1102 super(); 1103 call(); 1104 } 1105 1106 @ForceInline 1107 static Object make(int x) { 1108 return new MyValue43(x); 1109 } 1110 } 1111 1112 @Test 1113 @IR(failOn = {LOAD}) 1114 public MyValue43 test44(int x) { 1115 return (MyValue43) MyValue43.make(x); 1116 } 1117 1118 @Run(test = "test44") 1119 public void test44_verifier() { 1120 MyValue43 v = test44(rI); 1121 Asserts.assertEQ(rI, v.x); 1122 } 1123 1124 @LooselyConsistentValue 1125 static value class MyValue45 { 1126 Integer v; 1127 1128 MyValue45(Integer v) { 1129 this.v = v; 1130 } 1131 } 1132 1133 static value class MyValue45ValueHolder { 1134 @NullRestricted 1135 @Strict 1136 MyValue45 v; 1137 1138 MyValue45ValueHolder(Integer v) { 1139 this.v = new MyValue45(v); 1140 } 1141 } 1142 1143 static class MyValue45Holder { 1144 @NullRestricted 1145 @Strict 1146 MyValue45 v; 1147 1148 MyValue45Holder(Integer v) { 1149 this.v = new MyValue45(v); 1150 } 1151 } 1152 1153 @Test 1154 // TODO 8357580 more aggressive flattening 1155 // @IR(applyIfAnd = {"UseFieldFlattening", "true", "UseNullableValueFlattening", "true"}, counts = {IRNode.LOAD_I, "1", IRNode.LOAD_B, "1"}) 1156 public Integer test45(Object arg) { 1157 return ((MyValue45ValueHolder) arg).v.v; 1158 } 1159 1160 @Run(test = "test45") 1161 public void test45_verifier() { 1162 Integer v = null; 1163 Asserts.assertEQ(test45(new MyValue45ValueHolder(v)), v); 1164 v = rI; 1165 Asserts.assertEQ(test45(new MyValue45ValueHolder(v)), v); 1166 } 1167 1168 @Test 1169 // TODO 8357580 more aggressive flattening 1170 // @IR(applyIfAnd = {"UseFieldFlattening", "true", "UseNullableValueFlattening", "true"}, counts = {IRNode.LOAD_L, "1"}) 1171 // @IR(applyIfAnd = {"UseFieldFlattening", "true", "UseNullableValueFlattening", "true"}, failOn = {IRNode.LOAD_I, IRNode.LOAD_B}) 1172 public Integer test46(Object arg) { 1173 return ((MyValue45Holder) arg).v.v; 1174 } 1175 1176 @Run(test = "test46") 1177 public void test46_verifier() { 1178 Integer v = null; 1179 Asserts.assertEQ(test46(new MyValue45Holder(v)), v); 1180 v = rI; 1181 Asserts.assertEQ(test46(new MyValue45Holder(v)), v); 1182 } 1183 1184 static value class MyValue47 { 1185 byte b1; 1186 byte b2; 1187 1188 MyValue47(byte b1, byte b2) { 1189 this.b1 = b1; 1190 this.b2 = b2; 1191 } 1192 } 1193 1194 static value class MyValue47Holder { 1195 @NullRestricted 1196 @Strict 1197 MyValue47 v; 1198 1199 MyValue47Holder(int v) { 1200 byte b1 = (byte) v; 1201 byte b2 = (byte) (v >>> 8); 1202 this.v = new MyValue47(b1, b2); 1203 } 1204 } 1205 1206 static class MyValue47HolderHolder { 1207 @NullRestricted 1208 @Strict 1209 MyValue47Holder v; 1210 1211 MyValue47HolderHolder(MyValue47Holder v) { 1212 this.v = v; 1213 } 1214 } 1215 1216 @Test 1217 @IR(applyIfAnd = {"UseFieldFlattening", "true", "UseAtomicValueFlattening", "true"}, counts = {IRNode.LOAD_S, "1"}) 1218 @IR(applyIfAnd = {"UseFieldFlattening", "true", "UseAtomicValueFlattening", "true"}, failOn = {IRNode.LOAD_B}) 1219 public MyValue47Holder test47(MyValue47HolderHolder arg) { 1220 return arg.v; 1221 } 1222 1223 @Run(test = "test47") 1224 public void test47_verifier() { 1225 MyValue47Holder v = new MyValue47Holder(rI); 1226 Asserts.assertEQ(test47(new MyValue47HolderHolder(v)), v); 1227 } 1228 1229 static final MyValue47Holder[] MY_VALUE_47_HOLDERS = (MyValue47Holder[]) ValueClass.newNullRestrictedAtomicArray(MyValue47Holder.class, 2, new MyValue47Holder(rI)); 1230 1231 @Test 1232 @IR(applyIfAnd = {"UseFieldFlattening", "true", "UseArrayFlattening", "true", "UseAtomicValueFlattening", "true"}, counts = {IRNode.LOAD_S, "1"}) 1233 @IR(applyIfAnd = {"UseFieldFlattening", "true", "UseArrayFlattening", "true", "UseAtomicValueFlattening", "true"}, failOn = {IRNode.LOAD_B}) 1234 public MyValue47Holder test48() { 1235 return MY_VALUE_47_HOLDERS[0]; 1236 } 1237 1238 @Run(test = "test48") 1239 public void test48_verifier() { 1240 MyValue47Holder v = new MyValue47Holder(rI); 1241 Asserts.assertEQ(test48(), v); 1242 } 1243 }