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