1 /* 2 * Copyright (c) 2017, 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 runtime.valhalla.inlinetypes; 25 26 import java.io.File; 27 import java.io.IOException; 28 import java.io.PrintWriter; 29 import java.lang.constant.ClassDesc; 30 import java.lang.constant.MethodTypeDesc; 31 import java.lang.invoke.*; 32 import java.lang.ref.*; 33 import java.nio.ByteBuffer; 34 import java.time.chrono.ThaiBuddhistChronology; 35 import java.util.ArrayList; 36 import java.util.Arrays; 37 import java.util.List; 38 import java.util.concurrent.*; 39 40 import static jdk.test.lib.Asserts.*; 41 42 import java.lang.classfile.Label; 43 import java.lang.classfile.TypeKind; 44 import jdk.internal.vm.annotation.ImplicitlyConstructible; 45 import jdk.internal.vm.annotation.LooselyConsistentValue; 46 import jdk.internal.vm.annotation.NullRestricted; 47 import jdk.test.lib.Platform; 48 49 import javax.tools.*; 50 51 import test.java.lang.invoke.lib.InstructionHelper; 52 import static test.java.lang.invoke.lib.InstructionHelper.classDesc; 53 54 /** 55 * @test InlineTypesTest 56 * @summary Test data movement with inline types 57 * @modules java.base/jdk.internal.value 58 * @library /test/lib /test/jdk/java/lang/invoke/common 59 * @modules java.base/jdk.internal.vm.annotation 60 * @enablePreview 61 * @compile InlineTypesTest.java 62 * @run main/othervm -Xmx128m -XX:+ExplicitGCInvokesConcurrent 63 * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions 64 * runtime.valhalla.inlinetypes.InlineTypesTest 65 * @run main/othervm -Xmx128m -XX:+ExplicitGCInvokesConcurrent 66 * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions 67 * -XX:ForceNonTearable=* 68 * runtime.valhalla.inlinetypes.InlineTypesTest 69 */ 70 71 final class ContainerValue1 { 72 static TestValue1 staticInlineField; 73 @NullRestricted 74 TestValue1 nonStaticInlineField; 75 TestValue1[] valueArray; 76 } 77 78 @ImplicitlyConstructible 79 @LooselyConsistentValue 80 value class TestValue1 { 81 82 static TestValue1 staticValue = getInstance(); 83 84 final int i; 85 final String name; 86 87 public TestValue1() { 88 int now = (int)System.nanoTime(); 89 i = now; 90 name = Integer.valueOf(now).toString(); 91 } 92 93 public TestValue1(int i) { 94 this.i = i; 95 name = Integer.valueOf(i).toString(); 96 } 97 98 public static TestValue1 getInstance() { 99 return new TestValue1(); 100 } 101 102 public static TestValue1 getNonBufferedInstance() { 103 return (TestValue1) staticValue; 104 } 105 106 public boolean verify() { 107 if (name == null) return i == 0; 108 return Integer.valueOf(i).toString().compareTo(name) == 0; 109 } 110 } 111 112 final class ContainerValue2 { 113 static TestValue2 staticInlineField; 114 @NullRestricted 115 TestValue2 nonStaticInlineField; 116 TestValue2[] valueArray; 117 } 118 119 @ImplicitlyConstructible 120 @LooselyConsistentValue 121 value class TestValue2 { 122 static TestValue2 staticValue = getInstance(); 123 124 final long l; 125 final double d; 126 final String s; 127 128 public TestValue2() { 129 long now = System.nanoTime(); 130 l = now; 131 String stringNow = Long.valueOf(now).toString(); 132 s = stringNow; 133 d = Double.parseDouble(stringNow); 134 } 135 136 public TestValue2(long l) { 137 this.l = l; 138 String txt = Long.valueOf(l).toString(); 139 s = txt; 140 d = Double.parseDouble(txt); 141 } 142 143 public static TestValue2 getInstance() { 144 return new TestValue2(); 145 } 146 147 public static TestValue2 getNonBufferedInstance() { 148 return (TestValue2) staticValue; 149 } 150 151 public boolean verify() { 152 if (s == null) { 153 return d == 0 && l == 0; 154 } 155 return Long.valueOf(l).toString().compareTo(s) == 0 156 && Double.parseDouble(s) == d; 157 } 158 } 159 160 final class ContainerValue3 { 161 static TestValue3 staticInlineField; 162 @NullRestricted 163 TestValue3 nonStaticInlineField; 164 TestValue3[] valueArray; 165 } 166 167 @ImplicitlyConstructible 168 @LooselyConsistentValue 169 value class TestValue3 { 170 171 static TestValue3 staticValue = getInstance(); 172 173 final byte b; 174 175 public TestValue3() { 176 b = 123; 177 } 178 179 public TestValue3(byte b) { 180 this.b = b; 181 } 182 183 public static TestValue3 getInstance() { 184 return new TestValue3(); 185 } 186 187 public static TestValue3 getNonBufferedInstance() { 188 return (TestValue3) staticValue; 189 } 190 191 public boolean verify() { 192 return b == 0 || b == 123; 193 } 194 } 195 196 final class ContainerValue4 { 197 static TestValue4 staticInlineField; 198 @NullRestricted 199 TestValue4 nonStaticInlineField; 200 TestValue4[] valueArray; 201 } 202 203 @ImplicitlyConstructible 204 @LooselyConsistentValue 205 value class TestValue4 { 206 207 static TestValue4 staticValue = getInstance(); 208 209 final byte b1; 210 final byte b2; 211 final byte b3; 212 final byte b4; 213 final short s1; 214 final short s2; 215 final int i; 216 final long l; 217 final String val; 218 219 public TestValue4() { 220 this((int) System.nanoTime()); 221 } 222 223 public TestValue4(int i) { 224 this.i = i; 225 val = Integer.valueOf(i).toString(); 226 ByteBuffer bf = ByteBuffer.allocate(8); 227 bf.putInt(0, i); 228 bf.putInt(4, i); 229 l = bf.getLong(0); 230 s1 = bf.getShort(2); 231 s2 = bf.getShort(0); 232 b1 = bf.get(3); 233 b2 = bf.get(2); 234 b3 = bf.get(1); 235 b4 = bf.get(0); 236 } 237 238 public static TestValue4 getInstance() { 239 return new TestValue4(); 240 } 241 242 public static TestValue4 getNonBufferedInstance() { 243 return (TestValue4) staticValue; 244 } 245 246 public boolean verify() { 247 if (val == null) { 248 return i == 0 && l == 0 && b1 == 0 && b2 == 0 && b3 == 0 && b4 == 0 249 && s1 == 0 && s2 == 0; 250 } 251 ByteBuffer bf = ByteBuffer.allocate(8); 252 bf.putInt(0, i); 253 bf.putInt(4, i); 254 long nl = bf.getLong(0); 255 bf.clear(); 256 bf.putShort(0, s2); 257 bf.putShort(2, s1); 258 int from_s = bf.getInt(0); 259 bf.clear(); 260 bf.put(0, b4); 261 bf.put(1, b3); 262 bf.put(2, b2); 263 bf.put(3, b1); 264 int from_b = bf.getInt(0); 265 return l == nl && Integer.valueOf(i).toString().compareTo(val) == 0 266 && from_s == i && from_b == i; 267 } 268 } 269 270 public class InlineTypesTest { 271 272 public static void main(String[] args) { 273 Class<?> inlineClass = runtime.valhalla.inlinetypes.TestValue1.class; 274 Class<?> testClasses[] = { 275 runtime.valhalla.inlinetypes.TestValue1.class, 276 runtime.valhalla.inlinetypes.TestValue2.class, 277 runtime.valhalla.inlinetypes.TestValue3.class, 278 runtime.valhalla.inlinetypes.TestValue4.class 279 }; 280 Class<?> containerClasses[] = { 281 runtime.valhalla.inlinetypes.ContainerValue1.class, 282 runtime.valhalla.inlinetypes.ContainerValue2.class, 283 runtime.valhalla.inlinetypes.ContainerValue3.class, 284 runtime.valhalla.inlinetypes.ContainerValue4.class 285 }; 286 287 for (int i = 0; i < testClasses.length; i++) { 288 try { 289 testExecutionStackToLocalVariable(testClasses[i]); 290 testExecutionStackToFields(testClasses[i], containerClasses[i]); 291 testExecutionStackToInlineArray(testClasses[i], containerClasses[i]); 292 } catch (Throwable t) { 293 t.printStackTrace(); 294 throw new RuntimeException(t); 295 } 296 } 297 } 298 299 static MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); 300 301 static void testExecutionStackToLocalVariable(Class<?> inlineClass) throws Throwable { 302 String sig = "()L" + inlineClass.getName().replace('.', '/') + ";"; 303 final MethodTypeDesc voidReturnClass = MethodTypeDesc.ofDescriptor(sig); 304 final ClassDesc systemClassDesc = classDesc(System.class); 305 final ClassDesc inlineClassDesc = classDesc(inlineClass); 306 MethodHandle fromExecStackToLocalVar = InstructionHelper.buildMethodHandle( 307 LOOKUP, 308 "execStackToLocalVar", 309 MethodType.methodType(boolean.class), 310 CODE -> { 311 CODE.invokestatic(systemClassDesc, "gc", MethodTypeDesc.ofDescriptor("()V")); 312 int n = -1; 313 while (n < 1024) { 314 n++; 315 CODE 316 .invokestatic(inlineClassDesc, "getInstance", voidReturnClass) 317 .astore(n); 318 n++; 319 CODE 320 .invokestatic(inlineClassDesc, "getNonBufferedInstance", voidReturnClass) 321 .astore(n); 322 } 323 CODE.invokestatic(systemClassDesc, "gc", MethodTypeDesc.ofDescriptor("()V")); 324 Label endLabel = CODE.newLabel(); 325 while (n > 0) { 326 CODE 327 .aload(n) 328 .invokevirtual(inlineClassDesc, "verify", MethodTypeDesc.ofDescriptor("()Z")) 329 .iconst_1() 330 .if_icmpne(endLabel); 331 n--; 332 } 333 CODE 334 .iconst_1() 335 .return_(TypeKind.BOOLEAN) 336 .labelBinding(endLabel) 337 .iconst_0() 338 .return_(TypeKind.BOOLEAN); 339 }); 340 boolean result = (boolean) fromExecStackToLocalVar.invokeExact(); 341 System.out.println(result); 342 assertTrue(result, "Invariant"); 343 } 344 345 static void testExecutionStackToFields(Class<?> inlineClass, Class<?> containerClass) throws Throwable { 346 final int ITERATIONS = Platform.isDebugBuild() ? 3 : 512; 347 String sig = "()L" + inlineClass.getName().replace('.', '/') + ";"; 348 final MethodTypeDesc voidReturnClass = MethodTypeDesc.ofDescriptor(sig); 349 final ClassDesc systemClassDesc = classDesc(System.class); 350 final ClassDesc inlineClassDesc = classDesc(inlineClass); 351 final ClassDesc containerClassDesc = classDesc(containerClass); 352 353 MethodHandle fromExecStackToFields = InstructionHelper.buildMethodHandle( 354 LOOKUP, 355 "execStackToFields", 356 MethodType.methodType(boolean.class), 357 CODE -> { 358 Label loop = CODE.newLabel(); 359 Label end = CODE.newLabel(); 360 Label failed = CODE.newLabel(); 361 CODE 362 .invokestatic(systemClassDesc, "gc", MethodTypeDesc.ofDescriptor("()V"), false) 363 .new_(containerClassDesc) 364 .dup() 365 .invokespecial(containerClassDesc, "<init>", MethodTypeDesc.ofDescriptor("()V")) 366 .astore(1) 367 .iconst_m1() 368 .istore(2) 369 .labelBinding(loop) 370 .iload(2) 371 .ldc(ITERATIONS) 372 .if_icmpeq(end) 373 .aload(1) 374 .invokestatic(inlineClassDesc, "getInstance", voidReturnClass) 375 .putfield(containerClassDesc, "nonStaticInlineField", inlineClassDesc) 376 .invokestatic(systemClassDesc, "gc", MethodTypeDesc.ofDescriptor("()V")) 377 .aload(1) 378 .getfield(containerClassDesc, "nonStaticInlineField", inlineClassDesc) 379 .invokevirtual(inlineClassDesc, "verify", MethodTypeDesc.ofDescriptor("()Z")) 380 .iconst_1() 381 .if_icmpne(failed) 382 .aload(1) 383 .invokestatic(inlineClassDesc, "getNonBufferedInstance", voidReturnClass) 384 .putfield(containerClassDesc, "nonStaticInlineField", inlineClassDesc) 385 .invokestatic(systemClassDesc, "gc", MethodTypeDesc.ofDescriptor("()V")) 386 .aload(1) 387 .getfield(containerClassDesc, "nonStaticInlineField", inlineClassDesc) 388 .invokevirtual(inlineClassDesc, "verify", MethodTypeDesc.ofDescriptor("()Z")) 389 .iconst_1() 390 .if_icmpne(failed) 391 .invokestatic(inlineClassDesc, "getInstance", voidReturnClass) 392 .putstatic(containerClassDesc, "staticInlineField", inlineClassDesc) 393 .invokestatic(systemClassDesc, "gc", MethodTypeDesc.ofDescriptor("()V")) 394 .getstatic(containerClassDesc, "staticInlineField", inlineClassDesc) 395 .checkcast(inlineClassDesc) 396 .invokevirtual(inlineClassDesc, "verify", MethodTypeDesc.ofDescriptor("()Z")) 397 .iconst_1() 398 .if_icmpne(failed) 399 .invokestatic(inlineClassDesc, "getNonBufferedInstance", voidReturnClass) 400 .putstatic(containerClassDesc, "staticInlineField", inlineClassDesc) 401 .invokestatic(systemClassDesc, "gc", MethodTypeDesc.ofDescriptor("()V")) 402 .getstatic(containerClassDesc, "staticInlineField", inlineClassDesc) 403 .checkcast(inlineClassDesc) 404 .invokevirtual(inlineClassDesc, "verify", MethodTypeDesc.ofDescriptor("()Z")) 405 .iconst_1() 406 .if_icmpne(failed) 407 .iinc(2, 1) 408 .goto_(loop) 409 .labelBinding(end) 410 .iconst_1() 411 .return_(TypeKind.BOOLEAN) 412 .labelBinding(failed) 413 .iconst_0() 414 .return_(TypeKind.BOOLEAN); 415 }); 416 boolean result = (boolean) fromExecStackToFields.invokeExact(); 417 System.out.println(result); 418 assertTrue(result, "Invariant"); 419 } 420 421 static void testExecutionStackToInlineArray(Class<?> inlineClass, Class<?> containerClass) throws Throwable { 422 final int ITERATIONS = Platform.isDebugBuild() ? 3 : 100; 423 String sig = "()L" + inlineClass.getName().replace('.', '/') + ";"; 424 final MethodTypeDesc voidReturnClass = MethodTypeDesc.ofDescriptor(sig); 425 final ClassDesc systemClassDesc = classDesc(System.class); 426 final ClassDesc inlineClassDesc = classDesc(inlineClass); 427 final ClassDesc containerClassDesc = classDesc(containerClass); 428 429 MethodHandle fromExecStackToInlineArray = InstructionHelper.buildMethodHandle( 430 LOOKUP, 431 "execStackToInlineArray", 432 MethodType.methodType(boolean.class), 433 CODE -> { 434 Label loop1 = CODE.newLabel(); 435 Label loop2 = CODE.newLabel(); 436 Label end1 = CODE.newLabel(); 437 Label end2 = CODE.newLabel(); 438 Label failed = CODE.newLabel(); 439 CODE 440 .invokestatic(systemClassDesc, "gc", MethodTypeDesc.ofDescriptor("()V")) 441 .new_(containerClassDesc) 442 .dup() 443 .invokespecial(containerClassDesc, "<init>", MethodTypeDesc.ofDescriptor("()V")) 444 .astore(1) 445 .ldc(ITERATIONS * 3) 446 .anewarray(inlineClassDesc) 447 .astore(2) 448 .aload(2) 449 .aload(1) 450 .swap() 451 .putfield(containerClassDesc, "valueArray", inlineClassDesc.arrayType()) 452 .iconst_0() 453 .istore(3) 454 .labelBinding(loop1) 455 .iload(3) 456 .ldc(ITERATIONS *3) 457 .if_icmpge(end1) 458 .aload(2) 459 .iload(3) 460 .invokestatic(inlineClassDesc, "getInstance", voidReturnClass) 461 .aastore() 462 .iinc(3, 1) 463 .aload(2) 464 .iload(3) 465 .invokestatic(inlineClassDesc, "getNonBufferedInstance", voidReturnClass) 466 .aastore() 467 .iinc(3, 1) 468 .aload(2) 469 .iload(3) 470 .new_(inlineClassDesc) 471 .dup() 472 .invokespecial(inlineClassDesc, "<init>", MethodTypeDesc.ofDescriptor("()V")) 473 .aastore() 474 .iinc(3, 1) 475 .goto_(loop1) 476 .labelBinding(end1) 477 .invokestatic(systemClassDesc, "gc", MethodTypeDesc.ofDescriptor("()V")) 478 .iconst_0() 479 .istore(3) 480 .labelBinding(loop2) 481 .iload(3) 482 .ldc(ITERATIONS * 3) 483 .if_icmpge(end2) 484 .aload(2) 485 .iload(3) 486 .aaload() 487 .invokevirtual(inlineClassDesc, "verify", MethodTypeDesc.ofDescriptor("()Z")) 488 .iconst_1() 489 .if_icmpne(failed) 490 .iinc(3, 1) 491 .goto_(loop2) 492 .labelBinding(end2) 493 .iconst_1() 494 .return_(TypeKind.BOOLEAN) 495 .labelBinding(failed) 496 .iconst_0() 497 .return_(TypeKind.BOOLEAN); 498 }); 499 boolean result = (boolean) fromExecStackToInlineArray.invokeExact(); 500 System.out.println(result); 501 assertTrue(result, "Invariant"); 502 } 503 504 }