< prev index next >

test/compiler/valhalla/valuetypes/ValueTypeTestBench.java

Print this page




  35 
  36 package compiler.valhalla.valuetypes;
  37 
  38 import compiler.whitebox.CompilerWhiteBoxTest;
  39 import jdk.internal.misc.Unsafe;
  40 import jdk.test.lib.Asserts;
  41 import jdk.test.lib.Platform;
  42 import jdk.test.lib.ProcessTools;
  43 import jdk.test.lib.OutputAnalyzer;
  44 import jdk.test.lib.Utils;
  45 import sun.hotspot.WhiteBox;
  46 
  47 import java.lang.annotation.Retention;
  48 import java.lang.annotation.RetentionPolicy;
  49 import java.lang.reflect.Method;
  50 import java.util.ArrayList;
  51 import java.util.Hashtable;
  52 import java.util.regex.Matcher;
  53 import java.util.regex.Pattern;
  54 
  55 // Test value type
  56 __ByValue final class MyValue {
  57     final int x;
  58     final long y;
  59     final double z;


  60 
  61     private MyValue(int x, long y, double z) {
  62         this.x = x;
  63         this.y = y;
  64         this.z = z;


  65     }
  66 
  67     @DontInline
  68     public static MyValue createDontInline(int x, long y, double z) {
  69         return __Make MyValue(x, y, z);
  70     }
  71 
  72     @ForceInline
  73     public static MyValue createInline(int x, long y, double z) {
  74         return __Make MyValue(x, y, z);
  75     }
  76 
  77     @DontInline
  78     public String toStringDontInline() {
  79         return "MyValue: x=" + x + " y=" + y + " z=" + z;

















  80     }
  81 
  82     @ForceInline
  83     public String toStringInline() {
  84         return "MyValue: x=" + x + " y=" + y + " z=" + z;





  85     }
  86 }
  87 
  88 public class ValueTypeTestBench {
  89     // Print ideal graph after execution of each test
  90     private static final boolean PRINT_GRAPH = true;
  91 










  92     // ========== Test definitions ==========
  93 
  94     // Receive value type through call to interpreter
  95     @Test(failOn = ALLOC + STORE)
  96     public double test1() {
  97         MyValue v = MyValue.createDontInline(rI, rL, rD);
  98         return v.x + v.y + v.z;
  99     }
 100 
 101     @DontCompile
 102     public void test1_verifier(boolean warmup) {
 103         double result = test1();
 104         Asserts.assertEQ(result, rI + rL + rD);
 105     }
 106 
 107     // Receive value type from interpreter via parameter
 108     @Test(failOn = ALLOC + STORE)
 109     public double test2(MyValue v) {
 110         return v.x + v.y + v.z;
 111     }
 112 
 113     @DontCompile
 114     public void test2_verifier(boolean warmup) {
 115         MyValue v = MyValue.createDontInline(rI, rL, rD);
 116         double result = test2(v);
 117         Asserts.assertEQ(result, rI + rL + rD);
 118     }
 119 
 120     // Return incoming value type without accessing fields
 121     @Test(failOn = ALLOC + LOAD + STORE)
 122     public MyValue test3(MyValue v) {
 123         return v;
 124     }
 125 
 126     @DontCompile
 127     public void test3_verifier(boolean warmup) {
 128         MyValue v1 = MyValue.createDontInline(rI, rL, rD);
 129         MyValue v2 = test3(v1);
 130         Asserts.assertEQ(v1.x, v2.x);
 131         Asserts.assertEQ(v1.y, v2.y);
 132         Asserts.assertEQ(v1.z, v2.z);
 133     }
 134 
 135     // Create a value type in compiled code and only use fields.
 136     // Allocation should go away because value type does not escape.
 137     @Test(failOn = ALLOC + LOAD + STORE)
 138     public double test4() {
 139         MyValue v = MyValue.createInline(rI, rL, rD);
 140         return v.x + v.y + v.z;
 141     }
 142 
 143     @DontCompile
 144     public void test4_verifier(boolean warmup) {
 145         double result = test4();
 146         Asserts.assertEQ(result, rI + rL + rD);
 147     }
 148 
 149     // Create a value type in compiled code and pass it to
 150     // an inlined compiled method via a call.
 151     @Test(failOn = ALLOC + LOAD + STORE)
 152     public double test5() {
 153         MyValue v = MyValue.createInline(rI, rL, rD);
 154         return test5Inline(v);
 155     }
 156 
 157     @ForceInline
 158     public double test5Inline(MyValue v) {
 159         return v.x + v.y + v.z;
 160     }
 161 
 162     @DontCompile
 163     public void test5_verifier(boolean warmup) {
 164         double result = test5();
 165         Asserts.assertEQ(result, rI + rL + rD);
 166     }
 167 
 168     // Create a value type in compiled code and pass it to
 169     // the interpreter via a call.
 170     @Test(match = {ALLOC}, matchCount = {1}, failOn = LOAD)
 171     public double test6() {
 172         MyValue v = MyValue.createInline(rI, rL, rD);
 173         // Pass to interpreter
 174         return sumValue(v);
 175     }
 176 
 177     @DontCompile
 178     public void test6_verifier(boolean warmup) {
 179         double result = test6();
 180         Asserts.assertEQ(result, rI + rL + rD);
 181     }
 182 
 183     // Create a value type in compiled code and pass it to
 184     // the interpreter by returning.
 185     @Test(match = {ALLOC}, matchCount = {1}, failOn = LOAD)
 186     public MyValue test7(int x, long y, double z) {
 187         return MyValue.createInline(x, y, z);
 188     }
 189 
 190     @DontCompile
 191     public void test7_verifier(boolean warmup) {
 192         MyValue v = test7(rI, rL, rD);
 193         double result = v.x + v.y + v.z;
 194         Asserts.assertEQ(result, rI + rL + rD);
 195     }
 196 
 197     // Merge value types created from two branches
 198     @Test(failOn = ALLOC + STORE)
 199     public double test8(boolean b) {
 200         MyValue v;
 201         if (b) {
 202             v = MyValue.createInline(rI, rL, rD);
 203         } else {
 204             v = MyValue.createDontInline(rI + 1, rL + 1, rD + 1);
 205         }
 206         return v.x + v.y + v.z;
 207     }
 208 
 209     @DontCompile
 210     public void test8_verifier(boolean warmup) {
 211         Asserts.assertEQ(test8(true), rI + rL + rD);
 212         Asserts.assertEQ(test8(false), rI + 1 + rL + 1 + ((double)rD + 1));
 213     }
 214 
 215     // Merge value types created from two branches
 216     @Test(match = {ALLOC, STORE}, matchCount = {1, 3}, failOn = LOAD)
 217     public MyValue test9(boolean b) {
 218         MyValue v;
 219         if (b) {
 220             // Value type is not allocated
 221             v = MyValue.createInline(rI, rL, rD);
 222         } else {
 223             // Value type is allocated by the callee
 224             v = MyValue.createDontInline(rI + 1, rL + 1, rD + 1);
 225         }
 226         // Need to allocate value type if 'b' is true
 227         double sum = sumValue(v);
 228         if (b) {
 229             v = MyValue.createDontInline(rI, rL, sum);
 230         } else {
 231             v = MyValue.createDontInline(rI, rL, sum + 1);
 232         }
 233         // Don't need to allocate value type because both branches allocate
 234         return v;
 235     }
 236 
 237     @DontCompile
 238     public void test9_verifier(boolean warmup) {
 239         MyValue v = test9(true);
 240         Asserts.assertEQ(v.x, rI);
 241         Asserts.assertEQ(v.y, rL);
 242         Asserts.assertEQ(v.z, rI + rL + rD);
 243 
 244         v = test9(false);
 245         Asserts.assertEQ(v.x, rI);
 246         Asserts.assertEQ(v.y, rL);
 247         Asserts.assertEQ(v.z, rI + rL + ((double)rD + 1));
 248     }
 249 
 250     // Merge value types created in a loop (not inlined)
 251     @Test(failOn = ALLOC + STORE)
 252     public double test10(int x, long y, double z) {
 253         MyValue v = MyValue.createDontInline(x, y, z);
 254         for (int i = 0; i < 10; ++i) {
 255             v = MyValue.createDontInline(v.x + 1, v.y + 1, v.z + 1);
 256         }
 257         return v.x + v.y + v.z;
 258     }
 259 
 260     @DontCompile
 261     public void test10_verifier(boolean warmup) {
 262         double result = test10(rI, rL, rD);
 263         Asserts.assertEQ(result, rI + rL + 20 + ((double)rD + 10));
 264     }
 265 
 266     // Merge value types created in a loop (inlined)
 267     @Test(failOn = ALLOC + LOAD + STORE + LOOP)
 268     public double test11(int x, long y, double z) {
 269         MyValue v = MyValue.createInline(x, y, z);
 270         for (int i = 0; i < 10; ++i) {
 271             v = MyValue.createInline(v.x + 1, v.y + 1, v.z + 1);
 272         }
 273         return v.x + v.y + v.z;
 274     }
 275 
 276     @DontCompile
 277     public void test11_verifier(boolean warmup) {
 278         double result = test11(rI, rL, rD);
 279         Asserts.assertEQ(result, rI + rL + 20 + ((double)rD + 10));
 280     }
 281 
 282     // Test loop with uncommon trap referencing a value type
 283     @Test(match = {TRAP, SCOBJ}, matchCount = {1, 1}, failOn = ALLOC + LOAD + STORE)
 284     public double test12(boolean b) {
 285         MyValue v = MyValue.createInline(rI, rL, rD);
 286         double result = 42;
 287         for (int i = 0; i < 1000; ++i) {
 288             if (b) {
 289                 result += v.x;
 290             } else {
 291                 // Uncommon trap referencing v. We delegate allocation to the
 292                 // interpreter by adding a SafePointScalarObjectNode.
 293                 result = sumValue(v);
 294             }
 295         }
 296         return result;
 297     }
 298 
 299     @DontCompile
 300     public void test12_verifier(boolean warmup) {
 301         double result = test12(warmup);
 302         Asserts.assertEQ(result, warmup ? 42 + (1000*(double)rI) : (rI + rL + rD));
 303     }
 304 
 305     // Test loop with uncommon trap referencing a value type
 306     @Test(match = {TRAP, LOAD}, matchCount = {1, 1}, failOn = ALLOC + STORE + SCOBJ)
 307     public double test13(boolean b) {
 308         MyValue v = MyValue.createDontInline(rI, rL, rD);
 309         double result = 42;
 310         for (int i = 0; i < 1000; ++i) {
 311             if (b) {
 312                 result += v.x;
 313             } else {
 314                 // Uncommon trap referencing v. Should not allocate
 315                 // but just pass the existing oop to the uncommon trap.
 316                 result = sumValue(v);
 317             }
 318         }
 319         return result;
 320     }
 321 
 322     @DontCompile
 323     public void test13_verifier(boolean warmup) {
 324         double result = test13(warmup);
 325         Asserts.assertEQ(result, warmup ? 42 + (1000*(double)rI) : (rI + rL + rD));
 326     }
 327 
 328     // Create a value type in a non-inlined method and then call a
 329     // non-inlined method on that value type.
 330     @Test(failOn = (ALLOC + STORE))
 331     public String test14() {
 332         MyValue v = MyValue.createDontInline(32, 64L, 128.0);
 333         String s = v.toStringDontInline();
 334         return s;
 335     }
 336 
 337     @DontCompile
 338     public void test14_verifier(boolean b) {
 339         String s = test14();
 340         System.out.println("Result is: " + s);
 341     }
 342 
 343     // Create a value type in an inlined method and then call a
 344     // non-inlined method on that value type.
 345     @Test(match = {ALLOC}, matchCount = {1})
 346     public String test15() {
 347         MyValue v = MyValue.createInline(65, 129L, 257.0);
 348         String s = v.toStringDontInline();
 349         return s;
 350     }
 351 
 352     @DontCompile
 353     public void test15_verifier(boolean b) {
 354         String s = test15();
 355         System.out.println("Result is: " + s);
 356     }
 357 
 358     // Create a value type in a non-inlined method and then call an
 359     // inlined method on that value type. Allocations are due to building
 360     // String objects and not due to allocating value types.
 361     @Test(match = {ALLOC}, matchCount = {2})
 362     public String test16() {
 363         MyValue v = MyValue.createDontInline(130, 258L, 514.0);
 364         String s = v.toStringInline();
 365         return s;
 366     }
 367 
 368     @DontCompile
 369     public void test16_verifier(boolean b) {
 370         String s = test16();
 371         System.out.println("Result is: " + s);
 372     }
 373 
 374     // Create a value type in an inlined method and then call an
 375     // inlined method on that value type. Allocations are due to
 376     // building String objects and not due to allocating value types.
 377     @Test(match = {ALLOC}, matchCount = {2})
 378     public String test17() {
 379         MyValue v = MyValue.createInline(259, 515L, 1027.0);
 380         String s = v.toStringInline();
 381         return s;
 382     }
 383 
 384     @DontCompile
 385     public void test17_verifier(boolean b) {
 386         String s = test17();
 387         System.out.println("Result is: " + s);
 388     }
 389 
 390     // Create a value type in compiled code and pass it to the
 391     // interpreter via a call. The value is live at the first call so
 392     // debug info should include a reference to all its fields.
 393     @Test(match = {ALLOC}, matchCount = {1}, failOn = LOAD)
 394     public double test18() {
 395         MyValue v = MyValue.createInline(rI, rL, rD);
 396         sumValue(v);
 397         return sumValue(v);
 398     }
 399 
 400     @DontCompile
 401     public void test18_verifier(boolean warmup) {
 402         double result = test18();
 403         Asserts.assertEQ(result, rI + rL + rD);
 404     }
 405 
 406     // Create a value type in compiled code and pass it to the
 407     // interpreter via a call. The value type is passed twice but
 408     // should only be allocated once.
 409     @Test(match = {ALLOC}, matchCount = {1}, failOn = LOAD)
 410     public double test19() {
 411         MyValue v = MyValue.createInline(rI, rL, rD);
 412         return sumValue(v, v);
 413     }
 414 
 415     @DontCompile





 416     public void test19_verifier(boolean warmup) {
 417         double result = test19();
 418         Asserts.assertEQ(result, rI + rL + rD);
 419     }
 420 
 421     // Create a value type in compiled code and pass it to the
 422     // interpreter via a call. The value type is live at the uncommon
 423     // trap: verify that deoptimization causes the value type to be
 424     // correctly allocated.
 425     @Test(match = {ALLOC}, matchCount = {1}, failOn = LOAD)
 426     public double test20(boolean flag) {
 427         MyValue v = MyValue.createInline(rI, rL, rD);
 428         if (flag) { 
 429             // uncommon trap
 430             WHITE_BOX.deoptimizeMethod(tests.get("ValueTypeTestBench::test16"));
 431         }
 432         return sumValue(v);
 433     }
 434 
 435     @DontCompile
 436     public void test20_verifier(boolean warmup) {
 437         double result = test20(false);
 438         Asserts.assertEQ(result, rI + rL + rD);
 439         if (!warmup) {
 440             result = test20(true);
 441             Asserts.assertEQ(result, rI + rL + rD);
 442         }
 443     }
 444 
 445     // ========== Helper methods ==========
 446 
 447     @DontCompile
 448     public double sumValue(MyValue v) {
 449         return v.x + v.y + v.z;








 450     }    
 451 
 452     @DontCompile
 453     public double sumValue(MyValue v, MyValue dummy) {
 454         return v.x + v.y + v.z;








 455     }
 456 

 457     // ========== Test infrastructure ==========
 458 
 459     private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
 460     private static final int COMP_LEVEL_ANY = -1;
 461     private static final int COMP_LEVEL_FULL_OPTIMIZATION = 4;
 462     private static final Hashtable<String, Method> tests = new Hashtable<String, Method>();
 463     private static final int WARMUP = 10;


 464 
 465     // Regular expressions used  to match nodes in the PrintIdeal output
 466     private static final String START = "(\\d+\\t(.*";
 467     private static final String MID = ".*)+\\t===.*";
 468     private static final String END = ")|";
 469     private static final String ALLOC = START + "CallStaticJava" + MID + "_new_instance_Java" + END;
 470     private static final String LOAD  = START + "Load" + MID + "valuetype\\*" + END;
 471     private static final String STORE = START + "Store" + MID + "valuetype\\*" + END;
 472     private static final String LOOP  = START + "Loop" + MID + "" + END;
 473     private static final String TRAP  = START + "CallStaticJava" + MID + "uncommon_trap" + END;
 474     // TODO: match field values of scalar replaced object
 475     private static final String SCOBJ = "(.*# ScObj.*" + END;
 476 
 477 
 478     // Random test values
 479     private static final int    rI = Utils.getRandomInstance().nextInt();
 480     private static final long   rL = Utils.getRandomInstance().nextLong();
 481     private static final double rD = Utils.getRandomInstance().nextDouble();
 482 
 483     static {
 484         // Gather all test methods and put them in Hashtable
 485         for (Method m : ValueTypeTestBench.class.getDeclaredMethods()) {
 486             if (m.isAnnotationPresent(Test.class)) {
 487                 tests.put("ValueTypeTestBench::" + m.getName(), m);
 488             }
 489         }
 490     }
 491 
 492     public static void main(String[] args) throws Throwable {
 493         if (args.length == 0) {
 494             // Run tests in own process and verify output
 495             OutputAnalyzer oa = ProcessTools.executeTestJvm("-noverify",
 496                                                             "-XX:+UnlockDiagnosticVMOptions", "-Xbootclasspath/a:.", "-XX:+WhiteBoxAPI",
 497                                                             "-XX:-TieredCompilation", "-XX:-BackgroundCompilation", "-XX:-UseOnStackReplacement",
 498                                                             "-XX:CompileCommand=quiet", "-XX:+PrintCompilation", "-XX:+PrintIdeal", "-XX:+PrintOptoAssembly",
 499                                                             "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.ValueTypeTestBench::*",
 500                                                             "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue::*",

 501                                                             ValueTypeTestBench.class.getName(), "run");

 502             String output = oa.getOutput();
 503             oa.shouldHaveExitValue(0);

 504             parseOutput(output);
 505         } else {






 506             // Execute tests
 507             ValueTypeTestBench bench = new ValueTypeTestBench();
 508             bench.run();
 509         }
 510     }
 511 
 512     public static void parseOutput(String output) throws Exception {
 513         String split = "b        compiler.valhalla.valuetypes.";
 514         String[] compilations = output.split(split);
 515         // Print header
 516         System.out.println(compilations[0]);
 517         // Iterate over compilation output
 518         for (String graph : compilations) {
 519             String[] lines = graph.split("\\n");
 520             String testName = lines[0].split(" ")[0];
 521             Method test = tests.get(testName);
 522             if (test == null) {
 523                 // Skip helper methods
 524                 continue;
 525             }


 571 
 572     public void setup(Method[] methods) {
 573         for (Method m : methods) {
 574             if (m.isAnnotationPresent(Test.class)) {
 575                 // Don't inline tests
 576                 WHITE_BOX.testSetDontInlineMethod(m, true);
 577             }
 578             if (m.isAnnotationPresent(DontCompile.class)) {
 579                 WHITE_BOX.makeMethodNotCompilable(m, COMP_LEVEL_ANY, true);
 580                 WHITE_BOX.makeMethodNotCompilable(m, COMP_LEVEL_ANY, false);
 581             }
 582             if (m.isAnnotationPresent(ForceInline.class)) {
 583                 WHITE_BOX.testSetForceInlineMethod(m, true);
 584             } else if (m.isAnnotationPresent(DontInline.class)) {
 585                 WHITE_BOX.testSetDontInlineMethod(m, true);
 586             }
 587         }
 588     }
 589 
 590     public void run() throws Exception {
 591         System.out.format("rI = %d, rL = %d, rD = %f\n", rI, rL, rD);
 592         setup(this.getClass().getDeclaredMethods());
 593         setup(MyValue.class.getDeclaredMethods());

 594 
 595         // Execute tests
 596         for (Method test : tests.values()) {
 597             Method verifier = getClass().getDeclaredMethod(test.getName() + "_verifier", boolean.class);
 598             // Warmup using verifier method
 599             for (int i = 0; i < WARMUP; ++i) {
 600                 verifier.invoke(this, true);
 601             }
 602             // Trigger compilation
 603             WHITE_BOX.enqueueMethodForCompilation(test, COMP_LEVEL_FULL_OPTIMIZATION);
 604             Asserts.assertTrue(WHITE_BOX.isMethodCompiled(test, false));
 605             // Check result
 606             verifier.invoke(this, false);
 607         }
 608     }
 609 }
 610 
 611 // Mark method as test
 612 @Retention(RetentionPolicy.RUNTIME)
 613 @interface Test {
 614     // Regular expression used to match forbidden IR nodes
 615     // in the C2 IR emitted for this test.
 616     String failOn() default "";
 617     // Regular expressions used to match and count IR nodes.
 618     String[] match() default { };
 619     int[] matchCount() default { };
 620 }
 621 
 622 // Force method inlining during compilation
 623 @Retention(RetentionPolicy.RUNTIME)
 624 @interface ForceInline { }


  35 
  36 package compiler.valhalla.valuetypes;
  37 
  38 import compiler.whitebox.CompilerWhiteBoxTest;
  39 import jdk.internal.misc.Unsafe;
  40 import jdk.test.lib.Asserts;
  41 import jdk.test.lib.Platform;
  42 import jdk.test.lib.ProcessTools;
  43 import jdk.test.lib.OutputAnalyzer;
  44 import jdk.test.lib.Utils;
  45 import sun.hotspot.WhiteBox;
  46 
  47 import java.lang.annotation.Retention;
  48 import java.lang.annotation.RetentionPolicy;
  49 import java.lang.reflect.Method;
  50 import java.util.ArrayList;
  51 import java.util.Hashtable;
  52 import java.util.regex.Matcher;
  53 import java.util.regex.Pattern;
  54 
  55 // Test value types
  56 __ByValue final class MyValue1 {
  57     final int x;
  58     final long y;
  59     final MyValue2 v1;
  60     final MyValue2 v2;
  61     final int c;
  62 
  63     private MyValue1(int x, long y, MyValue2 v1, MyValue2 v2, int c) {
  64         this.x = x;
  65         this.y = y;
  66         this.v1 = v1;
  67         this.v2 = v2;
  68         this.c = c;
  69     }
  70 
  71     @DontInline
  72     public static MyValue1 createDontInline(int x, long y) {
  73         return __Make MyValue1(x, y, MyValue2.createInline(x, x < y), MyValue2.createInline(x, x > y), ValueTypeTestBench.rI);
  74     }
  75 
  76     @ForceInline
  77     public static MyValue1 createInline(int x, long y) {
  78         return __Make MyValue1(x, y, MyValue2.createInline(x, x < y), MyValue2.createInline(x, x > y), ValueTypeTestBench.rI);
  79     }
  80 
  81     @ForceInline
  82     public long hash() {
  83         return x + y + c + v1.hash() + v2.hash();
  84     }
  85 
  86     @DontCompile
  87     public long hashInterpreted() {
  88         return x + y + c + v1.hash() + v2.hash();
  89     }
  90 }
  91 
  92 __ByValue final class MyValue2 {
  93     final int x;
  94     final boolean b;
  95     final long c;
  96 
  97     private MyValue2(int x, boolean b, long c) {
  98         this.x = x;
  99         this.b = b;
 100         this.c = c;
 101     }
 102 
 103     @ForceInline
 104     public static MyValue2 createInline(int x, boolean b) {
 105         return __Make MyValue2(x, b, ValueTypeTestBench.rL);
 106     }
 107 
 108     @ForceInline
 109     public long hash() {
 110         return x + (b ? 0 : 1) + c;
 111     }
 112 }
 113 
 114 public class ValueTypeTestBench {
 115     // Print ideal graph after execution of each test
 116     private static final boolean PRINT_GRAPH = true;
 117 
 118     // ========== Helper methods ==========
 119 
 120     public long hash() {
 121         return hash(rI, rL);
 122     }
 123 
 124     public long hash(int x, long y) {
 125         return MyValue1.createInline(x, y).hash();
 126     }
 127 
 128     // ========== Test definitions ==========
 129 
 130     // Receive value type through call to interpreter
 131     @Test(failOn = ALLOC + STORE + TRAP)
 132     public long test1() {
 133         MyValue1 v = MyValue1.createDontInline(rI, rL);
 134         return v.hash();
 135     }
 136 
 137     @DontCompile
 138     public void test1_verifier(boolean warmup) {
 139         long result = test1();
 140         Asserts.assertEQ(result, hash());
 141     }
 142 
 143     // Receive value type from interpreter via parameter
 144     @Test(failOn = ALLOC + STORE + TRAP)
 145     public long test2(MyValue1 v) {
 146         return v.hash();
 147     }
 148 
 149     @DontCompile
 150     public void test2_verifier(boolean warmup) {
 151         MyValue1 v = MyValue1.createDontInline(rI, rL);
 152         long result = test2(v);
 153         Asserts.assertEQ(result, hash());
 154     }
 155 
 156     // Return incoming value type without accessing fields
 157     @Test(failOn = ALLOC + LOAD + STORE + TRAP)
 158     public MyValue1 test3(MyValue1 v) {
 159         return v;
 160     }
 161 
 162     @DontCompile
 163     public void test3_verifier(boolean warmup) {
 164         MyValue1 v1 = MyValue1.createDontInline(rI, rL);
 165         MyValue1 v2 = test3(v1);
 166         Asserts.assertEQ(v1.x, v2.x);
 167         Asserts.assertEQ(v1.y, v2.y);

 168     }
 169 
 170     // Create a value type in compiled code and only use fields.
 171     // Allocation should go away because value type does not escape.
 172     @Test(failOn = ALLOC + LOAD + STORE + TRAP)
 173     public long test4() {
 174         MyValue1 v = MyValue1.createInline(rI, rL);
 175         return v.hash();
 176     }
 177 
 178     @DontCompile
 179     public void test4_verifier(boolean warmup) {
 180         long result = test4();
 181         Asserts.assertEQ(result, hash());
 182     }
 183 
 184     // Create a value type in compiled code and pass it to
 185     // an inlined compiled method via a call.
 186     @Test(failOn = ALLOC + LOAD + STORE + TRAP)
 187     public long test5() {
 188         MyValue1 v = MyValue1.createInline(rI, rL);
 189         return test5Inline(v);
 190     }
 191 
 192     @ForceInline
 193     public long test5Inline(MyValue1 v) {
 194         return v.hash();
 195     }
 196 
 197     @DontCompile
 198     public void test5_verifier(boolean warmup) {
 199         long result = test5();
 200         Asserts.assertEQ(result, hash());
 201     }
 202 
 203     // Create a value type in compiled code and pass it to
 204     // the interpreter via a call.
 205     @Test(match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP)
 206     public long test6() {
 207         MyValue1 v = MyValue1.createInline(rI, rL);
 208         // Pass to interpreter
 209         return v.hashInterpreted();
 210     }
 211 
 212     @DontCompile
 213     public void test6_verifier(boolean warmup) {
 214         long result = test6();
 215         Asserts.assertEQ(result, hash());
 216     }
 217 
 218     // Create a value type in compiled code and pass it to
 219     // the interpreter by returning.
 220     @Test(match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP)
 221     public MyValue1 test7(int x, long y) {
 222         return MyValue1.createInline(x, y);
 223     }
 224 
 225     @DontCompile
 226     public void test7_verifier(boolean warmup) {
 227         MyValue1 v = test7(rI, rL);
 228         Asserts.assertEQ(v.hash(), hash());

 229     }
 230 
 231     // Merge value types created from two branches
 232     @Test(failOn = ALLOC + STORE + TRAP)
 233     public long test8(boolean b) {
 234         MyValue1 v;
 235         if (b) {
 236             v = MyValue1.createInline(rI, rL);
 237         } else {
 238             v = MyValue1.createDontInline(rI + 1, rL + 1);
 239         }
 240         return v.hash();
 241     }
 242 
 243     @DontCompile
 244     public void test8_verifier(boolean warmup) {
 245         Asserts.assertEQ(test8(true), hash());
 246         Asserts.assertEQ(test8(false), hash(rI + 1, rL + 1));
 247     }
 248 
 249     // Merge value types created from two branches
 250     @Test(match = {ALLOC, STORE}, matchCount = {1, 9}, failOn = LOAD + TRAP)
 251     public MyValue1 test9(boolean b) {
 252         MyValue1 v;
 253         if (b) {
 254             // Value type is not allocated
 255             v = MyValue1.createInline(rI, rL);
 256         } else {
 257             // Value type is allocated by the callee
 258             v = MyValue1.createDontInline(rI + 1, rL + 1);
 259         }
 260         // Need to allocate value type if 'b' is true
 261         long sum = v.hashInterpreted();
 262         if (b) {
 263             v = MyValue1.createDontInline(rI, sum);
 264         } else {
 265             v = MyValue1.createDontInline(rI, sum + 1);
 266         }
 267         // Don't need to allocate value type because both branches allocate
 268         return v;
 269     }
 270 
 271     @DontCompile
 272     public void test9_verifier(boolean warmup) {
 273         MyValue1 v = test9(true);
 274         Asserts.assertEQ(v.x, rI);
 275         Asserts.assertEQ(v.y, hash());


 276         v = test9(false);
 277         Asserts.assertEQ(v.x, rI);
 278         Asserts.assertEQ(v.y, hash(rI + 1, rL + 1) + 1);

 279     }
 280 
 281     // Merge value types created in a loop (not inlined)
 282     @Test(failOn = ALLOC + STORE + TRAP)
 283     public long test10(int x, long y) {
 284         MyValue1 v = MyValue1.createDontInline(x, y);
 285         for (int i = 0; i < 10; ++i) {
 286             v = MyValue1.createDontInline(v.x + 1, v.y + 1);
 287         }
 288         return v.hash();
 289     }
 290 
 291     @DontCompile
 292     public void test10_verifier(boolean warmup) {
 293         long result = test10(rI, rL);
 294         Asserts.assertEQ(result, hash(rI + 10, rL + 10));
 295     }
 296 
 297     // Merge value types created in a loop (inlined)
 298     @Test(failOn = ALLOC + LOAD + STORE + LOOP + TRAP)
 299     public long test11(int x, long y) {
 300         MyValue1 v = MyValue1.createInline(x, y);
 301         for (int i = 0; i < 10; ++i) {
 302             v = MyValue1.createInline(v.x + 1, v.y + 1);
 303         }
 304         return v.hash();
 305     }
 306 
 307     @DontCompile
 308     public void test11_verifier(boolean warmup) {
 309         long result = test11(rI, rL);
 310         Asserts.assertEQ(result, hash(rI + 10, rL + 10));
 311     }
 312 
 313     // Test loop with uncommon trap referencing a value type
 314     @Test(match = {TRAP, SCOBJ}, matchCount = {1, 1}, failOn = ALLOC + LOAD + STORE)
 315     public long test12(boolean b) {
 316         MyValue1 v = MyValue1.createInline(rI, rL);
 317         long result = 42;
 318         for (int i = 0; i < 1000; ++i) {
 319             if (b) {
 320                 result += v.x;
 321             } else {
 322                 // Uncommon trap referencing v. We delegate allocation to the
 323                 // interpreter by adding a SafePointScalarObjectNode.
 324                 result = v.hashInterpreted();
 325             }
 326         }
 327         return result;
 328     }
 329 
 330     @DontCompile
 331     public void test12_verifier(boolean warmup) {
 332         long result = test12(warmup);
 333         Asserts.assertEQ(result, warmup ? 42 + (1000*rI) : hash());
 334     }
 335 
 336     // Test loop with uncommon trap referencing a value type
 337     @Test(match = {TRAP, LOAD}, matchCount = {1, 1}, failOn = ALLOC + STORE + SCOBJ)
 338     public long test13(boolean b) {
 339         MyValue1 v = MyValue1.createDontInline(rI, rL);
 340         long result = 42;
 341         for (int i = 0; i < 1000; ++i) {
 342             if (b) {
 343                 result += v.x;
 344             } else {
 345                 // Uncommon trap referencing v. Should not allocate
 346                 // but just pass the existing oop to the uncommon trap.
 347                 result = v.hashInterpreted();
 348             }
 349         }
 350         return result;
 351     }
 352 
 353     @DontCompile
 354     public void test13_verifier(boolean warmup) {
 355         long result = test13(warmup);
 356         Asserts.assertEQ(result, warmup ? 42 + (1000*rI) : hash());
 357     }
 358 
 359     // Create a value type in a non-inlined method and then call a
 360     // non-inlined method on that value type.
 361     @Test(failOn = (ALLOC + LOAD + STORE + TRAP))
 362     public long test14() {
 363         MyValue1 v = MyValue1.createDontInline(rI, rL);
 364         return v.hashInterpreted();

 365     }
 366 
 367     @DontCompile
 368     public void test14_verifier(boolean b) {
 369         long result = test14();
 370         Asserts.assertEQ(result, hash());
 371     }
 372 
 373     // Create a value type in an inlined method and then call a
 374     // non-inlined method on that value type.
 375     @Test(failOn = (LOAD + TRAP), match = {ALLOC}, matchCount = {1})
 376     public long test15() {
 377         MyValue1 v = MyValue1.createInline(rI, rL);
 378         return v.hashInterpreted();

 379     }
 380 
 381     @DontCompile
 382     public void test15_verifier(boolean b) {
 383         long result = test15();
 384         Asserts.assertEQ(result, hash());
 385     }
 386 
 387     // Create a value type in a non-inlined method and then call an
 388     // inlined method on that value type.
 389     @Test(failOn = (ALLOC + STORE + TRAP))
 390     public long test16() {
 391         MyValue1 v = MyValue1.createDontInline(rI, rL);
 392         return v.hash();


 393     }
 394 
 395     @DontCompile
 396     public void test16_verifier(boolean b) {
 397         long result = test16();
 398         Asserts.assertEQ(result, hash());
 399     }
 400 
 401     // Create a value type in an inlined method and then call an
 402     // inlined method on that value type.
 403     @Test(failOn = (ALLOC + LOAD + STORE + TRAP))
 404     public long test17() {
 405         MyValue1 v = MyValue1.createInline(rI, rL);
 406         return v.hash();


 407     }
 408 
 409     @DontCompile
 410     public void test17_verifier(boolean b) {
 411         long result = test17();
 412         Asserts.assertEQ(result, hash());
 413     }
 414 
 415     // Create a value type in compiled code and pass it to the
 416     // interpreter via a call. The value is live at the first call so
 417     // debug info should include a reference to all its fields.
 418     @Test(match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP)
 419     public long test18() {
 420         MyValue1 v = MyValue1.createInline(rI, rL);
 421         v.hashInterpreted();
 422         return v.hashInterpreted();
 423     }
 424 
 425     @DontCompile
 426     public void test18_verifier(boolean warmup) {
 427         long result = test18();
 428         Asserts.assertEQ(result, hash());
 429     }
 430 
 431     // Create a value type in compiled code and pass it to the
 432     // interpreter via a call. The value type is passed twice but
 433     // should only be allocated once.
 434     @Test(match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP)
 435     public long test19() {
 436         MyValue1 v = MyValue1.createInline(rI, rL);
 437         return sumValue(v, v);
 438     }
 439 
 440     @DontCompile
 441     public long sumValue(MyValue1 v, MyValue1 dummy) {
 442         return v.hash();
 443     }
 444 
 445     @DontCompile
 446     public void test19_verifier(boolean warmup) {
 447         long result = test19();
 448         Asserts.assertEQ(result, hash());
 449     }
 450 
 451     // Create a value type in compiled code and pass it to the
 452     // interpreter via a call. The value type is live at the uncommon
 453     // trap: verify that deoptimization causes the value type to be
 454     // correctly allocated.
 455     @Test(match = {ALLOC}, matchCount = {1}, failOn = LOAD)
 456     public long test20(boolean flag) {
 457         MyValue1 v = MyValue1.createInline(rI, rL);
 458         if (flag) {
 459             // uncommon trap
 460             WHITE_BOX.deoptimizeMethod(tests.get("ValueTypeTestBench::test16"));
 461         }
 462         return v.hashInterpreted();
 463     }
 464 
 465     @DontCompile
 466     public void test20_verifier(boolean warmup) {
 467         long result = test20(false);
 468         Asserts.assertEQ(result, hash());
 469         if (!warmup) {
 470             result = test20(true);
 471             Asserts.assertEQ(result, hash());
 472         }
 473     }
 474 
 475     // Value type fields in regular object
 476     MyValue1 val1;
 477     MyValue2 val2;
 478 
 479     // Test value type fields in objects
 480     @Test(failOn = (ALLOC + TRAP))
 481     public long test21(int x, long y) {
 482         // Compute hash of value type fields
 483         long result = val1.hash() + val2.hash();
 484         // Update fields
 485         val1 = MyValue1.createInline(x, y);
 486         val2 = MyValue2.createInline(x, true);
 487         return result;
 488     }
 489 
 490     @DontCompile
 491     public void test21_verifier(boolean warmup) {
 492         // Check if hash computed by test18 is correct
 493         val1 = MyValue1.createInline(rI, rL);
 494         val2 = val1.v2;
 495         long hash = val1.hash() + val2.hash();
 496         long result = test21(rI + 1, rL + 1);
 497         Asserts.assertEQ(result, hash);
 498         // Check if value type fields were updated
 499         Asserts.assertEQ(val1.hash(), hash(rI + 1, rL + 1));
 500         Asserts.assertEQ(val2.hash(), MyValue2.createInline(rI + 1, true).hash());
 501     }
 502 
 503 
 504     // ========== Test infrastructure ==========
 505 
 506     private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
 507     private static final int COMP_LEVEL_ANY = -1;
 508     private static final int COMP_LEVEL_FULL_OPTIMIZATION = 4;
 509     private static final Hashtable<String, Method> tests = new Hashtable<String, Method>();
 510     private static final int WARMUP = 10;
 511     private static boolean USE_COMPILER = WHITE_BOX.getBooleanVMFlag("UseCompiler");
 512     private static boolean PRINT_IDEAL  = WHITE_BOX.getBooleanVMFlag("PrintIdeal");
 513 
 514     // Regular expressions used  to match nodes in the PrintIdeal output
 515     private static final String START = "(\\d+\\t(.*";
 516     private static final String MID = ".*)+\\t===.*";
 517     private static final String END = ")|";
 518     private static final String ALLOC = START + "CallStaticJava" + MID + "_new_instance_Java" + END;
 519     private static final String LOAD  = START + "Load" + MID + "valuetype\\*" + END;
 520     private static final String STORE = START + "Store" + MID + "valuetype\\*" + END;
 521     private static final String LOOP  = START + "Loop" + MID + "" + END;
 522     private static final String TRAP  = START + "CallStaticJava" + MID + "uncommon_trap" + END;
 523     // TODO: match field values of scalar replaced object
 524     private static final String SCOBJ = "(.*# ScObj.*" + END;
 525 

 526     // Random test values
 527     public static final int  rI = Utils.getRandomInstance().nextInt() % 1000;
 528     public static final long rL = Utils.getRandomInstance().nextLong() % 1000;

 529 
 530     static {
 531         // Gather all test methods and put them in Hashtable
 532         for (Method m : ValueTypeTestBench.class.getDeclaredMethods()) {
 533             if (m.isAnnotationPresent(Test.class)) {
 534                 tests.put("ValueTypeTestBench::" + m.getName(), m);
 535             }
 536         }
 537     }
 538 
 539     public static void main(String[] args) throws Throwable {
 540         if (args.length == 0) {
 541             // Run tests in own process and verify output
 542             OutputAnalyzer oa = ProcessTools.executeTestJvm("-noverify",
 543                     "-XX:+UnlockDiagnosticVMOptions", "-Xbootclasspath/a:.", "-XX:+WhiteBoxAPI",
 544                     "-XX:-TieredCompilation", "-XX:-BackgroundCompilation", "-XX:-UseOnStackReplacement",
 545                     "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+PrintCompilation", "-XX:+PrintIdeal", "-XX:+PrintOptoAssembly",
 546                     "-XX:CompileCommand=quiet", "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.ValueTypeTestBench::*",
 547                     "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue1::*",
 548                     "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue2::*",
 549                     ValueTypeTestBench.class.getName(), "run");
 550             // If ideal graph printing is enabled/supported, verify output
 551             String output = oa.getOutput();
 552             oa.shouldHaveExitValue(0);
 553             if (output.contains("PrintIdeal enabled")) {
 554                 parseOutput(output);
 555             } else {
 556                 System.out.println("WARNING: test methods not compiled or PrintIdeal disabled! Running with -Xint or release build?");
 557             }
 558         } else {
 559             if (USE_COMPILER && PRINT_IDEAL) {
 560                 System.out.println("PrintIdeal enabled");
 561             }
 562             // Execute tests
 563             ValueTypeTestBench bench = new ValueTypeTestBench();
 564             bench.run();
 565         }
 566     }
 567 
 568     public static void parseOutput(String output) throws Exception {
 569         String split = "b        compiler.valhalla.valuetypes.";
 570         String[] compilations = output.split(split);
 571         // Print header
 572         System.out.println(compilations[0]);
 573         // Iterate over compilation output
 574         for (String graph : compilations) {
 575             String[] lines = graph.split("\\n");
 576             String testName = lines[0].split(" ")[0];
 577             Method test = tests.get(testName);
 578             if (test == null) {
 579                 // Skip helper methods
 580                 continue;
 581             }


 627 
 628     public void setup(Method[] methods) {
 629         for (Method m : methods) {
 630             if (m.isAnnotationPresent(Test.class)) {
 631                 // Don't inline tests
 632                 WHITE_BOX.testSetDontInlineMethod(m, true);
 633             }
 634             if (m.isAnnotationPresent(DontCompile.class)) {
 635                 WHITE_BOX.makeMethodNotCompilable(m, COMP_LEVEL_ANY, true);
 636                 WHITE_BOX.makeMethodNotCompilable(m, COMP_LEVEL_ANY, false);
 637             }
 638             if (m.isAnnotationPresent(ForceInline.class)) {
 639                 WHITE_BOX.testSetForceInlineMethod(m, true);
 640             } else if (m.isAnnotationPresent(DontInline.class)) {
 641                 WHITE_BOX.testSetDontInlineMethod(m, true);
 642             }
 643         }
 644     }
 645 
 646     public void run() throws Exception {
 647         System.out.format("rI = %d, rL = %d\n", rI, rL);
 648         setup(this.getClass().getDeclaredMethods());
 649         setup(MyValue1.class.getDeclaredMethods());
 650         setup(MyValue2.class.getDeclaredMethods());
 651 
 652         // Execute tests
 653         for (Method test : tests.values()) {
 654             Method verifier = getClass().getDeclaredMethod(test.getName() + "_verifier", boolean.class);
 655             // Warmup using verifier method
 656             for (int i = 0; i < WARMUP; ++i) {
 657                 verifier.invoke(this, true);
 658             }
 659             // Trigger compilation
 660             WHITE_BOX.enqueueMethodForCompilation(test, COMP_LEVEL_FULL_OPTIMIZATION);
 661             Asserts.assertTrue(!USE_COMPILER || WHITE_BOX.isMethodCompiled(test, false), test + " not compiled");
 662             // Check result
 663             verifier.invoke(this, false);
 664         }
 665     }
 666 }
 667 
 668 // Mark method as test
 669 @Retention(RetentionPolicy.RUNTIME)
 670 @interface Test {
 671     // Regular expression used to match forbidden IR nodes
 672     // in the C2 IR emitted for this test.
 673     String failOn() default "";
 674     // Regular expressions used to match and count IR nodes.
 675     String[] match() default { };
 676     int[] matchCount() default { };
 677 }
 678 
 679 // Force method inlining during compilation
 680 @Retention(RetentionPolicy.RUNTIME)
 681 @interface ForceInline { }
< prev index next >