1 /* 2 * Copyright (c) 2024, 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 java.util.Random; 27 28 import jdk.test.lib.Asserts; 29 import jdk.test.lib.Utils; 30 import jdk.test.whitebox.WhiteBox; 31 32 /** 33 * @test TestValueConstruction 34 * @summary Test construction of value objects. 35 * @key randomness 36 * @library /testlibrary /test/lib /compiler/whitebox / 37 * @enablePreview 38 * @build jdk.test.whitebox.WhiteBox 39 * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox 40 * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch 41 * -XX:CompileCommand=inline,TestValueConstruction::checkDeopt 42 * compiler.valhalla.inlinetypes.TestValueConstruction 43 * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch -XX:+IgnoreUnrecognizedVMOptions -XX:+DeoptimizeALot 44 * -XX:CompileCommand=inline,TestValueConstruction::checkDeopt 45 * compiler.valhalla.inlinetypes.TestValueConstruction 46 * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 47 * -XX:CompileCommand=compileonly,*TestValueConstruction::test* -Xbatch 48 * -XX:CompileCommand=inline,TestValueConstruction::checkDeopt 49 * compiler.valhalla.inlinetypes.TestValueConstruction 50 * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 51 * -XX:CompileCommand=dontinline,*MyValue*::<init> -Xbatch 52 * -XX:CompileCommand=inline,TestValueConstruction::checkDeopt 53 * compiler.valhalla.inlinetypes.TestValueConstruction 54 * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 55 * -XX:CompileCommand=dontinline,*Object::<init> -Xbatch 56 * -XX:CompileCommand=inline,TestValueConstruction::checkDeopt 57 * compiler.valhalla.inlinetypes.TestValueConstruction 58 * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+IgnoreUnrecognizedVMOptions -XX:+DeoptimizeALot 59 * -XX:CompileCommand=dontinline,*Object::<init> -Xbatch 60 * -XX:CompileCommand=inline,TestValueConstruction::checkDeopt 61 * compiler.valhalla.inlinetypes.TestValueConstruction 62 * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 63 * -XX:CompileCommand=dontinline,*MyAbstract::<init> -Xbatch 64 * -XX:CompileCommand=inline,TestValueConstruction::checkDeopt 65 * compiler.valhalla.inlinetypes.TestValueConstruction 66 * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbatch 67 * -XX:-TieredCompilation -XX:+StressIncrementalInlining 68 * -XX:CompileCommand=inline,TestValueConstruction::checkDeopt 69 * compiler.valhalla.inlinetypes.TestValueConstruction 70 * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 71 * -XX:-TieredCompilation -XX:+StressIncrementalInlining 72 * -XX:CompileCommand=inline,TestValueConstruction::checkDeopt 73 * -XX:CompileCommand=compileonly,*TestValueConstruction::test* -Xbatch 74 * compiler.valhalla.inlinetypes.TestValueConstruction 75 * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 76 * -XX:-TieredCompilation -XX:+StressIncrementalInlining 77 * -XX:CompileCommand=inline,TestValueConstruction::checkDeopt 78 * -XX:CompileCommand=dontinline,*MyValue*::<init> -Xbatch 79 * compiler.valhalla.inlinetypes.TestValueConstruction 80 * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 81 * -XX:-TieredCompilation -XX:+StressIncrementalInlining 82 * -XX:CompileCommand=inline,TestValueConstruction::checkDeopt 83 * -XX:CompileCommand=dontinline,*Object::<init> -Xbatch 84 * compiler.valhalla.inlinetypes.TestValueConstruction 85 * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 86 * -XX:-TieredCompilation -XX:+StressIncrementalInlining 87 * -XX:CompileCommand=inline,TestValueConstruction::checkDeopt 88 * -XX:CompileCommand=dontinline,*MyAbstract::<init> -Xbatch 89 * compiler.valhalla.inlinetypes.TestValueConstruction 90 */ 91 92 public class TestValueConstruction { 93 static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); 94 95 static boolean VERBOSE = false; 96 static boolean deopt[] = new boolean[13]; 97 98 static void reportDeopt(int deoptNum) { 99 System.out.println("Deopt " + deoptNum + " triggered"); 100 if (VERBOSE) { 101 new Exception().printStackTrace(System.out); 102 } 103 } 104 105 // Trigger deopts at various places 106 static void checkDeopt(int deoptNum) { 107 if (deopt[deoptNum]) { 108 // C2 will add an uncommon trap here 109 reportDeopt(deoptNum); 110 } 111 } 112 113 static interface MyInterface { 114 115 } 116 117 static value class MyValue1 implements MyInterface { 118 int x; 119 120 public MyValue1(int x) { 121 checkDeopt(0); 122 this.x = x; 123 checkDeopt(1); 124 super(); 125 checkDeopt(2); 126 } 127 128 public MyValue1(int x, int deoptNum1, int deoptNum2, int deoptNum3) { 129 checkDeopt(deoptNum1); 130 this.x = x; 131 checkDeopt(deoptNum2); 132 super(); 133 checkDeopt(deoptNum3); 134 } 135 136 public String toString() { 137 return "x: " + x; 138 } 139 } 140 141 static abstract value class MyAbstract1 { } 142 143 static value class MyValue2 extends MyAbstract1 { 144 int x; 145 146 public MyValue2(int x) { 147 checkDeopt(0); 148 this.x = x; 149 checkDeopt(1); 150 super(); 151 checkDeopt(2); 152 } 153 154 public String toString() { 155 return "x: " + x; 156 } 157 } 158 159 static abstract value class MyAbstract2 { 160 public MyAbstract2(int x) { 161 checkDeopt(0); 162 } 163 } 164 165 static value class MyValue3 extends MyAbstract2 { 166 int x; 167 168 public MyValue3(int x) { 169 checkDeopt(1); 170 this(x, 0); 171 helper1(this, x, 2); // 'this' escapes through argument 172 helper2(x, 3); // 'this' escapes through receiver 173 checkDeopt(4); 174 } 175 176 public MyValue3(int x, int unused) { 177 this.x = helper3(x, 5); 178 super(x); 179 helper1(this, x, 6); // 'this' escapes through argument 180 helper2(x, 7); // 'this' escapes through receiver 181 checkDeopt(8); 182 } 183 184 public static void helper1(MyValue3 obj, int x, int deoptNum) { 185 checkDeopt(deoptNum); 186 Asserts.assertEQ(obj.x, x); 187 } 188 189 public void helper2(int x, int deoptNum) { 190 checkDeopt(deoptNum); 191 Asserts.assertEQ(this.x, x); 192 } 193 194 public static int helper3(int x, int deoptNum) { 195 checkDeopt(deoptNum); 196 return x; 197 } 198 199 public String toString() { 200 return "x: " + x; 201 } 202 } 203 204 static value class MyValue4 { 205 Integer x; 206 207 public MyValue4(int x) { 208 checkDeopt(0); 209 this.x = x; 210 checkDeopt(1); 211 super(); 212 checkDeopt(2); 213 } 214 215 public String toString() { 216 return "x: " + x; 217 } 218 } 219 220 static value class MyValue5 { 221 int x; 222 223 public MyValue5(int x, boolean b) { 224 checkDeopt(0); 225 if (b) { 226 checkDeopt(1); 227 this.x = 42; 228 checkDeopt(2); 229 } else { 230 checkDeopt(3); 231 this.x = x; 232 checkDeopt(4); 233 } 234 checkDeopt(5); 235 super(); 236 checkDeopt(6); 237 } 238 239 public String toString() { 240 return "x: " + x; 241 } 242 } 243 244 static value class MyValue6 { 245 int x; 246 MyValue1 val1; 247 MyValue1 val2; 248 249 public MyValue6(int x) { 250 checkDeopt(0); 251 this.x = x; 252 checkDeopt(1); 253 this.val1 = new MyValue1(x, 2, 3, 4); 254 checkDeopt(5); 255 this.val2 = new MyValue1(x + 1, 6, 7, 8); 256 checkDeopt(9); 257 super(); 258 checkDeopt(10); 259 } 260 261 public String toString() { 262 return "x: " + x + ", val1: [" + val1 + "], val2: [" + val2 + "]"; 263 } 264 } 265 266 // Same as MyValue6 but unused MyValue1 construction 267 static value class MyValue7 { 268 int x; 269 270 public MyValue7(int x) { 271 checkDeopt(0); 272 this.x = x; 273 checkDeopt(1); 274 new MyValue1(42, 2, 3, 4); 275 checkDeopt(5); 276 new MyValue1(43, 6, 7, 8); 277 checkDeopt(9); 278 super(); 279 checkDeopt(10); 280 } 281 282 public String toString() { 283 return "x: " + x; 284 } 285 } 286 287 // Constructor calling another constructor of the same value class with control flow dependent initialization 288 static value class MyValue8 { 289 int x; 290 291 public MyValue8(int x) { 292 checkDeopt(0); 293 this(x, 0); 294 checkDeopt(1); 295 } 296 297 public MyValue8(int x, int unused1) { 298 checkDeopt(2); 299 if ((x % 2) == 0) { 300 checkDeopt(3); 301 this.x = 42; 302 checkDeopt(4); 303 } else { 304 checkDeopt(5); 305 this.x = x; 306 checkDeopt(6); 307 } 308 checkDeopt(7); 309 super(); 310 checkDeopt(8); 311 } 312 313 public MyValue8(int x, int unused1, int unused2) { 314 checkDeopt(3); 315 this.x = x; 316 checkDeopt(4); 317 } 318 319 public static MyValue8 valueOf(int x) { 320 checkDeopt(0); 321 if ((x % 2) == 0) { 322 checkDeopt(1); 323 return new MyValue8(42, 0, 0); 324 } else { 325 checkDeopt(2); 326 return new MyValue8(x, 0, 0); 327 } 328 } 329 330 public String toString() { 331 return "x: " + x; 332 } 333 } 334 335 // Constructor calling another constructor of a different value class 336 static value class MyValue9 { 337 MyValue8 val; 338 339 public MyValue9(int x) { 340 checkDeopt(9); 341 this(x, 0); 342 checkDeopt(10); 343 } 344 345 public MyValue9(int i, int unused1) { 346 checkDeopt(11); 347 val = new MyValue8(i); 348 checkDeopt(12); 349 } 350 351 public MyValue9(int x, int unused1, int unused2) { 352 checkDeopt(5); 353 this(x, 0, 0, 0); 354 checkDeopt(6); 355 } 356 357 public MyValue9(int i, int unused1, int unused2, int unused3) { 358 checkDeopt(7); 359 val = MyValue8.valueOf(i); 360 checkDeopt(8); 361 } 362 363 public String toString() { 364 return "val: [" + val + "]"; 365 } 366 } 367 368 // Constructor with a loop 369 static value class MyValue10 { 370 int x; 371 int y; 372 373 public MyValue10(int x, int cnt) { 374 checkDeopt(0); 375 this.x = x; 376 checkDeopt(1); 377 int res = 0; 378 for (int i = 0; i < cnt; ++i) { 379 checkDeopt(2); 380 res += x; 381 checkDeopt(3); 382 } 383 checkDeopt(4); 384 this.y = res; 385 checkDeopt(5); 386 super(); 387 checkDeopt(6); 388 } 389 390 public String toString() { 391 return "x: " + x + ", y: " + y; 392 } 393 } 394 395 // Value class with recursive field definitions 396 static value class MyValue11 { 397 int x; 398 MyValue11 val1; 399 MyValue11 val2; 400 401 public MyValue11(int x) { 402 checkDeopt(0); 403 this.x = x; 404 checkDeopt(1); 405 this.val1 = new MyValue11(x + 1, 2, 3, 4, 5); 406 checkDeopt(6); 407 this.val2 = new MyValue11(x + 2, 7, 8, 9, 10); 408 checkDeopt(11); 409 } 410 411 public MyValue11(int x, int deoptNum1, int deoptNum2, int deoptNum3, int deoptNum4) { 412 checkDeopt(deoptNum1); 413 this.x = x; 414 checkDeopt(deoptNum2); 415 this.val1 = null; 416 checkDeopt(deoptNum3); 417 this.val2 = null; 418 checkDeopt(deoptNum4); 419 } 420 421 public String toString() { 422 return "x: " + x + ", val1: [" + (val1 != this ? val1 : "this") + "], val2: [" + (val2 != this ? val2 : "this") + "]"; 423 } 424 } 425 426 public static int test1(int x) { 427 MyValue1 val = new MyValue1(x); 428 checkDeopt(3); 429 return val.x; 430 } 431 432 public static MyValue1 helper1(int x) { 433 return new MyValue1(x); 434 } 435 436 public static Object test2(int x) { 437 return helper1(x); 438 } 439 440 public static Object test3(int limit) { 441 MyValue1 res = null; 442 for (int i = 0; i <= 10; ++i) { 443 res = new MyValue1(i); 444 checkDeopt(3); 445 } 446 return res; 447 } 448 449 public static MyValue1 test4(int x) { 450 MyValue1 v = new MyValue1(x); 451 checkDeopt(3); 452 v = new MyValue1(x); 453 return v; 454 } 455 456 public static int test5(int x) { 457 MyValue2 val = new MyValue2(x); 458 checkDeopt(3); 459 return val.x; 460 } 461 462 public static MyValue2 helper2(int x) { 463 return new MyValue2(x); 464 } 465 466 public static Object test6(int x) { 467 return helper2(x); 468 } 469 470 public static Object test7(int limit) { 471 MyValue2 res = null; 472 for (int i = 0; i <= 10; ++i) { 473 res = new MyValue2(i); 474 checkDeopt(3); 475 } 476 return res; 477 } 478 479 public static MyValue2 test8(int x) { 480 MyValue2 v = new MyValue2(x); 481 checkDeopt(3); 482 v = new MyValue2(x); 483 return v; 484 } 485 486 public static int test9(int x) { 487 MyValue3 val = new MyValue3(x); 488 checkDeopt(9); 489 return val.x; 490 } 491 492 public static MyValue3 helper3(int x) { 493 return new MyValue3(x); 494 } 495 496 public static Object test10(int x) { 497 return helper3(x); 498 } 499 500 public static Object test11(int limit) { 501 MyValue3 res = null; 502 for (int i = 0; i <= 10; ++i) { 503 checkDeopt(9); 504 res = new MyValue3(i); 505 } 506 return res; 507 } 508 509 public static MyValue3 test12(int x) { 510 MyValue3 v = new MyValue3(x); 511 checkDeopt(9); 512 v = new MyValue3(x); 513 return v; 514 } 515 516 public static MyValue4 test13(int x) { 517 return new MyValue4(x); 518 } 519 520 public static MyValue5 test14(int x, boolean b) { 521 return new MyValue5(x, b); 522 } 523 524 public static Object test15(int x) { 525 return new MyValue6(x); 526 } 527 528 public static Object test16(int x) { 529 return new MyValue7(x); 530 } 531 532 public static MyValue8 test17(int x) { 533 return new MyValue8(x); 534 } 535 536 public static MyValue8 test18(int x) { 537 return new MyValue8(x, 0); 538 } 539 540 public static MyValue8 test19(int x) { 541 return MyValue8.valueOf(x); 542 } 543 544 public static MyValue9 test20(int x) { 545 return new MyValue9(x); 546 } 547 548 public static MyValue9 test21(int x) { 549 return new MyValue9(x, 0); 550 } 551 552 public static MyValue9 test22(int x) { 553 return new MyValue9(x, 0, 0); 554 } 555 556 public static MyValue9 test23(int x) { 557 return new MyValue9(x, 0, 0, 0); 558 } 559 560 public static MyValue10 test24(int x, int cnt) { 561 return new MyValue10(x, cnt); 562 } 563 564 public static MyValue11 test25(int x) { 565 return new MyValue11(x); 566 } 567 568 public static void main(String[] args) throws Exception { 569 Random rand = Utils.getRandomInstance(); 570 571 // Randomly exclude some constructors from inlining via the WhiteBox API because CompileCommands don't match on different signatures. 572 WHITE_BOX.testSetDontInlineMethod(MyValue1.class.getConstructor(int.class), rand.nextBoolean()); 573 WHITE_BOX.testSetDontInlineMethod(MyValue1.class.getConstructor(int.class, int.class, int.class, int.class), rand.nextBoolean()); 574 WHITE_BOX.testSetDontInlineMethod(MyValue3.class.getConstructor(int.class), rand.nextBoolean()); 575 WHITE_BOX.testSetDontInlineMethod(MyValue3.class.getConstructor(int.class, int.class), rand.nextBoolean()); 576 WHITE_BOX.testSetDontInlineMethod(MyValue8.class.getConstructor(int.class), rand.nextBoolean()); 577 WHITE_BOX.testSetDontInlineMethod(MyValue8.class.getConstructor(int.class, int.class), rand.nextBoolean()); 578 WHITE_BOX.testSetDontInlineMethod(MyValue8.class.getConstructor(int.class, int.class, int.class), rand.nextBoolean()); 579 WHITE_BOX.testSetDontInlineMethod(MyValue9.class.getConstructor(int.class), rand.nextBoolean()); 580 WHITE_BOX.testSetDontInlineMethod(MyValue9.class.getConstructor(int.class, int.class), rand.nextBoolean()); 581 WHITE_BOX.testSetDontInlineMethod(MyValue9.class.getConstructor(int.class, int.class, int.class), rand.nextBoolean()); 582 WHITE_BOX.testSetDontInlineMethod(MyValue9.class.getConstructor(int.class, int.class, int.class, int.class), rand.nextBoolean()); 583 WHITE_BOX.testSetDontInlineMethod(MyValue11.class.getConstructor(int.class), rand.nextBoolean()); 584 WHITE_BOX.testSetDontInlineMethod(MyValue11.class.getConstructor(int.class, int.class, int.class, int.class, int.class), rand.nextBoolean()); 585 586 Integer deoptNum = Integer.getInteger("deoptNum"); 587 if (deoptNum == null) { 588 deoptNum = rand.nextInt(deopt.length); 589 } 590 for (int x = 0; x <= 50_000; ++x) { 591 if (x == 50_000) { 592 // Last iteration, trigger deoptimization 593 deopt[deoptNum] = true; 594 } 595 Asserts.assertEQ(test1(x), x); 596 Asserts.assertEQ(test2(x), new MyValue1(x)); 597 Asserts.assertEQ(test3(10), new MyValue1(10)); 598 Asserts.assertEQ(test4(x), new MyValue1(x)); 599 Asserts.assertEQ(test5(x), x); 600 Asserts.assertEQ(test6(x), new MyValue2(x)); 601 Asserts.assertEQ(test7(10), new MyValue2(10)); 602 Asserts.assertEQ(test8(x), new MyValue2(x)); 603 Asserts.assertEQ(test9(x), x); 604 Asserts.assertEQ(test10(x), new MyValue3(x)); 605 Asserts.assertEQ(test11(10), new MyValue3(10)); 606 Asserts.assertEQ(test12(x), new MyValue3(x)); 607 Asserts.assertEQ(test13(x), new MyValue4(x)); 608 Asserts.assertEQ(test14(x, (x % 2) == 0), new MyValue5(x, (x % 2) == 0)); 609 Asserts.assertEQ(test15(x), new MyValue6(x)); 610 Asserts.assertEQ(test16(x), new MyValue7(x)); 611 Asserts.assertEQ(test17(x), new MyValue8(x)); 612 Asserts.assertEQ(test18(x), new MyValue8(x)); 613 Asserts.assertEQ(test19(x), new MyValue8(x)); 614 Asserts.assertEQ(test20(x), new MyValue9(x)); 615 Asserts.assertEQ(test21(x), new MyValue9(x)); 616 Asserts.assertEQ(test22(x), new MyValue9(x)); 617 Asserts.assertEQ(test23(x), new MyValue9(x)); 618 Asserts.assertEQ(test24(x, x % 10), new MyValue10(x, x % 10)); 619 Asserts.assertEQ(test25(x), new MyValue11(x)); 620 } 621 } 622 }