1 /*
   2  * Copyright (c) 2017, 2019, 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.valuetypes;
  25 
  26 import jdk.test.lib.Asserts;
  27 
  28 import java.lang.reflect.Method;
  29 
  30 /*
  31  * @test
  32  * @summary Test value type calling convention optimizations
  33  * @library /testlibrary /test/lib /compiler/whitebox /
  34  * @compile -XDallowWithFieldOperator TestCallingConvention.java
  35  * @run driver ClassFileInstaller sun.hotspot.WhiteBox jdk.test.lib.Platform
  36  * @run main/othervm/timeout=120 -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions
  37  *                               -XX:+UnlockExperimentalVMOptions -XX:+WhiteBoxAPI -XX:+EnableValhalla
  38  *                               compiler.valhalla.valuetypes.ValueTypeTest
  39  *                               compiler.valhalla.valuetypes.TestCallingConvention
  40  */
  41 public class TestCallingConvention extends ValueTypeTest {
  42     // Extra VM parameters for some test scenarios. See ValueTypeTest.getVMParameters()
  43     @Override
  44     public String[] getExtraVMParameters(int scenario) {
  45         switch (scenario) {
  46         case 3: return new String[] {"-XX:ValueArrayElemMaxFlatSize=0"};
  47         }
  48         return null;
  49     }
  50 
  51     public static void main(String[] args) throws Throwable {
  52         TestCallingConvention test = new TestCallingConvention();
  53         test.run(args, MyValue1.class, MyValue2.class, MyValue2Inline.class, MyValue4.class, Test27Value1.class, Test27Value2.class, Test27Value3.class);
  54     }
  55 
  56     // Test interpreter to compiled code with various signatures
  57     @Test(failOn = ALLOC + STORE + TRAP)
  58     public long test1(MyValue2 v) {
  59         return v.hash();
  60     }
  61 
  62     @DontCompile
  63     public void test1_verifier(boolean warmup) {
  64         MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
  65         long result = test1(v);
  66         Asserts.assertEQ(result, v.hashInterpreted());
  67     }
  68 
  69     @Test(failOn = ALLOC + STORE + TRAP)
  70     public long test2(int i1, MyValue2 v, int i2) {
  71         return v.hash() + i1 - i2;
  72     }
  73 
  74     @DontCompile
  75     public void test2_verifier(boolean warmup) {
  76         MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
  77         long result = test2(rI, v, 2*rI);
  78         Asserts.assertEQ(result, v.hashInterpreted() - rI);
  79     }
  80 
  81     @Test(failOn = ALLOC + STORE + TRAP)
  82     public long test3(long l1, MyValue2 v, long l2) {
  83         return v.hash() + l1 - l2;
  84     }
  85 
  86     @DontCompile
  87     public void test3_verifier(boolean warmup) {
  88         MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
  89         long result = test3(rL, v, 2*rL);
  90         Asserts.assertEQ(result, v.hashInterpreted() - rL);
  91     }
  92 
  93     @Test(failOn = ALLOC + STORE + TRAP)
  94     public long test4(int i, MyValue2 v, long l) {
  95         return v.hash() + i + l;
  96     }
  97 
  98     @DontCompile
  99     public void test4_verifier(boolean warmup) {
 100         MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
 101         long result = test4(rI, v, rL);
 102         Asserts.assertEQ(result, v.hashInterpreted() + rL + rI);
 103     }
 104 
 105     @Test(failOn = ALLOC + STORE + TRAP)
 106     public long test5(long l, MyValue2 v, int i) {
 107         return v.hash() + i + l;
 108     }
 109 
 110     @DontCompile
 111     public void test5_verifier(boolean warmup) {
 112         MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
 113         long result = test5(rL, v, rI);
 114         Asserts.assertEQ(result, v.hashInterpreted() + rL + rI);
 115     }
 116 
 117     @Test(failOn = ALLOC + STORE + TRAP)
 118     public long test6(long l, MyValue1 v1, int i, MyValue2 v2) {
 119         return v1.hash() + i + l + v2.hash();
 120     }
 121 
 122     @DontCompile
 123     public void test6_verifier(boolean warmup) {
 124         MyValue1 v1 = MyValue1.createWithFieldsDontInline(rI, rL);
 125         MyValue2 v2 = MyValue2.createWithFieldsInline(rI, true);
 126         long result = test6(rL, v1, rI, v2);
 127         Asserts.assertEQ(result, v1.hashInterpreted() + rL + rI + v2.hashInterpreted());
 128     }
 129 
 130     // Test compiled code to interpreter with various signatures
 131     @DontCompile
 132     public long test7_interp(MyValue2 v) {
 133         return v.hash();
 134     }
 135 
 136     @Test(failOn = ALLOC + STORE + TRAP)
 137     public long test7(MyValue2 v) {
 138         return test7_interp(v);
 139     }
 140 
 141     @DontCompile
 142     public void test7_verifier(boolean warmup) {
 143         MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
 144         long result = test7(v);
 145         Asserts.assertEQ(result, v.hashInterpreted());
 146     }
 147 
 148     @DontCompile
 149     public long test8_interp(int i1, MyValue2 v, int i2) {
 150         return v.hash() + i1 - i2;
 151     }
 152 
 153     @Test(failOn = ALLOC + STORE + TRAP)
 154     public long test8(int i1, MyValue2 v, int i2) {
 155         return test8_interp(i1, v, i2);
 156     }
 157 
 158     @DontCompile
 159     public void test8_verifier(boolean warmup) {
 160         MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
 161         long result = test8(rI, v, 2*rI);
 162         Asserts.assertEQ(result, v.hashInterpreted() - rI);
 163     }
 164 
 165     @DontCompile
 166     public long test9_interp(long l1, MyValue2 v, long l2) {
 167         return v.hash() + l1 - l2;
 168     }
 169 
 170     @Test(failOn = ALLOC + STORE + TRAP)
 171     public long test9(long l1, MyValue2 v, long l2) {
 172         return test9_interp(l1, v, l2);
 173     }
 174 
 175     @DontCompile
 176     public void test9_verifier(boolean warmup) {
 177         MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
 178         long result = test9(rL, v, 2*rL);
 179         Asserts.assertEQ(result, v.hashInterpreted() - rL);
 180     }
 181 
 182     @DontCompile
 183     public long test10_interp(int i, MyValue2 v, long l) {
 184         return v.hash() + i + l;
 185     }
 186 
 187     @Test(failOn = ALLOC + STORE + TRAP)
 188     public long test10(int i, MyValue2 v, long l) {
 189         return test10_interp(i, v, l);
 190     }
 191 
 192     @DontCompile
 193     public void test10_verifier(boolean warmup) {
 194         MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
 195         long result = test10(rI, v, rL);
 196         Asserts.assertEQ(result, v.hashInterpreted() + rL + rI);
 197     }
 198 
 199     @DontCompile
 200     public long test11_interp(long l, MyValue2 v, int i) {
 201         return v.hash() + i + l;
 202     }
 203 
 204     @Test(failOn = ALLOC + STORE + TRAP)
 205     public long test11(long l, MyValue2 v, int i) {
 206         return test11_interp(l, v, i);
 207     }
 208 
 209     @DontCompile
 210     public void test11_verifier(boolean warmup) {
 211         MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
 212         long result = test11(rL, v, rI);
 213         Asserts.assertEQ(result, v.hashInterpreted() + rL + rI);
 214     }
 215 
 216     @DontCompile
 217     public long test12_interp(long l, MyValue1 v1, int i, MyValue2 v2) {
 218         return v1.hash() + i + l + v2.hash();
 219     }
 220 
 221     @Test(failOn = ALLOC + STORE + TRAP)
 222     public long test12(long l, MyValue1 v1, int i, MyValue2 v2) {
 223         return test12_interp(l, v1, i, v2);
 224     }
 225 
 226     @DontCompile
 227     public void test12_verifier(boolean warmup) {
 228         MyValue1 v1 = MyValue1.createWithFieldsDontInline(rI, rL);
 229         MyValue2 v2 = MyValue2.createWithFieldsInline(rI, true);
 230         long result = test12(rL, v1, rI, v2);
 231         Asserts.assertEQ(result, v1.hashInterpreted() + rL + rI + v2.hashInterpreted());
 232     }
 233 
 234     // Test that debug info at a call is correct
 235     @DontCompile
 236     public long test13_interp(MyValue2 v, MyValue1[] va, boolean deopt) {
 237         if (deopt) {
 238             // uncommon trap
 239             WHITE_BOX.deoptimizeMethod(tests.get(getClass().getSimpleName() + "::test13"));
 240         }
 241         return v.hash() + va[0].hash() + va[1].hash();
 242     }
 243 
 244     @Test(failOn = ALLOC + STORE + TRAP)
 245     public long test13(MyValue2 v, MyValue1[] va, boolean flag, long l) {
 246         return test13_interp(v, va, flag) + l;
 247     }
 248 
 249     @DontCompile
 250     public void test13_verifier(boolean warmup) {
 251         MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
 252         MyValue1[] va = new MyValue1[2];
 253         va[0] = MyValue1.createWithFieldsDontInline(rI, rL);
 254         va[1] = MyValue1.createWithFieldsDontInline(rI, rL);
 255         long result = test13(v, va, !warmup, rL);
 256         Asserts.assertEQ(result, v.hashInterpreted() + va[0].hash() + va[1].hash() + rL);
 257     }
 258 
 259     // Test deoptimization at call return with return value in registers
 260     @DontCompile
 261     public MyValue2 test14_interp(boolean deopt) {
 262         if (deopt) {
 263             // uncommon trap
 264             WHITE_BOX.deoptimizeMethod(tests.get(getClass().getSimpleName() + "::test14"));
 265         }
 266         return MyValue2.createWithFieldsInline(rI, true);
 267     }
 268 
 269     @Test()
 270     public MyValue2 test14(boolean flag) {
 271         return test14_interp(flag);
 272     }
 273 
 274     @DontCompile
 275     public void test14_verifier(boolean warmup) {
 276         MyValue2 result = test14(!warmup);
 277         MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
 278         Asserts.assertEQ(result.hash(), v.hash());
 279     }
 280 
 281     // Return value types in registers from interpreter -> compiled
 282     final MyValue3 test15_vt = MyValue3.create();
 283     @DontCompile
 284     public MyValue3 test15_interp() {
 285         return test15_vt;
 286     }
 287 
 288     MyValue3 test15_vt2;
 289     @Test(valid = ValueTypeReturnedAsFieldsOn, failOn = ALLOC + LOAD + TRAP)
 290     @Test(valid = ValueTypeReturnedAsFieldsOff)
 291     public void test15() {
 292         test15_vt2 = test15_interp();
 293     }
 294 
 295     @DontCompile
 296     public void test15_verifier(boolean warmup) {
 297         test15();
 298         test15_vt.verify(test15_vt2);
 299     }
 300 
 301     // Return value types in registers from compiled -> interpreter
 302     final MyValue3 test16_vt = MyValue3.create();
 303     @Test(valid = ValueTypeReturnedAsFieldsOn, failOn = ALLOC + STORE + TRAP)
 304     @Test(valid = ValueTypeReturnedAsFieldsOff)
 305     public MyValue3 test16() {
 306         return test16_vt;
 307     }
 308 
 309     @DontCompile
 310     public void test16_verifier(boolean warmup) {
 311         MyValue3 vt = test16();
 312         test16_vt.verify(vt);
 313     }
 314 
 315     // Return value types in registers from compiled -> compiled
 316     final MyValue3 test17_vt = MyValue3.create();
 317     @DontInline
 318     public MyValue3 test17_comp() {
 319         return test17_vt;
 320     }
 321 
 322     MyValue3 test17_vt2;
 323     @Test(valid = ValueTypeReturnedAsFieldsOn, failOn = ALLOC + LOAD + TRAP)
 324     @Test(valid = ValueTypeReturnedAsFieldsOff)
 325     public void test17() {
 326         test17_vt2 = test17_comp();
 327     }
 328 
 329     @DontCompile
 330     public void test17_verifier(boolean warmup) throws Exception {
 331         Method helper_m = getClass().getDeclaredMethod("test17_comp");
 332         if (!warmup && USE_COMPILER && !WHITE_BOX.isMethodCompiled(helper_m, false)) {
 333             WHITE_BOX.enqueueMethodForCompilation(helper_m, COMP_LEVEL_FULL_OPTIMIZATION);
 334             Asserts.assertTrue(WHITE_BOX.isMethodCompiled(helper_m, false), "test17_comp not compiled");
 335         }
 336         test17();
 337         test17_vt.verify(test17_vt2);
 338     }
 339 
 340     // Same tests as above but with a value type that cannot be returned in registers
 341 
 342     // Return value types in registers from interpreter -> compiled
 343     final MyValue4 test18_vt = MyValue4.create();
 344     @DontCompile
 345     public MyValue4 test18_interp() {
 346         return test18_vt;
 347     }
 348 
 349     MyValue4 test18_vt2;
 350     @Test
 351     public void test18() {
 352         test18_vt2 = test18_interp();
 353     }
 354 
 355     @DontCompile
 356     public void test18_verifier(boolean warmup) {
 357         test18();
 358         test18_vt.verify(test18_vt2);
 359     }
 360 
 361     // Return value types in registers from compiled -> interpreter
 362     final MyValue4 test19_vt = MyValue4.create();
 363     @Test
 364     public MyValue4 test19() {
 365         return test19_vt;
 366     }
 367 
 368     @DontCompile
 369     public void test19_verifier(boolean warmup) {
 370         MyValue4 vt = test19();
 371         test19_vt.verify(vt);
 372     }
 373 
 374     // Return value types in registers from compiled -> compiled
 375     final MyValue4 test20_vt = MyValue4.create();
 376     @DontInline
 377     public MyValue4 test20_comp() {
 378         return test20_vt;
 379     }
 380 
 381     MyValue4 test20_vt2;
 382     @Test
 383     public void test20() {
 384         test20_vt2 = test20_comp();
 385     }
 386 
 387     @DontCompile
 388     public void test20_verifier(boolean warmup) throws Exception {
 389         Method helper_m = getClass().getDeclaredMethod("test20_comp");
 390         if (!warmup && USE_COMPILER && !WHITE_BOX.isMethodCompiled(helper_m, false)) {
 391             WHITE_BOX.enqueueMethodForCompilation(helper_m, COMP_LEVEL_FULL_OPTIMIZATION);
 392             Asserts.assertTrue(WHITE_BOX.isMethodCompiled(helper_m, false), "test20_comp not compiled");
 393         }
 394         test20();
 395         test20_vt.verify(test20_vt2);
 396     }
 397 
 398     // Test no result from inlined method for incremental inlining
 399     final MyValue3 test21_vt = MyValue3.create();
 400     public MyValue3 test21_inlined() {
 401         throw new RuntimeException();
 402     }
 403 
 404     @Test
 405     public MyValue3 test21() {
 406         try {
 407             return test21_inlined();
 408         } catch (RuntimeException ex) {
 409             return test21_vt;
 410         }
 411     }
 412 
 413     @DontCompile
 414     public void test21_verifier(boolean warmup) {
 415         MyValue3 vt = test21();
 416         test21_vt.verify(vt);
 417     }
 418 
 419     // Test returning a non-flattened value type as fields
 420     MyValue3.box test22_vt = MyValue3.create();
 421 
 422     @Test
 423     public MyValue3 test22() {
 424         return test22_vt;
 425     }
 426 
 427     @DontCompile
 428     public void test22_verifier(boolean warmup) {
 429         MyValue3 vt = test22();
 430         test22_vt.verify(vt);
 431     }
 432 
 433     // Test calling a method that has circular register/stack dependencies when unpacking value type arguments
 434     value class TestValue23 {
 435         final double f1;
 436         TestValue23(double val) {
 437             f1 = val;
 438         }
 439     }
 440 
 441     static double test23Callee(int i1, int i2, int i3, int i4, int i5, int i6,
 442                                TestValue23 v1, TestValue23 v2, TestValue23 v3, TestValue23 v4, TestValue23 v5, TestValue23 v6, TestValue23 v7, TestValue23 v8,
 443                                double d1, double d2, double d3, double d4, double d5, double d6, double d7, double d8) {
 444         return i1 + i2 + i3 + i4 + i5 + i6 + v1.f1 + v2.f1 + v3.f1 + v4.f1 + v5.f1 + v6.f1 + v7.f1 + v8.f1 + d1 + d2 + d3 + d4 + d5 + d6 + d7 + d8;
 445     }
 446 
 447     @Test
 448     public double test23(int i1, int i2, int i3, int i4, int i5, int i6,
 449                          TestValue23 v1, TestValue23 v2, TestValue23 v3, TestValue23 v4, TestValue23 v5, TestValue23 v6, TestValue23 v7, TestValue23 v8,
 450                          double d1, double d2, double d3, double d4, double d5, double d6, double d7, double d8) {
 451         return test23Callee(i1, i2, i3, i4, i5, i6,
 452                             v1, v2, v3, v4, v5, v6, v7, v8,
 453                             d1, d2, d3, d4, d5, d6, d7, d8);
 454     }
 455 
 456     @DontCompile
 457     public void test23_verifier(boolean warmup) {
 458         TestValue23 vt = new TestValue23(rI);
 459         double res1 = test23(rI, rI, rI, rI, rI, rI,
 460                             vt, vt, vt, vt, vt, vt, vt, vt,
 461                             rI, rI, rI, rI, rI, rI, rI, rI);
 462         double res2 = test23Callee(rI, rI, rI, rI, rI, rI,
 463                                    vt, vt, vt, vt, vt, vt, vt, vt,
 464                                    rI, rI, rI, rI, rI, rI, rI, rI);
 465         double res3 = 6*rI + 8*rI + 8*rI;
 466         Asserts.assertEQ(res1, res2);
 467         Asserts.assertEQ(res2, res3);
 468     }
 469 
 470     // Should not return a nullable value type as fields
 471     @Test
 472     public MyValue2.box test24() {
 473         return null;
 474     }
 475 
 476     @DontCompile
 477     public void test24_verifier(boolean warmup) {
 478         MyValue2.box vt = test24();
 479         Asserts.assertEQ(vt, null);
 480     }
 481 
 482     // Same as test24 but with control flow and inlining
 483     @ForceInline
 484     public MyValue2.box test26_callee(boolean b) {
 485         if (b) {
 486             return null;
 487         } else {
 488             return MyValue2.createWithFieldsInline(rI, true);
 489         }
 490     }
 491 
 492     @Test
 493     public MyValue2.box test26(boolean b) {
 494         return test26_callee(b);
 495     }
 496 
 497     @DontCompile
 498     public void test26_verifier(boolean warmup) {
 499         MyValue2.box vt = test26(true);
 500         Asserts.assertEQ(vt, null);
 501         vt = test26(false);
 502         Asserts.assertEQ(vt.hash(), MyValue2.createWithFieldsInline(rI, true).hash());
 503     }
 504 
 505     // Test calling convention with deep hierarchy of flattened fields
 506     value final class Test27Value1 {
 507         final Test27Value2.val valueField;
 508 
 509         private Test27Value1(Test27Value2 val2) {
 510             valueField = val2;
 511         }
 512 
 513         @DontInline
 514         public int test(Test27Value1 val1) {
 515             return valueField.test(valueField) + val1.valueField.test(valueField);
 516         }
 517     }
 518 
 519     value final class Test27Value2 {
 520         final Test27Value3.val valueField;
 521 
 522         private Test27Value2(Test27Value3 val3) {
 523             valueField = val3;
 524         }
 525 
 526         @DontInline
 527         public int test(Test27Value2 val2) {
 528             return valueField.test(valueField) + val2.valueField.test(valueField);
 529         }
 530     }
 531 
 532     value final class Test27Value3 {
 533         final int x;
 534 
 535         private Test27Value3(int x) {
 536             this.x = x;
 537         }
 538 
 539         @DontInline
 540         public int test(Test27Value3 val3) {
 541             return x + val3.x;
 542         }
 543     }
 544 
 545     @Test
 546     public int test27(Test27Value1 val) {
 547         return val.test(val);
 548     }
 549 
 550     @DontCompile
 551     public void test27_verifier(boolean warmup) {
 552         Test27Value3 val3 = new Test27Value3(rI);
 553         Test27Value2 val2 = new Test27Value2(val3);
 554         Test27Value1 val1 = new Test27Value1(val2);
 555         int result = test27(val1);
 556         Asserts.assertEQ(result, 8*rI);
 557     }
 558 
 559     static final MyValue1.box test28Val = MyValue1.createWithFieldsDontInline(rI, rL);
 560 
 561     @Test
 562     @Warmup(0)
 563     public String test28() {
 564         return test28Val.toString();
 565     }
 566 
 567     @DontCompile
 568     public void test28_verifier(boolean warmup) {
 569         String result = test28();
 570     }
 571 }