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