1 /* 2 * Copyright (c) 2024, 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.gcbarriers; 25 26 import compiler.lib.ir_framework.*; 27 import java.lang.invoke.VarHandle; 28 import java.lang.invoke.MethodHandles; 29 import java.lang.ref.Reference; 30 import java.lang.ref.ReferenceQueue; 31 import java.lang.ref.SoftReference; 32 import java.lang.ref.WeakReference; 33 import java.util.concurrent.ThreadLocalRandom; 34 import jdk.test.lib.Asserts; 35 36 /** 37 * @test 38 * @summary Test that G1 barriers are generated and optimized as expected. 39 * @library /test/lib / 40 * @requires vm.gc.G1 41 * @run driver compiler.gcbarriers.TestG1BarrierGeneration 42 */ 43 44 public class TestG1BarrierGeneration { 45 static final String PRE_ONLY = "pre"; 46 static final String POST_ONLY = "post"; 47 static final String POST_ONLY_NOT_NULL = "post notnull"; 48 static final String PRE_AND_POST = "pre post"; 49 static final String PRE_AND_POST_NOT_NULL = "pre post notnull"; 50 static final String ANY = ".*"; 51 52 static class Outer { 53 Object f; 54 } 55 56 static class OuterWithVolatileField { 57 volatile Object f; 58 } 59 60 static class OuterWithFewFields implements Cloneable { 61 Object f1; 62 Object f2; 63 public Object clone() throws CloneNotSupportedException { 64 return super.clone(); 65 } 66 } 67 68 static class OuterWithManyFields implements Cloneable { 69 Object f1; 70 Object f2; 71 Object f3; 72 Object f4; 73 Object f5; 74 Object f6; 75 Object f7; 76 Object f8; 77 Object f9; 78 Object f10; 79 public Object clone() throws CloneNotSupportedException { 80 return super.clone(); 81 } 82 } 83 84 static final VarHandle fVarHandle; 85 static { 86 MethodHandles.Lookup l = MethodHandles.lookup(); 87 try { 88 fVarHandle = l.findVarHandle(Outer.class, "f", Object.class); 89 } catch (Exception e) { 90 throw new Error(e); 91 } 92 } 93 94 @DontInline 95 static void nonInlinedMethod() {} 96 97 public static void main(String[] args) { 98 TestFramework framework = new TestFramework(); 99 Scenario[] scenarios = new Scenario[2*2]; 100 int scenarioIndex = 0; 101 for (int i = 0; i < 2; i++) { 102 for (int j = 0; j < 2; j++) { 103 scenarios[scenarioIndex] = 104 new Scenario(scenarioIndex, 105 "-XX:CompileCommand=inline,java.lang.ref.*::*", 106 "-XX:" + (i == 0 ? "-" : "+") + "UseCompressedOops", 107 "-XX:" + (j == 0 ? "-" : "+") + "ReduceInitialCardMarks"); 108 scenarioIndex++; 109 } 110 } 111 framework.addScenarios(scenarios); 112 framework.start(); 113 } 114 115 @Test 116 @IR(applyIf = {"UseCompressedOops", "false"}, 117 counts = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, PRE_AND_POST, "1"}, 118 phase = CompilePhase.FINAL_CODE) 119 @IR(applyIf = {"UseCompressedOops", "true"}, 120 counts = {IRNode.G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG, PRE_AND_POST, "1"}, 121 phase = CompilePhase.FINAL_CODE) 122 public static void testStore(Outer o, Object o1) { 123 o.f = o1; 124 } 125 126 @Test 127 @IR(applyIf = {"UseCompressedOops", "false"}, 128 counts = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, PRE_ONLY, "1"}, 129 phase = CompilePhase.FINAL_CODE) 130 @IR(applyIf = {"UseCompressedOops", "true"}, 131 counts = {IRNode.G1_STORE_N_WITH_BARRIER_FLAG, PRE_ONLY, "1"}, 132 phase = CompilePhase.FINAL_CODE) 133 public static void testStoreNull(Outer o) { 134 o.f = null; 135 } 136 137 @Test 138 @IR(applyIf = {"UseCompressedOops", "false"}, 139 counts = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, PRE_ONLY, "1"}, 140 phase = CompilePhase.FINAL_CODE) 141 @IR(applyIf = {"UseCompressedOops", "true"}, 142 counts = {IRNode.G1_STORE_N_WITH_BARRIER_FLAG, PRE_ONLY, "1"}, 143 phase = CompilePhase.FINAL_CODE) 144 public static void testStoreObfuscatedNull(Outer o, Object o1) { 145 Object o2 = o1; 146 for (int i = 0; i < 4; i++) { 147 if ((i % 2) == 0) { 148 o2 = null; 149 } 150 } 151 // o2 is null here, but this is only known to C2 after applying some 152 // optimizations (loop unrolling, IGVN). 153 o.f = o2; 154 } 155 156 @Test 157 @IR(applyIf = {"UseCompressedOops", "false"}, 158 counts = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, PRE_AND_POST_NOT_NULL, "1"}, 159 phase = CompilePhase.FINAL_CODE) 160 @IR(applyIf = {"UseCompressedOops", "true"}, 161 counts = {IRNode.G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG, PRE_AND_POST_NOT_NULL, "1"}, 162 phase = CompilePhase.FINAL_CODE) 163 public static void testStoreNotNull(Outer o, Object o1) { 164 if (o1.hashCode() == 42) { 165 return; 166 } 167 o.f = o1; 168 } 169 170 @Test 171 @IR(applyIf = {"UseCompressedOops", "false"}, 172 counts = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, PRE_AND_POST, "2"}, 173 phase = CompilePhase.FINAL_CODE) 174 @IR(applyIf = {"UseCompressedOops", "true"}, 175 counts = {IRNode.G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG, PRE_AND_POST, "2"}, 176 phase = CompilePhase.FINAL_CODE) 177 public static void testStoreTwice(Outer o, Outer p, Object o1) { 178 o.f = o1; 179 p.f = o1; 180 } 181 182 @Test 183 @IR(applyIf = {"UseCompressedOops", "false"}, 184 counts = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, PRE_AND_POST, "1"}, 185 phase = CompilePhase.FINAL_CODE) 186 @IR(applyIf = {"UseCompressedOops", "true"}, 187 counts = {IRNode.G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG, PRE_AND_POST, "1"}, 188 phase = CompilePhase.FINAL_CODE) 189 public static void testStoreVolatile(OuterWithVolatileField o, Object o1) { 190 o.f = o1; 191 } 192 193 @Test 194 @IR(applyIfAnd = {"UseCompressedOops", "false", "ReduceInitialCardMarks", "false"}, 195 counts = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, POST_ONLY, "1"}, 196 phase = CompilePhase.FINAL_CODE) 197 @IR(applyIfAnd = {"UseCompressedOops", "true", "ReduceInitialCardMarks", "false"}, 198 counts = {IRNode.G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG, POST_ONLY, "1"}, 199 phase = CompilePhase.FINAL_CODE) 200 @IR(applyIfAnd = {"UseCompressedOops", "false", "ReduceInitialCardMarks", "true"}, 201 failOn = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, ANY}, 202 phase = CompilePhase.FINAL_CODE) 203 @IR(applyIfAnd = {"UseCompressedOops", "true", "ReduceInitialCardMarks", "true"}, 204 failOn = {IRNode.G1_STORE_N_WITH_BARRIER_FLAG, ANY, 205 IRNode.G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG, ANY}, 206 phase = CompilePhase.FINAL_CODE) 207 public static Outer testStoreOnNewObject(Object o1) { 208 Outer o = new Outer(); 209 o.f = o1; 210 return o; 211 } 212 213 @Test 214 @IR(failOn = {IRNode.STORE_P, IRNode.STORE_N}, 215 phase = CompilePhase.BEFORE_MACRO_EXPANSION) 216 public static Outer testStoreNullOnNewObject() { 217 Outer o = new Outer(); 218 o.f = null; 219 return o; 220 } 221 222 @Test 223 @IR(applyIfAnd = {"UseCompressedOops", "false", "ReduceInitialCardMarks", "false"}, 224 counts = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, POST_ONLY_NOT_NULL, "1"}, 225 phase = CompilePhase.FINAL_CODE) 226 @IR(applyIfAnd = {"UseCompressedOops", "true", "ReduceInitialCardMarks", "false"}, 227 counts = {IRNode.G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG, POST_ONLY_NOT_NULL, "1"}, 228 phase = CompilePhase.FINAL_CODE) 229 @IR(applyIfAnd = {"UseCompressedOops", "false", "ReduceInitialCardMarks", "true"}, 230 failOn = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, ANY}, 231 phase = CompilePhase.FINAL_CODE) 232 @IR(applyIfAnd = {"UseCompressedOops", "true", "ReduceInitialCardMarks", "true"}, 233 failOn = {IRNode.G1_STORE_N_WITH_BARRIER_FLAG, ANY, 234 IRNode.G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG, ANY}, 235 phase = CompilePhase.FINAL_CODE) 236 public static Outer testStoreNotNullOnNewObject(Object o1) { 237 if (o1.hashCode() == 42) { 238 return null; 239 } 240 Outer o = new Outer(); 241 o.f = o1; 242 return o; 243 } 244 245 @Test 246 @IR(applyIfAnd = {"UseCompressedOops", "false", "ReduceInitialCardMarks", "false"}, 247 counts = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, POST_ONLY, "2"}, 248 phase = CompilePhase.FINAL_CODE) 249 @IR(applyIfAnd = {"UseCompressedOops", "true", "ReduceInitialCardMarks", "false"}, 250 counts = {IRNode.G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG, POST_ONLY, "2"}, 251 phase = CompilePhase.FINAL_CODE) 252 @IR(applyIfAnd = {"UseCompressedOops", "false", "ReduceInitialCardMarks", "true"}, 253 failOn = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, ANY}, 254 phase = CompilePhase.FINAL_CODE) 255 @IR(applyIfAnd = {"UseCompressedOops", "true", "ReduceInitialCardMarks", "true"}, 256 failOn = {IRNode.G1_STORE_N_WITH_BARRIER_FLAG, ANY, 257 IRNode.G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG, ANY}, 258 phase = CompilePhase.FINAL_CODE) 259 public static Outer testStoreOnNewObjectInTwoPaths(Object o1, boolean c) { 260 Outer o; 261 if (c) { 262 o = new Outer(); 263 o.f = o1; 264 } else { 265 o = new Outer(); 266 o.f = o1; 267 } 268 return o; 269 } 270 271 @Test 272 @IR(applyIfAnd = {"UseCompressedOops", "false", "ReduceInitialCardMarks", "false"}, 273 counts = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, POST_ONLY, "1"}, 274 phase = CompilePhase.FINAL_CODE) 275 @IR(applyIfAnd = {"UseCompressedOops", "true", "ReduceInitialCardMarks", "false"}, 276 counts = {IRNode.G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG, POST_ONLY, "1"}, 277 phase = CompilePhase.FINAL_CODE) 278 @IR(applyIfAnd = {"UseCompressedOops", "false", "ReduceInitialCardMarks", "true"}, 279 failOn = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, ANY}, 280 phase = CompilePhase.FINAL_CODE) 281 @IR(applyIfAnd = {"UseCompressedOops", "true", "ReduceInitialCardMarks", "true"}, 282 failOn = {IRNode.G1_STORE_N, IRNode.G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG, ANY}, 283 phase = CompilePhase.FINAL_CODE) 284 public static Outer testStoreConditionallyOnNewObject(Object o1, boolean c) { 285 Outer o = new Outer(); 286 if (c) { 287 o.f = o1; 288 } 289 return o; 290 } 291 292 @Test 293 @IR(applyIfAnd = {"UseCompressedOops", "false", "ReduceInitialCardMarks", "false"}, 294 counts = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, POST_ONLY, "1"}, 295 phase = CompilePhase.FINAL_CODE) 296 @IR(applyIfAnd = {"UseCompressedOops", "true", "ReduceInitialCardMarks", "false"}, 297 counts = {IRNode.G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG, POST_ONLY, "1"}, 298 phase = CompilePhase.FINAL_CODE) 299 @IR(applyIfAnd = {"UseCompressedOops", "false", "ReduceInitialCardMarks", "true"}, 300 failOn = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, ANY}, 301 phase = CompilePhase.FINAL_CODE) 302 @IR(applyIfAnd = {"UseCompressedOops", "true", "ReduceInitialCardMarks", "true"}, 303 failOn = {IRNode.G1_STORE_N, IRNode.G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG, ANY}, 304 phase = CompilePhase.FINAL_CODE) 305 public static Outer testStoreOnNewObjectAfterException(Object o1, boolean c) throws Exception { 306 Outer o = new Outer(); 307 if (c) { 308 throw new Exception(""); 309 } 310 o.f = o1; 311 return o; 312 } 313 314 @Test 315 @IR(applyIf = {"UseCompressedOops", "false"}, 316 counts = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, PRE_AND_POST, "1"}, 317 phase = CompilePhase.FINAL_CODE) 318 @IR(applyIf = {"UseCompressedOops", "true"}, 319 counts = {IRNode.G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG, PRE_AND_POST, "1"}, 320 phase = CompilePhase.FINAL_CODE) 321 public static Outer testStoreOnNewObjectAfterCall(Object o1) { 322 Outer o = new Outer(); 323 nonInlinedMethod(); 324 o.f = o1; 325 return o; 326 } 327 328 @Run(test = {"testStore", 329 "testStoreNull", 330 "testStoreObfuscatedNull", 331 "testStoreNotNull", 332 "testStoreTwice", 333 "testStoreVolatile", 334 "testStoreOnNewObject", 335 "testStoreNullOnNewObject", 336 "testStoreNotNullOnNewObject", 337 "testStoreOnNewObjectInTwoPaths", 338 "testStoreConditionallyOnNewObject", 339 "testStoreOnNewObjectAfterException", 340 "testStoreOnNewObjectAfterCall"}) 341 public void runStoreTests() { 342 { 343 Outer o = new Outer(); 344 Object o1 = new Object(); 345 testStore(o, o1); 346 Asserts.assertEquals(o1, o.f); 347 } 348 { 349 Outer o = new Outer(); 350 testStoreNull(o); 351 Asserts.assertNull(o.f); 352 } 353 { 354 Outer o = new Outer(); 355 Object o1 = new Object(); 356 testStoreObfuscatedNull(o, o1); 357 Asserts.assertNull(o.f); 358 } 359 { 360 Outer o = new Outer(); 361 Object o1 = new Object(); 362 testStoreNotNull(o, o1); 363 Asserts.assertEquals(o1, o.f); 364 } 365 { 366 Outer o = new Outer(); 367 Outer p = new Outer(); 368 Object o1 = new Object(); 369 testStoreTwice(o, p, o1); 370 Asserts.assertEquals(o1, o.f); 371 Asserts.assertEquals(o1, p.f); 372 } 373 { 374 OuterWithVolatileField o = new OuterWithVolatileField(); 375 Object o1 = new Object(); 376 testStoreVolatile(o, o1); 377 Asserts.assertEquals(o1, o.f); 378 } 379 { 380 Object o1 = new Object(); 381 Outer o = testStoreOnNewObject(o1); 382 Asserts.assertEquals(o1, o.f); 383 } 384 { 385 Outer o = testStoreNullOnNewObject(); 386 Asserts.assertNull(o.f); 387 } 388 { 389 Object o1 = new Object(); 390 Outer o = testStoreNotNullOnNewObject(o1); 391 Asserts.assertEquals(o1, o.f); 392 } 393 { 394 Object o1 = new Object(); 395 Outer o = testStoreOnNewObjectInTwoPaths(o1, ThreadLocalRandom.current().nextBoolean()); 396 Asserts.assertEquals(o1, o.f); 397 } 398 { 399 Object o1 = new Object(); 400 boolean c = ThreadLocalRandom.current().nextBoolean(); 401 Outer o = testStoreConditionallyOnNewObject(o1, c); 402 Asserts.assertTrue(o.f == (c ? o1 : null)); 403 } 404 { 405 Object o1 = new Object(); 406 boolean c = ThreadLocalRandom.current().nextBoolean(); 407 try { 408 Outer o = testStoreOnNewObjectAfterException(o1, c); 409 } catch (Exception e) {} 410 } 411 { 412 Object o1 = new Object(); 413 Outer o = testStoreOnNewObjectAfterCall(o1); 414 Asserts.assertEquals(o1, o.f); 415 } 416 } 417 418 @Test 419 @IR(applyIf = {"UseCompressedOops", "false"}, 420 counts = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, PRE_AND_POST, "1"}, 421 phase = CompilePhase.FINAL_CODE) 422 @IR(applyIf = {"UseCompressedOops", "true"}, 423 counts = {IRNode.G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG, PRE_AND_POST, "1"}, 424 phase = CompilePhase.FINAL_CODE) 425 public static void testArrayStore(Object[] a, int index, Object o1) { 426 a[index] = o1; 427 } 428 429 @Test 430 @IR(applyIf = {"UseCompressedOops", "false"}, 431 counts = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, PRE_ONLY, "1"}, 432 phase = CompilePhase.FINAL_CODE) 433 @IR(applyIf = {"UseCompressedOops", "true"}, 434 counts = {IRNode.G1_STORE_N_WITH_BARRIER_FLAG, PRE_ONLY, "1"}, 435 phase = CompilePhase.FINAL_CODE) 436 public static void testArrayStoreNull(Object[] a, int index) { 437 a[index] = null; 438 } 439 440 @Test 441 @IR(applyIf = {"UseCompressedOops", "false"}, 442 counts = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, PRE_AND_POST_NOT_NULL, "1"}, 443 phase = CompilePhase.FINAL_CODE) 444 @IR(applyIf = {"UseCompressedOops", "true"}, 445 counts = {IRNode.G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG, PRE_AND_POST_NOT_NULL, "1"}, 446 phase = CompilePhase.FINAL_CODE) 447 public static void testArrayStoreNotNull(Object[] a, int index, Object o1) { 448 if (o1.hashCode() == 42) { 449 return; 450 } 451 a[index] = o1; 452 } 453 454 @Test 455 @IR(applyIf = {"UseCompressedOops", "false"}, 456 counts = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, PRE_AND_POST, "2"}, 457 phase = CompilePhase.FINAL_CODE) 458 @IR(applyIf = {"UseCompressedOops", "true"}, 459 counts = {IRNode.G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG, PRE_AND_POST, "2"}, 460 phase = CompilePhase.FINAL_CODE) 461 public static void testArrayStoreTwice(Object[] a, Object[] b, int index, Object o1) { 462 a[index] = o1; 463 b[index] = o1; 464 } 465 466 @Test 467 @IR(applyIfAnd = {"UseCompressedOops", "false", "ReduceInitialCardMarks", "false"}, 468 counts = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, POST_ONLY, "1"}, 469 phase = CompilePhase.FINAL_CODE) 470 @IR(applyIfAnd = {"UseCompressedOops", "true", "ReduceInitialCardMarks", "false"}, 471 counts = {IRNode.G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG, POST_ONLY, "1"}, 472 phase = CompilePhase.FINAL_CODE) 473 @IR(applyIfAnd = {"UseCompressedOops", "false", "ReduceInitialCardMarks", "true"}, 474 failOn = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, ANY}, 475 phase = CompilePhase.FINAL_CODE) 476 @IR(applyIfAnd = {"UseCompressedOops", "true", "ReduceInitialCardMarks", "true"}, 477 failOn = {IRNode.G1_STORE_N_WITH_BARRIER_FLAG, ANY, 478 IRNode.G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG, ANY}, 479 phase = CompilePhase.FINAL_CODE) 480 public static Object[] testStoreOnNewArrayAtKnownIndex(Object o1) { 481 Object[] a = new Object[10]; 482 a[4] = o1; 483 return a; 484 } 485 486 @Test 487 @IR(applyIfAnd = {"UseCompressedOops", "false", "ReduceInitialCardMarks", "false"}, 488 counts = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, POST_ONLY, "1"}, 489 phase = CompilePhase.FINAL_CODE) 490 @IR(applyIfAnd = {"UseCompressedOops", "true", "ReduceInitialCardMarks", "false"}, 491 counts = {IRNode.G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG, POST_ONLY, "1"}, 492 phase = CompilePhase.FINAL_CODE) 493 @IR(applyIfAnd = {"UseCompressedOops", "false", "ReduceInitialCardMarks", "true"}, 494 failOn = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, ANY}, 495 phase = CompilePhase.FINAL_CODE) 496 @IR(applyIfAnd = {"UseCompressedOops", "true", "ReduceInitialCardMarks", "true"}, 497 failOn = {IRNode.G1_STORE_N_WITH_BARRIER_FLAG, ANY, 498 IRNode.G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG, ANY}, 499 phase = CompilePhase.FINAL_CODE) 500 public static Object[] testStoreOnNewArrayAtUnknownIndex(Object o1, int index) { 501 Object[] a = new Object[10]; 502 a[index] = o1; 503 return a; 504 } 505 506 @Test 507 @IR(failOn = IRNode.SAFEPOINT) 508 @IR(applyIfAnd = {"UseCompressedOops", "false", "ReduceInitialCardMarks", "false"}, 509 counts = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, POST_ONLY, "1"}, 510 phase = CompilePhase.FINAL_CODE) 511 @IR(applyIfAnd = {"UseCompressedOops", "true", "ReduceInitialCardMarks", "false"}, 512 counts = {IRNode.G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG, POST_ONLY, "1"}, 513 phase = CompilePhase.FINAL_CODE) 514 @IR(applyIfAnd = {"UseCompressedOops", "false", "ReduceInitialCardMarks", "true"}, 515 failOn = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, ANY}, 516 phase = CompilePhase.FINAL_CODE) 517 @IR(applyIfAnd = {"UseCompressedOops", "true", "ReduceInitialCardMarks", "true"}, 518 failOn = {IRNode.G1_STORE_N, IRNode.G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG, ANY}, 519 phase = CompilePhase.FINAL_CODE) 520 public static Object[] testStoreAllOnNewSmallArray(Object o1) { 521 Object[] a = new Object[64]; 522 for (int i = 0; i < a.length; i++) { 523 a[i] = o1; 524 } 525 return a; 526 } 527 528 @Test 529 @IR(counts = {IRNode.SAFEPOINT, "1"}) 530 @IR(applyIf = {"UseCompressedOops", "false"}, 531 counts = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, PRE_AND_POST, "1"}, 532 phase = CompilePhase.FINAL_CODE) 533 @IR(applyIf = {"UseCompressedOops", "true"}, 534 counts = {IRNode.G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG, PRE_AND_POST, "1"}, 535 phase = CompilePhase.FINAL_CODE) 536 public static Object[] testStoreAllOnNewLargeArray(Object o1) { 537 Object[] a = new Object[1024]; 538 for (int i = 0; i < a.length; i++) { 539 a[i] = o1; 540 } 541 return a; 542 } 543 544 @Run(test = {"testArrayStore", 545 "testArrayStoreNull", 546 "testArrayStoreNotNull", 547 "testArrayStoreTwice", 548 "testStoreOnNewArrayAtKnownIndex", 549 "testStoreOnNewArrayAtUnknownIndex", 550 "testStoreAllOnNewSmallArray", 551 "testStoreAllOnNewLargeArray"}) 552 public void runArrayStoreTests() { 553 { 554 Object[] a = new Object[10]; 555 Object o1 = new Object(); 556 testArrayStore(a, 4, o1); 557 Asserts.assertEquals(o1, a[4]); 558 } 559 { 560 Object[] a = new Object[10]; 561 testArrayStoreNull(a, 4); 562 Asserts.assertNull(a[4]); 563 } 564 { 565 Object[] a = new Object[10]; 566 Object o1 = new Object(); 567 testArrayStoreNotNull(a, 4, o1); 568 Asserts.assertEquals(o1, a[4]); 569 } 570 { 571 Object[] a = new Object[10]; 572 Object[] b = new Object[10]; 573 Object o1 = new Object(); 574 testArrayStoreTwice(a, b, 4, o1); 575 Asserts.assertEquals(o1, a[4]); 576 Asserts.assertEquals(o1, b[4]); 577 } 578 { 579 Object o1 = new Object(); 580 Object[] a = testStoreOnNewArrayAtKnownIndex(o1); 581 Asserts.assertEquals(o1, a[4]); 582 } 583 { 584 Object o1 = new Object(); 585 Object[] a = testStoreOnNewArrayAtUnknownIndex(o1, 5); 586 Asserts.assertEquals(o1, a[5]); 587 } 588 { 589 Object o1 = new Object(); 590 Object[] a = testStoreAllOnNewSmallArray(o1); 591 for (int i = 0; i < a.length; i++) { 592 Asserts.assertEquals(o1, a[i]); 593 } 594 } 595 { 596 Object o1 = new Object(); 597 Object[] a = testStoreAllOnNewLargeArray(o1); 598 for (int i = 0; i < a.length; i++) { 599 Asserts.assertEquals(o1, a[i]); 600 } 601 } 602 } 603 604 @Test 605 public static Object[] testCloneArrayOfObjects(Object[] a) { 606 Object[] a1 = null; 607 try { 608 a1 = a.clone(); 609 } catch (Exception e) {} 610 return a1; 611 } 612 613 @Test 614 @IR(applyIf = {"ReduceInitialCardMarks", "true"}, 615 failOn = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, ANY, 616 IRNode.G1_STORE_N_WITH_BARRIER_FLAG, ANY, 617 IRNode.G1_ENCODE_P_AND_STORE_N_WITH_BARRIER_FLAG, ANY}, 618 phase = CompilePhase.FINAL_CODE) 619 @IR(applyIfAnd = {"ReduceInitialCardMarks", "false", "UseCompressedOops", "false"}, 620 counts = {IRNode.G1_STORE_P_WITH_BARRIER_FLAG, POST_ONLY, "2"}, 621 phase = CompilePhase.FINAL_CODE) 622 @IR(applyIfAnd = {"ReduceInitialCardMarks", "false", "UseCompressedOops", "true"}, 623 counts = {IRNode.G1_STORE_N_WITH_BARRIER_FLAG, POST_ONLY, "2"}, 624 phase = CompilePhase.FINAL_CODE) 625 public static OuterWithFewFields testCloneObjectWithFewFields(OuterWithFewFields o) { 626 Object o1 = null; 627 try { 628 o1 = o.clone(); 629 } catch (Exception e) {} 630 return (OuterWithFewFields)o1; 631 } 632 633 @Test 634 @IR(applyIf = {"ReduceInitialCardMarks", "true"}, 635 counts = {IRNode.CALL_OF, "jlong_disjoint_arraycopy", "1"}) 636 @IR(applyIf = {"ReduceInitialCardMarks", "false"}, 637 counts = {IRNode.CALL_OF, "G1BarrierSetRuntime::clone", "1"}) 638 public static OuterWithManyFields testCloneObjectWithManyFields(OuterWithManyFields o) { 639 Object o1 = null; 640 try { 641 o1 = o.clone(); 642 } catch (Exception e) {} 643 return (OuterWithManyFields)o1; 644 } 645 646 @Run(test = {"testCloneArrayOfObjects", 647 "testCloneObjectWithFewFields", 648 "testCloneObjectWithManyFields"}) 649 public void runCloneTests() { 650 { 651 Object o1 = new Object(); 652 Object[] a = new Object[4]; 653 for (int i = 0; i < 4; i++) { 654 a[i] = o1; 655 } 656 Object[] a1 = testCloneArrayOfObjects(a); 657 for (int i = 0; i < 4; i++) { 658 Asserts.assertEquals(o1, a1[i]); 659 } 660 } 661 { 662 Object a = new Object(); 663 Object b = new Object(); 664 OuterWithFewFields o = new OuterWithFewFields(); 665 o.f1 = a; 666 o.f2 = b; 667 OuterWithFewFields o1 = testCloneObjectWithFewFields(o); 668 Asserts.assertEquals(a, o1.f1); 669 Asserts.assertEquals(b, o1.f2); 670 } 671 { 672 Object a = new Object(); 673 Object b = new Object(); 674 Object c = new Object(); 675 Object d = new Object(); 676 Object e = new Object(); 677 Object f = new Object(); 678 Object g = new Object(); 679 Object h = new Object(); 680 Object i = new Object(); 681 Object j = new Object(); 682 OuterWithManyFields o = new OuterWithManyFields(); 683 o.f1 = a; 684 o.f2 = b; 685 o.f3 = c; 686 o.f4 = d; 687 o.f5 = e; 688 o.f6 = f; 689 o.f7 = g; 690 o.f8 = h; 691 o.f9 = i; 692 o.f10 = j; 693 OuterWithManyFields o1 = testCloneObjectWithManyFields(o); 694 Asserts.assertEquals(a, o1.f1); 695 Asserts.assertEquals(b, o1.f2); 696 Asserts.assertEquals(c, o1.f3); 697 Asserts.assertEquals(d, o1.f4); 698 Asserts.assertEquals(e, o1.f5); 699 Asserts.assertEquals(f, o1.f6); 700 Asserts.assertEquals(g, o1.f7); 701 Asserts.assertEquals(h, o1.f8); 702 Asserts.assertEquals(i, o1.f9); 703 Asserts.assertEquals(j, o1.f10); 704 } 705 } 706 707 @Test 708 @IR(applyIf = {"UseCompressedOops", "false"}, 709 counts = {IRNode.G1_COMPARE_AND_EXCHANGE_P_WITH_BARRIER_FLAG, PRE_AND_POST, "1"}, 710 phase = CompilePhase.FINAL_CODE) 711 @IR(applyIf = {"UseCompressedOops", "true"}, 712 counts = {IRNode.G1_COMPARE_AND_EXCHANGE_N_WITH_BARRIER_FLAG, PRE_AND_POST, "1"}, 713 phase = CompilePhase.FINAL_CODE) 714 static Object testCompareAndExchange(Outer o, Object oldVal, Object newVal) { 715 return fVarHandle.compareAndExchange(o, oldVal, newVal); 716 } 717 718 @Test 719 @IR(applyIf = {"UseCompressedOops", "false"}, 720 counts = {IRNode.G1_COMPARE_AND_SWAP_P_WITH_BARRIER_FLAG, PRE_AND_POST, "1"}, 721 phase = CompilePhase.FINAL_CODE) 722 @IR(applyIf = {"UseCompressedOops", "true"}, 723 counts = {IRNode.G1_COMPARE_AND_SWAP_N_WITH_BARRIER_FLAG, PRE_AND_POST, "1"}, 724 phase = CompilePhase.FINAL_CODE) 725 static boolean testCompareAndSwap(Outer o, Object oldVal, Object newVal) { 726 return fVarHandle.compareAndSet(o, oldVal, newVal); 727 } 728 729 @Test 730 @IR(applyIf = {"UseCompressedOops", "false"}, 731 counts = {IRNode.G1_GET_AND_SET_P_WITH_BARRIER_FLAG, PRE_AND_POST, "1"}, 732 phase = CompilePhase.FINAL_CODE) 733 @IR(applyIf = {"UseCompressedOops", "true"}, 734 counts = {IRNode.G1_GET_AND_SET_N_WITH_BARRIER_FLAG, PRE_AND_POST, "1"}, 735 phase = CompilePhase.FINAL_CODE) 736 static Object testGetAndSet(Outer o, Object newVal) { 737 return fVarHandle.getAndSet(o, newVal); 738 } 739 740 // IR checks are disabled for s390 because barriers are not elided (to be investigated). 741 @Test 742 @IR(applyIfAnd = {"UseCompressedOops", "false", "ReduceInitialCardMarks", "false"}, 743 applyIfPlatform = {"s390", "false"}, 744 counts = {IRNode.G1_COMPARE_AND_EXCHANGE_P_WITH_BARRIER_FLAG, POST_ONLY, "1"}, 745 phase = CompilePhase.FINAL_CODE) 746 @IR(applyIfAnd = {"UseCompressedOops", "true", "ReduceInitialCardMarks", "false"}, 747 applyIfPlatform = {"s390", "false"}, 748 counts = {IRNode.G1_COMPARE_AND_EXCHANGE_N_WITH_BARRIER_FLAG, POST_ONLY, "1"}, 749 phase = CompilePhase.FINAL_CODE) 750 @IR(applyIfAnd = {"UseCompressedOops", "false", "ReduceInitialCardMarks", "true"}, 751 applyIfPlatform = {"s390", "false"}, 752 failOn = {IRNode.G1_COMPARE_AND_EXCHANGE_P_WITH_BARRIER_FLAG, ANY}, 753 phase = CompilePhase.FINAL_CODE) 754 @IR(applyIfAnd = {"UseCompressedOops", "true", "ReduceInitialCardMarks", "true"}, 755 applyIfPlatform = {"s390", "false"}, 756 failOn = {IRNode.G1_COMPARE_AND_EXCHANGE_N_WITH_BARRIER_FLAG, ANY}, 757 phase = CompilePhase.FINAL_CODE) 758 static Object testCompareAndExchangeOnNewObject(Object oldVal, Object newVal) { 759 Outer o = new Outer(); 760 o.f = oldVal; 761 return fVarHandle.compareAndExchange(o, oldVal, newVal); 762 } 763 764 // IR checks are disabled for s390 when OOPs compression is disabled 765 // because barriers are not elided in this configuration (to be investigated). 766 @Test 767 @IR(applyIfAnd = {"UseCompressedOops", "false", "ReduceInitialCardMarks", "false"}, 768 applyIfPlatform = {"s390", "false"}, 769 counts = {IRNode.G1_COMPARE_AND_SWAP_P_WITH_BARRIER_FLAG, POST_ONLY, "1"}, 770 phase = CompilePhase.FINAL_CODE) 771 @IR(applyIfAnd = {"UseCompressedOops", "true", "ReduceInitialCardMarks", "false"}, 772 counts = {IRNode.G1_COMPARE_AND_SWAP_N_WITH_BARRIER_FLAG, POST_ONLY, "1"}, 773 phase = CompilePhase.FINAL_CODE) 774 @IR(applyIfAnd = {"UseCompressedOops", "false", "ReduceInitialCardMarks", "true"}, 775 applyIfPlatform = {"s390", "false"}, 776 failOn = {IRNode.G1_COMPARE_AND_SWAP_P_WITH_BARRIER_FLAG, ANY}, 777 phase = CompilePhase.FINAL_CODE) 778 @IR(applyIfAnd = {"UseCompressedOops", "true", "ReduceInitialCardMarks", "true"}, 779 failOn = {IRNode.G1_COMPARE_AND_SWAP_N_WITH_BARRIER_FLAG, ANY}, 780 phase = CompilePhase.FINAL_CODE) 781 static boolean testCompareAndSwapOnNewObject(Object oldVal, Object newVal) { 782 Outer o = new Outer(); 783 o.f = oldVal; 784 return fVarHandle.compareAndSet(o, oldVal, newVal); 785 } 786 787 @Test 788 @IR(applyIfAnd = {"UseCompressedOops", "false", "ReduceInitialCardMarks", "false"}, 789 counts = {IRNode.G1_GET_AND_SET_P_WITH_BARRIER_FLAG, POST_ONLY, "1"}, 790 phase = CompilePhase.FINAL_CODE) 791 @IR(applyIfAnd = {"UseCompressedOops", "true", "ReduceInitialCardMarks", "false"}, 792 counts = {IRNode.G1_GET_AND_SET_N_WITH_BARRIER_FLAG, POST_ONLY, "1"}, 793 phase = CompilePhase.FINAL_CODE) 794 @IR(applyIfAnd = {"UseCompressedOops", "false", "ReduceInitialCardMarks", "true"}, 795 failOn = {IRNode.G1_GET_AND_SET_P_WITH_BARRIER_FLAG, ANY}, 796 phase = CompilePhase.FINAL_CODE) 797 @IR(applyIfAnd = {"UseCompressedOops", "true", "ReduceInitialCardMarks", "true"}, 798 failOn = {IRNode.G1_GET_AND_SET_N_WITH_BARRIER_FLAG, ANY}, 799 phase = CompilePhase.FINAL_CODE) 800 static Object testGetAndSetOnNewObject(Object oldVal, Object newVal) { 801 Outer o = new Outer(); 802 o.f = oldVal; 803 return fVarHandle.getAndSet(o, newVal); 804 } 805 806 @Test 807 @IR(applyIfAnd = {"UseCompressedOops", "false", "ReduceInitialCardMarks", "false"}, 808 counts = {IRNode.G1_GET_AND_SET_P_WITH_BARRIER_FLAG, POST_ONLY, "1"}, 809 phase = CompilePhase.FINAL_CODE) 810 @IR(applyIfAnd = {"UseCompressedOops", "true", "ReduceInitialCardMarks", "false"}, 811 counts = {IRNode.G1_GET_AND_SET_N_WITH_BARRIER_FLAG, POST_ONLY, "1"}, 812 phase = CompilePhase.FINAL_CODE) 813 @IR(applyIfAnd = {"UseCompressedOops", "false", "ReduceInitialCardMarks", "true"}, 814 failOn = {IRNode.G1_GET_AND_SET_P_WITH_BARRIER_FLAG, ANY}, 815 phase = CompilePhase.FINAL_CODE) 816 @IR(applyIfAnd = {"UseCompressedOops", "true", "ReduceInitialCardMarks", "true"}, 817 failOn = {IRNode.G1_GET_AND_SET_N_WITH_BARRIER_FLAG, ANY}, 818 phase = CompilePhase.FINAL_CODE) 819 static Object testGetAndSetConditionallyOnNewObject(Object oldVal, Object newVal, boolean c) { 820 Outer o = new Outer(); 821 o.f = oldVal; 822 if (c) { 823 return fVarHandle.getAndSet(o, newVal); 824 } 825 return oldVal; 826 } 827 828 @Test 829 @IR(applyIfAnd = {"UseCompressedOops", "false", "ReduceInitialCardMarks", "false"}, 830 counts = {IRNode.G1_GET_AND_SET_P_WITH_BARRIER_FLAG, POST_ONLY, "1"}, 831 phase = CompilePhase.FINAL_CODE) 832 @IR(applyIfAnd = {"UseCompressedOops", "true", "ReduceInitialCardMarks", "false"}, 833 counts = {IRNode.G1_GET_AND_SET_N_WITH_BARRIER_FLAG, POST_ONLY, "1"}, 834 phase = CompilePhase.FINAL_CODE) 835 @IR(applyIfAnd = {"UseCompressedOops", "false", "ReduceInitialCardMarks", "true"}, 836 failOn = {IRNode.G1_GET_AND_SET_P_WITH_BARRIER_FLAG, ANY}, 837 phase = CompilePhase.FINAL_CODE) 838 @IR(applyIfAnd = {"UseCompressedOops", "true", "ReduceInitialCardMarks", "true"}, 839 failOn = {IRNode.G1_GET_AND_SET_N_WITH_BARRIER_FLAG, ANY}, 840 phase = CompilePhase.FINAL_CODE) 841 static Object testGetAndSetOnNewObjectAfterException(Object oldVal, Object newVal, boolean c) throws Exception { 842 Outer o = new Outer(); 843 if (c) { 844 throw new Exception(""); 845 } 846 o.f = oldVal; 847 return fVarHandle.getAndSet(o, newVal); 848 } 849 850 @Test 851 @IR(applyIf = {"UseCompressedOops", "false"}, 852 counts = {IRNode.G1_GET_AND_SET_P_WITH_BARRIER_FLAG, PRE_AND_POST, "1"}, 853 phase = CompilePhase.FINAL_CODE) 854 @IR(applyIf = {"UseCompressedOops", "true"}, 855 counts = {IRNode.G1_GET_AND_SET_N_WITH_BARRIER_FLAG, PRE_AND_POST, "1"}, 856 phase = CompilePhase.FINAL_CODE) 857 static Object testGetAndSetOnNewObjectAfterCall(Object oldVal, Object newVal) { 858 Outer o = new Outer(); 859 nonInlinedMethod(); 860 o.f = oldVal; 861 return fVarHandle.getAndSet(o, newVal); 862 } 863 864 @Run(test = {"testCompareAndExchange", 865 "testCompareAndSwap", 866 "testGetAndSet", 867 "testCompareAndExchangeOnNewObject", 868 "testCompareAndSwapOnNewObject", 869 "testGetAndSetOnNewObject", 870 "testGetAndSetConditionallyOnNewObject", 871 "testGetAndSetOnNewObjectAfterException", 872 "testGetAndSetOnNewObjectAfterCall"}) 873 public void runAtomicTests() { 874 { 875 Outer o = new Outer(); 876 Object oldVal = new Object(); 877 o.f = oldVal; 878 Object newVal = new Object(); 879 Object oldVal2 = testCompareAndExchange(o, oldVal, newVal); 880 Asserts.assertEquals(oldVal, oldVal2); 881 Asserts.assertEquals(o.f, newVal); 882 } 883 { 884 Outer o = new Outer(); 885 Object oldVal = new Object(); 886 o.f = oldVal; 887 Object newVal = new Object(); 888 boolean b = testCompareAndSwap(o, oldVal, newVal); 889 Asserts.assertTrue(b); 890 Asserts.assertEquals(o.f, newVal); 891 } 892 { 893 Outer o = new Outer(); 894 Object oldVal = new Object(); 895 o.f = oldVal; 896 Object newVal = new Object(); 897 Object oldVal2 = testGetAndSet(o, newVal); 898 Asserts.assertEquals(oldVal, oldVal2); 899 Asserts.assertEquals(o.f, newVal); 900 } 901 { 902 Object oldVal = new Object(); 903 Object newVal = new Object(); 904 Object oldVal2 = testCompareAndExchangeOnNewObject(oldVal, newVal); 905 Asserts.assertEquals(oldVal, oldVal2); 906 } 907 { 908 Object oldVal = new Object(); 909 Object newVal = new Object(); 910 boolean b = testCompareAndSwapOnNewObject(oldVal, newVal); 911 Asserts.assertTrue(b); 912 } 913 { 914 Object oldVal = new Object(); 915 Object newVal = new Object(); 916 Object oldVal2 = testGetAndSetOnNewObject(oldVal, newVal); 917 Asserts.assertEquals(oldVal, oldVal2); 918 } 919 { 920 Object oldVal = new Object(); 921 Object newVal = new Object(); 922 boolean c = ThreadLocalRandom.current().nextBoolean(); 923 Object oldVal2 = testGetAndSetConditionallyOnNewObject(oldVal, newVal, c); 924 Asserts.assertEquals(oldVal, oldVal2); 925 } 926 { 927 Object oldVal = new Object(); 928 Object newVal = new Object(); 929 boolean c = ThreadLocalRandom.current().nextBoolean(); 930 try { 931 Object oldVal2 = testGetAndSetOnNewObjectAfterException(oldVal, newVal, c); 932 } catch (Exception e) {} 933 } 934 { 935 Object oldVal = new Object(); 936 Object newVal = new Object(); 937 Object oldVal2 = testGetAndSetOnNewObjectAfterCall(oldVal, newVal); 938 Asserts.assertEquals(oldVal, oldVal2); 939 } 940 } 941 942 @Test 943 @IR(applyIf = {"UseCompressedOops", "false"}, 944 counts = {IRNode.G1_LOAD_P_WITH_BARRIER_FLAG, PRE_ONLY, "1"}, 945 phase = CompilePhase.FINAL_CODE) 946 @IR(applyIf = {"UseCompressedOops", "true"}, 947 counts = {IRNode.G1_LOAD_N_WITH_BARRIER_FLAG, PRE_ONLY, "1"}, 948 phase = CompilePhase.FINAL_CODE) 949 static Object testLoadSoftReference(SoftReference<Object> ref) { 950 return ref.get(); 951 } 952 953 @Test 954 @IR(applyIf = {"UseCompressedOops", "false"}, 955 counts = {IRNode.G1_LOAD_P_WITH_BARRIER_FLAG, PRE_ONLY, "1"}, 956 phase = CompilePhase.FINAL_CODE) 957 @IR(applyIf = {"UseCompressedOops", "true"}, 958 counts = {IRNode.G1_LOAD_N_WITH_BARRIER_FLAG, PRE_ONLY, "1"}, 959 phase = CompilePhase.FINAL_CODE) 960 static Object testLoadWeakReference(WeakReference<Object> ref) { 961 return ref.get(); 962 } 963 964 @Run(test = {"testLoadSoftReference", 965 "testLoadWeakReference"}) 966 public void runReferenceTests() { 967 { 968 Object o1 = new Object(); 969 SoftReference<Object> sref = new SoftReference<Object>(o1); 970 Object o2 = testLoadSoftReference(sref); 971 Asserts.assertTrue(o2 == o1 || o2 == null); 972 } 973 { 974 Object o1 = new Object(); 975 WeakReference<Object> wref = new WeakReference<Object>(o1); 976 Object o2 = testLoadWeakReference(wref); 977 Asserts.assertTrue(o2 == o1 || o2 == null); 978 } 979 } 980 }