Skip to content

Commit

Permalink
8292260: [BACKOUT] JDK-8279219: [REDO] C2 crash when allocating array…
Browse files Browse the repository at this point in the history
… of size too large

Reviewed-by: stuefe
Backport-of: 967a28c3d85fdde6d5eb48aa0edd8f7597772469
  • Loading branch information
jerboaa committed Aug 12, 2022
1 parent e068eba commit 63e4b5c
Show file tree
Hide file tree
Showing 11 changed files with 75 additions and 255 deletions.
49 changes: 48 additions & 1 deletion src/hotspot/share/opto/callnode.cpp
Expand Up @@ -1378,7 +1378,6 @@ AllocateNode::AllocateNode(Compile* C, const TypeFunc *atype,
init_req( KlassNode , klass_node);
init_req( InitialTest , initial_test);
init_req( ALength , topnode);
init_req( ValidLengthTest , topnode);
C->add_macro_node(this);
}

Expand All @@ -1399,6 +1398,54 @@ void AllocateNode::compute_MemBar_redundancy(ciMethod* initializer)
}
}

//=============================================================================
Node* AllocateArrayNode::Ideal(PhaseGVN *phase, bool can_reshape) {
if (remove_dead_region(phase, can_reshape)) return this;
// Don't bother trying to transform a dead node
if (in(0) && in(0)->is_top()) return NULL;

const Type* type = phase->type(Ideal_length());
if (type->isa_int() && type->is_int()->_hi < 0) {
if (can_reshape) {
PhaseIterGVN *igvn = phase->is_IterGVN();
// Unreachable fall through path (negative array length),
// the allocation can only throw so disconnect it.
Node* proj = proj_out_or_null(TypeFunc::Control);
Node* catchproj = NULL;
if (proj != NULL) {
for (DUIterator_Fast imax, i = proj->fast_outs(imax); i < imax; i++) {
Node *cn = proj->fast_out(i);
if (cn->is_Catch()) {
catchproj = cn->as_Multi()->proj_out_or_null(CatchProjNode::fall_through_index);
break;
}
}
}
if (catchproj != NULL && catchproj->outcnt() > 0 &&
(catchproj->outcnt() > 1 ||
catchproj->unique_out()->Opcode() != Op_Halt)) {
assert(catchproj->is_CatchProj(), "must be a CatchProjNode");
Node* nproj = catchproj->clone();
igvn->register_new_node_with_optimizer(nproj);

Node *frame = new ParmNode( phase->C->start(), TypeFunc::FramePtr );
frame = phase->transform(frame);
// Halt & Catch Fire
Node* halt = new HaltNode(nproj, frame, "unexpected negative array length");
phase->C->root()->add_req(halt);
phase->transform(halt);

igvn->replace_node(catchproj, phase->C->top());
return this;
}
} else {
// Can't correct it during regular GVN so register for IGVN
phase->C->record_for_igvn(this);
}
}
return NULL;
}

// Retrieve the length from the AllocateArrayNode. Narrow the type with a
// CastII, if appropriate. If we are not allowed to create new nodes, and
// a CastII is appropriate, return NULL.
Expand Down
10 changes: 5 additions & 5 deletions src/hotspot/share/opto/callnode.hpp
Expand Up @@ -839,7 +839,6 @@ class AllocateNode : public CallNode {
KlassNode, // type (maybe dynamic) of the obj.
InitialTest, // slow-path test (may be constant)
ALength, // array length (or TOP if none)
ValidLengthTest,
ParmLimit
};

