1 /*
  2  * Copyright (c) 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 compiler.valhalla.inlinetypes;
 25 
 26 import java.util.Random;
 27 
 28 import jdk.test.lib.Asserts;
 29 import jdk.test.lib.Utils;
 30 import jdk.test.whitebox.WhiteBox;
 31 
 32 /**
 33  * @test TestValueConstruction
 34  * @summary Test construction of value objects.
 35  * @key randomness
 36  * @library /testlibrary /test/lib /compiler/whitebox /
 37  * @enablePreview
 38  * @build jdk.test.whitebox.WhiteBox
 39  * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
 40  * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch
 41  *                   -XX:CompileCommand=inline,TestValueConstruction::checkDeopt
 42  *                   compiler.valhalla.inlinetypes.TestValueConstruction
 43  * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+IgnoreUnrecognizedVMOptions -XX:+DeoptimizeALot
 44  *                   -XX:CompileCommand=inline,TestValueConstruction::checkDeopt
 45  *                   compiler.valhalla.inlinetypes.TestValueConstruction
 46  * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
 47  *                   -XX:CompileCommand=compileonly,*TestValueConstruction::test* -Xbatch
 48  *                   -XX:CompileCommand=inline,TestValueConstruction::checkDeopt
 49  *                   compiler.valhalla.inlinetypes.TestValueConstruction
 50  * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
 51  *                   -XX:CompileCommand=dontinline,*MyValue*::<init> -Xbatch
 52  *                   -XX:CompileCommand=inline,TestValueConstruction::checkDeopt
 53  *                   compiler.valhalla.inlinetypes.TestValueConstruction
 54  * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
 55  *                   -XX:CompileCommand=dontinline,*Object::<init> -Xbatch
 56  *                   -XX:CompileCommand=inline,TestValueConstruction::checkDeopt
 57  *                   compiler.valhalla.inlinetypes.TestValueConstruction
 58  * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+IgnoreUnrecognizedVMOptions -XX:+DeoptimizeALot
 59  *                   -XX:CompileCommand=dontinline,*Object::<init> -Xbatch
 60  *                   -XX:CompileCommand=inline,TestValueConstruction::checkDeopt
 61  *                   compiler.valhalla.inlinetypes.TestValueConstruction
 62  * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
 63  *                   -XX:CompileCommand=dontinline,*MyAbstract::<init> -Xbatch
 64  *                   -XX:CompileCommand=inline,TestValueConstruction::checkDeopt
 65  *                   compiler.valhalla.inlinetypes.TestValueConstruction
 66  * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch
 67  *                   -XX:-TieredCompilation -XX:+StressIncrementalInlining
 68  *                   -XX:CompileCommand=inline,TestValueConstruction::checkDeopt
 69  *                   compiler.valhalla.inlinetypes.TestValueConstruction
 70  * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
 71  *                   -XX:-TieredCompilation -XX:+StressIncrementalInlining
 72  *                   -XX:CompileCommand=inline,TestValueConstruction::checkDeopt
 73  *                   -XX:CompileCommand=compileonly,*TestValueConstruction::test* -Xbatch
 74  *                   compiler.valhalla.inlinetypes.TestValueConstruction
 75  * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
 76  *                   -XX:-TieredCompilation -XX:+StressIncrementalInlining
 77  *                   -XX:CompileCommand=inline,TestValueConstruction::checkDeopt
 78  *                   -XX:CompileCommand=dontinline,*MyValue*::<init> -Xbatch
 79  *                   compiler.valhalla.inlinetypes.TestValueConstruction
 80  * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
 81  *                   -XX:-TieredCompilation -XX:+StressIncrementalInlining
 82  *                   -XX:CompileCommand=inline,TestValueConstruction::checkDeopt
 83  *                   -XX:CompileCommand=dontinline,*Object::<init> -Xbatch
 84  *                   compiler.valhalla.inlinetypes.TestValueConstruction
 85  * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
 86  *                   -XX:-TieredCompilation -XX:+StressIncrementalInlining
 87  *                   -XX:CompileCommand=inline,TestValueConstruction::checkDeopt
 88  *                   -XX:CompileCommand=dontinline,*MyAbstract::<init> -Xbatch
 89  *                   compiler.valhalla.inlinetypes.TestValueConstruction
 90  */
 91 
 92 public class TestValueConstruction {
 93     static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
 94 
 95     static boolean VERBOSE = false;
 96     static boolean deopt[] = new boolean[13];
 97 
 98     static void reportDeopt(int deoptNum) {
 99         System.out.println("Deopt " + deoptNum + " triggered");
100         if (VERBOSE) {
101             new Exception().printStackTrace(System.out);
102         }
103     }
104 
105     // Trigger deopts at various places
106     static void checkDeopt(int deoptNum) {
107         if (deopt[deoptNum]) {
108             // C2 will add an uncommon trap here
109             reportDeopt(deoptNum);
110         }
111     }
112 
113     static interface MyInterface {
114 
115     }
116 
117     static value class MyValue1 implements MyInterface {
118         int x;
119 
120         public MyValue1(int x) {
121             checkDeopt(0);
122             this.x = x;
123             checkDeopt(1);
124             super();
125             checkDeopt(2);
126         }
127 
128         public MyValue1(int x, int deoptNum1, int deoptNum2, int deoptNum3) {
129             checkDeopt(deoptNum1);
130             this.x = x;
131             checkDeopt(deoptNum2);
132             super();
133             checkDeopt(deoptNum3);
134         }
135 
136         public String toString() {
137             return "x: " + x;
138         }
139     }
140 
141     static abstract value class MyAbstract1 { }
142 
143     static value class MyValue2 extends MyAbstract1 {
144         int x;
145 
146         public MyValue2(int x) {
147             checkDeopt(0);
148             this.x = x;
149             checkDeopt(1);
150             super();
151             checkDeopt(2);
152         }
153 
154         public String toString() {
155             return "x: " + x;
156         }
157     }
158 
159     static abstract value class MyAbstract2 {
160         public MyAbstract2(int x) {
161             checkDeopt(0);
162         }
163     }
164 
165     static value class MyValue3 extends MyAbstract2 {
166         int x;
167 
168         public MyValue3(int x) {
169             checkDeopt(1);
170             this(x, 0);
171             helper1(this, x, 2); // 'this' escapes through argument
172             helper2(x, 3); // 'this' escapes through receiver
173             checkDeopt(4);
174         }
175 
176         public MyValue3(int x, int unused) {
177             this.x = helper3(x, 5);
178             super(x);
179             helper1(this, x, 6); // 'this' escapes through argument
180             helper2(x, 7); // 'this' escapes through receiver
181             checkDeopt(8);
182         }
183 
184         public static void helper1(MyValue3 obj, int x, int deoptNum) {
185             checkDeopt(deoptNum);
186             Asserts.assertEQ(obj.x, x);
187         }
188 
189         public void helper2(int x, int deoptNum) {
190             checkDeopt(deoptNum);
191             Asserts.assertEQ(this.x, x);
192         }
193 
194         public static int helper3(int x, int deoptNum) {
195             checkDeopt(deoptNum);
196             return x;
197         }
198 
199         public String toString() {
200             return "x: " + x;
201         }
202     }
203 
204     static value class MyValue4 {
205         Integer x;
206 
207         public MyValue4(int x) {
208             checkDeopt(0);
209             this.x = x;
210             checkDeopt(1);
211             super();
212             checkDeopt(2);
213         }
214 
215         public String toString() {
216             return "x: " + x;
217         }
218     }
219 
220     static value class MyValue5 {
221         int x;
222 
223         public MyValue5(int x, boolean b) {
224             checkDeopt(0);
225             if (b) {
226                 checkDeopt(1);
227                 this.x = 42;
228                 checkDeopt(2);
229             } else {
230                 checkDeopt(3);
231                 this.x = x;
232                 checkDeopt(4);
233             }
234             checkDeopt(5);
235             super();
236             checkDeopt(6);
237         }
238 
239         public String toString() {
240             return "x: " + x;
241         }
242     }
243 
244     static value class MyValue6 {
245         int x;
246         MyValue1 val1;
247         MyValue1 val2;
248 
249         public MyValue6(int x) {
250             checkDeopt(0);
251             this.x = x;
252             checkDeopt(1);
253             this.val1 = new MyValue1(x, 2, 3, 4);
254             checkDeopt(5);
255             this.val2 = new MyValue1(x + 1, 6, 7, 8);
256             checkDeopt(9);
257             super();
258             checkDeopt(10);
259         }
260 
261         public String toString() {
262             return "x: " + x + ", val1: [" + val1 + "], val2: [" + val2 + "]";
263         }
264     }
265 
266     // Same as MyValue6 but unused MyValue1 construction
267     static value class MyValue7 {
268         int x;
269 
270         public MyValue7(int x) {
271             checkDeopt(0);
272             this.x = x;
273             checkDeopt(1);
274             new MyValue1(42, 2, 3, 4);
275             checkDeopt(5);
276             new MyValue1(43, 6, 7, 8);
277             checkDeopt(9);
278             super();
279             checkDeopt(10);
280         }
281 
282         public String toString() {
283             return "x: " + x;
284         }
285     }
286 
287     // Constructor calling another constructor of the same value class with control flow dependent initialization
288     static value class MyValue8 {
289         int x;
290 
291         public MyValue8(int x) {
292             checkDeopt(0);
293             this(x, 0);
294             checkDeopt(1);
295         }
296 
297         public MyValue8(int x, int unused1) {
298             checkDeopt(2);
299             if ((x % 2) == 0) {
300                 checkDeopt(3);
301                 this.x = 42;
302                 checkDeopt(4);
303             } else {
304                 checkDeopt(5);
305                 this.x = x;
306                 checkDeopt(6);
307             }
308             checkDeopt(7);
309             super();
310             checkDeopt(8);
311         }
312 
313         public MyValue8(int x, int unused1, int unused2) {
314             checkDeopt(3);
315             this.x = x;
316             checkDeopt(4);
317         }
318 
319         public static MyValue8 valueOf(int x) {
320             checkDeopt(0);
321             if ((x % 2) == 0) {
322                 checkDeopt(1);
323                 return new MyValue8(42, 0, 0);
324             } else {
325                 checkDeopt(2);
326                 return new MyValue8(x, 0, 0);
327             }
328         }
329 
330         public String toString() {
331             return "x: " + x;
332         }
333     }
334 
335     // Constructor calling another constructor of a different value class
336     static value class MyValue9 {
337         MyValue8 val;
338 
339         public MyValue9(int x) {
340             checkDeopt(9);
341             this(x, 0);
342             checkDeopt(10);
343         }
344 
345         public MyValue9(int i, int unused1) {
346             checkDeopt(11);
347             val = new MyValue8(i);
348             checkDeopt(12);
349         }
350 
351         public MyValue9(int x, int unused1, int unused2) {
352             checkDeopt(5);
353             this(x, 0, 0, 0);
354             checkDeopt(6);
355         }
356 
357         public MyValue9(int i, int unused1, int unused2, int unused3) {
358             checkDeopt(7);
359             val = MyValue8.valueOf(i);
360             checkDeopt(8);
361         }
362 
363         public String toString() {
364             return "val: [" + val + "]";
365         }
366     }
367 
368     // Constructor with a loop
369     static value class MyValue10 {
370         int x;
371         int y;
372 
373         public MyValue10(int x, int cnt) {
374             checkDeopt(0);
375             this.x = x;
376             checkDeopt(1);
377             int res = 0;
378             for (int i = 0; i < cnt; ++i) {
379                 checkDeopt(2);
380                 res += x;
381                 checkDeopt(3);
382             }
383             checkDeopt(4);
384             this.y = res;
385             checkDeopt(5);
386             super();
387             checkDeopt(6);
388         }
389 
390         public String toString() {
391             return "x: " + x + ", y: " + y;
392         }
393     }
394 
395     // Value class with recursive field definitions
396     static value class MyValue11 {
397         int x;
398         MyValue11 val1;
399         MyValue11 val2;
400 
401         public MyValue11(int x) {
402             checkDeopt(0);
403             this.x = x;
404             checkDeopt(1);
405             this.val1 = new MyValue11(x + 1, 2, 3, 4, 5);
406             checkDeopt(6);
407             this.val2 = new MyValue11(x + 2, 7, 8, 9, 10);
408             checkDeopt(11);
409         }
410 
411         public MyValue11(int x, int deoptNum1, int deoptNum2, int deoptNum3, int deoptNum4) {
412             checkDeopt(deoptNum1);
413             this.x = x;
414             checkDeopt(deoptNum2);
415             this.val1 = null;
416             checkDeopt(deoptNum3);
417             this.val2 = null;
418             checkDeopt(deoptNum4);
419         }
420 
421         public String toString() {
422             return "x: " + x + ", val1: [" + (val1 != this ? val1 : "this") + "], val2: [" + (val2 != this ? val2 : "this") + "]";
423         }
424     }
425 
426     public static int test1(int x) {
427         MyValue1 val = new MyValue1(x);
428         checkDeopt(3);
429         return val.x;
430     }
431 
432     public static MyValue1 helper1(int x) {
433         return new MyValue1(x);
434     }
435 
436     public static Object test2(int x) {
437         return helper1(x);
438     }
439 
440     public static Object test3(int limit) {
441         MyValue1 res = null;
442         for (int i = 0; i <= 10; ++i) {
443             res = new MyValue1(i);
444             checkDeopt(3);
445         }
446         return res;
447     }
448 
449     public static MyValue1 test4(int x) {
450         MyValue1 v = new MyValue1(x);
451         checkDeopt(3);
452         v = new MyValue1(x);
453         return v;
454     }
455 
456     public static int test5(int x) {
457         MyValue2 val = new MyValue2(x);
458         checkDeopt(3);
459         return val.x;
460     }
461 
462     public static MyValue2 helper2(int x) {
463         return new MyValue2(x);
464     }
465 
466     public static Object test6(int x) {
467         return helper2(x);
468     }
469 
470     public static Object test7(int limit) {
471         MyValue2 res = null;
472         for (int i = 0; i <= 10; ++i) {
473             res = new MyValue2(i);
474             checkDeopt(3);
475         }
476         return res;
477     }
478 
479     public static MyValue2 test8(int x) {
480         MyValue2 v = new MyValue2(x);
481         checkDeopt(3);
482         v = new MyValue2(x);
483         return v;
484     }
485 
486     public static int test9(int x) {
487         MyValue3 val = new MyValue3(x);
488         checkDeopt(9);
489         return val.x;
490     }
491 
492     public static MyValue3 helper3(int x) {
493         return new MyValue3(x);
494     }
495 
496     public static Object test10(int x) {
497         return helper3(x);
498     }
499 
500     public static Object test11(int limit) {
501         MyValue3 res = null;
502         for (int i = 0; i <= 10; ++i) {
503             checkDeopt(9);
504             res = new MyValue3(i);
505         }
506         return res;
507     }
508 
509     public static MyValue3 test12(int x) {
510         MyValue3 v = new MyValue3(x);
511         checkDeopt(9);
512         v = new MyValue3(x);
513         return v;
514     }
515 
516     public static MyValue4 test13(int x) {
517         return new MyValue4(x);
518     }
519 
520     public static MyValue5 test14(int x, boolean b) {
521         return new MyValue5(x, b);
522     }
523 
524     public static Object test15(int x) {
525         return new MyValue6(x);
526     }
527 
528     public static Object test16(int x) {
529         return new MyValue7(x);
530     }
531 
532     public static MyValue8 test17(int x) {
533         return new MyValue8(x);
534     }
535 
536     public static MyValue8 test18(int x) {
537         return new MyValue8(x, 0);
538     }
539 
540     public static MyValue8 test19(int x) {
541         return MyValue8.valueOf(x);
542     }
543 
544     public static MyValue9 test20(int x) {
545         return new MyValue9(x);
546     }
547 
548     public static MyValue9 test21(int x) {
549         return new MyValue9(x, 0);
550     }
551 
552     public static MyValue9 test22(int x) {
553         return new MyValue9(x, 0, 0);
554     }
555 
556     public static MyValue9 test23(int x) {
557         return new MyValue9(x, 0, 0, 0);
558     }
559 
560     public static MyValue10 test24(int x, int cnt) {
561         return new MyValue10(x, cnt);
562     }
563 
564     public static MyValue11 test25(int x) {
565         return new MyValue11(x);
566     }
567 
568     public static void main(String[] args) throws Exception {
569         Random rand = Utils.getRandomInstance();
570 
571         // Randomly exclude some constructors from inlining via the WhiteBox API because CompileCommands don't match on different signatures.
572         WHITE_BOX.testSetDontInlineMethod(MyValue1.class.getConstructor(int.class), rand.nextBoolean());
573         WHITE_BOX.testSetDontInlineMethod(MyValue1.class.getConstructor(int.class, int.class, int.class, int.class), rand.nextBoolean());
574         WHITE_BOX.testSetDontInlineMethod(MyValue3.class.getConstructor(int.class), rand.nextBoolean());
575         WHITE_BOX.testSetDontInlineMethod(MyValue3.class.getConstructor(int.class, int.class), rand.nextBoolean());
576         WHITE_BOX.testSetDontInlineMethod(MyValue8.class.getConstructor(int.class), rand.nextBoolean());
577         WHITE_BOX.testSetDontInlineMethod(MyValue8.class.getConstructor(int.class, int.class), rand.nextBoolean());
578         WHITE_BOX.testSetDontInlineMethod(MyValue8.class.getConstructor(int.class, int.class, int.class), rand.nextBoolean());
579         WHITE_BOX.testSetDontInlineMethod(MyValue9.class.getConstructor(int.class), rand.nextBoolean());
580         WHITE_BOX.testSetDontInlineMethod(MyValue9.class.getConstructor(int.class, int.class), rand.nextBoolean());
581         WHITE_BOX.testSetDontInlineMethod(MyValue9.class.getConstructor(int.class, int.class, int.class), rand.nextBoolean());
582         WHITE_BOX.testSetDontInlineMethod(MyValue9.class.getConstructor(int.class, int.class, int.class, int.class), rand.nextBoolean());
583         WHITE_BOX.testSetDontInlineMethod(MyValue11.class.getConstructor(int.class), rand.nextBoolean());
584         WHITE_BOX.testSetDontInlineMethod(MyValue11.class.getConstructor(int.class, int.class, int.class, int.class, int.class), rand.nextBoolean());
585 
586         Integer deoptNum = Integer.getInteger("deoptNum");
587         if (deoptNum == null) {
588             deoptNum = rand.nextInt(deopt.length);
589         }
590         for (int x = 0; x <= 50_000; ++x) {
591             if (x == 50_000) {
592                 // Last iteration, trigger deoptimization
593                 deopt[deoptNum] = true;
594             }
595             Asserts.assertEQ(test1(x), x);
596             Asserts.assertEQ(test2(x), new MyValue1(x));
597             Asserts.assertEQ(test3(10), new MyValue1(10));
598             Asserts.assertEQ(test4(x), new MyValue1(x));
599             Asserts.assertEQ(test5(x), x);
600             Asserts.assertEQ(test6(x), new MyValue2(x));
601             Asserts.assertEQ(test7(10), new MyValue2(10));
602             Asserts.assertEQ(test8(x), new MyValue2(x));
603             Asserts.assertEQ(test9(x), x);
604             Asserts.assertEQ(test10(x), new MyValue3(x));
605             Asserts.assertEQ(test11(10), new MyValue3(10));
606             Asserts.assertEQ(test12(x), new MyValue3(x));
607             Asserts.assertEQ(test13(x), new MyValue4(x));
608             Asserts.assertEQ(test14(x, (x % 2) == 0), new MyValue5(x, (x % 2) == 0));
609             Asserts.assertEQ(test15(x), new MyValue6(x));
610             Asserts.assertEQ(test16(x), new MyValue7(x));
611             Asserts.assertEQ(test17(x), new MyValue8(x));
612             Asserts.assertEQ(test18(x), new MyValue8(x));
613             Asserts.assertEQ(test19(x), new MyValue8(x));
614             Asserts.assertEQ(test20(x), new MyValue9(x));
615             Asserts.assertEQ(test21(x), new MyValue9(x));
616             Asserts.assertEQ(test22(x), new MyValue9(x));
617             Asserts.assertEQ(test23(x), new MyValue9(x));
618             Asserts.assertEQ(test24(x, x % 10), new MyValue10(x, x % 10));
619             Asserts.assertEQ(test25(x), new MyValue11(x));
620         }
621     }
622 }