1 /* 2 * Copyright (c) 2017, 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 jdk.internal.value.ValueClass; 30 import jdk.internal.vm.annotation.LooselyConsistentValue; 31 import jdk.internal.vm.annotation.NullRestricted; 32 import jdk.internal.vm.annotation.Strict; 33 34 import static compiler.valhalla.inlinetypes.InlineTypeIRNode.*; 35 import static compiler.valhalla.inlinetypes.InlineTypes.rI; 36 import static compiler.valhalla.inlinetypes.InlineTypes.rL; 37 38 /* 39 * @test 40 * @key randomness 41 * @summary Test on stack replacement (OSR) with value classes. 42 * @library /test/lib / 43 * @requires (os.simpleArch == "x64" | os.simpleArch == "aarch64") 44 * @enablePreview 45 * @modules java.base/jdk.internal.value 46 * java.base/jdk.internal.vm.annotation 47 * @run main/othervm/timeout=300 compiler.valhalla.inlinetypes.TestOnStackReplacement 48 */ 49 50 public class TestOnStackReplacement { 51 52 public static void main(String[] args) throws Throwable { 53 Scenario[] scenarios = InlineTypes.DEFAULT_SCENARIOS; 54 scenarios[3].addFlags("-XX:-UseArrayFlattening"); 55 56 InlineTypes.getFramework() 57 .addScenarios(scenarios) 58 .addHelperClasses(MyValue1.class, 59 MyValue2.class, 60 MyValue2Inline.class, 61 MyValue3.class, 62 MyValue3Inline.class) 63 .start(); 64 } 65 66 // Helper methods 67 68 protected long hash() { 69 return hash(rI, rL); 70 } 71 72 protected long hash(int x, long y) { 73 return MyValue1.createWithFieldsInline(x, y).hash(); 74 } 75 76 // Test OSR compilation 77 @Test(compLevel = CompLevel.WAIT_FOR_COMPILATION) 78 public long test1() { 79 MyValue1 v = MyValue1.createWithFieldsInline(rI, rL); 80 MyValue1[] va = (MyValue1[])ValueClass.newNullRestrictedNonAtomicArray(MyValue1.class, Math.abs(rI) % 3, MyValue1.DEFAULT); 81 for (int i = 0; i < va.length; ++i) { 82 va[i] = MyValue1.createWithFieldsInline(rI, rL); 83 } 84 long result = 0; 85 // Long loop to trigger OSR compilation 86 for (int i = 0; i < 50_000; ++i) { 87 // Reference local value object in interpreter state 88 result = v.hash(); 89 for (int j = 0; j < va.length; ++j) { 90 result += va[j].hash(); 91 } 92 } 93 return result; 94 } 95 96 @Run(test = "test1") 97 @Warmup(0) 98 public void test1_verifier() { 99 long result = test1(); 100 Asserts.assertEQ(result, ((Math.abs(rI) % 3) + 1) * hash()); 101 } 102 103 // Test loop peeling 104 @Test(compLevel = CompLevel.WAIT_FOR_COMPILATION) 105 @IR(failOn = {ALLOC, LOAD, STORE}) 106 public void test2() { 107 MyValue1 v = MyValue1.createWithFieldsInline(0, 1); 108 // Trigger OSR compilation and loop peeling 109 for (int i = 0; i < 50_000; ++i) { 110 if (v.x != i || v.y != i + 1) { 111 // Uncommon trap 112 throw new RuntimeException("test2 failed"); 113 } 114 v = MyValue1.createWithFieldsInline(i + 1, i + 2); 115 } 116 } 117 118 @Run(test = "test2") 119 @Warmup(0) 120 public void test2_verifier() { 121 test2(); 122 } 123 124 // Test loop peeling and unrolling 125 @Test(compLevel = CompLevel.WAIT_FOR_COMPILATION) 126 public void test3() { 127 MyValue1 v1 = MyValue1.createWithFieldsInline(0, 0); 128 MyValue1 v2 = MyValue1.createWithFieldsInline(1, 1); 129 // Trigger OSR compilation and loop peeling 130 for (int i = 0; i < 50_000; ++i) { 131 if (v1.x != 2*i || v2.x != i+1 || v2.y != i+1) { 132 // Uncommon trap 133 throw new RuntimeException("test3 failed"); 134 } 135 v1 = MyValue1.createWithFieldsInline(2*(i+1), 0); 136 v2 = MyValue1.createWithFieldsInline(i+2, i+2); 137 } 138 } 139 140 //@Run(test = "test3") 141 //@Warmup(0) 142 public void test3_verifier() { 143 test3(); 144 } 145 146 // OSR compilation with Object local 147 @DontCompile 148 public Object test4_init() { 149 return MyValue1.createWithFieldsInline(rI, rL); 150 } 151 152 @DontCompile 153 public Object test4_body() { 154 return MyValue1.createWithFieldsInline(rI, rL); 155 } 156 157 @Test(compLevel = CompLevel.WAIT_FOR_COMPILATION) 158 public Object test4() { 159 Object vt = test4_init(); 160 for (int i = 0; i < 50_000; i++) { 161 if (i % 2 == 1) { 162 vt = test4_body(); 163 } 164 } 165 return vt; 166 } 167 168 @Run(test = "test4") 169 @Warmup(0) 170 public void test4_verifier() { 171 test4(); 172 } 173 174 // OSR compilation with null value class local 175 176 MyValue1 nullField; 177 178 @Test(compLevel = CompLevel.WAIT_FOR_COMPILATION) 179 public void test5() { 180 MyValue1 vt = nullField; 181 for (int i = 0; i < 50_000; i++) { 182 if (vt != null) { 183 throw new RuntimeException("test5 failed: vt should be null"); 184 } 185 } 186 } 187 188 @Run(test = "test5") 189 @Warmup(0) 190 public void test5_verifier() { 191 test5(); 192 } 193 194 // Test OSR in method with value class receiver 195 @LooselyConsistentValue 196 value class Test6Value { 197 public int f = 0; 198 199 public int test() { 200 int res = 0; 201 for (int i = 1; i < 20_000; ++i) { 202 res -= i; 203 } 204 return res; 205 } 206 } 207 208 @Test(compLevel = CompLevel.WAIT_FOR_COMPILATION) 209 public void test6() { 210 Test6Value tmp = new Test6Value(); 211 for (int i = 0; i < 100; ++i) { 212 tmp.test(); 213 } 214 } 215 216 @Run(test = "test6") 217 @Warmup(0) 218 public void test6_verifier() { 219 test6(); 220 } 221 222 // Similar to test6 but with more fields and reserved stack entry 223 @LooselyConsistentValue 224 static value class Test7Value1 { 225 public int i1 = rI; 226 public int i2 = rI; 227 public int i3 = rI; 228 public int i4 = rI; 229 public int i5 = rI; 230 public int i6 = rI; 231 } 232 233 @LooselyConsistentValue 234 static value class Test7Value2 { 235 public int i1 = rI; 236 public int i2 = rI; 237 public int i3 = rI; 238 public int i4 = rI; 239 public int i5 = rI; 240 public int i6 = rI; 241 public int i7 = rI; 242 public int i8 = rI; 243 public int i9 = rI; 244 public int i10 = rI; 245 public int i11 = rI; 246 public int i12 = rI; 247 public int i13 = rI; 248 public int i14 = rI; 249 public int i15 = rI; 250 public int i16 = rI; 251 public int i17 = rI; 252 public int i18 = rI; 253 public int i19 = rI; 254 public int i20 = rI; 255 public int i21 = rI; 256 257 @Strict 258 @NullRestricted 259 public Test7Value1 vt = new Test7Value1(); 260 261 public int test(String[] args) { 262 int res = 0; 263 for (int i = 1; i < 20_000; ++i) { 264 res -= i; 265 } 266 return res; 267 } 268 } 269 270 @Test(compLevel = CompLevel.WAIT_FOR_COMPILATION) 271 public void test7() { 272 Test7Value2 tmp = new Test7Value2(); 273 for (int i = 0; i < 10; ++i) { 274 tmp.test(null); 275 } 276 } 277 278 @Run(test = "test7") 279 @Warmup(0) 280 public void test7_verifier() { 281 test7(); 282 } 283 284 // Test OSR with scalarized value class return 285 MyValue3 test8_vt; 286 287 @DontInline 288 public MyValue3 test8_callee(int len) { 289 test8_vt = MyValue3.create(); 290 int val = 0; 291 for (int i = 0; i < len; ++i) { 292 val = i; 293 } 294 test8_vt = test8_vt.setI(test8_vt, val); 295 return test8_vt; 296 } 297 298 @Test 299 public int test8(int start) { 300 MyValue3 vt = test8_callee(start); 301 test8_vt.verify(vt); 302 int result = 0; 303 for (int i = 0; i < 50_000; ++i) { 304 result += i; 305 } 306 return result; 307 } 308 309 @Run(test = "test8") 310 @Warmup(2) 311 public void test8_verifier() { 312 test8(1); 313 test8(50_000); 314 } 315 }