1 /* 2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 package runtime.valhalla.valuetypes; 25 26 import java.lang.invoke.*; 27 import java.lang.ref.*; 28 import java.util.concurrent.*; 29 import jdk.experimental.value.*; 30 31 import static jdk.test.lib.Asserts.*; 32 import jdk.test.lib.Utils; 33 import sun.hotspot.WhiteBox; 34 35 /** 36 * @test ValueOops 37 * @summary Test embedding oops into Value types 38 * @library /test/lib 39 * @compile PersonVcc.java 40 * @compile -XDenableValueTypes Person.java 41 * @compile -XDenableValueTypes ValueOops.java 42 * @run main ClassFileInstaller sun.hotspot.WhiteBox 43 * @run driver ClassFileInstaller sun.hotspot.WhiteBox 44 * sun.hotspot.WhiteBox$WhiteBoxPermission 45 * @run main/othervm -Xint -noverify -XX:+UseSerialGC -Xmx128m -XX:+EnableMVT -XX:+EnableValhalla 46 * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 47 * runtime.valhalla.valuetypes.ValueOops 48 * @run main/othervm -Xint -noverify -XX:+UseG1GC -Xmx128m -XX:+EnableMVT -XX:+EnableValhalla 49 * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 50 * runtime.valhalla.valuetypes.ValueOops 51 * @run main/othervm -Xint -noverify -XX:+UseParallelGC -Xmx128m -XX:+EnableMVT -XX:+EnableValhalla 52 * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 53 * runtime.valhalla.valuetypes.ValueOops 54 * @run main/othervm -Xint -noverify -XX:+UseConcMarkSweepGC -Xmx128m -XX:+EnableMVT -XX:+EnableValhalla 55 * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 56 * runtime.valhalla.valuetypes.ValueOops 57 * @run main/othervm -Xcomp -noverify -XX:+UseSerialGC -Xmx128m -XX:+EnableMVT -XX:+EnableValhalla 58 * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 59 * runtime.valhalla.valuetypes.ValueOops 60 * @run main/othervm -Xcomp -noverify -XX:+UseG1GC -Xmx128m -XX:+EnableMVT -XX:+EnableValhalla 61 * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 62 * runtime.valhalla.valuetypes.ValueOops 63 * @run main/othervm -Xcomp -noverify -XX:+UseParallelGC -Xmx128m -XX:+EnableMVT -XX:+EnableValhalla 64 * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 65 * runtime.valhalla.valuetypes.ValueOops 66 * @run main/othervm -Xcomp -noverify -XX:+UseConcMarkSweepGC -Xmx128m -XX:+EnableMVT -XX:+EnableValhalla 67 * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 68 * runtime.valhalla.valuetypes.ValueOops 69 */ 70 public class ValueOops { 71 72 // Note: -noverify can not be eliminated. Possible issue with ValueType::mhName(), 73 // does not translate '.' to '/' for JVM. 74 // java.lang.ClassFormatError: Illegal method name "runtime.valhalla.valuetypes.PersonVcc_default" 75 // 76 // Extra debug: -XX:+VerifyOops -XX:+VerifyStack -XX:+VerifyLastFrame -XX:+VerifyBeforeGC -XX:+VerifyAfterGC -XX:+VerifyDuringGC -XX:VerifySubSet=threads,heap 77 // Even more debugging: -XX:+TraceNewOopMapGeneration -Xlog:gc*=info 78 79 static final int NOF_PEOPLE = 1000; // Exercise arrays of this size 80 81 static int MIN_ACTIVE_GC_COUNT = 10; // Run active workload for this number of GC passes 82 83 static int MED_ACTIVE_GC_COUNT = 4; // Medium life span in terms of GC passes 84 85 static final String TEST_STRING1 = "Test String 1"; 86 static final String TEST_STRING2 = "Test String 2"; 87 88 static boolean USE_COMPILER = WhiteBox.getWhiteBox().getBooleanVMFlag("UseCompiler"); 89 90 public static void main(String[] args) { 91 if (args.length > 0) { 92 MIN_ACTIVE_GC_COUNT = Integer.parseInt(args[0]); 93 } 94 testClassLoad(); 95 testBytecodes(); 96 testVvt(); 97 testMvt(); 98 99 if (!USE_COMPILER) { 100 testOopMaps(); 101 } 102 103 // Check we survive GC... 104 testOverGc(); // Exercise root scan / oopMap 105 testActiveGc(); // Brute force 106 } 107 108 /** 109 * Test ClassFileParser can load values with reference fields 110 */ 111 public static void testClassLoad() { 112 // VVT 113 String s = Person.class.toString(); 114 new Bar(); 115 new BarWithValue(); 116 s = BarValue.class.toString(); 117 s = ObjectWithObjectValue.class.toString(); 118 s = ObjectWithObjectValues.class.toString(); 119 120 // MVT 121 Class<?> vccClass = PersonVcc.class; 122 ValueType<?> vt = ValueType.forClass(vccClass); 123 Class<?> boxClass = vt.boxClass(); 124 Class<?> dvtClass = vt.valueClass(); 125 Class<?> arrayClass = vt.arrayValueClass(); 126 s = dvtClass.toString(); 127 } 128 129 /** 130 * Test value type opcodes are okay with reference fields in values 131 * I.e. ValueKlass::value_store() 132 */ 133 public static void testBytecodes() { 134 try { 135 // Craft Value Type class how you will, using MVT class for simplicity just here 136 ValueType<?> vt = ValueType.forClass(PersonVcc.class); 137 Class<?> vtClass = vt.valueClass(); 138 Class<?> arrayClass = vt.arrayValueClass(); 139 Class<?> boxClass = vt.boxClass(); 140 MethodHandles.Lookup lookup = MethodHandles.lookup(); 141 142 // Exercise with null refs... 143 144 // anewarray 145 Object array = MethodHandleBuilder.loadCode( 146 lookup, 147 "anewarrayPerson", 148 MethodType.methodType(Object.class, Integer.TYPE), 149 CODE->{ 150 CODE 151 .iload(0) 152 .anewarray(vtClass) 153 .astore(0) 154 .aload(0) 155 .areturn(); 156 }).invoke(NOF_PEOPLE); 157 158 // vaload 159 // vstore 160 // vload 161 // vastore 162 // vbox 163 int arraySlot = 0; 164 int indexSlot = 1; 165 int valueSlot = 2; 166 Object obj = MethodHandleBuilder.loadCode( 167 lookup, 168 "valoadBoxPerson", 169 MethodType.methodType(Object.class, arrayClass, Integer.TYPE), 170 CODE->{ 171 CODE 172 .aload(arraySlot) 173 .iload(indexSlot) 174 .vaload() 175 .vstore(valueSlot) 176 .aload(arraySlot) 177 .iload(indexSlot) 178 .vload(valueSlot) 179 .vastore() 180 .vload(valueSlot) 181 .vbox(boxClass) 182 .areturn(); 183 }).invoke(array, 7); 184 validateDefaultPersonVcc(obj); 185 186 // vreturn 187 MethodHandle loadValueFromArray = MethodHandleBuilder.loadCode( 188 lookup, 189 "valoadVreturnPerson", 190 MethodType.methodType(vtClass, arrayClass, Integer.TYPE), 191 CODE->{ 192 CODE 193 .aload(arraySlot) 194 .iload(indexSlot) 195 .vaload() 196 .vreturn(); 197 }); 198 MethodHandle box = MethodHandleBuilder.loadCode( 199 lookup, 200 "boxPersonVcc", 201 MethodType.methodType(boxClass, vtClass), 202 CODE->{ 203 CODE 204 .vload(0) 205 .vbox(boxClass) 206 .areturn(); 207 }); 208 MethodHandle loadValueFromArrayBoxed = 209 MethodHandles.filterReturnValue(loadValueFromArray, box); 210 obj = loadValueFromArrayBoxed.invoke(array, 0); 211 validateDefaultPersonVcc(obj); 212 213 // vunbox 214 MethodHandle unbox = MethodHandleBuilder.loadCode( 215 lookup, 216 "unboxPersonVcc", 217 MethodType.methodType(vtClass, boxClass), 218 CODE->{ 219 CODE 220 .aload(0) 221 .vunbox(vtClass) 222 .vreturn(); 223 }); 224 MethodHandle unboxBox = MethodHandles.filterReturnValue(unbox, box); 225 obj = unboxBox.invoke(createDefaultPersonVcc()); 226 validateDefaultPersonVcc(obj); 227 228 /* 229 vgetfield 230 qputfield 231 qgetfield 232 233 going to need VVT for VT fields and vgetfield 234 check test coverage in testVvt() 235 */ 236 237 // Exercise with live refs... 238 Thread.holdsLock("Debug here"); 239 // vunbox 240 // vastore 241 // vaload 242 // vstore 243 // vload 244 // vbox 245 int index = 3; 246 obj = MethodHandleBuilder.loadCode( 247 lookup, 248 "unboxStoreLoadPersonVcc", 249 MethodType.methodType(boxClass, arrayClass, Integer.TYPE, boxClass), 250 CODE->{ 251 CODE 252 .aload(arraySlot) 253 .iload(indexSlot) 254 .aload(valueSlot) 255 .vunbox(vtClass) 256 .vastore() 257 .aload(arraySlot) 258 .iload(indexSlot) 259 .vaload() 260 .vstore(valueSlot) 261 .vload(valueSlot) 262 .vbox(boxClass) 263 .areturn(); 264 }).invoke(array, index, createIndexedPersonVcc(index)); 265 validateIndexedPersonVcc(obj, index); 266 267 // Check the neighbours 268 validateDefaultPersonVcc(loadValueFromArrayBoxed.invoke(array, index - 1)); 269 validateDefaultPersonVcc(loadValueFromArrayBoxed.invoke(array, index + 1)); 270 271 // vreturn 272 validateIndexedPersonVcc(unboxBox.invoke((PersonVcc)obj), index); 273 } 274 catch (Throwable t) { fail("testBytecodes", t); } 275 } 276 277 static class Couple { 278 public Person onePerson; 279 public Person otherPerson; 280 } 281 282 static final __ByValue class Composition { 283 public final Person onePerson; 284 public final Person otherPerson; 285 286 private Composition() { 287 this.onePerson = Person.create(0, null, null); 288 this.otherPerson = Person.create(0, null, null); 289 } 290 291 __ValueFactory public static Composition create(Person onePerson, Person otherPerson) { 292 Composition comp = __MakeDefault Composition(); 293 comp.onePerson = onePerson; 294 comp.otherPerson = otherPerson; 295 return comp; 296 } 297 } 298 299 /** 300 * Check value type operations with "Valhalla Value Types" (VVT) 301 */ 302 public static void testVvt() { 303 // Exercise creation, vgetfield, vreturn with null refs 304 validateDefaultPerson(createDefaultPerson()); 305 306 // anewarray, vaload, vastore 307 int index = 7; 308 Person[] array = new Person[NOF_PEOPLE]; 309 validateDefaultPerson(array[index]); 310 311 // Now with refs... 312 validateIndexedPerson(createIndexedPerson(index), index); 313 array[index] = createIndexedPerson(index); 314 validateIndexedPerson(array[index], index); 315 316 // Check the neighbours 317 validateDefaultPerson(array[index - 1]); 318 validateDefaultPerson(array[index + 1]); 319 320 // getfield/putfield 321 Couple couple = new Couple(); 322 validateDefaultPerson(couple.onePerson); 323 validateDefaultPerson(couple.otherPerson); 324 325 couple.onePerson = createIndexedPerson(index); 326 validateIndexedPerson(couple.onePerson, index); 327 328 Composition composition = Composition.create(couple.onePerson, couple.onePerson); 329 validateIndexedPerson(composition.onePerson, index); 330 validateIndexedPerson(composition.otherPerson, index); 331 } 332 333 /** 334 * Check value type operations with "Minimal Value Types" (MVT) 335 */ 336 public static void testMvt() { 337 try { 338 // MVT... 339 ValueType<?> vt = ValueType.forClass(PersonVcc.class); 340 Class<?> vtClass = vt.valueClass(); 341 Class<?> arrayClass = vt.arrayValueClass(); 342 343 Object obj = vt.defaultValueConstant().invoke(); 344 validateDefaultPersonVcc(obj); 345 346 obj = MethodHandles.filterReturnValue(vt.unbox(), vt.box()) 347 .invoke(createDefaultPersonVcc()); 348 validateDefaultPersonVcc(obj); 349 350 int index = 11; 351 obj = MethodHandles.filterReturnValue(vt.unbox(), vt.box()) 352 .invoke(createIndexedPersonVcc(index)); 353 validateIndexedPersonVcc(obj, index); 354 355 testMvtArray("testMvt.array.1", 1); 356 } 357 catch (Throwable t) { 358 fail("testMvtfailed", t); 359 } 360 } 361 362 /** 363 * MVT array operations... 364 */ 365 public static void testMvtPeopleArray() { 366 testMvtArray("testMvtPeopleArray", NOF_PEOPLE); 367 } 368 369 public static void testMvtArray(String testName, int arrayLength) { 370 try { 371 Class<?> vcc = PersonVcc.class; 372 ValueType<?> vt = ValueType.forClass(vcc); 373 Class<?> dvtClass = vt.valueClass(); 374 Class<?> arrayClass = vt.arrayValueClass(); 375 376 MethodHandle arrayElemGet = MethodHandles.arrayElementGetter(arrayClass); 377 MethodHandle arrayElemSet = MethodHandles.arrayElementSetter(arrayClass); 378 379 MethodHandles.Lookup lookup = MethodHandles.lookup(); 380 MethodHandle getId = lookup.findGetter(dvtClass, "id", Integer.TYPE); 381 MethodHandle getFirstName = lookup.findGetter(dvtClass, "firstName", String.class); 382 MethodHandle getLastName = lookup.findGetter(dvtClass, "lastName", String.class); 383 384 MethodHandle getIdFromArray = MethodHandles.filterReturnValue(arrayElemGet, getId); 385 MethodHandle getFnFromArray = MethodHandles.filterReturnValue(arrayElemGet, getFirstName); 386 MethodHandle getLnFromArray = MethodHandles.filterReturnValue(arrayElemGet, getLastName); 387 388 Object people = vt.newArray().invoke(arrayLength); 389 for (int i = 0; i < arrayLength; i++) { 390 arrayElemSet.invoke(people, i, createIndexedPersonVcc(i)); 391 } 392 393 for (int i = 0; i < arrayLength; i++) { 394 validateIndexedPersonVcc(arrayElemGet.invoke(people, i), i); 395 396 int id = (int) getIdFromArray.invoke(people, i); 397 assertTrue(id == i, "Invalid field: Id"); 398 String fn = (String) getFnFromArray.invoke(people, i); 399 assertTrue(fn.equals(firstName(i)), "Invalid field: firstName"); 400 String ln = (String) getLnFromArray.invoke(people, i); 401 assertTrue(ln.equals(lastName(i)), "Invalid field: lastName"); 402 } 403 } 404 catch (Throwable t) { 405 fail(testName + " failed", t); 406 } 407 } 408 409 /** 410 * Check oop map generation for klass layout and frame... 411 */ 412 public static void testOopMaps() { 413 Object[] objects = WhiteBox.getWhiteBox().getObjectsViaKlassOopMaps(new Couple()); 414 assertTrue(objects.length == 4, "Expected 4 oops"); 415 for (int i = 0; i < objects.length; i++) { 416 assertTrue(objects[i] == null, "not-null"); 417 } 418 419 String fn1 = "Sam"; 420 String ln1 = "Smith"; 421 String fn2 = "Jane"; 422 String ln2 = "Jones"; 423 Couple couple = new Couple(); 424 couple.onePerson = Person.create(0, fn1, ln1); 425 couple.otherPerson = Person.create(1, fn2, ln2); 426 objects = WhiteBox.getWhiteBox().getObjectsViaKlassOopMaps(couple); 427 assertTrue(objects.length == 4, "Expected 4 oops"); 428 assertTrue(objects[0] == fn1, "Bad oop fn1"); 429 assertTrue(objects[1] == ln1, "Bad oop ln1"); 430 assertTrue(objects[2] == fn2, "Bad oop fn2"); 431 assertTrue(objects[3] == ln2, "Bad oop ln2"); 432 433 objects = WhiteBox.getWhiteBox().getObjectsViaOopIterator(couple); 434 assertTrue(objects.length == 4, "Expected 4 oops"); 435 assertTrue(objects[0] == fn1, "Bad oop fn1"); 436 assertTrue(objects[1] == ln1, "Bad oop ln1"); 437 assertTrue(objects[2] == fn2, "Bad oop fn2"); 438 assertTrue(objects[3] == ln2, "Bad oop ln2"); 439 440 // Array.. 441 objects = WhiteBox.getWhiteBox().getObjectsViaOopIterator(createPeople()); 442 assertTrue(objects.length == NOF_PEOPLE * 2, "Unexpected length: " + objects.length); 443 int o = 0; 444 for (int i = 0; i < NOF_PEOPLE; i++) { 445 assertTrue(objects[o++].equals(firstName(i)), "Bad firstName"); 446 assertTrue(objects[o++].equals(lastName(i)), "Bad lastName"); 447 } 448 449 // Sanity check, FixMe need more test cases 450 objects = testFrameOops(couple); 451 assertTrue(objects.length == 5, "Number of frame oops incorrect = " + objects.length); 452 assertTrue(objects[0] == couple, "Bad oop 0"); 453 assertTrue(objects[1] == fn1, "Bad oop 1"); 454 assertTrue(objects[2] == ln1, "Bad oop 2"); 455 assertTrue(objects[3] == TEST_STRING1, "Bad oop 3"); 456 assertTrue(objects[4] == TEST_STRING2, "Bad oop 4"); 457 458 testFrameOopsVBytecodes(); 459 } 460 461 static final String GET_OOP_MAP_NAME = "getOopMap"; 462 static final String GET_OOP_MAP_DESC = "()[Ljava/lang/Object;"; 463 464 static Object[] getOopMap() { 465 Object[] oopMap = WhiteBox.getWhiteBox().getObjectsViaFrameOopIterator(2); 466 /* Remove this frame (class mirror for this method), and above class mirror */ 467 Object[] trimmedOopMap = new Object[oopMap.length - 2]; 468 System.arraycopy(oopMap, 2, trimmedOopMap, 0, trimmedOopMap.length); 469 return trimmedOopMap; 470 } 471 472 // Expecting Couple couple, Person couple.onePerson, and Person (created here) 473 public static Object[] testFrameOops(Couple couple) { 474 int someId = 89898; 475 Person person = couple.onePerson; 476 assertTrue(person.getId() == 0, "Bad Person"); 477 Person anotherPerson = Person.create(someId, TEST_STRING1, TEST_STRING2); 478 assertTrue(anotherPerson.getId() == someId, "Bad Person"); 479 return getOopMap(); 480 } 481 482 // Debug... 483 static void dumpOopMap(Object[] oopMap) { 484 System.out.println("Oop Map len: " + oopMap.length); 485 for (int i = 0; i < oopMap.length; i++) { 486 System.out.println("[" + i + "] = " + oopMap[i]); 487 } 488 } 489 490 /** 491 * Just some check sanity checks with vdefault, vwithfield, vstore and vload 492 * 493 * Changes to javac slot usage may well break this test 494 */ 495 public static void testFrameOopsVBytecodes() { 496 int nofOopMaps = 4; 497 Object[][] oopMaps = new Object[nofOopMaps][]; 498 String[] inputArgs = new String[] { "aName", "aDescription", "someNotes" }; 499 500 FooValue.testFrameOopsDefault(oopMaps); 501 502 dumpOopMap(oopMaps[0]); 503 dumpOopMap(oopMaps[1]); 504 dumpOopMap(oopMaps[2]); 505 dumpOopMap(oopMaps[3]); 506 507 // Test-D0 Slots=R Stack=Q(RRR)RV 508 assertTrue(oopMaps[0].length == 5 && 509 oopMaps[0][1] == null && 510 oopMaps[0][2] == null && 511 oopMaps[0][3] == null, "Test-D0 incorrect"); 512 513 // Test-D1 Slots=R Stack=RV 514 assertTrue(oopMaps[1].length == 2, "Test-D1 incorrect"); 515 516 // Test-D2 Slots=RQ(RRR) Stack=RV 517 assertTrue(oopMaps[2].length == 5 && 518 oopMaps[2][1] == null && 519 oopMaps[2][2] == null && 520 oopMaps[2][3] == null, "Test-D2 incorrect"); 521 522 // Test-D3 Slots=R Stack=Q(RRR)RV 523 assertTrue(oopMaps[3].length == 6 && 524 oopMaps[3][1] == null && 525 oopMaps[3][2] == null && 526 oopMaps[3][3] == null && 527 oopMaps[3][4] == null, "Test-D3 incorrect"); 528 529 // With ref fields... 530 String name = "TestName"; 531 String desc = "TestDesc"; 532 String note = "TestNotes"; 533 FooValue.testFrameOopsRefs(name, desc, note, oopMaps); 534 dumpOopMap(oopMaps[0]); 535 536 // Test-R0 Slots=RR Stack=Q(RRR)RV 537 assertTrue(oopMaps[0].length == 6 && 538 oopMaps[0][2] == name && 539 oopMaps[0][3] == desc && 540 oopMaps[0][4] == note, "Test-R0 incorrect"); 541 542 /** 543 * TODO: vwithfield from method handle cooked from anonymous class within the value class 544 * even with "MethodHandles.privateLookupIn()" will fail final putfield rules 545 */ 546 } 547 548 /** 549 * Check forcing GC for combination of VT on stack/LVT etc works 550 */ 551 public static void testOverGc() { 552 try { 553 Class<?> vccClass = PersonVcc.class; 554 ValueType<?> vt = ValueType.forClass(vccClass); 555 Class<?> vtClass = vt.valueClass(); 556 Class<?> arrayClass = vt.arrayValueClass(); 557 558 MethodHandles.Lookup lookup = MethodHandles.lookup(); 559 doGc(); 560 561 // VT on stack and lvt, null refs, see if GC flies 562 MethodHandle moveValueThroughStackAndLvt = MethodHandleBuilder.loadCode( 563 lookup, 564 "gcOverPerson", 565 MethodType.methodType(vccClass, vccClass), 566 CODE->{ 567 CODE 568 .aload(0) 569 .vunbox(vtClass) 570 .invokestatic(ValueOops.class, "doGc", "()V", false) // Stack 571 .vstore(0) 572 .invokestatic(ValueOops.class, "doGc", "()V", false) // LVT 573 .vload(0) 574 .iconst_1() // push a litte further down 575 .invokestatic(ValueOops.class, "doGc", "()V", false) // Stack,LVT 576 .pop() 577 .vbox(vccClass) 578 .areturn(); 579 }); 580 Object obj = moveValueThroughStackAndLvt.invoke(createDefaultPersonVcc()); 581 validateDefaultPersonVcc(obj); 582 doGc(); 583 obj = null; 584 doGc(); 585 586 int index = 4711; 587 obj = moveValueThroughStackAndLvt.invoke(createIndexedPersonVcc(index)); 588 validateIndexedPersonVcc(obj, index); 589 doGc(); 590 obj = null; 591 doGc(); 592 } 593 catch (Throwable t) { fail("testOverGc", t); } 594 } 595 596 static void submitNewWork(ForkJoinPool fjPool, int size) { 597 for (int i = 0; i < size; i++) { 598 fjPool.execute(ValueOops::testMvtPeopleArray); 599 for (int j = 0; j < 100; j++) { 600 // JDK-8186718 random crashes in interpreter vbox and vunbox (with G1) 601 // test needs refactoring to more specific use cases for debugging. 602 //fjPool.execute(ValueOops::testBytecodes); 603 fjPool.execute(ValueOops::testVvt); 604 fjPool.execute(ValueOops::testMvt); 605 } 606 } 607 } 608 609 static void sleepNoThrow(long ms) { 610 try { 611 Thread.sleep(ms); 612 } 613 catch (Throwable t) {} 614 } 615 616 /** 617 * Run some workloads with different object/value life times... 618 */ 619 public static void testActiveGc() { 620 try { 621 int nofThreads = 7; 622 int workSize = nofThreads * 10; 623 624 Object longLivedObjects = createLongLived(); 625 Object longLivedPeople = createPeople(); 626 Object longLivedPeopleVcc = createPeopleVcc(); 627 628 Object medLivedObjects = createLongLived(); 629 Object medLivedPeople = createPeople(); 630 Object medLivedPeopleVcc = createPeopleVcc(); 631 632 doGc(); 633 634 ForkJoinPool fjPool = new ForkJoinPool(nofThreads, ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true); 635 636 // submit work until we see some GC 637 Reference ref = createRef(); 638 submitNewWork(fjPool, workSize); 639 while (ref.get() != null) { 640 if (fjPool.hasQueuedSubmissions()) { 641 sleepNoThrow(1L); 642 } 643 else { 644 workSize *= 2; // Grow the submission size 645 submitNewWork(fjPool, workSize); 646 } 647 } 648 649 // Keep working and actively GC, until MIN_ACTIVE_GC_COUNT 650 int nofActiveGc = 1; 651 ref = createRef(); 652 while (nofActiveGc < MIN_ACTIVE_GC_COUNT) { 653 if (ref.get() == null) { 654 nofActiveGc++; 655 ref = createRef(); 656 if (nofActiveGc % MED_ACTIVE_GC_COUNT == 0) { 657 validateLongLived(medLivedObjects); 658 validatePeople(medLivedPeople); 659 validatePeopleVcc(medLivedPeopleVcc); 660 661 medLivedObjects = createLongLived(); 662 medLivedPeople = createPeople(); 663 medLivedPeopleVcc = createPeopleVcc(); 664 } 665 } 666 else if (fjPool.hasQueuedSubmissions()) { 667 sleepNoThrow((long) Utils.getRandomInstance().nextInt(1000)); 668 doGc(); 669 } 670 else { 671 submitNewWork(fjPool, workSize); 672 } 673 } 674 fjPool.shutdown(); 675 676 validateLongLived(medLivedObjects); 677 validatePeople(medLivedPeople); 678 validatePeopleVcc(medLivedPeopleVcc); 679 medLivedObjects = null; 680 medLivedPeople = null; 681 medLivedPeopleVcc = null; 682 683 validateLongLived(longLivedObjects); 684 validatePeople(longLivedPeople); 685 validatePeopleVcc(longLivedPeopleVcc); 686 687 longLivedObjects = null; 688 longLivedPeople = null; 689 longLivedPeopleVcc = null; 690 691 doGc(); 692 } 693 catch (Throwable t) { fail("testMvtActiveGc", t); } 694 } 695 696 static final ReferenceQueue<Object> REFQ = new ReferenceQueue<>(); 697 698 public static void doGc() { 699 // Create Reference, wait until it clears... 700 Reference ref = createRef(); 701 while (ref.get() != null) { 702 System.gc(); 703 } 704 } 705 706 static Reference createRef() { 707 return new WeakReference<Object>(new Object(), REFQ); 708 } 709 710 static void validatePersonVcc(Object obj, int id, String fn, String ln, boolean equals) { 711 assertTrue(obj.getClass() == PersonVcc.class, "Expected VCC class"); 712 PersonVcc person = (PersonVcc) obj; 713 assertTrue(person.id == id); 714 if (equals) { 715 assertTrue(fn.equals(person.getFirstName()), "Invalid field firstName"); 716 assertTrue(ln.equals(person.getLastName()), "Invalid field lastName"); 717 } 718 else { 719 assertTrue(person.getFirstName() == fn, "Invalid field firstName"); 720 assertTrue(person.getLastName() == ln, "Invalid field lastName"); 721 } 722 } 723 724 static PersonVcc createIndexedPersonVcc(int i) { 725 return PersonVcc.create(i, firstName(i), lastName(i)); 726 } 727 728 static void validateIndexedPersonVcc(Object obj, int i) { 729 validatePersonVcc(obj, i, firstName(i), lastName(i), true); 730 } 731 732 static PersonVcc createDefaultPersonVcc() { 733 return PersonVcc.create(0, null, null); 734 } 735 736 static void validateDefaultPersonVcc(Object obj) { 737 validatePersonVcc(obj, 0, null, null, false); 738 } 739 740 741 static void validatePerson(Person person, int id, String fn, String ln, boolean equals) { 742 assertTrue(person.id == id); 743 if (equals) { 744 assertTrue(fn.equals(person.getFirstName()), "Invalid field firstName"); 745 assertTrue(ln.equals(person.getLastName()), "Invalid field lastName"); 746 } 747 else { 748 assertTrue(person.getFirstName() == fn, "Invalid field firstName"); 749 assertTrue(person.getLastName() == ln, "Invalid field lastName"); 750 } 751 } 752 753 static Person createIndexedPerson(int i) { 754 return Person.create(i, firstName(i), lastName(i)); 755 } 756 757 static void validateIndexedPerson(Person person, int i) { 758 validatePerson(person, i, firstName(i), lastName(i), true); 759 } 760 761 static Person createDefaultPerson() { 762 return Person.create(0, null, null); 763 } 764 765 static void validateDefaultPerson(Person person) { 766 validatePerson(person, 0, null, null, false); 767 } 768 769 static String firstName(int i) { 770 return "FirstName-" + i; 771 } 772 773 static String lastName(int i) { 774 return "LastName-" + i; 775 } 776 777 static Object createLongLived() throws Throwable { 778 Object[] population = new Object[2]; 779 population[0] = createPeople(); 780 population[1] = createPeopleVcc(); 781 return population; 782 } 783 784 static void validateLongLived(Object pop) throws Throwable { 785 Object[] population = (Object[]) pop; 786 validatePeople(population[0]); 787 validatePeopleVcc(population[1]); 788 } 789 790 static Object createPeopleVcc() throws Throwable { 791 int arrayLength = NOF_PEOPLE; 792 Class<?> vccClass = PersonVcc.class; 793 ValueType<?> vt = ValueType.forClass(vccClass); 794 MethodHandle arrayElemSet = MethodHandles.arrayElementSetter(vt.arrayValueClass()); 795 796 Object people = vt.newArray().invoke(arrayLength); 797 for (int i = 0; i < arrayLength; i++) { 798 arrayElemSet.invoke(people, i, createIndexedPersonVcc(i)); 799 } 800 return people; 801 } 802 803 static void validatePeopleVcc(Object people) throws Throwable { 804 MethodHandle arrayElemGet = MethodHandles.arrayElementGetter( 805 ValueType.forClass((Class<?>)PersonVcc.class).arrayValueClass()); 806 807 int arrayLength = java.lang.reflect.Array.getLength(people); 808 assertTrue(arrayLength == NOF_PEOPLE); 809 for (int i = 0; i < arrayLength; i++) { 810 validateIndexedPersonVcc(arrayElemGet.invoke(people, i), i); 811 } 812 } 813 814 static Object createPeople() { 815 int arrayLength = NOF_PEOPLE; 816 Person[] people = new Person[arrayLength]; 817 for (int i = 0; i < arrayLength; i++) { 818 people[i] = createIndexedPerson(i); 819 } 820 return people; 821 } 822 823 static void validatePeople(Object array) { 824 Person[] people = (Person[]) array; 825 int arrayLength = people.length; 826 assertTrue(arrayLength == NOF_PEOPLE); 827 for (int i = 0; i < arrayLength; i++) { 828 validateIndexedPerson(people[i], i); 829 } 830 } 831 832 // Various field layouts...sanity testing, see MVTCombo testing for full-set 833 834 static final __ByValue class ObjectValue { 835 final Object object; 836 837 private ObjectValue(Object object) { 838 this.object = object; 839 } 840 } 841 842 static class ObjectWithObjectValue { 843 ObjectValue value1; 844 Object ref1; 845 } 846 847 static class ObjectWithObjectValues { 848 ObjectValue value1; 849 ObjectValue value2; 850 Object ref1; 851 } 852 853 static class Foo { 854 int id; 855 String name; 856 String description; 857 long timestamp; 858 String notes; 859 } 860 861 static class Bar extends Foo { 862 long extendedId; 863 String moreNotes; 864 int count; 865 String otherStuff; 866 } 867 868 static final __ByValue class FooValue { 869 final int id; 870 final String name; 871 final String description; 872 final long timestamp; 873 final String notes; 874 875 private FooValue() { 876 this.id = 0; 877 this.name = null; 878 this.description = null; 879 this.timestamp = 0L; 880 this.notes = null; 881 } 882 883 __ValueFactory public static FooValue create(int id, String name, String description, long timestamp, String notes) { 884 FooValue f = __MakeDefault FooValue(); 885 f.id = id; 886 f.name = name; 887 f.description = description; 888 f.timestamp = timestamp; 889 f.notes = notes; 890 return f; 891 } 892 893 public static void testFrameOopsDefault(Object[][] oopMaps) { 894 Class<?> fooValueCls = FooValue.class; 895 MethodType mt = MethodType.methodType(Void.TYPE, oopMaps.getClass()); 896 int oopMapsSlot = 0; 897 int vtSlot = 1; 898 899 // Slots 1=oopMaps 900 // OopMap Q=RRR (.name .description .someNotes) 901 try { 902 MethodHandleBuilder 903 .loadCode(MethodHandles.lookup(), 904 "exerciseVBytecodeExprStackWithDefault", mt, 905 CODE->{ 906 CODE 907 .vdefault(fooValueCls) 908 .aload(oopMapsSlot) 909 .iconst_0() // Test-D0 Slots=R Stack=Q(RRR)RV 910 .invokestatic(ValueOops.class, GET_OOP_MAP_NAME, GET_OOP_MAP_DESC, false) 911 .aastore() 912 .pop() 913 .aload(oopMapsSlot) 914 .iconst_1() // Test-D1 Slots=R Stack=RV 915 .invokestatic(ValueOops.class, GET_OOP_MAP_NAME, GET_OOP_MAP_DESC, false) 916 .aastore() 917 .vdefault(fooValueCls) 918 .vstore(vtSlot) 919 .aload(oopMapsSlot) 920 .iconst_2() // Test-D2 Slots=RQ(RRR) Stack=RV 921 .invokestatic(ValueOops.class, GET_OOP_MAP_NAME, GET_OOP_MAP_DESC, false) 922 .aastore() 923 .vload(vtSlot) 924 .aconst_null() 925 .astore(vtSlot) // Storing null over the Q slot won't remove the ref, but should be single null ref 926 .aload(oopMapsSlot) 927 .iconst_3() // Test-D3 Slots=R Stack=Q(RRR)RV 928 .invokestatic(ValueOops.class, GET_OOP_MAP_NAME, GET_OOP_MAP_DESC, false) 929 .aastore() 930 .pop() 931 .return_(); 932 }).invoke(oopMaps); 933 } catch (Throwable t) { fail("exerciseVBytecodeExprStackWithDefault", t); } 934 } 935 936 public static void testFrameOopsRefs(String name, String description, String notes, Object[][] oopMaps) { 937 Class<?> fooValueCls = FooValue.class; 938 FooValue f = create(4711, name, description, 9876543231L, notes); 939 FooValue[] fa = new FooValue[] { f }; 940 MethodType mt = MethodType.methodType(Void.TYPE, fa.getClass(), oopMaps.getClass()); 941 int fooArraySlot = 0; 942 int oopMapsSlot = 1; 943 try { 944 MethodHandleBuilder 945 .loadCode(MethodHandles.lookup(), 946 "exerciseVBytecodeExprStackWithRefs", mt, 947 CODE->{ 948 CODE 949 .aload(fooArraySlot) 950 .iconst_0() 951 .vaload() 952 .aload(oopMapsSlot) 953 .iconst_0() // Test-R0 Slots=RR Stack=Q(RRR)RV 954 .invokestatic(ValueOops.class, GET_OOP_MAP_NAME, GET_OOP_MAP_DESC, false) 955 .aastore() 956 .pop() 957 .return_(); 958 }).invoke(fa, oopMaps); 959 } catch (Throwable t) { fail("exerciseVBytecodeExprStackWithDefault", t); } 960 } 961 } 962 963 static class BarWithValue { 964 FooValue foo; 965 long extendedId; 966 String moreNotes; 967 int count; 968 String otherStuff; 969 } 970 971 static final __ByValue class BarValue { 972 final FooValue foo; 973 final long extendedId; 974 final String moreNotes; 975 final int count; 976 final String otherStuff; 977 978 private BarValue(FooValue foo, long extendedId, String moreNotes, int count, String otherStuff) { 979 this.foo = foo; 980 this.extendedId = extendedId; 981 this.moreNotes = moreNotes; 982 this.count = count; 983 this.otherStuff = otherStuff; 984 } 985 } 986 987 } 988