1 /*
2 * Copyright (c) 2026, Amazon.com, Inc. 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
25 package gc.stress.ihash;
26
27 /*
28 * @test id=g1
29 * @bug 8387150
30 * @summary Identity hash code stays stable across GC relocation when the
31 * C2-compiled hashCode intrinsic recomputes an address-based hash.
32 * @requires vm.gc.G1
33 * @requires vm.compiler2.enabled
34 * @requires vm.opt.UseCompactObjectHeaders == null | vm.opt.UseCompactObjectHeaders == true
35 * @run main/othervm -XX:+UseCompactObjectHeaders -XX:+UseG1GC -Xms64m -Xmx64m
36 * gc.stress.ihash.TestHashCodeC2Relocation
37 */
38
39 /*
40 * @test id=parallel
41 * @bug 8387150
42 * @summary Identity hash code stays stable across GC relocation when the
43 * C2-compiled hashCode intrinsic recomputes an address-based hash.
44 * @requires vm.gc.Parallel
45 * @requires vm.compiler2.enabled
46 * @requires vm.opt.UseCompactObjectHeaders == null | vm.opt.UseCompactObjectHeaders == true
47 * @run main/othervm -XX:+UseCompactObjectHeaders -XX:+UseParallelGC -Xms64m -Xmx64m
48 * gc.stress.ihash.TestHashCodeC2Relocation
49 */
50
51 /*
52 * @test id=serial
53 * @bug 8387150
54 * @summary Identity hash code stays stable across GC relocation when the
55 * C2-compiled hashCode intrinsic recomputes an address-based hash.
56 * @requires vm.gc.Serial
57 * @requires vm.compiler2.enabled
58 * @requires vm.opt.UseCompactObjectHeaders == null | vm.opt.UseCompactObjectHeaders == true
59 * @run main/othervm -XX:+UseCompactObjectHeaders -XX:+UseSerialGC -Xms64m -Xmx64m
60 * gc.stress.ihash.TestHashCodeC2Relocation
61 */
62
63 /*
64 * @test id=shenandoah
65 * @bug 8387150
66 * @summary Identity hash code stays stable across GC relocation when the
67 * C2-compiled hashCode intrinsic recomputes an address-based hash.
68 * @requires vm.gc.Shenandoah
69 * @requires vm.compiler2.enabled
70 * @requires vm.opt.UseCompactObjectHeaders == null | vm.opt.UseCompactObjectHeaders == true
71 * @run main/othervm -XX:+UseCompactObjectHeaders -XX:+UseShenandoahGC -Xms64m -Xmx64m
72 * gc.stress.ihash.TestHashCodeC2Relocation
73 */
74
75 /*
76 * @test id=zgc
77 * @bug 8387150
78 * @summary Identity hash code stays stable across GC relocation when the
79 * C2-compiled hashCode intrinsic recomputes an address-based hash.
80 * @requires vm.gc.Z
81 * @requires vm.compiler2.enabled
82 * @requires vm.opt.UseCompactObjectHeaders == null | vm.opt.UseCompactObjectHeaders == true
83 * @run main/othervm -XX:+UseCompactObjectHeaders -XX:+UseZGC -Xms64m -Xmx64m
84 * gc.stress.ihash.TestHashCodeC2Relocation
85 */
86
87 /**
88 * Regression test for a C2 miscompilation of the {@code System.identityHashCode}
89 * intrinsic under {@code -XX:+UseCompactObjectHeaders}.
90 *
91 * With compact headers there is no room in the 64-bit header to always store a
92 * 31-bit identity hash. An object that has been hashed but not yet expanded by
93 * the GC (hashctrl state {@code 0b01}) has its identity hash recomputed on every
94 * read from the object's current heap address ({@code FastHash(address)}). This
95 * is only correct while the object stays at the same address; when the GC
96 * relocates the object it freezes the hash into a hidden field (state
97 * {@code 0b11}).
98 *
99 * The C2 intrinsic inlined the {@code 0b01} recompute and materialized the raw
100 * object address with a free-floating {@code CastP2X} (null control input).
101 * Global Code Motion could hoist that materialization above a GC safepoint;
102 * after a relocation the oop reference is updated but the already-materialized
103 * raw address is not, so the recomputed hash was derived from the stale
104 * pre-relocation address. The result: {@code System.identityHashCode} returned
105 * inconsistent values for a single live object, violating the
106 * {@code Object.hashCode()} stability contract.
107 *
108 * The reproducer hashes a fresh object, forces young-gen relocation via
109 * allocation churn, and re-reads the hash. The "install after relocation"
110 * ordering, warmed up so probe() is C2-compiled, is the shape that triggered the
111 * miscompiled path. A correct VM reports zero mismatches.
112 */
113 public class TestHashCodeC2Relocation {
114
115 // Keep GC honest; defeat allocation elimination of the churn garbage.
116 static volatile Object sink;
117
118 static final long ITERS = 2_000_000L;
119
120 /** Allocate short-lived garbage so the next young GC relocates our surviving object. */
121 static void churn() {
122 Object[] junk = new Object[64];
123 for (int k = 0; k < 64; k++) {
124 junk[k] = new byte[256];
125 }
126 sink = junk;
127 }
128
129 /**
130 * @return a non-null description of the first observed violation, or null if stable.
131 */
132 static String probe(long iters, boolean installBefore) {
133 for (long i = 0; i < iters; i++) {
134 Object o = new Object();
135 int h0, h1;
136 if (installBefore) {
137 h0 = System.identityHashCode(o); // install hash, THEN relocate
138 churn();
139 h1 = System.identityHashCode(o);
140 } else {
141 churn(); // relocate, THEN first read installs/loads hash
142 h0 = System.identityHashCode(o);
143 h1 = System.identityHashCode(o);
144 }
145 if (h1 != h0) {
146 int settled = System.identityHashCode(o); // re-read returns the TRUE (frozen) hash
147 return "identityHashCode returned inconsistent values for a live object"
148 + " at iter=" + i + ": h0=" + h0 + " h1=" + h1
149 + " settledReread=" + settled;
150 }
151 }
152 return null;
153 }
154
155 public static void main(String[] args) {
156 // Warm up with the safe ordering first so that probe() is compiled by C2
157 // with the installBefore branch profiled -- the shape that exposed the bug.
158 String warmup = probe(ITERS, true);
159 if (warmup != null) {
160 throw new RuntimeException("[warmup/installBefore] " + warmup);
161 }
162
163 // Detection run: read the identity hash only after relocation.
164 String result = probe(ITERS, false);
165 if (result != null) {
166 throw new RuntimeException("[installAfter] " + result);
167 }
168
169 System.out.println("PASSED: identity hash codes stable across relocation");
170 }
171 }