1 /*
   2  * Copyright (c) 2021, 2025, 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.inlinetypes;
  25 
  26 import compiler.lib.ir_framework.*;
  27 import jdk.test.lib.Asserts;
  28 
  29 import java.lang.reflect.Method;
  30 
  31 import jdk.internal.value.ValueClass;
  32 import jdk.internal.vm.annotation.LooselyConsistentValue;
  33 import jdk.internal.vm.annotation.NullRestricted;
  34 import jdk.internal.vm.annotation.Strict;
  35 
  36 import static compiler.valhalla.inlinetypes.InlineTypeIRNode.*;
  37 import static compiler.valhalla.inlinetypes.InlineTypes.*;
  38 
  39 /*
  40  * @test
  41  * @key randomness
  42  * @bug 8327695
  43  * @summary Test the basic value class implementation in C2.
  44  * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64")
  45  * @library /test/lib /
  46  * @enablePreview
  47  * @modules java.base/jdk.internal.value
  48  *          java.base/jdk.internal.vm.annotation
  49  * @run main/othervm/timeout=300 compiler.valhalla.inlinetypes.TestBasicFunctionality
  50  */
  51 
  52 @ForceCompileClassInitializer
  53 public class TestBasicFunctionality {
  54 
  55     public static void main(String[] args) {
  56         InlineTypes.getFramework()
  57                    .addScenarios(InlineTypes.DEFAULT_SCENARIOS)
  58                    .addHelperClasses(MyValue1.class,
  59                                      MyValue2.class,
  60                                      MyValue2Inline.class,
  61                                      MyValue3.class,
  62                                      MyValue3Inline.class)
  63                    .start();
  64     }
  65 
  66     protected long hash() {
  67         return hash(rI, rL);
  68     }
  69 
  70     protected long hash(int x, long y) {
  71         return MyValue1.createWithFieldsInline(x, y).hash();
  72     }
  73 
  74     @DontInline
  75     static void call() {}
  76 
  77     // Receive value class through call to interpreter
  78     @Test
  79     @IR(failOn = {ALLOC, STORE, TRAP})
  80     public long test1() {
  81         MyValue1 v = MyValue1.createWithFieldsDontInline(rI, rL);
  82         return v.hash();
  83     }
  84 
  85     @Run(test = "test1")
  86     public void test1_verifier() {
  87         long result = test1();
  88         Asserts.assertEQ(result, hash());
  89     }
  90 
  91     // Receive value object from interpreter via parameter
  92     @Test
  93     @IR(failOn = {ALLOC, STORE, TRAP})
  94     public long test2(MyValue1 v) {
  95         return v.hash();
  96     }
  97 
  98     @Run(test = "test2")
  99     public void test2_verifier() {
 100         MyValue1 v = MyValue1.createWithFieldsDontInline(rI, rL);
 101         long result = test2(v);
 102         Asserts.assertEQ(result, hash());
 103     }
 104 
 105     // Return incoming value object without accessing fields
 106     @Test
 107     @IR(applyIf = {"InlineTypePassFieldsAsArgs", "true"},
 108         counts = {ALLOC, "= 1", STORE, "= 19"},
 109         failOn = {LOAD, TRAP})
 110     @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"},
 111         failOn = {ALLOC, LOAD, STORE, TRAP})
 112     public MyValue1 test3(MyValue1 v) {
 113         return v;
 114     }
 115 
 116     @Run(test = "test3")
 117     public void test3_verifier() {
 118         MyValue1 v1 = MyValue1.createWithFieldsDontInline(rI, rL);
 119         MyValue1 v2 = test3(v1);
 120         Asserts.assertEQ(v1.x, v2.x);
 121         Asserts.assertEQ(v1.y, v2.y);
 122     }
 123 
 124     // Create a value object in compiled code and only use fields.
 125     // Allocation should go away because value object does not escape.
 126     @Test
 127     @IR(failOn = {ALLOC, LOAD, STORE, TRAP})
 128     public long test4() {
 129         MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
 130         return v.hash();
 131     }
 132 
 133     @Run(test = "test4")
 134     public void test4_verifier() {
 135         long result = test4();
 136         Asserts.assertEQ(result, hash());
 137     }
 138 
 139     // Create a value object in compiled code and pass it to
 140     // an inlined compiled method via a call.
 141     @Test
 142     @IR(failOn = {ALLOC, LOAD, STORE, TRAP})
 143     public long test5() {
 144         MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
 145         return test5Inline(v);
 146     }
 147 
 148     @ForceInline
 149     public long test5Inline(MyValue1 v) {
 150         return v.hash();
 151     }
 152 
 153     @Run(test = "test5")
 154     public void test5_verifier() {
 155         long result = test5();
 156         Asserts.assertEQ(result, hash());
 157     }
 158 
 159     // Create a value object in compiled code and pass it to
 160     // the interpreter via a call.
 161     @Test
 162     @IR(applyIf = {"InlineTypePassFieldsAsArgs", "true"},
 163         counts = {ALLOC, "<= 1"}, // 1 MyValue2 allocation (if not the all-zero value)
 164         failOn = {LOAD, TRAP})
 165     @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"},
 166         counts = {ALLOC, "<= 2"}, // 1 MyValue1 and 1 MyValue2 allocation (if not the all-zero value)
 167         failOn = {LOAD, TRAP})
 168     public long test6() {
 169         MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
 170         // Pass to interpreter
 171         return v.hashInterpreted();
 172     }
 173 
 174     @Run(test = "test6")
 175     public void test6_verifier() {
 176         long result = test6();
 177         Asserts.assertEQ(result, hash());
 178     }
 179 
 180     // Create a value object in compiled code and pass it to
 181     // the interpreter by returning.
 182     @Test
 183     @IR(counts = {ALLOC, "<= 2"},
 184         failOn = {LOAD, TRAP})
 185     public MyValue1 test7(int x, long y) {
 186         return MyValue1.createWithFieldsInline(x, y);
 187     }
 188 
 189     @Run(test = "test7")
 190     public void test7_verifier() {
 191         MyValue1 v = test7(rI, rL);
 192         Asserts.assertEQ(v.hash(), hash());
 193     }
 194 
 195     // Merge value objects created from two branches
 196     @Test
 197     @IR(failOn = {ALLOC, STORE, TRAP})
 198     public long test8(boolean b) {
 199         MyValue1 v;
 200         if (b) {
 201             v = MyValue1.createWithFieldsInline(rI, rL);
 202         } else {
 203             v = MyValue1.createWithFieldsDontInline(rI + 1, rL + 1);
 204         }
 205         return v.hash();
 206     }
 207 
 208     @Run(test = "test8")
 209     public void test8_verifier() {
 210         Asserts.assertEQ(test8(true), hash());
 211         Asserts.assertEQ(test8(false), hash(rI + 1, rL + 1));
 212     }
 213 
 214 static MyValue1 tmp = null;
 215     // Merge value objects created from two branches
 216     @Test
 217     @IR(applyIf = {"InlineTypePassFieldsAsArgs", "true"},
 218         counts = {ALLOC, "= 1", LOAD, "= 19",
 219                   STORE, "= 3"}, // InitializeNode::coalesce_subword_stores merges stores
 220         failOn = {TRAP})
 221     @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"},
 222         counts = {ALLOC, "= 2", STORE, "= 19"},
 223         failOn = {LOAD, TRAP})
 224     public MyValue1 test9(boolean b, int localrI, long localrL) {
 225         MyValue1 v;
 226         if (b) {
 227             // Value object is not allocated
 228             // Do not use rI/rL directly here as null values may cause
 229             // some redundant null initializations to be optimized out
 230             // and matching to fail.
 231             v = MyValue1.createWithFieldsInline(localrI, localrL);
 232             v.hashInterpreted();
 233         } else {
 234             // Value object is allocated by the callee
 235             v = MyValue1.createWithFieldsDontInline(rI + 1, rL + 1);
 236         }
 237         // Need to allocate value object if 'b' is true
 238         long sum = v.hashInterpreted();
 239         if (b) {
 240             v = MyValue1.createWithFieldsDontInline(rI, sum);
 241         } else {
 242             v = MyValue1.createWithFieldsDontInline(rI, sum + 1);
 243         }
 244         // Don't need to allocate value object because both branches allocate
 245         return v;
 246     }
 247 
 248     @Run(test = "test9")
 249     public void test9_verifier() {
 250         MyValue1 v = test9(true, rI, rL);
 251         Asserts.assertEQ(v.x, rI);
 252         Asserts.assertEQ(v.y, hash());
 253         v = test9(false, rI, rL);
 254         Asserts.assertEQ(v.x, rI);
 255         Asserts.assertEQ(v.y, hash(rI + 1, rL + 1) + 1);
 256     }
 257 
 258     // Merge value objects created in a loop (not inlined)
 259     @Test
 260     @IR(failOn = {ALLOC, STORE, TRAP})
 261     public long test10(int x, long y) {
 262         MyValue1 v = MyValue1.createWithFieldsDontInline(x, y);
 263         for (int i = 0; i < 10; ++i) {
 264             v = MyValue1.createWithFieldsDontInline(v.x + 1, v.y + 1);
 265         }
 266         return v.hash();
 267     }
 268 
 269     @Run(test = "test10")
 270     public void test10_verifier() {
 271         long result = test10(rI, rL);
 272         Asserts.assertEQ(result, hash(rI + 10, rL + 10));
 273     }
 274 
 275     // Merge value objects created in a loop (inlined)
 276     @Test
 277     @IR(failOn = {ALLOC, LOAD, STORE, TRAP})
 278     public long test11(int x, long y) {
 279         MyValue1 v = MyValue1.createWithFieldsInline(x, y);
 280         for (int i = 0; i < 10; ++i) {
 281             v = MyValue1.createWithFieldsInline(v.x + 1, v.y + 1);
 282         }
 283         return v.hash();
 284     }
 285 
 286     @Run(test = "test11")
 287     public void test11_verifier() {
 288         long result = test11(rI, rL);
 289         Asserts.assertEQ(result, hash(rI + 10, rL + 10));
 290     }
 291 
 292     // Test loop with uncommon trap referencing a value object
 293     @Test
 294     @IR(applyIf = {"UseArrayFlattening", "true"},
 295         counts = {SCOBJ, ">= 1", LOAD, "<= 12"}) // TODO 8227588 (loads should be removed)
 296     public long test12(boolean b) {
 297         MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
 298         MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedNonAtomicArray(MyValue1.class, Math.abs(rI) % 10, MyValue1.DEFAULT);
 299         for (int i = 0; i < va.length; ++i) {
 300             va[i] = MyValue1.createWithFieldsInline(rI, rL);
 301         }
 302         long result = rL;
 303         for (int i = 0; i < 1000; ++i) {
 304             if (b) {
 305                 result += v.x;
 306             } else {
 307                 // Uncommon trap referencing v. We delegate allocation to the
 308                 // interpreter by adding a SafePointScalarObjectNode.
 309                 result = v.hashInterpreted();
 310                 for (int j = 0; j < va.length; ++j) {
 311                     result += va[j].hash();
 312                 }
 313             }
 314         }
 315         return result;
 316     }
 317 
 318     @Run(test = "test12")
 319     public void test12_verifier(RunInfo info) {
 320         long result = test12(info.isWarmUp());
 321         Asserts.assertEQ(result, info.isWarmUp() ? rL + (1000 * rI) : ((Math.abs(rI) % 10) + 1) * hash());
 322     }
 323 
 324     // Test loop with uncommon trap referencing a value object
 325     @Test
 326     public long test13(boolean b) {
 327         MyValue1 v = MyValue1.createWithFieldsDontInline(rI, rL);
 328         MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedNonAtomicArray(MyValue1.class, Math.abs(rI) % 10, MyValue1.DEFAULT);
 329         for (int i = 0; i < va.length; ++i) {
 330             va[i] = MyValue1.createWithFieldsDontInline(rI, rL);
 331         }
 332         long result = rL;
 333         for (int i = 0; i < 1000; ++i) {
 334             if (b) {
 335                 result += v.x;
 336             } else {
 337                 // Uncommon trap referencing v. Should not allocate
 338                 // but just pass the existing oop to the uncommon trap.
 339                 result = v.hashInterpreted();
 340                 for (int j = 0; j < va.length; ++j) {
 341                     result += va[j].hashInterpreted();
 342                 }
 343             }
 344         }
 345         return result;
 346     }
 347 
 348     @Run(test = "test13")
 349     public void test13_verifier(RunInfo info) {
 350         long result = test13(info.isWarmUp());
 351         Asserts.assertEQ(result, info.isWarmUp() ? rL + (1000 * rI) : ((Math.abs(rI) % 10) + 1) * hash());
 352     }
 353 
 354     // Create a value object in a non-inlined method and then call a
 355     // non-inlined method on that value object.
 356     @Test
 357     @IR(applyIf = {"InlineTypePassFieldsAsArgs", "true"},
 358         failOn = {ALLOC, STORE, TRAP},
 359         counts = {LOAD, "= 19"})
 360     @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"},
 361         failOn = {ALLOC, LOAD, STORE, TRAP})
 362     public long test14() {
 363         MyValue1 v = MyValue1.createWithFieldsDontInline(rI, rL);
 364         return v.hashInterpreted();
 365     }
 366 
 367     @Run(test = "test14")
 368     public void test14_verifier() {
 369         long result = test14();
 370         Asserts.assertEQ(result, hash());
 371     }
 372 
 373     // Create a value object in an inlined method and then call a
 374     // non-inlined method on that value object.
 375     @Test
 376     @IR(applyIf = {"InlineTypePassFieldsAsArgs", "true"},
 377         failOn = {LOAD, TRAP},
 378         counts = {ALLOC, "<= 1"}) // 1 MyValue2 allocation (if not the all-zero value)
 379     @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"},
 380         failOn = {LOAD, TRAP},
 381         counts = {ALLOC, "<= 2"}) // 1 MyValue1 and 1 MyValue2 allocation (if not the all-zero value)
 382     public long test15() {
 383         MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
 384         return v.hashInterpreted();
 385     }
 386 
 387     @Run(test = "test15")
 388     public void test15_verifier() {
 389         long result = test15();
 390         Asserts.assertEQ(result, hash());
 391     }
 392 
 393     // Create a value object in a non-inlined method and then call an
 394     // inlined method on that value object.
 395     @Test
 396     @IR(failOn = {ALLOC, STORE, TRAP})
 397     public long test16() {
 398         MyValue1 v = MyValue1.createWithFieldsDontInline(rI, rL);
 399         return v.hash();
 400     }
 401 
 402     @Run(test = "test16")
 403     public void test16_verifier() {
 404         long result = test16();
 405         Asserts.assertEQ(result, hash());
 406     }
 407 
 408     // Create a value object in an inlined method and then call an
 409     // inlined method on that value object.
 410     @Test
 411     @IR(failOn = {ALLOC, LOAD, STORE, TRAP})
 412     public long test17() {
 413         MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
 414         return v.hash();
 415     }
 416 
 417     @Run(test = "test17")
 418     public void test17_verifier() {
 419         long result = test17();
 420         Asserts.assertEQ(result, hash());
 421     }
 422 
 423     // Create a value object in compiled code and pass it to the
 424     // interpreter via a call. The value object is live at the first call so
 425     // debug info should include a reference to all its fields.
 426     @Test
 427     @IR(applyIf = {"InlineTypePassFieldsAsArgs", "true"},
 428         counts = {ALLOC, "<= 1"}, // 1 MyValue2 allocation (if not the all-zero value)
 429         failOn = {LOAD, TRAP})
 430     @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"},
 431         counts = {ALLOC, "<= 2"}, // 1 MyValue1 and 1 MyValue2 allocation (if not the all-zero value)
 432         failOn = {LOAD, TRAP})
 433     public long test18() {
 434         MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
 435         v.hashInterpreted();
 436         return v.hashInterpreted();
 437     }
 438 
 439     @Run(test = "test18")
 440     public void test18_verifier() {
 441         long result = test18();
 442         Asserts.assertEQ(result, hash());
 443     }
 444 
 445     // Create a value object in compiled code and pass it to the
 446     // interpreter via a call. The value object is passed twice but
 447     // should only be allocated once.
 448     @Test
 449     @IR(applyIf = {"InlineTypePassFieldsAsArgs", "true"},
 450         counts = {ALLOC, "<= 1"}, // 1 MyValue2 allocation (if not the all-zero value)
 451         failOn = {LOAD, TRAP})
 452     @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"},
 453         counts = {ALLOC, "<= 2"}, // 1 MyValue1 and 1 MyValue2 allocation (if not the all-zero value)
 454         failOn = {LOAD, TRAP})
 455     public long test19() {
 456         MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
 457         return sumValue(v, v);
 458     }
 459 
 460     @DontCompile
 461     public long sumValue(MyValue1 v, MyValue1 dummy) {
 462         return v.hash();
 463     }
 464 
 465     @Run(test = "test19")
 466     public void test19_verifier() {
 467         long result = test19();
 468         Asserts.assertEQ(result, hash());
 469     }
 470 
 471     // Create a value type (array) in compiled code and pass it to the
 472     // interpreter via a call. The value object is live at the uncommon
 473     // trap: verify that deoptimization causes the value object to be
 474     // correctly allocated.
 475     @Test
 476     @IR(applyIf = {"InlineTypePassFieldsAsArgs", "true"},
 477         counts = {ALLOC, "<= 1"}, // 1 MyValue2 allocation (if not the all-zero value)
 478         failOn = {LOAD})
 479     // TODO 8350865
 480     //@IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"},
 481     //    counts = {ALLOC, "<= 2"}, // 1 MyValue1 and 1 MyValue2 allocation (if not the all-zero value)
 482     //    failOn = LOAD)
 483     public long test20(boolean deopt, Method m) {
 484         MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
 485         MyValue2[] va = (MyValue2[])ValueClass.newNullRestrictedNonAtomicArray(MyValue2.class, 3, MyValue2.DEFAULT);
 486         if (deopt) {
 487             // uncommon trap
 488             TestFramework.deoptimize(m);
 489         }
 490 
 491         return v.hashInterpreted() + va[0].hashInterpreted() +
 492                va[1].hashInterpreted() + va[2].hashInterpreted();
 493     }
 494 
 495     @Run(test = "test20")
 496     public void test20_verifier(RunInfo info) {
 497         MyValue2[] va = (MyValue2[])ValueClass.newNullRestrictedNonAtomicArray(MyValue2.class, 42, MyValue2.DEFAULT);
 498         long result = test20(!info.isWarmUp(), info.getTest());
 499         Asserts.assertEQ(result, hash() + va[0].hash() + va[1].hash() + va[2].hash());
 500     }
 501 
 502     // Value class fields in regular object
 503     MyValue1 val1;
 504     MyValue2 val2;
 505     @Strict
 506     @NullRestricted
 507     final MyValue1 val3 = MyValue1.createWithFieldsInline(rI, rL);
 508     @Strict
 509     @NullRestricted
 510     static MyValue1 val4 = MyValue1.DEFAULT;
 511     @Strict
 512     @NullRestricted
 513     static final MyValue1 val5 = MyValue1.createWithFieldsInline(rI, rL);
 514 
 515     // Test value class fields in objects
 516     @Test
 517     @IR(counts = {ALLOC, "= 4"}, failOn = TRAP)
 518     public long test21(int x, long y) {
 519         // Compute hash of value class fields
 520         long result = val1.hash() + val2.hash() + val3.hash() + val4.hash() + val5.hash();
 521         // Update fields
 522         val1 = MyValue1.createWithFieldsInline(x, y);
 523         val2 = MyValue2.createWithFieldsInline(x, rD);
 524         val4 = MyValue1.createWithFieldsInline(x, y);
 525         return result;
 526     }
 527 
 528     @Run(test = "test21")
 529     public void test21_verifier() {
 530         // Check if hash computed by test18 is correct
 531         val1 = MyValue1.createWithFieldsInline(rI, rL);
 532         val2 = val1.v2;
 533         // val3 is initialized in the constructor
 534         val4 = val1;
 535         // val5 is initialized in the static initializer
 536         long hash = val1.hash() + val2.hash() + val3.hash() + val4.hash() + val5.hash();
 537         long result = test21(rI + 1, rL + 1);
 538         Asserts.assertEQ(result, hash);
 539         // Check if value class fields were updated
 540         Asserts.assertEQ(val1.hash(), hash(rI + 1, rL + 1));
 541         Asserts.assertEQ(val2.hash(), MyValue2.createWithFieldsInline(rI + 1, rD).hash());
 542         Asserts.assertEQ(val4.hash(), hash(rI + 1, rL + 1));
 543     }
 544 
 545     // Test folding of constant value class fields
 546     @Test
 547     @IR(failOn = {ALLOC, LOAD, STORE, LOOP, TRAP})
 548     public long test22() {
 549         // This should be constant folded
 550         return val5.hash() + val5.v3.hash();
 551     }
 552 
 553     @Run(test = "test22")
 554     public void test22_verifier() {
 555         long result = test22();
 556         Asserts.assertEQ(result, val5.hash() + val5.v3.hash());
 557     }
 558 
 559     // Test aconst_init
 560     @Test
 561     @IR(failOn = {ALLOC, LOAD, STORE, LOOP, TRAP})
 562     public long test23() {
 563         MyValue2 v = MyValue2.createDefaultInline();
 564         return v.hash();
 565     }
 566 
 567     @Run(test = "test23")
 568     public void test23_verifier() {
 569         long result = test23();
 570         Asserts.assertEQ(result, MyValue2.createDefaultInline().hash());
 571     }
 572 
 573     // Test aconst_init
 574     @Test
 575     @IR(failOn = {ALLOC, STORE, LOOP, TRAP})
 576     public long test24() {
 577         MyValue1 v1 = MyValue1.createDefaultInline();
 578         MyValue1 v2 = MyValue1.createDefaultDontInline();
 579         return v1.hashPrimitive() + v2.hashPrimitive();
 580     }
 581 
 582     @Run(test = "test24")
 583     public void test24_verifier() {
 584         long result = test24();
 585         Asserts.assertEQ(result, 2 * MyValue1.createDefaultInline().hashPrimitive());
 586     }
 587 
 588     // Test field initialization
 589     @Test
 590     @IR(failOn = {ALLOC, LOAD, STORE, LOOP, TRAP})
 591     public long test25() {
 592         MyValue2 v = MyValue2.createWithFieldsInline(rI, rD);
 593         return v.hash();
 594     }
 595 
 596     @Run(test = "test25")
 597     public void test25_verifier() {
 598         long result = test25();
 599         Asserts.assertEQ(result, MyValue2.createWithFieldsInline(rI, rD).hash());
 600     }
 601 
 602     // Test field initialization
 603     @Test
 604     @IR(failOn = {ALLOC, STORE, LOOP, TRAP})
 605     public long test26() {
 606         MyValue1 v1 = MyValue1.createWithFieldsInline(rI, rL);
 607         MyValue1 v2 = MyValue1.createWithFieldsDontInline(rI, rL);
 608         return v1.hash() + v2.hash();
 609     }
 610 
 611     @Run(test = "test26")
 612     public void test26_verifier() {
 613         long result = test26();
 614         Asserts.assertEQ(result, 2 * hash());
 615     }
 616 
 617     class TestClass27 {
 618         @Strict
 619         @NullRestricted
 620         public MyValue1 v = MyValue1.DEFAULT;
 621     }
 622 
 623     // Test allocation elimination of unused object with initialized value class field
 624     @Test
 625     @IR(failOn = {ALLOC, LOAD, STORE, LOOP})
 626     public void test27(boolean deopt, Method m) {
 627         TestClass27 unused = new TestClass27();
 628         MyValue1 v = MyValue1.createWithFieldsInline(rI, rL);
 629         unused.v = v;
 630         if (deopt) {
 631             // uncommon trap
 632             TestFramework.deoptimize(m);
 633         }
 634     }
 635 
 636     @Run(test = "test27")
 637     public void test27_verifier(RunInfo info) {
 638         test27(!info.isWarmUp(), info.getTest());
 639     }
 640 
 641     @Strict
 642     @NullRestricted
 643     static MyValue3 staticVal3 = MyValue3.DEFAULT;
 644     @Strict
 645     @NullRestricted
 646     static MyValue3 staticVal3_copy = MyValue3.DEFAULT;
 647 
 648     // Check elimination of redundant value class allocations
 649     @Test
 650     @IR(counts = {ALLOC, "= 1"})
 651     public MyValue3 test28(MyValue3[] va) {
 652         // Create value object and force allocation
 653         MyValue3 vt = MyValue3.create();
 654         va[0] = vt;
 655         staticVal3 = vt;
 656         vt.verify(staticVal3);
 657 
 658         // Value object is now allocated, make a copy and force allocation.
 659         // Because copy is equal to vt, C2 should remove this redundant allocation.
 660         MyValue3 copy = MyValue3.setC(vt, vt.c);
 661         va[0] = copy;
 662         staticVal3_copy = copy;
 663         copy.verify(staticVal3_copy);
 664         return copy;
 665     }
 666 
 667     @Run(test = "test28")
 668     public void test28_verifier() {
 669         MyValue3[] va = (MyValue3[])ValueClass.newNullRestrictedNonAtomicArray(MyValue3.class, 1, MyValue3.DEFAULT);
 670         MyValue3 vt = test28(va);
 671         staticVal3.verify(vt);
 672         staticVal3.verify(va[0]);
 673         staticVal3_copy.verify(vt);
 674         staticVal3_copy.verify(va[0]);
 675     }
 676 
 677     // Verify that only dominating allocations are re-used
 678     @Test
 679     public MyValue3 test29(boolean warmup) {
 680         MyValue3 vt = MyValue3.create();
 681         if (warmup) {
 682             staticVal3 = vt; // Force allocation
 683         }
 684         // Force allocation to verify that above
 685         // non-dominating allocation is not re-used
 686         MyValue3 copy = MyValue3.setC(vt, vt.c);
 687         staticVal3_copy = copy;
 688         copy.verify(vt);
 689         return copy;
 690     }
 691 
 692     @Run(test = "test29")
 693     public void test29_verifier(RunInfo info) {
 694         MyValue3 vt = test29(info.isWarmUp());
 695         if (info.isWarmUp()) {
 696             staticVal3.verify(vt);
 697         }
 698     }
 699 
 700     // Verify that C2 recognizes value class loads and re-uses the oop to avoid allocations
 701     @Test
 702     @IR(applyIf = {"UseArrayFlattening", "true"},
 703         failOn = {ALLOC, ALLOCA, STORE})
 704     public MyValue3 test30() {
 705         // C2 can re-use the oop of staticVal3 because staticVal3 is equal to copy
 706         MyValue3[] va = (MyValue3[])ValueClass.newNullRestrictedNonAtomicArray(MyValue3.class, 1, MyValue3.DEFAULT);
 707         MyValue3 copy = MyValue3.copy(staticVal3);
 708         va[0] = copy;
 709         copy.verify(va[0]);
 710         staticVal3 = copy;
 711         copy.verify(staticVal3);
 712         return copy;
 713     }
 714 
 715     @Run(test = "test30")
 716     public void test30_verifier() {
 717         staticVal3 = MyValue3.create();
 718         MyValue3 vt = test30();
 719         staticVal3.verify(vt);
 720     }
 721 
 722     // Verify that C2 recognizes value class loads and re-uses the oop to avoid allocations
 723     @Test
 724     @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"},
 725         failOn = {ALLOC, ALLOCA, STORE})
 726     public MyValue3 test31() {
 727         // C2 can re-use the oop returned by createDontInline()
 728         // because the corresponding value object is equal to 'copy'.
 729         MyValue3[] va = (MyValue3[])ValueClass.newNullRestrictedNonAtomicArray(MyValue3.class, 1, MyValue3.DEFAULT);
 730         MyValue3 copy = MyValue3.copy(MyValue3.createDontInline());
 731         va[0] = copy;
 732         copy.verify(va[0]);
 733         staticVal3 = copy;
 734         copy.verify(staticVal3);
 735         return copy;
 736     }
 737 
 738     @Run(test = "test31")
 739     public void test31_verifier() {
 740         MyValue3 vt = test31();
 741         staticVal3.verify(vt);
 742     }
 743 
 744     // Verify that C2 recognizes value class loads and re-uses the oop to avoid allocations
 745     @Test
 746     @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"},
 747         failOn = {ALLOC, ALLOCA, STORE})
 748     public MyValue3 test32(MyValue3 vt) {
 749         // C2 can re-use the oop of vt because vt is equal to 'copy'.
 750         MyValue3[] va = (MyValue3[])ValueClass.newNullRestrictedNonAtomicArray(MyValue3.class, 1, MyValue3.DEFAULT);
 751         MyValue3 copy = MyValue3.copy(vt);
 752         va[0] = copy;
 753         copy.verify(vt);
 754         staticVal3 = copy;
 755         copy.verify(staticVal3);
 756         return copy;
 757     }
 758 
 759     @Run(test = "test32")
 760     public void test32_verifier() {
 761         MyValue3 vt = MyValue3.create();
 762         MyValue3 result = test32(vt);
 763         staticVal3.verify(vt);
 764         result.verify(vt);
 765     }
 766 
 767     // Test correct identification of value object copies
 768     @Test
 769     public MyValue3 test33() {
 770         MyValue3[] va = (MyValue3[])ValueClass.newNullRestrictedNonAtomicArray(MyValue3.class, 1, MyValue3.DEFAULT);
 771         MyValue3 vt = MyValue3.copy(staticVal3);
 772         vt = MyValue3.setI(vt, vt.c);
 773         // vt is not equal to staticVal3, so C2 should not re-use the oop
 774         va[0] = vt;
 775         Asserts.assertEQ(va[0].i, (int)vt.c);
 776         staticVal3 = vt;
 777         vt.verify(staticVal3);
 778         return vt;
 779     }
 780 
 781     @Run(test = "test33")
 782     public void test33_verifier() {
 783         staticVal3 = MyValue3.create();
 784         MyValue3 vt = test33();
 785         Asserts.assertEQ(staticVal3.i, (int)staticVal3.c);
 786         Asserts.assertEQ(vt.i, (int)staticVal3.c);
 787     }
 788 
 789     static final MyValue3[] test34Array = (MyValue3[])ValueClass.newNullRestrictedNonAtomicArray(MyValue3.class, 2, MyValue3.DEFAULT);
 790 
 791     // Verify that the all-zero value class is never allocated.
 792     // C2 code should load and use the all-zero oop from the java mirror.
 793     @Test
 794     // The concept of a pre-allocated "all-zero value" was removed.
 795     // @IR(applyIf = {"UseArrayFlattening", "true"},
 796     //     failOn = {ALLOC, ALLOCA, LOAD, STORE, LOOP, TRAP})
 797     public MyValue3 test34() {
 798         // Explicitly create all-zero value
 799         MyValue3 vt = MyValue3.createDefault();
 800         test34Array[0] = vt;
 801         staticVal3 = vt;
 802         vt.verify(vt);
 803 
 804         // Load all-zero value from uninitialized value class array
 805         MyValue3[] dva = (MyValue3[])ValueClass.newNullRestrictedNonAtomicArray(MyValue3.class, 1, MyValue3.DEFAULT);
 806         staticVal3_copy = dva[0];
 807         test34Array[1] = dva[0];
 808         dva[0].verify(dva[0]);
 809         return vt;
 810     }
 811 
 812     @Run(test = "test34")
 813     public void test34_verifier() {
 814         MyValue3 vt = MyValue3.createDefault();
 815         test34Array[0] = MyValue3.create();
 816         test34Array[1] = MyValue3.create();
 817         MyValue3 res = test34();
 818         res.verify(vt);
 819         staticVal3.verify(vt);
 820         staticVal3_copy.verify(vt);
 821         test34Array[0].verify(vt);
 822         test34Array[1].verify(vt);
 823     }
 824 
 825     static final MyValue3[] test35Array = (MyValue3[])ValueClass.newNullRestrictedNonAtomicArray(MyValue3.class, 1, MyValue3.DEFAULT);
 826 
 827     // Same as above but manually initialize value class fields to all-zero.
 828     @Test
 829     // The concept of a pre-allocated "all-zero value" was removed.
 830     // @IR(applyIf = {"UseArrayFlattening", "true"},
 831     //     failOn = {ALLOC, ALLOCA, LOAD, STORE, LOOP, TRAP})
 832     public MyValue3 test35(MyValue3 vt) {
 833         vt = MyValue3.setC(vt, (char)0);
 834         vt = MyValue3.setBB(vt, (byte)0);
 835         vt = MyValue3.setS(vt, (short)0);
 836         vt = MyValue3.setI(vt, 0);
 837         vt = MyValue3.setL(vt, 0);
 838         vt = MyValue3.setO(vt, null);
 839         vt = MyValue3.setF1(vt, 0);
 840         vt = MyValue3.setF2(vt, 0);
 841         vt = MyValue3.setF3(vt, 0);
 842         vt = MyValue3.setF4(vt, 0);
 843         vt = MyValue3.setF5(vt, 0);
 844         vt = MyValue3.setF6(vt, 0);
 845         vt = MyValue3.setV1(vt, MyValue3Inline.createDefault());
 846         test35Array[0] = vt;
 847         staticVal3 = vt;
 848         vt.verify(vt);
 849         return vt;
 850     }
 851 
 852     @Run(test = "test35")
 853     public void test35_verifier() {
 854         MyValue3 vt = MyValue3.createDefault();
 855         test35Array[0] = MyValue3.create();
 856         MyValue3 res = test35(test35Array[0]);
 857         res.verify(vt);
 858         staticVal3.verify(vt);
 859         test35Array[0].verify(vt);
 860     }
 861 
 862     // Merge value objects created from two branches
 863 
 864     private Object test36_helper(Object v) {
 865         return v;
 866     }
 867 
 868     @Test
 869     @IR(failOn = {ALLOC, STORE, TRAP})
 870     public long test36(boolean b) {
 871         Object o;
 872         if (b) {
 873             o = test36_helper(MyValue1.createWithFieldsInline(rI, rL));
 874         } else {
 875             o = test36_helper(MyValue1.createWithFieldsDontInline(rI + 1, rL + 1));
 876         }
 877         MyValue1 v = (MyValue1)o;
 878         return v.hash();
 879     }
 880 
 881     @Run(test = "test36")
 882     public void test36_verifier() {
 883         Asserts.assertEQ(test36(true), hash());
 884         Asserts.assertEQ(test36(false), hash(rI + 1, rL + 1));
 885     }
 886 
 887     // Test correct loading of flattened fields
 888     @LooselyConsistentValue
 889     value class Test37Value2 {
 890         int x = 0;
 891         int y = 0;
 892     }
 893 
 894     @LooselyConsistentValue
 895     value class Test37Value1 {
 896         double d = 0;
 897         float f = 0;
 898         @Strict
 899         @NullRestricted
 900         Test37Value2 v = new Test37Value2();
 901     }
 902 
 903     @Test
 904     public Test37Value1 test37(Test37Value1 vt) {
 905         return vt;
 906     }
 907 
 908     @Run(test = "test37")
 909     public void test37_verifier() {
 910         Test37Value1 vt = new Test37Value1();
 911         Asserts.assertEQ(test37(vt), vt);
 912     }
 913 
 914     // Test elimination of value class allocations without a unique CheckCastPP
 915     @LooselyConsistentValue
 916     static value class Test38Value {
 917         public int i;
 918         public Test38Value(int i) { this.i = i; }
 919     }
 920 
 921     @Strict
 922     @NullRestricted
 923     static Test38Value test38Field = new Test38Value(0);
 924 
 925     @Test
 926     public void test38() {
 927         for (int i = 3; i < 100; ++i) {
 928             int j = 1;
 929             while (++j < 11) {
 930                 try {
 931                     test38Field = new Test38Value(i);
 932                 } catch (ArithmeticException ae) { }
 933             }
 934         }
 935     }
 936 
 937     @Run(test = "test38")
 938     public void test38_verifier() {
 939         test38Field = new Test38Value(0);
 940         test38();
 941         Asserts.assertEQ(test38Field, new Test38Value(99));
 942     }
 943 
 944     // Tests split if with value class Phi users
 945     @LooselyConsistentValue
 946     static value class Test39Value {
 947         public int iFld1;
 948         public int iFld2;
 949 
 950         public Test39Value(int i1, int i2) { iFld1 = i1; iFld2 = i2; }
 951     }
 952 
 953     static int test39A1[][] = new int[400][400];
 954     static double test39A2[] = new double[400];
 955     @Strict
 956     @NullRestricted
 957     static Test39Value test39Val = new Test39Value(0, 0);
 958 
 959     @DontInline
 960     public int[] getArray() {
 961         return new int[400];
 962     }
 963 
 964     @Test
 965     public int test39() {
 966         int result = 0;
 967         for (int i = 0; i < 100; ++i) {
 968             switch ((i >>> 1) % 3) {
 969                 case 0:
 970                     test39A1[i][i] = i;
 971                     break;
 972                 case 1:
 973                     for (int j = 0; j < 100; ++j) {
 974                         test39A1[i] = getArray();
 975                         test39Val = new Test39Value(j, test39Val.iFld2);
 976                     }
 977                     break;
 978                 case 2:
 979                     for (float f = 142; f > i; f--) {
 980                         test39A2[i + 1] += 3;
 981                     }
 982                     result += test39Val.iFld1;
 983                     break;
 984             }
 985             double d1 = 1;
 986             while (++d1 < 142) {
 987                 test39A1[(i >>> 1) % 400][i + 1] = result;
 988                 test39Val = new Test39Value(i, test39Val.iFld2);
 989             }
 990         }
 991         return result;
 992     }
 993 
 994     @Run(test = "test39")
 995     @Warmup(10)
 996     public void test39_verifier() {
 997         int result = test39();
 998         Asserts.assertEQ(result, 1552);
 999     }
