Skip to content

Commit 23069e9

Browse files
committed
8358334: C2/Shenandoah: incorrect execution with Unsafe
Reviewed-by: shade, wkemper Backport-of: 46cfc1e1940ff6b91c4f0cb0a9161fd0aef37c38
1 parent b2e77ea commit 23069e9

File tree

3 files changed

+128
-37
lines changed

3 files changed

+128
-37
lines changed

src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp

Lines changed: 51 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -639,6 +639,34 @@ void ShenandoahBarrierC2Support::verify(RootNode* root) {
639639
}
640640
#endif
641641

642+
bool ShenandoahBarrierC2Support::is_anti_dependent_load_at_control(PhaseIdealLoop* phase, Node* maybe_load, Node* store,
643+
Node* control) {
644+
return maybe_load->is_Load() && phase->C->can_alias(store->adr_type(), phase->C->get_alias_index(maybe_load->adr_type())) &&
645+
phase->ctrl_or_self(maybe_load) == control;
646+
}
647+
648+
void ShenandoahBarrierC2Support::maybe_push_anti_dependent_loads(PhaseIdealLoop* phase, Node* maybe_store, Node* control, Unique_Node_List &wq) {
649+
if (!maybe_store->is_Store() && !maybe_store->is_LoadStore()) {
650+
return;
651+
}
652+
Node* mem = maybe_store->in(MemNode::Memory);
653+
for (DUIterator_Fast imax, i = mem->fast_outs(imax); i < imax; i++) {
654+
Node* u = mem->fast_out(i);
655+
if (is_anti_dependent_load_at_control(phase, u, maybe_store, control)) {
656+
wq.push(u);
657+
}
658+
}
659+
}
660+
661+
void ShenandoahBarrierC2Support::push_data_inputs_at_control(PhaseIdealLoop* phase, Node* n, Node* ctrl, Unique_Node_List &wq) {
662+
for (uint i = 0; i < n->req(); i++) {
663+
Node* in = n->in(i);
664+
if (in != nullptr && (ShenandoahIUBarrier ? (phase->ctrl_or_self(in) == ctrl) : (phase->has_ctrl(in) && phase->get_ctrl(in) == ctrl))) {
665+
wq.push(in);
666+
}
667+
}
668+
}
669+
642670
bool ShenandoahBarrierC2Support::is_dominator_same_ctrl(Node* c, Node* d, Node* n, PhaseIdealLoop* phase) {
643671
// That both nodes have the same control is not sufficient to prove
644672
// domination, verify that there's no path from d to n
@@ -653,22 +681,9 @@ bool ShenandoahBarrierC2Support::is_dominator_same_ctrl(Node* c, Node* d, Node*
653681
if (m->is_Phi() && m->in(0)->is_Loop()) {
654682
assert(phase->ctrl_or_self(m->in(LoopNode::EntryControl)) != c, "following loop entry should lead to new control");
655683
} else {
656-
if (m->is_Store() || m->is_LoadStore()) {
657-
// Take anti-dependencies into account
658-
Node* mem = m->in(MemNode::Memory);
659-
for (DUIterator_Fast imax, i = mem->fast_outs(imax); i < imax; i++) {
660-
Node* u = mem->fast_out(i);
661-
if (u->is_Load() && phase->C->can_alias(m->adr_type(), phase->C->get_alias_index(u->adr_type())) &&
662-
phase->ctrl_or_self(u) == c) {
663-
wq.push(u);
664-
}
665-
}
666-
}
667-
for (uint i = 0; i < m->req(); i++) {
668-
if (m->in(i) != nullptr && phase->ctrl_or_self(m->in(i)) == c) {
669-
wq.push(m->in(i));
670-
}
671-
}
684+
// Take anti-dependencies into account
685+
maybe_push_anti_dependent_loads(phase, m, c, wq);
686+
push_data_inputs_at_control(phase, m, c, wq);
672687
}
673688
}
674689
return true;
@@ -1020,7 +1035,20 @@ void ShenandoahBarrierC2Support::call_lrb_stub(Node*& ctrl, Node*& val, Node* lo
10201035
phase->register_new_node(val, ctrl);
10211036
}
10221037

1023-
void ShenandoahBarrierC2Support::fix_ctrl(Node* barrier, Node* region, const MemoryGraphFixer& fixer, Unique_Node_List& uses, Unique_Node_List& uses_to_ignore, uint last, PhaseIdealLoop* phase) {
1038+
void ShenandoahBarrierC2Support::collect_nodes_above_barrier(Unique_Node_List &nodes_above_barrier, PhaseIdealLoop* phase, Node* ctrl, Node* init_raw_mem) {
1039+
nodes_above_barrier.clear();
1040+
if (phase->has_ctrl(init_raw_mem) && phase->get_ctrl(init_raw_mem) == ctrl && !init_raw_mem->is_Phi()) {
1041+
nodes_above_barrier.push(init_raw_mem);
1042+
}
1043+
for (uint next = 0; next < nodes_above_barrier.size(); next++) {
1044+
Node* n = nodes_above_barrier.at(next);
1045+
// Take anti-dependencies into account
1046+
maybe_push_anti_dependent_loads(phase, n, ctrl, nodes_above_barrier);
1047+
push_data_inputs_at_control(phase, n, ctrl, nodes_above_barrier);
1048+
}
1049+
}
1050+
1051+
void ShenandoahBarrierC2Support::fix_ctrl(Node* barrier, Node* region, const MemoryGraphFixer& fixer, Unique_Node_List& uses, Unique_Node_List& nodes_above_barrier, uint last, PhaseIdealLoop* phase) {
10241052
Node* ctrl = phase->get_ctrl(barrier);
10251053
Node* init_raw_mem = fixer.find_mem(ctrl, barrier);
10261054

@@ -1031,30 +1059,17 @@ void ShenandoahBarrierC2Support::fix_ctrl(Node* barrier, Node* region, const Mem
10311059
// control will be after the expanded barrier. The raw memory (if
10321060
// its memory is control dependent on the barrier's input control)
10331061
// must stay above the barrier.
1034-
uses_to_ignore.clear();
1035-
if (phase->has_ctrl(init_raw_mem) && phase->get_ctrl(init_raw_mem) == ctrl && !init_raw_mem->is_Phi()) {
1036-
uses_to_ignore.push(init_raw_mem);
1037-
}
1038-
for (uint next = 0; next < uses_to_ignore.size(); next++) {
1039-
Node *n = uses_to_ignore.at(next);
1040-
for (uint i = 0; i < n->req(); i++) {
1041-
Node* in = n->in(i);
1042-
if (in != nullptr && phase->has_ctrl(in) && phase->get_ctrl(in) == ctrl) {
1043-
uses_to_ignore.push(in);
1044-
}
1045-
}
1046-
}
1062+
collect_nodes_above_barrier(nodes_above_barrier, phase, ctrl, init_raw_mem);
10471063
for (DUIterator_Fast imax, i = ctrl->fast_outs(imax); i < imax; i++) {
10481064
Node* u = ctrl->fast_out(i);
10491065
if (u->_idx < last &&
10501066
u != barrier &&
10511067
!u->depends_only_on_test() && // preserve dependency on test
1052-
!uses_to_ignore.member(u) &&
1068+
!nodes_above_barrier.member(u) &&
10531069
(u->in(0) != ctrl || (!u->is_Region() && !u->is_Phi())) &&
10541070
(ctrl->Opcode() != Op_CatchProj || u->Opcode() != Op_CreateEx)) {
10551071
Node* old_c = phase->ctrl_or_self(u);
1056-
Node* c = old_c;
1057-
if (c != ctrl ||
1072+
if (old_c != ctrl ||
10581073
is_dominator_same_ctrl(old_c, barrier, u, phase) ||
10591074
ShenandoahBarrierSetC2::is_shenandoah_state_load(u)) {
10601075
phase->igvn().rehash_node_delayed(u);
@@ -1334,7 +1349,7 @@ void ShenandoahBarrierC2Support::pin_and_expand(PhaseIdealLoop* phase) {
13341349

13351350
// Expand load-reference-barriers
13361351
MemoryGraphFixer fixer(Compile::AliasIdxRaw, true, phase);
1337-
Unique_Node_List uses_to_ignore;
1352+
Unique_Node_List nodes_above_barriers;
13381353
for (int i = state->load_reference_barriers_count() - 1; i >= 0; i--) {
13391354
ShenandoahLoadReferenceBarrierNode* lrb = state->load_reference_barrier(i);
13401355
uint last = phase->C->unique();
@@ -1429,7 +1444,7 @@ void ShenandoahBarrierC2Support::pin_and_expand(PhaseIdealLoop* phase) {
14291444
Node* out_val = val_phi;
14301445
phase->register_new_node(val_phi, region);
14311446

1432-
fix_ctrl(lrb, region, fixer, uses, uses_to_ignore, last, phase);
1447+
fix_ctrl(lrb, region, fixer, uses, nodes_above_barriers, last, phase);
14331448

14341449
ctrl = orig_ctrl;
14351450

@@ -1585,7 +1600,7 @@ void ShenandoahBarrierC2Support::pin_and_expand(PhaseIdealLoop* phase) {
15851600
phase->register_control(region, loop, heap_stable_ctrl->in(0));
15861601
phase->register_new_node(phi, region);
15871602

1588-
fix_ctrl(barrier, region, fixer, uses, uses_to_ignore, last, phase);
1603+
fix_ctrl(barrier, region, fixer, uses, nodes_above_barriers, last, phase);
15891604
for(uint next = 0; next < uses.size(); next++ ) {
15901605
Node *n = uses.at(next);
15911606
assert(phase->get_ctrl(n) == init_ctrl, "bad control");

src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.hpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,16 @@ class ShenandoahBarrierC2Support : public AllStatic {
6262
PhaseIdealLoop* phase, int flags);
6363
static void call_lrb_stub(Node*& ctrl, Node*& val, Node* load_addr,
6464
DecoratorSet decorators, PhaseIdealLoop* phase);
65+
66+
static void collect_nodes_above_barrier(Unique_Node_List &nodes_above_barrier, PhaseIdealLoop* phase, Node* ctrl,
67+
Node* init_raw_mem);
68+
6569
static void test_in_cset(Node*& ctrl, Node*& not_cset_ctrl, Node* val, Node* raw_mem, PhaseIdealLoop* phase);
6670
static void move_gc_state_test_out_of_loop(IfNode* iff, PhaseIdealLoop* phase);
6771
static void merge_back_to_back_tests(Node* n, PhaseIdealLoop* phase);
6872
static bool merge_point_safe(Node* region);
6973
static bool identical_backtoback_ifs(Node *n, PhaseIdealLoop* phase);
70-
static void fix_ctrl(Node* barrier, Node* region, const MemoryGraphFixer& fixer, Unique_Node_List& uses, Unique_Node_List& uses_to_ignore, uint last, PhaseIdealLoop* phase);
74+
static void fix_ctrl(Node* barrier, Node* region, const MemoryGraphFixer& fixer, Unique_Node_List& uses, Unique_Node_List& nodes_above_barrier, uint last, PhaseIdealLoop* phase);
7175
static IfNode* find_unswitching_candidate(const IdealLoopTree *loop, PhaseIdealLoop* phase);
7276

7377
static Node* get_load_addr(PhaseIdealLoop* phase, VectorSet& visited, Node* lrb);
@@ -82,6 +86,11 @@ class ShenandoahBarrierC2Support : public AllStatic {
8286
static void pin_and_expand(PhaseIdealLoop* phase);
8387
static void optimize_after_expansion(VectorSet& visited, Node_Stack& nstack, Node_List& old_new, PhaseIdealLoop* phase);
8488

89+
static void push_data_inputs_at_control(PhaseIdealLoop* phase, Node* n, Node* ctrl,
90+
Unique_Node_List &wq);
91+
static bool is_anti_dependent_load_at_control(PhaseIdealLoop* phase, Node* maybe_load, Node* store, Node* control);
92+
93+
static void maybe_push_anti_dependent_loads(PhaseIdealLoop* phase, Node* maybe_store, Node* control, Unique_Node_List &wq);
8594
#ifdef ASSERT
8695
static void verify(RootNode* root);
8796
#endif
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright (c) 2025, Red Hat, Inc. 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+
* @test
26+
* @bug 8358334
27+
* @summary C2/Shenandoah: incorrect execution with Unsafe
28+
* @requires vm.gc.Shenandoah
29+
* @modules java.base/jdk.internal.misc:+open
30+
*
31+
* @run main/othervm -XX:-UseOnStackReplacement -XX:-BackgroundCompilation -XX:-TieredCompilation -XX:+UseShenandoahGC
32+
* TestLostAntiDependencyAtExpansion
33+
*
34+
*
35+
*/
36+
37+
import jdk.internal.misc.Unsafe;
38+
39+
public class TestLostAntiDependencyAtExpansion {
40+
static final jdk.internal.misc.Unsafe UNSAFE = Unsafe.getUnsafe();
41+
42+
public static void main(String[] args) {
43+
long addr = UNSAFE.allocateMemory(8);
44+
for (int i = 0; i < 20_000; i++) {
45+
UNSAFE.putLong(addr, 42L);
46+
long res = test1(addr);
47+
if (res != 42L) {
48+
throw new RuntimeException("Incorrect result: " + res);
49+
}
50+
}
51+
}
52+
53+
static class A {
54+
long field;
55+
}
56+
57+
static A a = new A();
58+
59+
private static long test1(long addr) {
60+
long tmp = UNSAFE.getLong(addr);
61+
62+
UNSAFE.putLong(addr, 0L);
63+
64+
return tmp + a.field;
65+
}
66+
67+
}

0 commit comments

Comments
 (0)