1 /*
   2  * Copyright (c) 2017, 2018, 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 -XDemitQtypes -XDenableValueTypes -XDallowWithFieldOperator -XDallowFlattenabilityModifiers TestCallingConvention.java
  36  * @run driver ClassFileInstaller sun.hotspot.WhiteBox jdk.test.lib.Platform
  37  * @run main/othervm/timeout=120 -Xbootclasspath/a:. -ea -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:-ValueArrayFlatten"};
  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);
  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.box test22_vt = MyValue3.create();
 422     
 423     @Test
 424     public MyValue3 test22() {
 425         return 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     value 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 }