1 /* 2 * Copyright (c) 2021, 2024, 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.ImplicitlyConstructible; 33 import jdk.internal.vm.annotation.LooselyConsistentValue; 34 import jdk.internal.vm.annotation.NullRestricted; 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 // Receive value class through call to interpreter 75 @Test 76 @IR(failOn = {ALLOC, STORE, TRAP}) 77 public long test1() { 78 MyValue1 v = MyValue1.createWithFieldsDontInline(rI, rL); 79 return v.hash(); 80 } 81 82 @Run(test = "test1") 83 public void test1_verifier() { 84 long result = test1(); 85 Asserts.assertEQ(result, hash()); 86 } 87 88 // Receive value object from interpreter via parameter 89 @Test 90 @IR(failOn = {ALLOC, STORE, TRAP}) 91 public long test2(MyValue1 v) { 92 return v.hash(); 93 } 94 95 @Run(test = "test2") 96 public void test2_verifier() { 97 MyValue1 v = MyValue1.createWithFieldsDontInline(rI, rL); 98 long result = test2(v); 99 Asserts.assertEQ(result, hash()); 100 } 101 102 // Return incoming value object without accessing fields 103 @Test 104 @IR(applyIf = {"InlineTypePassFieldsAsArgs", "true"}, 105 counts = {ALLOC, "= 1", STORE, "= 19"}, 106 failOn = {LOAD, TRAP}) 107 @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"}, 108 failOn = {ALLOC, LOAD, STORE, TRAP}) 109 public MyValue1 test3(MyValue1 v) { 110 return v; 111 } 112 113 @Run(test = "test3") 114 public void test3_verifier() { 115 MyValue1 v1 = MyValue1.createWithFieldsDontInline(rI, rL); 116 MyValue1 v2 = test3(v1); 117 Asserts.assertEQ(v1.x, v2.x); 118 Asserts.assertEQ(v1.y, v2.y); 119 } 120 121 // Create a value object in compiled code and only use fields. 122 // Allocation should go away because value object does not escape. 123 @Test 124 @IR(failOn = {ALLOC, LOAD, STORE, TRAP}) 125 public long test4() { 126 MyValue1 v = MyValue1.createWithFieldsInline(rI, rL); 127 return v.hash(); 128 } 129 130 @Run(test = "test4") 131 public void test4_verifier() { 132 long result = test4(); 133 Asserts.assertEQ(result, hash()); 134 } 135 136 // Create a value object in compiled code and pass it to 137 // an inlined compiled method via a call. 138 @Test 139 @IR(failOn = {ALLOC, LOAD, STORE, TRAP}) 140 public long test5() { 141 MyValue1 v = MyValue1.createWithFieldsInline(rI, rL); 142 return test5Inline(v); 143 } 144 145 @ForceInline 146 public long test5Inline(MyValue1 v) { 147 return v.hash(); 148 } 149 150 @Run(test = "test5") 151 public void test5_verifier() { 152 long result = test5(); 153 Asserts.assertEQ(result, hash()); 154 } 155 156 // Create a value object in compiled code and pass it to 157 // the interpreter via a call. 158 @Test 159 @IR(applyIf = {"InlineTypePassFieldsAsArgs", "true"}, 160 counts = {ALLOC, "<= 1"}, // 1 MyValue2 allocation (if not the default value) 161 failOn = {LOAD, TRAP}) 162 @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"}, 163 counts = {ALLOC, "<= 2"}, // 1 MyValue1 and 1 MyValue2 allocation (if not the default value) 164 failOn = {LOAD, TRAP}) 165 public long test6() { 166 MyValue1 v = MyValue1.createWithFieldsInline(rI, rL); 167 // Pass to interpreter 168 return v.hashInterpreted(); 169 } 170 171 @Run(test = "test6") 172 public void test6_verifier() { 173 long result = test6(); 174 Asserts.assertEQ(result, hash()); 175 } 176 177 // Create a value object in compiled code and pass it to 178 // the interpreter by returning. 179 @Test 180 @IR(counts = {ALLOC, "<= 2"}, 181 failOn = {LOAD, TRAP}) 182 public MyValue1 test7(int x, long y) { 183 return MyValue1.createWithFieldsInline(x, y); 184 } 185 186 @Run(test = "test7") 187 public void test7_verifier() { 188 MyValue1 v = test7(rI, rL); 189 Asserts.assertEQ(v.hash(), hash()); 190 } 191 192 // Merge value objects created from two branches 193 @Test 194 @IR(failOn = {ALLOC, STORE, TRAP}) 195 public long test8(boolean b) { 196 MyValue1 v; 197 if (b) { 198 v = MyValue1.createWithFieldsInline(rI, rL); 199 } else { 200 v = MyValue1.createWithFieldsDontInline(rI + 1, rL + 1); 201 } 202 return v.hash(); 203 } 204 205 @Run(test = "test8") 206 public void test8_verifier() { 207 Asserts.assertEQ(test8(true), hash()); 208 Asserts.assertEQ(test8(false), hash(rI + 1, rL + 1)); 209 } 210 211 static MyValue1 tmp = null; 212 // Merge value objects created from two branches 213 @Test 214 @IR(applyIf = {"InlineTypePassFieldsAsArgs", "true"}, 215 counts = {ALLOC, "= 1", LOAD, "= 19", 216 STORE, "= 3"}, // InitializeNode::coalesce_subword_stores merges stores 217 failOn = {TRAP}) 218 @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"}, 219 counts = {ALLOC, "= 2", STORE, "= 19"}, 220 failOn = {LOAD, TRAP}) 221 public MyValue1 test9(boolean b, int localrI, long localrL) { 222 MyValue1 v; 223 if (b) { 224 // Value object is not allocated 225 // Do not use rI/rL directly here as null values may cause 226 // some redundant null initializations to be optimized out 227 // and matching to fail. 228 v = MyValue1.createWithFieldsInline(localrI, localrL); 229 v.hashInterpreted(); 230 } else { 231 // Value object is allocated by the callee 232 v = MyValue1.createWithFieldsDontInline(rI + 1, rL + 1); 233 } 234 // Need to allocate value object if 'b' is true 235 long sum = v.hashInterpreted(); 236 if (b) { 237 v = MyValue1.createWithFieldsDontInline(rI, sum); 238 } else { 239 v = MyValue1.createWithFieldsDontInline(rI, sum + 1); 240 } 241 // Don't need to allocate value object because both branches allocate 242 return v; 243 } 244 245 @Run(test = "test9") 246 public void test9_verifier() { 247 MyValue1 v = test9(true, rI, rL); 248 Asserts.assertEQ(v.x, rI); 249 Asserts.assertEQ(v.y, hash()); 250 v = test9(false, rI, rL); 251 Asserts.assertEQ(v.x, rI); 252 Asserts.assertEQ(v.y, hash(rI + 1, rL + 1) + 1); 253 } 254 255 // Merge value objects created in a loop (not inlined) 256 @Test 257 @IR(failOn = {ALLOC, STORE, TRAP}) 258 public long test10(int x, long y) { 259 MyValue1 v = MyValue1.createWithFieldsDontInline(x, y); 260 for (int i = 0; i < 10; ++i) { 261 v = MyValue1.createWithFieldsDontInline(v.x + 1, v.y + 1); 262 } 263 return v.hash(); 264 } 265 266 @Run(test = "test10") 267 public void test10_verifier() { 268 long result = test10(rI, rL); 269 Asserts.assertEQ(result, hash(rI + 10, rL + 10)); 270 } 271 272 // Merge value objects created in a loop (inlined) 273 @Test 274 @IR(failOn = {ALLOC, LOAD, STORE, TRAP}) 275 public long test11(int x, long y) { 276 MyValue1 v = MyValue1.createWithFieldsInline(x, y); 277 for (int i = 0; i < 10; ++i) { 278 v = MyValue1.createWithFieldsInline(v.x + 1, v.y + 1); 279 } 280 return v.hash(); 281 } 282 283 @Run(test = "test11") 284 public void test11_verifier() { 285 long result = test11(rI, rL); 286 Asserts.assertEQ(result, hash(rI + 10, rL + 10)); 287 } 288 289 // Test loop with uncommon trap referencing a value object 290 // TODO 8315003 Re-enable 291 /* 292 @Test 293 @IR(applyIf = {"FlatArrayElementMaxSize", "= -1"}, 294 counts = {SCOBJ, ">= 1", LOAD, "<= 12"}) // TODO 8227588 (loads should be removed) 295 public long test12(boolean b) { 296 MyValue1 v = MyValue1.createWithFieldsInline(rI, rL); 297 MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedArray(MyValue1.class, Math.abs(rI) % 10); 298 for (int i = 0; i < va.length; ++i) { 299 va[i] = MyValue1.createWithFieldsInline(rI, rL); 300 } 301 long result = rL; 302 for (int i = 0; i < 1000; ++i) { 303 if (b) { 304 result += v.x; 305 } else { 306 // Uncommon trap referencing v. We delegate allocation to the 307 // interpreter by adding a SafePointScalarObjectNode. 308 result = v.hashInterpreted(); 309 for (int j = 0; j < va.length; ++j) { 310 result += va[j].hash(); 311 } 312 } 313 } 314 return result; 315 } 316 317 @Run(test = "test12") 318 public void test12_verifier(RunInfo info) { 319 long result = test12(info.isWarmUp()); 320 Asserts.assertEQ(result, info.isWarmUp() ? rL + (1000 * rI) : ((Math.abs(rI) % 10) + 1) * hash()); 321 } 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.newNullRestrictedArray(MyValue1.class, Math.abs(rI) % 10); 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 default value) 379 @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"}, 380 failOn = {LOAD, TRAP}, 381 counts = {ALLOC, "<= 2"}) // 1 MyValue1 and 1 MyValue2 allocation (if not the default 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 default value) 429 failOn = {LOAD, TRAP}) 430 @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"}, 431 counts = {ALLOC, "<= 2"}, // 1 MyValue1 and 1 MyValue2 allocation (if not the default 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 default value) 451 failOn = {LOAD, TRAP}) 452 @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"}, 453 counts = {ALLOC, "<= 2"}, // 1 MyValue1 and 1 MyValue2 allocation (if not the default 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 default value) 478 failOn = {LOAD}) 479 @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"}, 480 counts = {ALLOC, "<= 2"}, // 1 MyValue1 and 1 MyValue2 allocation (if not the default value) 481 failOn = LOAD) 482 public long test20(boolean deopt, Method m) { 483 MyValue1 v = MyValue1.createWithFieldsInline(rI, rL); 484 MyValue2[] va = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 3); 485 if (deopt) { 486 // uncommon trap 487 TestFramework.deoptimize(m); 488 } 489 490 return v.hashInterpreted() + va[0].hashInterpreted() + 491 va[1].hashInterpreted() + va[2].hashInterpreted(); 492 } 493 494 @Run(test = "test20") 495 public void test20_verifier(RunInfo info) { 496 MyValue2[] va = (MyValue2[])ValueClass.newNullRestrictedArray(MyValue2.class, 42); 497 long result = test20(!info.isWarmUp(), info.getTest()); 498 Asserts.assertEQ(result, hash() + va[0].hash() + va[1].hash() + va[2].hash()); 499 } 500 501 // Value class fields in regular object 502 @NullRestricted 503 MyValue1 val1; 504 @NullRestricted 505 MyValue2 val2; 506 @NullRestricted 507 final MyValue1 val3 = MyValue1.createWithFieldsInline(rI, rL); 508 @NullRestricted 509 static MyValue1 val4; 510 @NullRestricted 511 static final MyValue1 val5 = MyValue1.createWithFieldsInline(rI, rL); 512 513 // Test value class fields in objects 514 @Test 515 // TODO 8332886 Re-enable this 516 // @IR(counts = {ALLOC, "= 2"}, 517 // 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 @NullRestricted 619 public MyValue1 v; 620 } 621 622 // Test allocation elimination of unused object with initialized value class field 623 @Test 624 @IR(failOn = {ALLOC, LOAD, STORE, LOOP}) 625 public void test27(boolean deopt, Method m) { 626 TestClass27 unused = new TestClass27(); 627 MyValue1 v = MyValue1.createWithFieldsInline(rI, rL); 628 unused.v = v; 629 if (deopt) { 630 // uncommon trap 631 TestFramework.deoptimize(m); 632 } 633 } 634 635 @Run(test = "test27") 636 public void test27_verifier(RunInfo info) { 637 test27(!info.isWarmUp(), info.getTest()); 638 } 639 640 @NullRestricted 641 static MyValue3 staticVal3; 642 @NullRestricted 643 static MyValue3 staticVal3_copy; 644 645 // Check elimination of redundant value class allocations 646 @Test 647 // TODO 8332886 Remove the AlwaysIncrementalInline=false condition 648 @IR(applyIf = {"AlwaysIncrementalInline", "false"}, 649 counts = {ALLOC, "= 1"}) 650 public MyValue3 test28(MyValue3[] va) { 651 // Create value object and force allocation 652 MyValue3 vt = MyValue3.create(); 653 va[0] = vt; 654 staticVal3 = vt; 655 vt.verify(staticVal3); 656 657 // Value object is now allocated, make a copy and force allocation. 658 // Because copy is equal to vt, C2 should remove this redundant allocation. 659 MyValue3 copy = MyValue3.setC(vt, vt.c); 660 va[0] = copy; 661 staticVal3_copy = copy; 662 copy.verify(staticVal3_copy); 663 return copy; 664 } 665 666 @Run(test = "test28") 667 public void test28_verifier() { 668 MyValue3[] va = (MyValue3[])ValueClass.newNullRestrictedArray(MyValue3.class, 1); 669 MyValue3 vt = test28(va); 670 staticVal3.verify(vt); 671 staticVal3.verify(va[0]); 672 staticVal3_copy.verify(vt); 673 staticVal3_copy.verify(va[0]); 674 } 675 676 // Verify that only dominating allocations are re-used 677 @Test 678 public MyValue3 test29(boolean warmup) { 679 MyValue3 vt = MyValue3.create(); 680 if (warmup) { 681 staticVal3 = vt; // Force allocation 682 } 683 // Force allocation to verify that above 684 // non-dominating allocation is not re-used 685 MyValue3 copy = MyValue3.setC(vt, vt.c); 686 staticVal3_copy = copy; 687 copy.verify(vt); 688 return copy; 689 } 690 691 @Run(test = "test29") 692 public void test29_verifier(RunInfo info) { 693 MyValue3 vt = test29(info.isWarmUp()); 694 if (info.isWarmUp()) { 695 staticVal3.verify(vt); 696 } 697 } 698 699 // Verify that C2 recognizes value class loads and re-uses the oop to avoid allocations 700 @Test 701 @IR(applyIf = {"FlatArrayElementMaxSize", "= -1"}, 702 failOn = {ALLOC, ALLOCA, STORE}) 703 public MyValue3 test30() { 704 // C2 can re-use the oop of staticVal3 because staticVal3 is equal to copy 705 MyValue3[] va = (MyValue3[])ValueClass.newNullRestrictedArray(MyValue3.class, 1); 706 MyValue3 copy = MyValue3.copy(staticVal3); 707 va[0] = copy; 708 copy.verify(va[0]); 709 staticVal3 = copy; 710 copy.verify(staticVal3); 711 return copy; 712 } 713 714 @Run(test = "test30") 715 public void test30_verifier() { 716 staticVal3 = MyValue3.create(); 717 MyValue3 vt = test30(); 718 staticVal3.verify(vt); 719 } 720 721 // Verify that C2 recognizes value class loads and re-uses the oop to avoid allocations 722 @Test 723 @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"}, 724 failOn = {ALLOC, ALLOCA, STORE}) 725 public MyValue3 test31() { 726 // C2 can re-use the oop returned by createDontInline() 727 // because the corresponding value object is equal to 'copy'. 728 MyValue3[] va = (MyValue3[])ValueClass.newNullRestrictedArray(MyValue3.class, 1); 729 MyValue3 copy = MyValue3.copy(MyValue3.createDontInline()); 730 va[0] = copy; 731 copy.verify(va[0]); 732 staticVal3 = copy; 733 copy.verify(staticVal3); 734 return copy; 735 } 736 737 @Run(test = "test31") 738 public void test31_verifier() { 739 MyValue3 vt = test31(); 740 staticVal3.verify(vt); 741 } 742 743 // Verify that C2 recognizes value class loads and re-uses the oop to avoid allocations 744 @Test 745 @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"}, 746 failOn = {ALLOC, ALLOCA, STORE}) 747 public MyValue3 test32(MyValue3 vt) { 748 // C2 can re-use the oop of vt because vt is equal to 'copy'. 749 MyValue3[] va = (MyValue3[])ValueClass.newNullRestrictedArray(MyValue3.class, 1); 750 MyValue3 copy = MyValue3.copy(vt); 751 va[0] = copy; 752 copy.verify(vt); 753 staticVal3 = copy; 754 copy.verify(staticVal3); 755 return copy; 756 } 757 758 @Run(test = "test32") 759 public void test32_verifier() { 760 MyValue3 vt = MyValue3.create(); 761 MyValue3 result = test32(vt); 762 staticVal3.verify(vt); 763 result.verify(vt); 764 } 765 766 // Test correct identification of value object copies 767 @Test 768 public MyValue3 test33() { 769 MyValue3[] va = (MyValue3[])ValueClass.newNullRestrictedArray(MyValue3.class, 1); 770 MyValue3 vt = MyValue3.copy(staticVal3); 771 vt = MyValue3.setI(vt, vt.c); 772 // vt is not equal to staticVal3, so C2 should not re-use the oop 773 va[0] = vt; 774 Asserts.assertEQ(va[0].i, (int)vt.c); 775 staticVal3 = vt; 776 vt.verify(staticVal3); 777 return vt; 778 } 779 780 @Run(test = "test33") 781 public void test33_verifier() { 782 staticVal3 = MyValue3.create(); 783 MyValue3 vt = test33(); 784 Asserts.assertEQ(staticVal3.i, (int)staticVal3.c); 785 Asserts.assertEQ(vt.i, (int)staticVal3.c); 786 } 787 788 static final MyValue3[] test34Array = (MyValue3[])ValueClass.newNullRestrictedArray(MyValue3.class, 2); 789 790 // Verify that the default value class is never allocated. 791 // C2 code should load and use the default oop from the java mirror. 792 @Test 793 @IR(applyIf = {"FlatArrayElementMaxSize", "= -1"}, 794 failOn = {ALLOC, ALLOCA, LOAD, STORE, LOOP, TRAP}) 795 public MyValue3 test34() { 796 // Explicitly create default value 797 MyValue3 vt = MyValue3.createDefault(); 798 test34Array[0] = vt; 799 staticVal3 = vt; 800 vt.verify(vt); 801 802 // Load default value from uninitialized value class array 803 MyValue3[] dva = (MyValue3[])ValueClass.newNullRestrictedArray(MyValue3.class, 1); 804 staticVal3_copy = dva[0]; 805 test34Array[1] = dva[0]; 806 dva[0].verify(dva[0]); 807 return vt; 808 } 809 810 @Run(test = "test34") 811 public void test34_verifier() { 812 MyValue3 vt = MyValue3.createDefault(); 813 test34Array[0] = MyValue3.create(); 814 test34Array[1] = MyValue3.create(); 815 MyValue3 res = test34(); 816 res.verify(vt); 817 staticVal3.verify(vt); 818 staticVal3_copy.verify(vt); 819 test34Array[0].verify(vt); 820 test34Array[1].verify(vt); 821 } 822 823 static final MyValue3[] test35Array = (MyValue3[])ValueClass.newNullRestrictedArray(MyValue3.class, 1); 824 825 // Same as above but manually initialize value class fields to default. 826 @Test 827 @IR(applyIf = {"FlatArrayElementMaxSize", "= -1"}, 828 failOn = {ALLOC, ALLOCA, LOAD, STORE, LOOP, TRAP}) 829 public MyValue3 test35(MyValue3 vt) { 830 vt = MyValue3.setC(vt, (char)0); 831 vt = MyValue3.setBB(vt, (byte)0); 832 vt = MyValue3.setS(vt, (short)0); 833 vt = MyValue3.setI(vt, 0); 834 vt = MyValue3.setL(vt, 0); 835 vt = MyValue3.setO(vt, null); 836 vt = MyValue3.setF1(vt, 0); 837 vt = MyValue3.setF2(vt, 0); 838 vt = MyValue3.setF3(vt, 0); 839 vt = MyValue3.setF4(vt, 0); 840 vt = MyValue3.setF5(vt, 0); 841 vt = MyValue3.setF6(vt, 0); 842 vt = MyValue3.setV1(vt, MyValue3Inline.createDefault()); 843 test35Array[0] = vt; 844 staticVal3 = vt; 845 vt.verify(vt); 846 return vt; 847 } 848 849 @Run(test = "test35") 850 public void test35_verifier() { 851 MyValue3 vt = MyValue3.createDefault(); 852 test35Array[0] = MyValue3.create(); 853 MyValue3 res = test35(test35Array[0]); 854 res.verify(vt); 855 staticVal3.verify(vt); 856 test35Array[0].verify(vt); 857 } 858 859 // Merge value objects created from two branches 860 861 private Object test36_helper(Object v) { 862 return v; 863 } 864 865 @Test 866 @IR(failOn = {ALLOC, STORE, TRAP}) 867 public long test36(boolean b) { 868 Object o; 869 if (b) { 870 o = test36_helper(MyValue1.createWithFieldsInline(rI, rL)); 871 } else { 872 o = test36_helper(MyValue1.createWithFieldsDontInline(rI + 1, rL + 1)); 873 } 874 MyValue1 v = (MyValue1)o; 875 return v.hash(); 876 } 877 878 @Run(test = "test36") 879 public void test36_verifier() { 880 Asserts.assertEQ(test36(true), hash()); 881 Asserts.assertEQ(test36(false), hash(rI + 1, rL + 1)); 882 } 883 884 // Test correct loading of flattened fields 885 @ImplicitlyConstructible 886 @LooselyConsistentValue 887 value class Test37Value2 { 888 int x = 0; 889 int y = 0; 890 } 891 892 @ImplicitlyConstructible 893 @LooselyConsistentValue 894 value class Test37Value1 { 895 double d = 0; 896 float f = 0; 897 @NullRestricted 898 Test37Value2 v = new Test37Value2(); 899 } 900 901 @Test 902 public Test37Value1 test37(Test37Value1 vt) { 903 return vt; 904 } 905 906 @Run(test = "test37") 907 public void test37_verifier() { 908 Test37Value1 vt = new Test37Value1(); 909 Asserts.assertEQ(test37(vt), vt); 910 } 911 912 // Test elimination of value class allocations without a unique CheckCastPP 913 @ImplicitlyConstructible 914 @LooselyConsistentValue 915 value class Test38Value { 916 public int i; 917 public Test38Value(int i) { this.i = i; } 918 } 919 920 @NullRestricted 921 static Test38Value test38Field; 922 923 @Test 924 public void test38() { 925 for (int i = 3; i < 100; ++i) { 926 int j = 1; 927 while (++j < 11) { 928 try { 929 test38Field = new Test38Value(i); 930 } catch (ArithmeticException ae) { } 931 } 932 } 933 } 934 935 @Run(test = "test38") 936 public void test38_verifier() { 937 test38Field = new Test38Value(0); 938 test38(); 939 Asserts.assertEQ(test38Field, new Test38Value(99)); 940 } 941 942 // Tests split if with value class Phi users 943 @ImplicitlyConstructible 944 @LooselyConsistentValue 945 static value class Test39Value { 946 public int iFld1; 947 public int iFld2; 948 949 public Test39Value(int i1, int i2) { iFld1 = i1; iFld2 = i2; } 950 } 951 952 static int test39A1[][] = new int[400][400]; 953 static double test39A2[] = new double[400]; 954 @NullRestricted 955 static Test39Value test39Val = new Test39Value(0, 0); 956 957 @DontInline 958 public int[] getArray() { 959 return new int[400]; 960 } 961 962 @Test 963 public int test39() { 964 int result = 0; 965 for (int i = 0; i < 100; ++i) { 966 switch ((i >>> 1) % 3) { 967 case 0: 968 test39A1[i][i] = i; 969 break; 970 case 1: 971 for (int j = 0; j < 100; ++j) { 972 test39A1[i] = getArray(); 973 test39Val = new Test39Value(j, test39Val.iFld2); 974 } 975 break; 976 case 2: 977 for (float f = 142; f > i; f--) { 978 test39A2[i + 1] += 3; 979 } 980 result += test39Val.iFld1; 981 break; 982 } 983 double d1 = 1; 984 while (++d1 < 142) { 985 test39A1[(i >>> 1) % 400][i + 1] = result; 986 test39Val = new Test39Value(i, test39Val.iFld2); 987 } 988 } 989 return result; 990 } 991 992 @Run(test = "test39") 993 @Warmup(10) 994 public void test39_verifier() { 995 int result = test39(); 996 Asserts.assertEQ(result, 1552); 997 } 998 999 // Test scalar replacement of value class array containing value class with oop fields 1000 @Test 1001 public long test40(boolean b) { 1002 MyValue1[] va = {MyValue1.createWithFieldsInline(rI, rL)}; 1003 long result = 0; 1004 for (int i = 0; i < 1000; ++i) { 1005 if (!b) { 1006 result = va[0].hash(); 1007 } 1008 } 1009 return result; 1010 } 1011 1012 @Run(test = "test40") 1013 public void test40_verifier(RunInfo info) { 1014 long result = test40(info.isWarmUp()); 1015 Asserts.assertEQ(result, info.isWarmUp() ? 0 : hash()); 1016 } 1017 1018 static value class MyValue41 { 1019 int x; 1020 1021 public MyValue41(int x) { 1022 this.x = x; 1023 } 1024 1025 static MyValue41 make() { 1026 return new MyValue41(0); 1027 } 1028 } 1029 1030 static MyValue41 field41; 1031 1032 // Test detection of value object copies and removal of the MemBarRelease following the value buffer initialization 1033 @Test 1034 @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"}, 1035 failOn = {ALLOC, ALLOCA, STORE}) 1036 public void test41(MyValue41 val) { 1037 field41 = new MyValue41(val.x); 1038 } 1039 1040 @Run(test = "test41") 1041 public void test41_verifier() { 1042 MyValue41 val = new MyValue41(rI); 1043 test41(val); 1044 Asserts.assertEQ(field41, val); 1045 } 1046 1047 @DontInline 1048 public void test42_helper(MyValue41 val) { 1049 Asserts.assertEQ(val, new MyValue41(rI)); 1050 } 1051 1052 // Same as test41 but with call argument requiring buffering 1053 @Test 1054 @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"}, 1055 failOn = {ALLOC, ALLOCA, STORE}) 1056 public void test42(MyValue41 val) { 1057 test42_helper(new MyValue41(val.x)); 1058 } 1059 1060 @Run(test = "test42") 1061 public void test42_verifier() { 1062 MyValue41 val = new MyValue41(rI); 1063 test42(val); 1064 } 1065 }