1 /* 2 * Copyright (c) 2023, 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.gcbarriers; 25 26 import compiler.lib.ir_framework.*; 27 import java.lang.invoke.VarHandle; 28 import java.lang.invoke.MethodHandles; 29 import java.util.concurrent.ThreadLocalRandom; 30 31 /** 32 * @test id=Z 33 * @summary Test that the ZGC barrier elision optimization does not elide 34 * necessary barriers. The tests use volatile memory accesses and 35 * blackholes to prevent C2 from simply optimizing them away. 36 * @library /test/lib / 37 * @requires vm.gc.Z 38 * @run driver compiler.gcbarriers.TestZGCBarrierElision test-correctness 39 */ 40 41 /** 42 * @test id=ZGen 43 * @summary Test that the ZGC barrier elision optimization elides unnecessary 44 * barriers following simple allocation and domination rules. 45 * @library /test/lib / 46 * @requires vm.gc.Z & (vm.simpleArch == "x64" | vm.simpleArch == "aarch64") 47 * @run driver compiler.gcbarriers.TestZGCBarrierElision test-effectiveness 48 */ 49 50 class Inner {} 51 52 class Outer { 53 volatile Inner field1; 54 volatile Inner field2; 55 Outer() {} 56 } 57 58 class Common { 59 60 static Inner inner = new Inner(); 61 static Outer outer = new Outer(); 62 static Outer outer2 = new Outer(); 63 static Outer[] outerArray = new Outer[42]; 64 65 static final VarHandle field1VarHandle; 66 static final VarHandle field2VarHandle; 67 static { 68 MethodHandles.Lookup l = MethodHandles.lookup(); 69 try { 70 field1VarHandle = l.findVarHandle(Outer.class, "field1", Inner.class); 71 field2VarHandle = l.findVarHandle(Outer.class, "field2", Inner.class); 72 } catch (Exception e) { 73 throw new Error(e); 74 } 75 } 76 static final VarHandle outerArrayVarHandle = 77 MethodHandles.arrayElementVarHandle(Outer[].class); 78 79 static final String REMAINING = "strong"; 80 static final String ELIDED = "elided"; 81 82 static void blackhole(Object o) {} 83 static void nonInlinedMethod() {} 84 } 85 86 public class TestZGCBarrierElision { 87 88 public static void main(String[] args) { 89 if (args.length != 1) { 90 throw new IllegalArgumentException(); 91 } 92 Class testClass; 93 if (args[0].equals("test-correctness")) { 94 testClass = TestZGCCorrectBarrierElision.class; 95 } else if (args[0].equals("test-effectiveness")) { 96 testClass = TestZGCEffectiveBarrierElision.class; 97 } else { 98 throw new IllegalArgumentException(); 99 } 100 String commonName = Common.class.getName(); 101 TestFramework test = new TestFramework(testClass); 102 test.addFlags("-XX:+UseZGC", "-XX:+UnlockExperimentalVMOptions", 103 "-XX:CompileCommand=blackhole," + commonName + "::blackhole", 104 "-XX:CompileCommand=dontinline," + commonName + "::nonInlinedMethod", 105 "-XX:LoopMaxUnroll=0"); 106 test.start(); 107 } 108 } 109 110 class TestZGCCorrectBarrierElision { 111 112 @Test 113 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 114 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 115 static void testLoadThenStore(Outer o, Inner i) { 116 Common.blackhole(o.field1); 117 o.field1 = i; 118 } 119 120 @Test 121 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "2" }, phase = CompilePhase.FINAL_CODE) 122 static void testLoadThenLoadAnotherField(Outer o) { 123 Common.blackhole(o.field1); 124 Common.blackhole(o.field2); 125 } 126 127 @Test 128 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "2" }, phase = CompilePhase.FINAL_CODE) 129 static void testLoadThenLoadFromAnotherObject(Outer o1, Outer o2) { 130 Common.blackhole(o1.field1); 131 Common.blackhole(o2.field1); 132 } 133 134 @Run(test = {"testLoadThenStore", 135 "testLoadThenLoadAnotherField", 136 "testLoadThenLoadFromAnotherObject"}) 137 void runBasicTests() { 138 testLoadThenStore(Common.outer, Common.inner); 139 testLoadThenLoadAnotherField(Common.outer); 140 testLoadThenLoadFromAnotherObject(Common.outer, Common.outer2); 141 } 142 143 @Test 144 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 145 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 146 static void testArrayLoadThenStore(Outer[] a, Outer o) { 147 Common.blackhole(Common.outerArrayVarHandle.getVolatile(a, 0)); 148 Common.outerArrayVarHandle.setVolatile(a, 0, o); 149 } 150 151 @Test 152 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "2" }, phase = CompilePhase.FINAL_CODE) 153 static void testArrayLoadThenLoadAnotherElement(Outer[] a) { 154 Common.blackhole(Common.outerArrayVarHandle.getVolatile(a, 0)); 155 Common.blackhole(Common.outerArrayVarHandle.getVolatile(a, 10)); 156 } 157 158 @Run(test = {"testArrayLoadThenStore", 159 "testArrayLoadThenLoadAnotherElement"}) 160 void runArrayTests() { 161 testArrayLoadThenStore(Common.outerArray, Common.outer); 162 testArrayLoadThenLoadAnotherElement(Common.outerArray); 163 } 164 165 @Test 166 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "2" }, phase = CompilePhase.FINAL_CODE) 167 static void testConditionalStoreThenStore(Outer o, Inner i, int value) { 168 if (value % 2 == 0) { 169 o.field1 = i; 170 } 171 o.field1 = i; 172 } 173 174 @Test 175 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "2" }, phase = CompilePhase.FINAL_CODE) 176 static void testStoreThenCallThenStore(Outer o, Inner i) { 177 o.field1 = i; 178 Common.nonInlinedMethod(); 179 o.field1 = i; 180 } 181 182 @Run(test = {"testConditionalStoreThenStore", 183 "testStoreThenCallThenStore"}) 184 void runControlFlowTests() { 185 testConditionalStoreThenStore(Common.outer, Common.inner, ThreadLocalRandom.current().nextInt(0, 100)); 186 testStoreThenCallThenStore(Common.outer, Common.inner); 187 } 188 189 @Test 190 // TODO: 8353182 191 //@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 192 static void testAllocateThenAtomic(Inner i) { 193 Outer o = new Outer(); 194 Common.blackhole(o); 195 Common.field1VarHandle.getAndSet(o, i); 196 } 197 198 @Test 199 // TODO: 8329234 200 //@IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 201 // TODO: 8353182 202 //@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 203 static void testLoadThenAtomic(Outer o, Inner i) { 204 Common.blackhole(o.field1); 205 Common.field1VarHandle.getAndSet(o, i); 206 } 207 208 @Test 209 // TODO: 8353182 210 //@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "2" }, phase = CompilePhase.FINAL_CODE) 211 static void testAtomicThenAtomicAnotherField(Outer o, Inner i) { 212 Common.field1VarHandle.getAndSet(o, i); 213 Common.field2VarHandle.getAndSet(o, i); 214 } 215 216 @Test 217 // TODO: 8353182 218 //@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 219 static void testAllocateArrayThenAtomicAtKnownIndex(Outer o) { 220 Outer[] a = new Outer[42]; 221 Common.blackhole(a); 222 Common.outerArrayVarHandle.getAndSet(a, 2, o); 223 } 224 225 @Test 226 // TODO: 8353182 227 //@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 228 static void testAllocateArrayThenAtomicAtUnknownIndex(Outer o, int index) { 229 Outer[] a = new Outer[42]; 230 Common.blackhole(a); 231 Common.outerArrayVarHandle.getAndSet(a, index, o); 232 } 233 234 @Test 235 // TODO: 8353182 236 //@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "2" }, phase = CompilePhase.FINAL_CODE) 237 static void testArrayAtomicThenAtomicAtUnknownIndices(Outer[] a, Outer o, int index1, int index2) { 238 Common.outerArrayVarHandle.getAndSet(a, index1, o); 239 Common.outerArrayVarHandle.getAndSet(a, index2, o); 240 } 241 242 @Run(test = {"testAllocateThenAtomic", 243 "testLoadThenAtomic", 244 "testAtomicThenAtomicAnotherField", 245 "testAllocateArrayThenAtomicAtKnownIndex", 246 "testAllocateArrayThenAtomicAtUnknownIndex", 247 "testArrayAtomicThenAtomicAtUnknownIndices"}) 248 void runAtomicOperationTests() { 249 testAllocateThenAtomic(Common.inner); 250 testLoadThenAtomic(Common.outer, Common.inner); 251 testAtomicThenAtomicAnotherField(Common.outer, Common.inner); 252 testAllocateArrayThenAtomicAtKnownIndex(Common.outer); 253 testAllocateArrayThenAtomicAtUnknownIndex(Common.outer, 10); 254 testArrayAtomicThenAtomicAtUnknownIndices(Common.outerArray, Common.outer, 10, 20); 255 } 256 } 257 258 class TestZGCEffectiveBarrierElision { 259 260 @Test 261 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE) 262 static void testAllocateThenLoad() { 263 Outer o1 = new Outer(); 264 Common.blackhole(o1); 265 // This load is directly optimized away by C2. 266 Common.blackhole(o1.field1); 267 Common.blackhole(o1.field1); 268 } 269 270 @Test 271 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE) 272 static void testAllocateThenStore(Inner i) { 273 Outer o1 = new Outer(); 274 Common.blackhole(o1); 275 o1.field1 = i; 276 } 277 278 @Test 279 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 280 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE) 281 static void testLoadThenLoad(Outer o) { 282 Common.blackhole(o.field1); 283 Common.blackhole(o.field1); 284 } 285 286 @Test 287 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 288 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE) 289 static void testStoreThenStore(Outer o, Inner i) { 290 o.field1 = i; 291 o.field1 = i; 292 } 293 294 @Test 295 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 296 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE) 297 static void testStoreThenLoad(Outer o, Inner i) { 298 o.field1 = i; 299 Common.blackhole(o.field1); 300 } 301 302 @Run(test = {"testAllocateThenLoad", 303 "testAllocateThenStore", 304 "testLoadThenLoad", 305 "testStoreThenStore", 306 "testStoreThenLoad"}) 307 void runBasicTests() { 308 testAllocateThenLoad(); 309 testAllocateThenStore(Common.inner); 310 testLoadThenLoad(Common.outer); 311 testStoreThenStore(Common.outer, Common.inner); 312 testStoreThenLoad(Common.outer, Common.inner); 313 } 314 315 @Test 316 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE) 317 static void testAllocateArrayThenStoreAtKnownIndex(Outer o) { 318 Outer[] a = new Outer[42]; 319 Common.blackhole(a); 320 Common.outerArrayVarHandle.setVolatile(a, 0, o); 321 } 322 323 @Test 324 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE) 325 static void testAllocateArrayThenStoreAtUnknownIndex(Outer o, int index) { 326 Outer[] a = new Outer[42]; 327 Common.blackhole(a); 328 Common.outerArrayVarHandle.setVolatile(a, index, o); 329 } 330 331 @Test 332 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 333 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE) 334 static void testArrayLoadThenLoad(Outer[] a) { 335 Common.blackhole(Common.outerArrayVarHandle.getVolatile(a, 0)); 336 Common.blackhole(Common.outerArrayVarHandle.getVolatile(a, 0)); 337 } 338 339 @Test 340 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 341 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE) 342 static void testArrayStoreThenStore(Outer[] a, Outer o) { 343 Common.outerArrayVarHandle.setVolatile(a, 0, o); 344 Common.outerArrayVarHandle.setVolatile(a, 0, o); 345 } 346 347 @Test 348 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 349 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE) 350 static void testArrayStoreThenLoad(Outer[] a, Outer o) { 351 Common.outerArrayVarHandle.setVolatile(a, 0, o); 352 Common.blackhole(Common.outerArrayVarHandle.getVolatile(a, 0)); 353 } 354 355 @Run(test = {"testAllocateArrayThenStoreAtKnownIndex", 356 "testAllocateArrayThenStoreAtUnknownIndex", 357 "testArrayLoadThenLoad", 358 "testArrayStoreThenStore", 359 "testArrayStoreThenLoad"}) 360 void runArrayTests() { 361 testAllocateArrayThenStoreAtKnownIndex(Common.outer); 362 testAllocateArrayThenStoreAtUnknownIndex(Common.outer, 10); 363 testArrayLoadThenLoad(Common.outerArray); 364 testArrayStoreThenStore(Common.outerArray, Common.outer); 365 testArrayStoreThenLoad(Common.outerArray, Common.outer); 366 } 367 368 @Test 369 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 370 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE) 371 static void testStoreThenConditionalStore(Outer o, Inner i, int value) { 372 o.field1 = i; 373 if (value % 2 == 0) { 374 o.field1 = i; 375 } 376 } 377 378 @Test 379 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 380 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE) 381 static void testStoreThenStoreInLoop(Outer o, Inner i) { 382 o.field1 = i; 383 for (int j = 0; j < 100; j++) { 384 o.field1 = i; 385 } 386 } 387 388 @Run(test = {"testStoreThenConditionalStore", 389 "testStoreThenStoreInLoop"}) 390 void runControlFlowTests() { 391 testStoreThenConditionalStore(Common.outer, Common.inner, ThreadLocalRandom.current().nextInt(0, 100)); 392 testStoreThenStoreInLoop(Common.outer, Common.inner); 393 } 394 395 @Test 396 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 397 // TODO: 8353182 398 //@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE) 399 static void testStoreThenAtomic(Outer o, Inner i) { 400 o.field1 = i; 401 Common.field1VarHandle.getAndSet(o, i); 402 } 403 404 @Test 405 // TODO: 8353182 406 //@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 407 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE) 408 static void testAtomicThenLoad(Outer o, Inner i) { 409 Common.field1VarHandle.getAndSet(o, i); 410 Common.blackhole(o.field1); 411 } 412 413 @Test 414 // TODO: 8353182 415 //@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 416 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE) 417 static void testAtomicThenStore(Outer o, Inner i) { 418 Common.field1VarHandle.getAndSet(o, i); 419 o.field1 = i; 420 } 421 422 @Test 423 // TODO: 8353182 424 //@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 425 //@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE) 426 static void testAtomicThenAtomic(Outer o, Inner i) { 427 Common.field1VarHandle.getAndSet(o, i); 428 Common.field1VarHandle.getAndSet(o, i); 429 } 430 431 @Test 432 // TODO: 8353182 433 //@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 434 //@IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE) 435 static void testArrayAtomicThenAtomic(Outer[] a, Outer o) { 436 Common.outerArrayVarHandle.getAndSet(a, 0, o); 437 Common.outerArrayVarHandle.getAndSet(a, 0, o); 438 } 439 440 @Run(test = {"testStoreThenAtomic", 441 "testAtomicThenLoad", 442 "testAtomicThenStore", 443 "testAtomicThenAtomic", 444 "testArrayAtomicThenAtomic"}) 445 void runAtomicOperationTests() { 446 testStoreThenAtomic(Common.outer, Common.inner); 447 testAtomicThenLoad(Common.outer, Common.inner); 448 testAtomicThenStore(Common.outer, Common.inner); 449 testAtomicThenAtomic(Common.outer, Common.inner); 450 testArrayAtomicThenAtomic(Common.outerArray, Common.outer); 451 } 452 }