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 -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 1: return new String[] {"-XX:ValueTypesBufferMaxMemory=0"};
  48         case 3: return new String[] {"-XX:-ValueArrayFlatten"};
  49         }
  50         return null;
  51     }
  52 
  53    public static void main(String[] args) throws Throwable {
  54        TestCallingConvention test = new TestCallingConvention();
  55        test.run(args, MyValue1.class, MyValue2.class, MyValue2Inline.class, MyValue4.class);
  56    }
  57 
  58    // Test interpreter to compiled code with various signatures
  59    @Test(failOn = ALLOC + STORE + TRAP)
  60    public long test1(MyValue2 v) {
  61        return v.hash();
  62    }
  63 
  64    @DontCompile
  65    public void test1_verifier(boolean warmup) {
  66        MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
  67        long result = test1(v);
  68        Asserts.assertEQ(result, v.hashInterpreted());
  69    }
  70 
  71    @Test(failOn = ALLOC + STORE + TRAP)
  72    public long test2(int i1, MyValue2 v, int i2) {
  73        return v.hash() + i1 - i2;
  74    }
  75 
  76    @DontCompile
  77    public void test2_verifier(boolean warmup) {
  78        MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
  79        long result = test2(rI, v, 2*rI);
  80        Asserts.assertEQ(result, v.hashInterpreted() - rI);
  81    }
  82 
  83    @Test(failOn = ALLOC + STORE + TRAP)
  84    public long test3(long l1, MyValue2 v, long l2) {
  85        return v.hash() + l1 - l2;
  86    }
  87 
  88    @DontCompile
  89    public void test3_verifier(boolean warmup) {
  90        MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
  91        long result = test3(rL, v, 2*rL);
  92        Asserts.assertEQ(result, v.hashInterpreted() - rL);
  93    }
  94 
  95    @Test(failOn = ALLOC + STORE + TRAP)
  96    public long test4(int i, MyValue2 v, long l) {
  97        return v.hash() + i + l;
  98    }
  99 
 100    @DontCompile
 101    public void test4_verifier(boolean warmup) {
 102        MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
 103        long result = test4(rI, v, rL);
 104        Asserts.assertEQ(result, v.hashInterpreted() + rL + rI);
 105    }
 106 
 107    @Test(failOn = ALLOC + STORE + TRAP)
 108    public long test5(long l, MyValue2 v, int i) {
 109        return v.hash() + i + l;
 110    }
 111 
 112    @DontCompile
 113    public void test5_verifier(boolean warmup) {
 114        MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
 115        long result = test5(rL, v, rI);
 116        Asserts.assertEQ(result, v.hashInterpreted() + rL + rI);
 117    }
 118 
 119    @Test(failOn = ALLOC + STORE + TRAP)
 120    public long test6(long l, MyValue1 v1, int i, MyValue2 v2) {
 121        return v1.hash() + i + l + v2.hash();
 122    }
 123 
 124    @DontCompile
 125    public void test6_verifier(boolean warmup) {
 126        MyValue1 v1 = MyValue1.createWithFieldsDontInline(rI, rL);
 127        MyValue2 v2 = MyValue2.createWithFieldsInline(rI, true);
 128        long result = test6(rL, v1, rI, v2);
 129        Asserts.assertEQ(result, v1.hashInterpreted() + rL + rI + v2.hashInterpreted());
 130    }
 131 
 132    // Test compiled code to interpreter with various signatures
 133    @DontCompile
 134    public long test7_interp(MyValue2 v) {
 135        return v.hash();
 136    }
 137 
 138    @Test(failOn = ALLOC + STORE + TRAP)
 139    public long test7(MyValue2 v) {
 140        return test7_interp(v);
 141    }
 142 
 143    @DontCompile
 144    public void test7_verifier(boolean warmup) {
 145        MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
 146        long result = test7(v);
 147        Asserts.assertEQ(result, v.hashInterpreted());
 148    }
 149 
 150    @DontCompile
 151    public long test8_interp(int i1, MyValue2 v, int i2) {
 152        return v.hash() + i1 - i2;
 153    }
 154 
 155    @Test(failOn = ALLOC + STORE + TRAP)
 156    public long test8(int i1, MyValue2 v, int i2) {
 157        return test8_interp(i1, v, i2);
 158    }
 159 
 160    @DontCompile
 161    public void test8_verifier(boolean warmup) {
 162        MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
 163        long result = test8(rI, v, 2*rI);
 164        Asserts.assertEQ(result, v.hashInterpreted() - rI);
 165    }
 166 
 167    @DontCompile
 168    public long test9_interp(long l1, MyValue2 v, long l2) {
 169        return v.hash() + l1 - l2;
 170    }
 171 
 172    @Test(failOn = ALLOC + STORE + TRAP)
 173    public long test9(long l1, MyValue2 v, long l2) {
 174        return test9_interp(l1, v, l2);
 175    }
 176 
 177    @DontCompile
 178    public void test9_verifier(boolean warmup) {
 179        MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
 180        long result = test9(rL, v, 2*rL);
 181        Asserts.assertEQ(result, v.hashInterpreted() - rL);
 182    }
 183 
 184    @DontCompile
 185    public long test10_interp(int i, MyValue2 v, long l) {
 186        return v.hash() + i + l;
 187    }
 188 
 189    @Test(failOn = ALLOC + STORE + TRAP)
 190    public long test10(int i, MyValue2 v, long l) {
 191        return test10_interp(i, v, l);
 192    }
 193 
 194    @DontCompile
 195    public void test10_verifier(boolean warmup) {
 196        MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
 197        long result = test10(rI, v, rL);
 198        Asserts.assertEQ(result, v.hashInterpreted() + rL + rI);
 199    }
 200 
 201    @DontCompile
 202    public long test11_interp(long l, MyValue2 v, int i) {
 203        return v.hash() + i + l;
 204    }
 205 
 206    @Test(failOn = ALLOC + STORE + TRAP)
 207    public long test11(long l, MyValue2 v, int i) {
 208        return test11_interp(l, v, i);
 209    }
 210 
 211    @DontCompile
 212    public void test11_verifier(boolean warmup) {
 213        MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
 214        long result = test11(rL, v, rI);
 215        Asserts.assertEQ(result, v.hashInterpreted() + rL + rI);
 216    }
 217 
 218    @DontCompile
 219    public long test12_interp(long l, MyValue1 v1, int i, MyValue2 v2) {
 220        return v1.hash() + i + l + v2.hash();
 221    }
 222 
 223    @Test(failOn = ALLOC + STORE + TRAP)
 224    public long test12(long l, MyValue1 v1, int i, MyValue2 v2) {
 225        return test12_interp(l, v1, i, v2);
 226    }
 227 
 228    @DontCompile
 229    public void test12_verifier(boolean warmup) {
 230        MyValue1 v1 = MyValue1.createWithFieldsDontInline(rI, rL);
 231        MyValue2 v2 = MyValue2.createWithFieldsInline(rI, true);
 232        long result = test12(rL, v1, rI, v2);
 233        Asserts.assertEQ(result, v1.hashInterpreted() + rL + rI + v2.hashInterpreted());
 234    }
 235 
 236    // Test that debug info at a call is correct
 237    @DontCompile
 238    public long test13_interp(MyValue2 v, MyValue1[] va, boolean deopt) {
 239        if (deopt) {
 240            // uncommon trap
 241            WHITE_BOX.deoptimizeMethod(tests.get(getClass().getSimpleName() + "::test13"));
 242        }
 243        return v.hash() + va[0].hash() + va[1].hash();
 244    }
 245 
 246    @Test(failOn = ALLOC + STORE + TRAP)
 247    public long test13(MyValue2 v, MyValue1[] va, boolean flag, long l) {
 248        return test13_interp(v, va, flag) + l;
 249    }
 250 
 251    @DontCompile
 252    public void test13_verifier(boolean warmup) {
 253        MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
 254        MyValue1[] va = new MyValue1[2];
 255        va[0] = MyValue1.createWithFieldsDontInline(rI, rL);
 256        va[1] = MyValue1.createWithFieldsDontInline(rI, rL);
 257        long result = test13(v, va, !warmup, rL);
 258        Asserts.assertEQ(result, v.hashInterpreted() + va[0].hash() + va[1].hash() + rL);
 259    }
 260 
 261    // Test deoptimization at call return with return value in registers
 262    @DontCompile
 263    public MyValue2 test14_interp(boolean deopt) {
 264        if (deopt) {
 265            // uncommon trap
 266            WHITE_BOX.deoptimizeMethod(tests.get(getClass().getSimpleName() + "::test14"));
 267        }
 268        return MyValue2.createWithFieldsInline(rI, true);
 269    }
 270 
 271    @Test()
 272    public MyValue2 test14(boolean flag) {
 273        return test14_interp(flag);
 274    }
 275 
 276    @DontCompile
 277    public void test14_verifier(boolean warmup) {
 278        MyValue2 result = test14(!warmup);
 279        MyValue2 v = MyValue2.createWithFieldsInline(rI, true);
 280        Asserts.assertEQ(result.hash(), v.hash());
 281    }
 282 
 283    // Return value types in registers from interpreter -> compiled
 284    final MyValue3 test15_vt = MyValue3.create();
 285    @DontCompile
 286    public MyValue3 test15_interp() {
 287        return test15_vt;
 288    }
 289 
 290    MyValue3 test15_vt2;
 291    @Test(valid = ValueTypeReturnedAsFieldsOn, failOn = ALLOC + LOAD + TRAP)
 292    @Test(valid = ValueTypeReturnedAsFieldsOff)
 293    public void test15() {
 294        test15_vt2 = test15_interp();
 295    }
 296 
 297    @DontCompile
 298    public void test15_verifier(boolean warmup) {
 299        test15();
 300        test15_vt.verify(test15_vt2);
 301    }
 302 
 303    // Return value types in registers from compiled -> interpreter
 304    final MyValue3 test16_vt = MyValue3.create();
 305    @Test(valid = ValueTypeReturnedAsFieldsOn, failOn = ALLOC + STORE + TRAP)
 306    @Test(valid = ValueTypeReturnedAsFieldsOff)
 307    public MyValue3 test16() {
 308        return test16_vt;
 309    }
 310 
 311    @DontCompile
 312    public void test16_verifier(boolean warmup) {
 313        MyValue3 vt = test16();
 314        test16_vt.verify(vt);
 315    }
 316 
 317    // Return value types in registers from compiled -> compiled
 318    final MyValue3 test17_vt = MyValue3.create();
 319    @DontInline
 320    public MyValue3 test17_comp() {
 321        return test17_vt;
 322    }
 323 
 324    MyValue3 test17_vt2;
 325    @Test(valid = ValueTypeReturnedAsFieldsOn, failOn = ALLOC + LOAD + TRAP)
 326    @Test(valid = ValueTypeReturnedAsFieldsOff)
 327    public void test17() {
 328        test17_vt2 = test17_comp();
 329    }
 330 
 331    @DontCompile
 332    public void test17_verifier(boolean warmup) throws Exception {
 333        Method helper_m = getClass().getDeclaredMethod("test17_comp");
 334        if (!warmup && USE_COMPILER && !WHITE_BOX.isMethodCompiled(helper_m, false)) {
 335            WHITE_BOX.enqueueMethodForCompilation(helper_m, COMP_LEVEL_FULL_OPTIMIZATION);
 336            Asserts.assertTrue(WHITE_BOX.isMethodCompiled(helper_m, false), "test17_comp not compiled");
 337        }
 338        test17();
 339        test17_vt.verify(test17_vt2);
 340    }
 341 
 342    // Same tests as above but with a value type that cannot be returned in registers
 343 
 344    // Return value types in registers from interpreter -> compiled
 345    final MyValue4 test18_vt = MyValue4.create();
 346    @DontCompile
 347    public MyValue4 test18_interp() {
 348        return test18_vt;
 349    }
 350 
 351    MyValue4 test18_vt2;
 352    @Test
 353    public void test18() {
 354        test18_vt2 = test18_interp();
 355    }
 356 
 357    @DontCompile
 358    public void test18_verifier(boolean warmup) {
 359        test18();
 360        test18_vt.verify(test18_vt2);
 361    }
 362 
 363    // Return value types in registers from compiled -> interpreter
 364    final MyValue4 test19_vt = MyValue4.create();
 365    @Test
 366    public MyValue4 test19() {
 367        return test19_vt;
 368    }
 369 
 370    @DontCompile
 371    public void test19_verifier(boolean warmup) {
 372        MyValue4 vt = test19();
 373        test19_vt.verify(vt);
 374    }
 375 
 376    // Return value types in registers from compiled -> compiled
 377    final MyValue4 test20_vt = MyValue4.create();
 378    @DontInline
 379    public MyValue4 test20_comp() {
 380        return test20_vt;
 381    }
 382 
 383    MyValue4 test20_vt2;
 384    @Test
 385    public void test20() {
 386        test20_vt2 = test20_comp();
 387    }
 388 
 389    @DontCompile
 390    public void test20_verifier(boolean warmup) throws Exception {
 391        Method helper_m = getClass().getDeclaredMethod("test20_comp");
 392        if (!warmup && USE_COMPILER && !WHITE_BOX.isMethodCompiled(helper_m, false)) {
 393            WHITE_BOX.enqueueMethodForCompilation(helper_m, COMP_LEVEL_FULL_OPTIMIZATION);
 394            Asserts.assertTrue(WHITE_BOX.isMethodCompiled(helper_m, false), "test20_comp not compiled");
 395        }
 396        test20();
 397        test20_vt.verify(test20_vt2);
 398    }
 399 
 400    // Test no result from inlined method for incremental inlining
 401    final MyValue3 test21_vt = MyValue3.create();
 402    public MyValue3 test21_inlined() {
 403        throw new RuntimeException();
 404    }
 405 
 406    @Test
 407    public MyValue3 test21() {
 408        try {
 409            return test21_inlined();
 410        } catch (RuntimeException ex) {
 411            return test21_vt;
 412        }
 413    }
 414 
 415    @DontCompile
 416    public void test21_verifier(boolean warmup) {
 417        MyValue3 vt = test21();
 418        test21_vt.verify(vt);
 419    }
 420 }