Expand All @@ -849,7 +848,6 @@ class AllocateNode : public CallNode {
fields[KlassNode] = TypeInstPtr::NOTNULL;
fields[InitialTest] = TypeInt::BOOL;
fields[ALength] = t; // length (can be a bad length)
fields[ValidLengthTest] = TypeInt::BOOL;

const TypeTuple *domain = TypeTuple::make(ParmLimit, fields);

Expand Down Expand Up @@ -947,16 +945,18 @@ class AllocateNode : public CallNode {
//
class AllocateArrayNode : public AllocateNode {
public:
AllocateArrayNode(Compile* C, const TypeFunc* atype, Node* ctrl, Node* mem, Node* abio, Node* size, Node* klass_node,
Node* initial_test, Node* count_val, Node* valid_length_test)
AllocateArrayNode(Compile* C, const TypeFunc *atype, Node *ctrl, Node *mem, Node *abio,
Node* size, Node* klass_node, Node* initial_test,
Node* count_val
)
: AllocateNode(C, atype, ctrl, mem, abio, size, klass_node,
initial_test)
{
init_class_id(Class_AllocateArray);
set_req(AllocateNode::ALength, count_val);
set_req(AllocateNode::ValidLengthTest, valid_length_test);
}
virtual int Opcode() const;
virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);

// Dig the length operand out of a array allocation site.
Node* Ideal_length() {
Expand Down
11 changes: 0 additions & 11 deletions src/hotspot/share/opto/cfgnode.cpp
Expand Up @@ -2352,17 +2352,6 @@ const Type* CatchNode::Value(PhaseGVN* phase) const {
// Rethrows always throw exceptions, never return
if (call->entry_point() == OptoRuntime::rethrow_stub()) {
f[CatchProjNode::fall_through_index] = Type::TOP;
} else if (call->is_AllocateArray()) {
Node* klass_node = call->in(AllocateNode::KlassNode);
Node* length = call->in(AllocateNode::ALength);
const Type* length_type = phase->type(length);
const Type* klass_type = phase->type(klass_node);
Node* valid_length_test = call->in(AllocateNode::ValidLengthTest);
const Type* valid_length_test_t = phase->type(valid_length_test);
if (length_type == Type::TOP || klass_type == Type::TOP || valid_length_test_t == Type::TOP ||
valid_length_test_t->is_int()->is_con(0)) {
f[CatchProjNode::fall_through_index] = Type::TOP;
}
} else if( call->req() > TypeFunc::Parms ) {
const Type *arg0 = phase->type( call->in(TypeFunc::Parms) );
// Check for null receiver to virtual or interface calls
Expand Down
29 changes: 9 additions & 20 deletions src/hotspot/share/opto/compile.cpp
Expand Up @@ -3706,7 +3706,7 @@ bool Compile::final_graph_reshaping() {
// 'fall-thru' path, so expected kids is 1 less.
if (n->is_PCTable() && n->in(0) && n->in(0)->in(0)) {
if (n->in(0)->in(0)->is_Call()) {
CallNode* call = n->in(0)->in(0)->as_Call();
CallNode *call = n->in(0)->in(0)->as_Call();
if (call->entry_point() == OptoRuntime::rethrow_stub()) {
required_outcnt--; // Rethrow always has 1 less kid
} else if (call->req() > TypeFunc::Parms &&
Expand All @@ -3715,25 +3715,22 @@ bool Compile::final_graph_reshaping() {
// detected that the virtual call will always result in a null
// pointer exception. The fall-through projection of this CatchNode
// will not be populated.
Node* arg0 = call->in(TypeFunc::Parms);
Node *arg0 = call->in(TypeFunc::Parms);
if (arg0->is_Type() &&
arg0->as_Type()->type()->higher_equal(TypePtr::NULL_PTR)) {
required_outcnt--;
}
} else if (call->entry_point() == OptoRuntime::new_array_Java() ||
call->entry_point() == OptoRuntime::new_array_nozero_Java()) {
// Check for illegal array length. In such case, the optimizer has
} else if (call->entry_point() == OptoRuntime::new_array_Java() &&
call->req() > TypeFunc::Parms+1 &&
call->is_CallStaticJava()) {
// Check for negative array length. In such case, the optimizer has
// detected that the allocation attempt will always result in an
// exception. There is no fall-through projection of this CatchNode .
assert(call->is_CallStaticJava(), "static call expected");
assert(call->req() == call->jvms()->endoff() + 1, "missing extra input");
Node* valid_length_test = call->in(call->req()-1);
call->del_req(call->req()-1);
if (valid_length_test->find_int_con(1) == 0) {
Node *arg1 = call->in(TypeFunc::Parms+1);
if (arg1->is_Type() &&
arg1->as_Type()->type()->join(TypeInt::POS)->empty()) {
required_outcnt--;
}
assert(n->outcnt() == required_outcnt, "malformed control flow");
continue;
}
}
}
Expand All @@ -3742,14 +3739,6 @@ bool Compile::final_graph_reshaping() {
record_method_not_compilable("malformed control flow");
return true; // Not all targets reachable!
}
} else if (n->is_PCTable() && n->in(0) && n->in(0)->in(0) && n->in(0)->in(0)->is_Call()) {
CallNode* call = n->in(0)->in(0)->as_Call();
if (call->entry_point() == OptoRuntime::new_array_Java() ||
call->entry_point() == OptoRuntime::new_array_nozero_Java()) {
assert(call->is_CallStaticJava(), "static call expected");
assert(call->req() == call->jvms()->endoff() + 1, "missing extra input");
call->del_req(call->req()-1); // valid length test useless now
}
}
// Check that I actually visited all kids. Unreached kids
// must be infinite loops.
Expand Down
16 changes: 3 additions & 13 deletions src/hotspot/share/opto/graphKit.cpp
Expand Up @@ -2593,9 +2593,7 @@ void GraphKit::make_slow_call_ex(Node* call, ciInstanceKlass* ex_klass, bool sep
// Make a catch node with just two handlers: fall-through and catch-all
Node* i_o = _gvn.transform( new ProjNode(call, TypeFunc::I_O, separate_io_proj) );
Node* catc = _gvn.transform( new CatchNode(control(), i_o, 2) );
Node* norm = new CatchProjNode(catc, CatchProjNode::fall_through_index, CatchProjNode::no_handler_bci);
_gvn.set_type_bottom(norm);
C->record_for_igvn(norm);
Node* norm = _gvn.transform( new CatchProjNode(catc, CatchProjNode::fall_through_index, CatchProjNode::no_handler_bci) );
Node* excp = _gvn.transform( new CatchProjNode(catc, CatchProjNode::catch_all_index, CatchProjNode::no_handler_bci) );

{ PreserveJVMState pjvms(this);
Expand Down Expand Up @@ -3731,28 +3729,20 @@ Node* GraphKit::new_array(Node* klass_node, // array klass (maybe variable)
initial_slow_test = initial_slow_test->as_Bool()->as_int_value(&_gvn);
}

const TypeOopPtr* ary_type = _gvn.type(klass_node)->is_klassptr()->as_instance_type();
Node* valid_length_test = _gvn.intcon(1);
if (ary_type->klass()->is_array_klass()) {
BasicType bt = ary_type->klass()->as_array_klass()->element_type()->basic_type();
jint max = TypeAryPtr::max_array_length(bt);
Node* valid_length_cmp = _gvn.transform(new CmpUNode(length, intcon(max)));
valid_length_test = _gvn.transform(new BoolNode(valid_length_cmp, BoolTest::le));
}

// Create the AllocateArrayNode and its result projections
AllocateArrayNode* alloc
= new AllocateArrayNode(C, AllocateArrayNode::alloc_type(TypeInt::INT),
control(), mem, i_o(),
size, klass_node,
initial_slow_test,
length, valid_length_test);
length);

// Cast to correct type. Note that the klass_node may be constant or not,
// and in the latter case the actual array type will be inexact also.
// (This happens via a non-constant argument to inline_native_newArray.)
// In any case, the value of klass_node provides the desired array type.
const TypeInt* length_type = _gvn.find_int_type(length);
const TypeOopPtr* ary_type = _gvn.type(klass_node)->is_klassptr()->as_instance_type();
if (ary_type->isa_aryptr() && length_type != NULL) {
// Try to get a better type than POS for the size
ary_type = ary_type->is_aryptr()->cast_to_size(length_type);
Expand Down
14 changes: 3 additions & 11 deletions src/hotspot/share/opto/macro.cpp
Expand Up @@ -1288,8 +1288,7 @@ void PhaseMacroExpand::expand_allocate_common(
AllocateNode* alloc, // allocation node to be expanded
Node* length, // array length for an array allocation
const TypeFunc* slow_call_type, // Type of slow call
address slow_call_address, // Address of slow call
Node* valid_length_test // whether length is valid or not
address slow_call_address // Address of slow call
)
{

Expand Down Expand Up @@ -1629,12 +1628,6 @@ void PhaseMacroExpand::expand_allocate_common(
// Copy debug information and adjust JVMState information, then replace
// allocate node with the call
copy_call_debug_info((CallNode *) alloc, call);
// For array allocations, copy the valid length check to the call node so Compile::final_graph_reshaping() can verify
// that the call has the expected number of CatchProj nodes (in case the allocation always fails and the fallthrough
// path dies).
if (valid_length_test != NULL) {
call->add_req(valid_length_test);
}
if (!always_slow) {
call->set_cnt(PROB_UNLIKELY_MAG(4)); // Same effect as RC_UNCOMMON.
} else {
Expand Down Expand Up @@ -1989,12 +1982,11 @@ Node* PhaseMacroExpand::prefetch_allocation(Node* i_o, Node*& needgc_false,
void PhaseMacroExpand::expand_allocate(AllocateNode *alloc) {
expand_allocate_common(alloc, NULL,
OptoRuntime::new_instance_Type(),
OptoRuntime::new_instance_Java(), NULL);
OptoRuntime::new_instance_Java());
}

void PhaseMacroExpand::expand_allocate_array(AllocateArrayNode *alloc) {
Node* length = alloc->in(AllocateNode::ALength);
Node* valid_length_test = alloc->in(AllocateNode::ValidLengthTest);
InitializeNode* init = alloc->initialization();
Node* klass_node = alloc->in(AllocateNode::KlassNode);
ciKlass* k = _igvn.type(klass_node)->is_klassptr()->klass();
Expand All @@ -2009,7 +2001,7 @@ void PhaseMacroExpand::expand_allocate_array(AllocateArrayNode *alloc) {
}
expand_allocate_common(alloc, length,
OptoRuntime::new_array_Type(),
slow_call_address, valid_length_test);
slow_call_address);
}

//-------------------mark_eliminated_box----------------------------------
Expand Down
3 changes: 1 addition & 2 deletions src/hotspot/share/opto/macro.hpp
Expand Up @@ -98,8 +98,7 @@ class PhaseMacroExpand : public Phase {
void expand_allocate_common(AllocateNode* alloc,
Node* length,
const TypeFunc* slow_call_type,
address slow_call_address,
Node* valid_length_test);
address slow_call_address);
Node *value_from_mem(Node *mem, Node *ctl, BasicType ft, const Type *ftype, const TypeOopPtr *adr_t, AllocateNode *alloc);
Node *value_from_mem_phi(Node *mem, BasicType ft, const Type *ftype, const TypeOopPtr *adr_t, AllocateNode *alloc, Node_Stack *value_phis, int level);

Expand Down
13 changes: 6 additions & 7 deletions src/hotspot/share/opto/split_if.cpp
Expand Up @@ -128,8 +128,8 @@ bool PhaseIdealLoop::split_up( Node *n, Node *blk1, Node *blk2 ) {
}
} else {
// We might see an Opaque1 from a loop limit check here
assert(use->is_If() || use->is_CMove() || use->Opcode() == Op_Opaque1 || use->is_AllocateArray(), "unexpected node type");
Node *use_c = (use->is_If() || use->is_AllocateArray()) ? use->in(0) : get_ctrl(use);
assert(use->is_If() || use->is_CMove() || use->Opcode() == Op_Opaque1, "unexpected node type");
Node *use_c = use->is_If() ? use->in(0) : get_ctrl(use);
if (use_c == blk1 || use_c == blk2) {
assert(use->is_CMove(), "unexpected node type");
continue;
Expand Down Expand Up @@ -166,15 +166,14 @@ bool PhaseIdealLoop::split_up( Node *n, Node *blk1, Node *blk2 ) {
--j;
} else {
// We might see an Opaque1 from a loop limit check here
assert(u->is_If() || u->is_CMove() || u->Opcode() == Op_Opaque1 || u->is_AllocateArray(), "unexpected node type");
assert(u->is_AllocateArray() || u->in(1) == bol, "");
assert(!u->is_AllocateArray() || u->in(AllocateNode::ValidLengthTest) == bol, "wrong input to AllocateArray");
assert(u->is_If() || u->is_CMove() || u->Opcode() == Op_Opaque1, "unexpected node type");
assert(u->in(1) == bol, "");
// Get control block of either the CMove or the If input
Node *u_ctrl = (u->is_If() || u->is_AllocateArray()) ? u->in(0) : get_ctrl(u);
Node *u_ctrl = u->is_If() ? u->in(0) : get_ctrl(u);
assert((u_ctrl != blk1 && u_ctrl != blk2) || u->is_CMove(), "won't converge");
Node *x = bol->clone();
register_new_node(x, u_ctrl);
_igvn.replace_input_of(u, u->is_AllocateArray() ? AllocateNode::ValidLengthTest : 1, x);
_igvn.replace_input_of(u, 1, x);
--j;
}
}
Expand Down

This file was deleted.

0 comments on commit 63e4b5c

Please sign in to comment.