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 }