1000 
1001     // Test scalar replacement of value class array containing value class with oop fields
1002     @Test
1003     public long test40(boolean b) {
1004         MyValue1[] va = {MyValue1.createWithFieldsInline(rI, rL)};
1005         long result = 0;
1006         for (int i = 0; i < 1000; ++i) {
1007             if (!b) {
1008                 result = va[0].hash();
1009             }
1010         }
1011         return result;
1012     }
1013 
1014     @Run(test = "test40")
1015     public void test40_verifier(RunInfo info) {
1016         long result = test40(info.isWarmUp());
1017         Asserts.assertEQ(result, info.isWarmUp() ? 0 : hash());
1018     }
1019 
1020     static value class MyValue41 {
1021         int x;
1022 
1023         public MyValue41(int x) {
1024             this.x = x;
1025         }
1026 
1027         static MyValue41 make() {
1028             return new MyValue41(0);
1029         }
1030     }
1031 
1032     static MyValue41 field41;
1033 
1034     // Test detection of value object copies and removal of the MemBarRelease following the value buffer initialization
1035     @Test
1036     @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"},
1037         failOn = {ALLOC, ALLOCA, STORE})
1038     public void test41(MyValue41 val) {
1039         field41 = new MyValue41(val.x);
1040     }
1041 
1042     @Run(test = "test41")
1043     public void test41_verifier() {
1044         MyValue41 val = new MyValue41(rI);
1045         test41(val);
1046         Asserts.assertEQ(field41, val);
1047     }
1048 
1049     @DontInline
1050     public void test42_helper(MyValue41 val) {
1051         Asserts.assertEQ(val, new MyValue41(rI));
1052     }
1053 
1054     // Same as test41 but with call argument requiring buffering
1055     @Test
1056     @IR(applyIf = {"InlineTypePassFieldsAsArgs", "false"},
1057         failOn = {ALLOC, ALLOCA, STORE})
1058     public void test42(MyValue41 val) {
1059         test42_helper(new MyValue41(val.x));
1060     }
1061 
1062     @Run(test = "test42")
1063     public void test42_verifier() {
1064         MyValue41 val = new MyValue41(rI);
1065         test42(val);
1066     }
1067 
1068     static value class MyValue42 {
1069         int x;
1070 
1071         @ForceInline
1072         MyValue42(int x) {
1073             this.x = x;
1074             call();
1075             super();
1076         }
1077 
1078         @ForceInline
1079         static Object make(int x) {
1080             return new MyValue42(x);
1081         }
1082     }
1083 
1084     @Test
1085     @IR(failOn = {LOAD})
1086     public MyValue42 test43(int x) {
1087         return (MyValue42) MyValue42.make(x);
1088     }
1089 
1090     @Run(test = "test43")
1091     public void test43_verifier() {
1092         MyValue42 v = test43(rI);
1093         Asserts.assertEQ(rI, v.x);
1094     }
1095 
1096     static value class MyValue43 {
1097         int x;
1098 
1099         @ForceInline
1100         MyValue43(int x) {
1101             this.x = x;
1102             super();
1103             call();
1104         }
1105 
1106         @ForceInline
1107         static Object make(int x) {
1108             return new MyValue43(x);
1109         }
1110     }
1111 
1112     @Test
1113     @IR(failOn = {LOAD})
1114     public MyValue43 test44(int x) {
1115         return (MyValue43) MyValue43.make(x);
1116     }
1117 
1118     @Run(test = "test44")
1119     public void test44_verifier() {
1120         MyValue43 v = test44(rI);
1121         Asserts.assertEQ(rI, v.x);
1122     }
1123 
1124     @LooselyConsistentValue
1125     static value class MyValue45 {
1126         Integer v;
1127 
1128         MyValue45(Integer v) {
1129             this.v = v;
1130         }
1131     }
1132 
1133     static value class MyValue45ValueHolder {
1134         @NullRestricted
1135         @Strict
1136         MyValue45 v;
1137 
1138         MyValue45ValueHolder(Integer v) {
1139             this.v = new MyValue45(v);
1140         }
1141     }
1142 
1143     static class MyValue45Holder {
1144         @NullRestricted
1145         @Strict
1146         MyValue45 v;
1147 
1148         MyValue45Holder(Integer v) {
1149             this.v = new MyValue45(v);
1150         }
1151     }
1152 
1153     @Test
1154     // TODO 8357580 more aggressive flattening
1155     // @IR(applyIfAnd = {"UseFieldFlattening", "true", "UseNullableValueFlattening", "true"}, counts = {IRNode.LOAD_I, "1", IRNode.LOAD_B, "1"})
1156     public Integer test45(Object arg) {
1157         return ((MyValue45ValueHolder) arg).v.v;
1158     }
1159 
1160     @Run(test = "test45")
1161     public void test45_verifier() {
1162         Integer v = null;
1163         Asserts.assertEQ(test45(new MyValue45ValueHolder(v)), v);
1164         v = rI;
1165         Asserts.assertEQ(test45(new MyValue45ValueHolder(v)), v);
1166     }
1167 
1168     @Test
1169     // TODO 8357580 more aggressive flattening
1170     // @IR(applyIfAnd = {"UseFieldFlattening", "true", "UseNullableValueFlattening", "true"}, counts = {IRNode.LOAD_L, "1"})
1171     // @IR(applyIfAnd = {"UseFieldFlattening", "true", "UseNullableValueFlattening", "true"}, failOn = {IRNode.LOAD_I, IRNode.LOAD_B})
1172     public Integer test46(Object arg) {
1173         return ((MyValue45Holder) arg).v.v;
1174     }
1175 
1176     @Run(test = "test46")
1177     public void test46_verifier() {
1178         Integer v = null;
1179         Asserts.assertEQ(test46(new MyValue45Holder(v)), v);
1180         v = rI;
1181         Asserts.assertEQ(test46(new MyValue45Holder(v)), v);
1182     }
1183 
1184     static value class MyValue47 {
1185         byte b1;
1186         byte b2;
1187 
1188         MyValue47(byte b1, byte b2) {
1189             this.b1 = b1;
1190             this.b2 = b2;
1191         }
1192     }
1193 
1194     static value class MyValue47Holder {
1195         @NullRestricted
1196         @Strict
1197         MyValue47 v;
1198 
1199         MyValue47Holder(int v) {
1200             byte b1 = (byte) v;
1201             byte b2 = (byte) (v >>> 8);
1202             this.v = new MyValue47(b1, b2);
1203         }
1204     }
1205 
1206     static class MyValue47HolderHolder {
1207         @NullRestricted
1208         @Strict
1209         MyValue47Holder v;
1210 
1211         MyValue47HolderHolder(MyValue47Holder v) {
1212             this.v = v;
1213         }
1214     }
1215 
1216     @Test
1217     @IR(applyIfAnd = {"UseFieldFlattening", "true", "UseAtomicValueFlattening", "true"}, counts = {IRNode.LOAD_S, "1"})
1218     @IR(applyIfAnd = {"UseFieldFlattening", "true", "UseAtomicValueFlattening", "true"}, failOn = {IRNode.LOAD_B})
1219     public MyValue47Holder test47(MyValue47HolderHolder arg) {
1220         return arg.v;
1221     }
1222 
1223     @Run(test = "test47")
1224     public void test47_verifier() {
1225         MyValue47Holder v = new MyValue47Holder(rI);
1226         Asserts.assertEQ(test47(new MyValue47HolderHolder(v)), v);
1227     }
1228 
1229     static final MyValue47Holder[] MY_VALUE_47_HOLDERS = (MyValue47Holder[]) ValueClass.newNullRestrictedAtomicArray(MyValue47Holder.class, 2, new MyValue47Holder(rI));
1230 
1231     @Test
1232     @IR(applyIfAnd = {"UseFieldFlattening", "true", "UseArrayFlattening", "true", "UseAtomicValueFlattening", "true"}, counts = {IRNode.LOAD_S, "1"})
1233     @IR(applyIfAnd = {"UseFieldFlattening", "true", "UseArrayFlattening", "true", "UseAtomicValueFlattening", "true"}, failOn = {IRNode.LOAD_B})
1234     public MyValue47Holder test48() {
1235         return MY_VALUE_47_HOLDERS[0];
1236     }
1237 
1238     @Run(test = "test48")
1239     public void test48_verifier() {
1240         MyValue47Holder v = new MyValue47Holder(rI);
1241         Asserts.assertEQ(test48(), v);
1242     }
1243 }