8387395: [REDO] C2: SIGSEGV in compiled code due to missing ctrl

Reviewed-by: dlong, kvn, vlivanov
This commit is contained in:
Quan Anh Mai 2026-07-01 07:17:48 +00:00
parent b186074751
commit 28c79eb792
4 changed files with 101 additions and 10 deletions

View File

@ -3497,22 +3497,38 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f
ResourceMark rm;
Unique_Node_List wq;
wq.push(n);
// When we remove a CastPP, we need to pin all of its transitive users under the control of
// the removed node. The simplest approach is to pin all of the uses of the removed CastPP,
// but it is overly conservative, as an AddP does not really need pinning. As a result, we
// look through those nodes that do not need pinning and only pin memory access nodes under
// n->in(0).
for (uint next = 0; next < wq.size(); ++next) {
Node *m = wq.at(next);
for (DUIterator_Fast imax, i = m->fast_outs(imax); i < imax; i++) {
Node* use = m->fast_out(i);
if (use->is_Mem() || use->is_EncodeNarrowPtr()) {
int use_op = use->Opcode();
if (use->is_CFG() || use->pinned() || // already pinned at the exact control
use->is_Cmp() || use_op == Op_CastP2X || use_op == Op_Conv2B) { // pure computations
continue;
} else if (use->is_EncodeNarrowPtr() || // EncodeP remembers whether its input is nullable, so it must be pinned
use_op == Op_PartialSubtypeCheck || // This accesses its pointer inputs, so it must depend on them being not-null
use->is_Mem() || use->is_memory_access_intrinsic()) {
use->ensure_control_or_add_prec(n->in(0));
} else if (use_op == Op_AddP ||
use_op == Op_CastPP || use_op == Op_CheckCastPP ||
use_op == Op_CMoveP || use_op == Op_CMoveN ||
use_op == Op_DecodeN || use_op == Op_DecodeNKlass ||
use_op == Op_VerifyVectorAlignment) {
// Look through use to find memory accesses if use does not need pinning
wq.push(use);
} else {
switch(use->Opcode()) {
case Op_AddP:
case Op_DecodeN:
case Op_DecodeNKlass:
case Op_CheckCastPP:
case Op_CastPP:
wq.push(use);
break;
}
// Should have handled all kinds of nodes, verify that we do not unexpectedly arrive
// here
assert(false, "unexpected node %s", use->Name());
// Be conservative in product and pin the unexpected use
use->ensure_control_or_add_prec(n->in(0));
}
}
}

View File

@ -3018,6 +3018,27 @@ bool Node::is_data_proj_of_pure_function(const Node* maybe_pure_function) const
return Opcode() == Op_Proj && as_Proj()->_con == TypeFunc::Parms && maybe_pure_function->is_CallLeafPure();
}
// Whether this is an intrinsic node that accesses memory and has a memory input, such as array
// equal intrinsic. Some nodes do access memory but do not have a memory input, such as
// PartialSubTypeCheck, they are not included here.
bool Node::is_memory_access_intrinsic() const {
switch (Opcode()) {
case Op_StrComp:
case Op_StrEquals:
case Op_StrIndexOf:
case Op_StrIndexOfChar:
case Op_StrCompressedCopy:
case Op_StrInflatedCopy:
case Op_AryEq:
case Op_CountPositives:
case Op_VectorizedHashCode:
case Op_EncodeISOArray:
return true;
default:
return false;
}
}
//--------------------------has_non_debug_uses------------------------------
// Checks whether the node has any non-debug uses or not.
bool Node::has_non_debug_uses() const {

View File

@ -1069,6 +1069,7 @@ public:
uint is_Copy() const { return (_flags & Flag_is_Copy); }
virtual bool is_CFG() const { return false; }
bool is_memory_access_intrinsic() const;
// If this node is control-dependent on a test, can it be rerouted to a dominating equivalent
// test? This means that the node can be executed safely as long as it happens after the test

View File

@ -0,0 +1,53 @@
/*
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package compiler.controldependency;
/*
* @test
* @bug 8385420
* @summary C2 correctly handles the case when the removed CastPPNode has a CMove use.
* @run main ${test.main.class}
* @run main/othervm -Xbatch -XX:CompileOnly=${test.main.class}::test
* -XX:+UnlockDiagnosticVMOptions -XX:+StressGCM ${test.main.class}
*
*/
public class TestRemoveCastPPWithCMoveUse {
public static void main(String[] args) {
for (int i = 0; i < 10_000; i++) {
test(null, false);
test(null, true);
test("", false);
test("", true);
}
}
static int test(String a, boolean flag) {
StringBuilder sb = new StringBuilder();
if (a == null) {
sb.append("");
} else {
sb.append(flag ? a : "");
}
return sb.length();
}
}