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