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 compiler.valhalla.inlinetypes;
 25 
 26 import compiler.lib.ir_framework.*;
 27 import jdk.test.lib.Asserts;
 28 import jdk.test.whitebox.WhiteBox;
 29 
 30 import java.lang.invoke.MethodHandle;
 31 import java.lang.invoke.MethodHandles;
 32 import java.lang.invoke.MethodType;
 33 import java.lang.reflect.Method;
 34 
 35 import jdk.internal.value.ValueClass;
 36 import jdk.internal.vm.annotation.ImplicitlyConstructible;
 37 import jdk.internal.vm.annotation.LooselyConsistentValue;
 38 import jdk.internal.vm.annotation.NullRestricted;
 39 
 40 import static compiler.valhalla.inlinetypes.InlineTypeIRNode.*;
 41 import static compiler.valhalla.inlinetypes.InlineTypes.*;
 42 
 43 /*
 44  * @test
 45  * @key randomness
 46  * @summary Test method handle support for inline types
 47  * @library /test/lib /
 48  * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64")
 49  * @enablePreview
 50  * @modules java.base/jdk.internal.value
 51  *          java.base/jdk.internal.vm.annotation
 52  * @run main/othervm/timeout=300 compiler.valhalla.inlinetypes.TestMethodHandles
 53  */
 54 
 55 @ForceCompileClassInitializer
 56 public class TestMethodHandles {
 57 
 58     static {
 59         try {
 60             Class<?> clazz = TestMethodHandles.class;
 61             MethodHandles.Lookup lookup = MethodHandles.lookup();
 62 
 63             MethodType mt = MethodType.methodType(MyValue3.class);
 64             test1_mh = lookup.findVirtual(clazz, "test1_target", mt);
 65             test2_mh = lookup.findVirtual(clazz, "test2_target", mt);
 66             test3_mh = lookup.findVirtual(clazz, "test3_target", mt);
 67 
 68             MethodType test4_mt1 = MethodType.methodType(int.class, MyValue1.class);
 69             MethodType test4_mt2 = MethodType.methodType(MyValue1.class);
 70             MethodHandle test4_mh1 = lookup.findStatic(clazz, "test4_helper1", test4_mt1);
 71             MethodHandle test4_mh2 = lookup.findStatic(clazz, "test4_helper2", test4_mt2);
 72             test4_mh = MethodHandles.filterReturnValue(test4_mh2, test4_mh1);
 73 
 74             MethodType test5_mt = MethodType.methodType(int.class, MyValue1.class);
 75             test5_mh = lookup.findVirtual(clazz, "test5_target", test5_mt);
 76 
 77             MethodType test6_mt = MethodType.methodType(MyValue3.class);
 78             MethodHandle test6_mh1 = lookup.findVirtual(clazz, "test6_target1", test6_mt);
 79             MethodHandle test6_mh2 = lookup.findVirtual(clazz, "test6_target2", test6_mt);
 80             MethodType boolean_mt = MethodType.methodType(boolean.class);
 81             MethodHandle test6_mh_test = lookup.findVirtual(clazz, "test6_test", boolean_mt);
 82             test6_mh = MethodHandles.guardWithTest(test6_mh_test, test6_mh1, test6_mh2);
 83 
 84             MethodType myvalue2_mt = MethodType.methodType(MyValue2.class);
 85             test7_mh1 = lookup.findStatic(clazz, "test7_target1", myvalue2_mt);
 86             MethodHandle test7_mh2 = lookup.findStatic(clazz, "test7_target2", myvalue2_mt);
 87             MethodHandle test7_mh_test = lookup.findStatic(clazz, "test7_test", boolean_mt);
 88             test7_mh = MethodHandles.guardWithTest(test7_mh_test,
 89                                                     MethodHandles.invoker(myvalue2_mt),
 90                                                     MethodHandles.dropArguments(test7_mh2, 0, MethodHandle.class));
 91 
 92             MethodHandle test8_mh1 = lookup.findStatic(clazz, "test8_target1", myvalue2_mt);
 93             test8_mh2 = lookup.findStatic(clazz, "test8_target2", myvalue2_mt);
 94             MethodHandle test8_mh_test = lookup.findStatic(clazz, "test8_test", boolean_mt);
 95             test8_mh = MethodHandles.guardWithTest(test8_mh_test,
 96                                                     MethodHandles.dropArguments(test8_mh1, 0, MethodHandle.class),
 97                                                     MethodHandles.invoker(myvalue2_mt));
 98 
 99             MethodType test9_mt = MethodType.methodType(MyValue3.class);
100             MethodHandle test9_mh1 = lookup.findVirtual(clazz, "test9_target1", test9_mt);
101             MethodHandle test9_mh2 = lookup.findVirtual(clazz, "test9_target2", test9_mt);
102             MethodHandle test9_mh3 = lookup.findVirtual(clazz, "test9_target3", test9_mt);
103             MethodType test9_mt2 = MethodType.methodType(boolean.class);
104             MethodHandle test9_mh_test1 = lookup.findVirtual(clazz, "test9_test1", test9_mt2);
105             MethodHandle test9_mh_test2 = lookup.findVirtual(clazz, "test9_test2", test9_mt2);
106             test9_mh = MethodHandles.guardWithTest(test9_mh_test1,
107                                                     test9_mh1,
108                                                     MethodHandles.guardWithTest(test9_mh_test2, test9_mh2, test9_mh3));
109 
110             MethodType test10_mt = MethodType.methodType(MyValue2.class);
111             MethodHandle test10_mh1 = lookup.findStatic(clazz, "test10_target1", test10_mt);
112             test10_mh2 = lookup.findStatic(clazz, "test10_target2", test10_mt);
113             test10_mh3 = lookup.findStatic(clazz, "test10_target3", test10_mt);
114             MethodType test10_mt2 = MethodType.methodType(boolean.class);
115             MethodType test10_mt3 = MethodType.methodType(MyValue2.class);
116             MethodHandle test10_mh_test1 = lookup.findStatic(clazz, "test10_test1", test10_mt2);
117             MethodHandle test10_mh_test2 = lookup.findStatic(clazz, "test10_test2", test10_mt2);
118             test10_mh = MethodHandles.guardWithTest(test10_mh_test1,
119                                                     MethodHandles.dropArguments(test10_mh1, 0, MethodHandle.class, MethodHandle.class),
120                                                     MethodHandles.guardWithTest(test10_mh_test2,
121                                                                                 MethodHandles.dropArguments(MethodHandles.invoker(test10_mt3), 1, MethodHandle.class),
122                                                                                 MethodHandles.dropArguments(MethodHandles.invoker(test10_mt3), 0, MethodHandle.class))
123                                                     );
124 
125             MethodHandle test11_mh1 = lookup.findStatic(clazz, "test11_target1", myvalue2_mt);
126             test11_mh2 = lookup.findStatic(clazz, "test11_target2", myvalue2_mt);
127             MethodHandle test11_mh_test = lookup.findStatic(clazz, "test11_test", boolean_mt);
128             test11_mh = MethodHandles.guardWithTest(test11_mh_test,
129                                                     MethodHandles.dropArguments(test11_mh1, 0, MethodHandle.class),
130                                                     MethodHandles.invoker(myvalue2_mt));
131         } catch (NoSuchMethodException | IllegalAccessException e) {
132             e.printStackTrace();
133             throw new RuntimeException("Method handle lookup failed");
134         }
135     }
136 
137     public static void main(String[] args) {
138         Scenario[] scenarios = InlineTypes.DEFAULT_SCENARIOS;
139 
140         // Prevent inlining through MethodHandle linkTo adapters to stress the calling convention
141         scenarios[2].addFlags("-DVerifyIR=false",
142                               "-XX:CompileCommand=dontinline,java.lang.invoke.DirectMethodHandle::internalMemberName");
143         scenarios[4].addFlags("-XX:CompileCommand=dontinline,java.lang.invoke.DirectMethodHandle::internalMemberName");
144 
145         InlineTypes.getFramework()
146                    .addScenarios(scenarios)
147                    .addFlags("-XX:CompileCommand=inline,java.lang.invoke.MethodHandleImpl::*")
148                    .addHelperClasses(MyValue1.class,
149                                      MyValue2.class,
150                                      MyValue2Inline.class,
151                                      MyValue3.class,
152                                      MyValue3Inline.class)
153                    .start();
154     }
155 
156     // Everything inlined
157     @NullRestricted
158     final MyValue3 test1_vt = MyValue3.create();
159 
160     @ForceInline
161     MyValue3 test1_target() {
162         return test1_vt;
163     }
164 
165     static final MethodHandle test1_mh;
166 
167     @Test
168     @IR(applyIf = {"InlineTypeReturnedAsFields", "true"},
169         failOn = {ALLOC, STORE, CALL})
170     @IR(applyIf = {"InlineTypeReturnedAsFields", "false"},
171         counts = {ALLOC, "= 1", STORE, "= 14"})
172     public MyValue3 test1() throws Throwable {
173         return (MyValue3)test1_mh.invokeExact(this);
174     }
175 
176     @Run(test = "test1")
177     @Warmup(10000)
178     public void test1_verifier() throws Throwable {
179         MyValue3 vt = test1();
180         test1_vt.verify(vt);
181     }
182 
183     // Leaf method not inlined but returned type is known
184     @NullRestricted
185     final MyValue3 test2_vt = MyValue3.create();
186 
187     @DontInline
188     MyValue3 test2_target() {
189         return test2_vt;
190     }
191 
192     static final MethodHandle test2_mh;
193 
194     @Test
195     public MyValue3 test2() throws Throwable {
196         return (MyValue3)test2_mh.invokeExact(this);
197     }
198 
199     @Run(test = "test2")
200     public void test2_verifier(RunInfo info) throws Throwable {
201         if (!info.isWarmUp()) {
202             Method helper_m = getClass().getDeclaredMethod("test2_target");
203             if (!TestFramework.isCompiled(helper_m)) {
204                 TestFramework.compile(helper_m, CompLevel.C2);
205             }
206         }
207         MyValue3 vt = test2();
208         test2_vt.verify(vt);
209     }
210 
211     // Leaf method not inlined and returned type not known
212     @NullRestricted
213     final MyValue3 test3_vt = MyValue3.create();
214 
215     @DontInline
216     MyValue3 test3_target() {
217         return test3_vt;
218     }
219 
220     static final MethodHandle test3_mh;
221 
222     @Test
223     public MyValue3 test3() throws Throwable {
224         return (MyValue3)test3_mh.invokeExact(this);
225     }
226 
227     @Run(test = "test3")
228     public void test3_verifier(RunInfo info) throws Throwable {
229         // hack so C2 doesn't know the target of the invoke call
230         Class c = Class.forName("java.lang.invoke.DirectMethodHandle");
231         Method m = c.getDeclaredMethod("internalMemberName", Object.class);
232         WhiteBox.getWhiteBox().testSetDontInlineMethod(m, info.isWarmUp());
233         MyValue3 vt = test3();
234         test3_vt.verify(vt);
235     }
236 
237     // When test75_helper1 is inlined in test75, the method handle
238     // linker that called it is passed a pointer to a copy of vt
239     // stored in memory. The method handle linker needs to load the
240     // fields from memory before it inlines test75_helper1.
241     static public int test4_helper1(MyValue1 vt) {
242         return vt.x;
243     }
244 
245     @NullRestricted
246     static MyValue1 test4_vt = MyValue1.createWithFieldsInline(rI, rL);
247 
248     static public MyValue1 test4_helper2() {
249         return test4_vt;
250     }
251 
252     static final MethodHandle test4_mh;
253 
254     @Test
255     public int test4() throws Throwable {
256         return (int)test4_mh.invokeExact();
257     }
258 
259     @Run(test = "test4")
260     public void test4_verifier() throws Throwable {
261         int i = test4();
262         Asserts.assertEQ(i, test4_vt.x);
263     }
264 
265     // Test method handle call with inline type argument
266     public int test5_target(MyValue1 vt) {
267         return vt.x;
268     }
269 
270     static final MethodHandle test5_mh;
271 
272     @NullRestricted
273     MyValue1 test5_vt = MyValue1.createWithFieldsInline(rI, rL);
274 
275     @Test
276     public int test5() throws Throwable {
277         return (int)test5_mh.invokeExact(this, test5_vt);
278     }
279 
280     @Run(test = "test5")
281     public void test5_verifier() throws Throwable {
282         int i = test5();
283         Asserts.assertEQ(i, test5_vt.x);
284     }
285 
286     // Return of target1 and target2 merged in a Lambda Form as an
287     // Object. Shouldn't cause any allocation
288     @NullRestricted
289     final MyValue3 test6_vt1 = MyValue3.create();
290 
291     @ForceInline
292     MyValue3 test6_target1() {
293         return test6_vt1;
294     }
295 
296     @NullRestricted
297     final MyValue3 test6_vt2 = MyValue3.create();
298 
299     @ForceInline
300     MyValue3 test6_target2() {
301         return test6_vt2;
302     }
303 
304     boolean test6_bool = true;
305     @ForceInline
306     boolean test6_test() {
307         return test6_bool;
308     }
309 
310     static final MethodHandle test6_mh;
311 
312     @Test
313     @IR(applyIf = {"InlineTypeReturnedAsFields", "true"},
314         failOn = {ALLOC, ALLOCA, STORE, STORE_INLINE_FIELDS})
315     public MyValue3 test6() throws Throwable {
316         return (MyValue3)test6_mh.invokeExact(this);
317     }
318 
319     @Run(test = "test6")
320     public void test6_verifier() throws Throwable {
321         test6_bool = !test6_bool;
322         MyValue3 vt = test6();
323         vt.verify(test6_bool ? test6_vt1 : test6_vt2);
324     }
325 
326     // Similar as above but with the method handle for target1 not
327     // constant. Shouldn't cause any allocation.
328     @ForceInline
329     static MyValue2 test7_target1() {
330         return MyValue2.createWithFieldsInline(rI, rD);
331     }
332 
333     @ForceInline
334     static MyValue2 test7_target2() {
335         return MyValue2.createWithFieldsInline(rI+1, rD+1);
336     }
337 
338     static boolean test7_bool = true;
339     @ForceInline
340     static boolean test7_test() {
341         return test7_bool;
342     }
343 
344     static final MethodHandle test7_mh;
345     static MethodHandle test7_mh1;
346 
347     @Test
348     public long test7() throws Throwable {
349         return ((MyValue2)test7_mh.invokeExact(test7_mh1)).hash();
350     }
351 
352     @Run(test = "test7")
353     public void test7_verifier() throws Throwable {
354         test7_bool = !test7_bool;
355         long hash = test7();
356         Asserts.assertEQ(hash, MyValue2.createWithFieldsInline(rI+(test7_bool ? 0 : 1), rD+(test7_bool ? 0 : 1)).hash());
357     }
358 
359     // Same as above but with the method handle for target2 not
360     // constant. Shouldn't cause any allocation.
361     @ForceInline
362     static MyValue2 test8_target1() {
363         return MyValue2.createWithFieldsInline(rI, rD);
364     }
365 
366     @ForceInline
367     static MyValue2 test8_target2() {
368         return MyValue2.createWithFieldsInline(rI+1, rD+1);
369     }
370 
371     static boolean test8_bool = true;
372     @ForceInline
373     static boolean test8_test() {
374         return test8_bool;
375     }
376 
377     static final MethodHandle test8_mh;
378     static MethodHandle test8_mh2;
379 
380     @Test
381     public long test8() throws Throwable {
382         return ((MyValue2)test8_mh.invokeExact(test8_mh2)).hash();
383     }
384 
385     @Run(test = "test8")
386     public void test8_verifier() throws Throwable {
387         test8_bool = !test8_bool;
388         long hash = test8();
389         Asserts.assertEQ(hash, MyValue2.createWithFieldsInline(rI+(test8_bool ? 0 : 1), rD+(test8_bool ? 0 : 1)).hash());
390     }
391 
392     // Return of target1, target2 and target3 merged in Lambda Forms
393     // as an Object. Shouldn't cause any allocation
394     @NullRestricted
395     final MyValue3 test9_vt1 = MyValue3.create();
396 
397     @ForceInline
398     MyValue3 test9_target1() {
399         return test9_vt1;
400     }
401 
402     @NullRestricted
403     final MyValue3 test9_vt2 = MyValue3.create();
404 
405     @ForceInline
406     MyValue3 test9_target2() {
407         return test9_vt2;
408     }
409 
410     @NullRestricted
411     final MyValue3 test9_vt3 = MyValue3.create();
412 
413     @ForceInline
414     MyValue3 test9_target3() {
415         return test9_vt3;
416     }
417 
418     boolean test9_bool1 = true;
419     @ForceInline
420     boolean test9_test1() {
421         return test9_bool1;
422     }
423 
424     boolean test9_bool2 = true;
425     @ForceInline
426     boolean test9_test2() {
427         return test9_bool2;
428     }
429 
430     static final MethodHandle test9_mh;
431 
432     @Test
433     @IR(applyIf = {"InlineTypeReturnedAsFields", "true"},
434         failOn = {ALLOC, ALLOCA, STORE, STORE_INLINE_FIELDS})
435    public MyValue3 test9() throws Throwable {
436         return (MyValue3)test9_mh.invokeExact(this);
437     }
438 
439     static int test9_i = 0;
440     @Run(test = "test9")
441     public void test9_verifier() throws Throwable {
442         test9_i++;
443         test9_bool1 = (test9_i % 2) == 0;
444         test9_bool2 = (test9_i % 3) == 0;
445         MyValue3 vt = test9();
446         vt.verify(test9_bool1 ? test9_vt1 : (test9_bool2 ? test9_vt2 : test9_vt3));
447     }
448 
449     // Same as above but with non constant target2 and target3
450     @ForceInline
451     static MyValue2 test10_target1() {
452         return MyValue2.createWithFieldsInline(rI, rD);
453     }
454 
455     @ForceInline
456     static MyValue2 test10_target2() {
457         return MyValue2.createWithFieldsInline(rI+1, rD+1);
458     }
459 
460     @ForceInline
461     static MyValue2 test10_target3() {
462         return MyValue2.createWithFieldsInline(rI+2, rD+2);
463     }
464 
465     static boolean test10_bool1 = true;
466     @ForceInline
467     static boolean test10_test1() {
468         return test10_bool1;
469     }
470 
471     static boolean test10_bool2 = true;
472     @ForceInline
473     static boolean test10_test2() {
474         return test10_bool2;
475     }
476 
477     static final MethodHandle test10_mh;
478     static MethodHandle test10_mh2;
479     static MethodHandle test10_mh3;
480 
481     @Test
482     public long test10() throws Throwable {
483         return ((MyValue2)test10_mh.invokeExact(test10_mh2, test10_mh3)).hash();
484     }
485 
486     static int test10_i = 0;
487 
488     @Run(test = "test10")
489     public void test10_verifier() throws Throwable {
490         test10_i++;
491         test10_bool1 = (test10_i % 2) == 0;
492         test10_bool2 = (test10_i % 3) == 0;
493         long hash = test10();
494         int i = rI + (test10_bool1 ? 0 : (test10_bool2 ? 1 : 2));
495         double d = rD + (test10_bool1 ? 0 : (test10_bool2 ? 1 : 2));
496         Asserts.assertEQ(hash, MyValue2.createWithFieldsInline(i, d).hash());
497     }
498 
499     static int test11_i = 0;
500 
501     @ForceInline
502     static MyValue2 test11_target1() {
503         return MyValue2.createWithFieldsInline(rI+test11_i, rD+test11_i);
504     }
505 
506     @ForceInline
507     static MyValue2 test11_target2() {
508         return MyValue2.createWithFieldsInline(rI-test11_i, rD-test11_i);
509     }
510 
511     @ForceInline
512     static boolean test11_test() {
513         return (test11_i % 100) == 0;
514     }
515 
516     static final MethodHandle test11_mh;
517     static MethodHandle test11_mh2;
518 
519     // Check that a buffered inline type returned by a compiled lambda form
520     // is properly handled by the caller.
521     @Test
522     public long test11() throws Throwable {
523         return ((MyValue2)test11_mh.invokeExact(test11_mh2)).hash();
524     }
525 
526     @Run(test = "test11")
527     @Warmup(11000)
528     public void test11_verifier() throws Throwable {
529         test11_i++;
530         long hash = test11();
531         boolean b = (test11_i % 100) == 0;
532         Asserts.assertEQ(hash, MyValue2.createWithFieldsInline(rI+test11_i * (b ? 1 : -1), rD+test11_i * (b ? 1 : -1)).hash());
533     }
534 }