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 }