1 /* 2 * Copyright (c) 2023, 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 @IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 191 static void testAllocateThenAtomic(Inner i) { 192 Outer o = new Outer(); 193 Common.blackhole(o); 194 Common.field1VarHandle.getAndSet(o, i); 195 } 196 197 @Test 198 // TODO 8329234 199 // @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 200 @IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 201 static void testLoadThenAtomic(Outer o, Inner i) { 202 Common.blackhole(o.field1); 203 Common.field1VarHandle.getAndSet(o, i); 204 } 205 206 @Test 207 @IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "2" }, phase = CompilePhase.FINAL_CODE) 208 static void testAtomicThenAtomicAnotherField(Outer o, Inner i) { 209 Common.field1VarHandle.getAndSet(o, i); 210 Common.field2VarHandle.getAndSet(o, i); 211 } 212 213 @Test 214 @IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 215 static void testAllocateArrayThenAtomicAtKnownIndex(Outer o) { 216 Outer[] a = new Outer[42]; 217 Common.blackhole(a); 218 Common.outerArrayVarHandle.getAndSet(a, 2, o); 219 } 220 221 @Test 222 @IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 223 static void testAllocateArrayThenAtomicAtUnknownIndex(Outer o, int index) { 224 Outer[] a = new Outer[42]; 225 Common.blackhole(a); 226 Common.outerArrayVarHandle.getAndSet(a, index, o); 227 } 228 229 @Test 230 @IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "2" }, phase = CompilePhase.FINAL_CODE) 231 static void testArrayAtomicThenAtomicAtUnknownIndices(Outer[] a, Outer o, int index1, int index2) { 232 Common.outerArrayVarHandle.getAndSet(a, index1, o); 233 Common.outerArrayVarHandle.getAndSet(a, index2, o); 234 } 235 236 @Run(test = {"testAllocateThenAtomic", 237 "testLoadThenAtomic", 238 "testAtomicThenAtomicAnotherField", 239 "testAllocateArrayThenAtomicAtKnownIndex", 240 "testAllocateArrayThenAtomicAtUnknownIndex", 241 "testArrayAtomicThenAtomicAtUnknownIndices"}) 242 void runAtomicOperationTests() { 243 testAllocateThenAtomic(Common.inner); 244 testLoadThenAtomic(Common.outer, Common.inner); 245 testAtomicThenAtomicAnotherField(Common.outer, Common.inner); 246 testAllocateArrayThenAtomicAtKnownIndex(Common.outer); 247 testAllocateArrayThenAtomicAtUnknownIndex(Common.outer, 10); 248 testArrayAtomicThenAtomicAtUnknownIndices(Common.outerArray, Common.outer, 10, 20); 249 } 250 } 251 252 class TestZGCEffectiveBarrierElision { 253 254 @Test 255 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE) 256 static void testAllocateThenLoad() { 257 Outer o1 = new Outer(); 258 Common.blackhole(o1); 259 // This load is directly optimized away by C2. 260 Common.blackhole(o1.field1); 261 Common.blackhole(o1.field1); 262 } 263 264 @Test 265 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE) 266 static void testAllocateThenStore(Inner i) { 267 Outer o1 = new Outer(); 268 Common.blackhole(o1); 269 o1.field1 = i; 270 } 271 272 @Test 273 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 274 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE) 275 static void testLoadThenLoad(Outer o) { 276 Common.blackhole(o.field1); 277 Common.blackhole(o.field1); 278 } 279 280 @Test 281 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 282 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE) 283 static void testStoreThenStore(Outer o, Inner i) { 284 o.field1 = i; 285 o.field1 = i; 286 } 287 288 @Test 289 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 290 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE) 291 static void testStoreThenLoad(Outer o, Inner i) { 292 o.field1 = i; 293 Common.blackhole(o.field1); 294 } 295 296 @Run(test = {"testAllocateThenLoad", 297 "testAllocateThenStore", 298 "testLoadThenLoad", 299 "testStoreThenStore", 300 "testStoreThenLoad"}) 301 void runBasicTests() { 302 testAllocateThenLoad(); 303 testAllocateThenStore(Common.inner); 304 testLoadThenLoad(Common.outer); 305 testStoreThenStore(Common.outer, Common.inner); 306 testStoreThenLoad(Common.outer, Common.inner); 307 } 308 309 @Test 310 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE) 311 static void testAllocateArrayThenStoreAtKnownIndex(Outer o) { 312 Outer[] a = new Outer[42]; 313 Common.blackhole(a); 314 Common.outerArrayVarHandle.setVolatile(a, 0, o); 315 } 316 317 @Test 318 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE) 319 static void testAllocateArrayThenStoreAtUnknownIndex(Outer o, int index) { 320 Outer[] a = new Outer[42]; 321 Common.blackhole(a); 322 Common.outerArrayVarHandle.setVolatile(a, index, o); 323 } 324 325 @Test 326 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 327 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE) 328 static void testArrayLoadThenLoad(Outer[] a) { 329 Common.blackhole(Common.outerArrayVarHandle.getVolatile(a, 0)); 330 Common.blackhole(Common.outerArrayVarHandle.getVolatile(a, 0)); 331 } 332 333 @Test 334 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 335 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE) 336 static void testArrayStoreThenStore(Outer[] a, Outer o) { 337 Common.outerArrayVarHandle.setVolatile(a, 0, o); 338 Common.outerArrayVarHandle.setVolatile(a, 0, o); 339 } 340 341 @Test 342 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 343 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE) 344 static void testArrayStoreThenLoad(Outer[] a, Outer o) { 345 Common.outerArrayVarHandle.setVolatile(a, 0, o); 346 Common.blackhole(Common.outerArrayVarHandle.getVolatile(a, 0)); 347 } 348 349 @Run(test = {"testAllocateArrayThenStoreAtKnownIndex", 350 "testAllocateArrayThenStoreAtUnknownIndex", 351 "testArrayLoadThenLoad", 352 "testArrayStoreThenStore", 353 "testArrayStoreThenLoad"}) 354 void runArrayTests() { 355 testAllocateArrayThenStoreAtKnownIndex(Common.outer); 356 testAllocateArrayThenStoreAtUnknownIndex(Common.outer, 10); 357 testArrayLoadThenLoad(Common.outerArray); 358 testArrayStoreThenStore(Common.outerArray, Common.outer); 359 testArrayStoreThenLoad(Common.outerArray, Common.outer); 360 } 361 362 @Test 363 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 364 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE) 365 static void testStoreThenConditionalStore(Outer o, Inner i, int value) { 366 o.field1 = i; 367 if (value % 2 == 0) { 368 o.field1 = i; 369 } 370 } 371 372 @Test 373 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 374 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE) 375 static void testStoreThenStoreInLoop(Outer o, Inner i) { 376 o.field1 = i; 377 for (int j = 0; j < 100; j++) { 378 o.field1 = i; 379 } 380 } 381 382 @Run(test = {"testStoreThenConditionalStore", 383 "testStoreThenStoreInLoop"}) 384 void runControlFlowTests() { 385 testStoreThenConditionalStore(Common.outer, Common.inner, ThreadLocalRandom.current().nextInt(0, 100)); 386 testStoreThenStoreInLoop(Common.outer, Common.inner); 387 } 388 389 @Test 390 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 391 @IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE) 392 static void testStoreThenAtomic(Outer o, Inner i) { 393 o.field1 = i; 394 Common.field1VarHandle.getAndSet(o, i); 395 } 396 397 @Test 398 @IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 399 @IR(counts = { IRNode.Z_LOAD_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE) 400 static void testAtomicThenLoad(Outer o, Inner i) { 401 Common.field1VarHandle.getAndSet(o, i); 402 Common.blackhole(o.field1); 403 } 404 405 @Test 406 @IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 407 @IR(counts = { IRNode.Z_STORE_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE) 408 static void testAtomicThenStore(Outer o, Inner i) { 409 Common.field1VarHandle.getAndSet(o, i); 410 o.field1 = i; 411 } 412 413 @Test 414 @IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 415 @IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE) 416 static void testAtomicThenAtomic(Outer o, Inner i) { 417 Common.field1VarHandle.getAndSet(o, i); 418 Common.field1VarHandle.getAndSet(o, i); 419 } 420 421 @Test 422 @IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.REMAINING, "1" }, phase = CompilePhase.FINAL_CODE) 423 @IR(counts = { IRNode.Z_GET_AND_SET_P_WITH_BARRIER_FLAG, Common.ELIDED, "1" }, phase = CompilePhase.FINAL_CODE) 424 static void testArrayAtomicThenAtomic(Outer[] a, Outer o) { 425 Common.outerArrayVarHandle.getAndSet(a, 0, o); 426 Common.outerArrayVarHandle.getAndSet(a, 0, o); 427 } 428 429 @Run(test = {"testStoreThenAtomic", 430 "testAtomicThenLoad", 431 "testAtomicThenStore", 432 "testAtomicThenAtomic", 433 "testArrayAtomicThenAtomic"}) 434 void runAtomicOperationTests() { 435 testStoreThenAtomic(Common.outer, Common.inner); 436 testAtomicThenLoad(Common.outer, Common.inner); 437 testAtomicThenStore(Common.outer, Common.inner); 438 testAtomicThenAtomic(Common.outer, Common.inner); 439 testArrayAtomicThenAtomic(Common.outerArray, Common.outer); 440 } 441 }