8292301: [REDO v2] C2 crash when allocating array of size too large
Reviewed-by: xliu, thartmann, kvn
This commit is contained in:
parent
c13e0ef3f8
commit
1ea0d6b424
@ -286,7 +286,7 @@ public:
|
||||
virtual void verify_gc_barriers(Compile* compile, CompilePhase phase) const {}
|
||||
#endif
|
||||
|
||||
virtual bool final_graph_reshaping(Compile* compile, Node* n, uint opcode) const { return false; }
|
||||
virtual bool final_graph_reshaping(Compile* compile, Node* n, uint opcode, Unique_Node_List& dead_nodes) const { return false; }
|
||||
|
||||
virtual bool escape_add_to_con_graph(ConnectionGraph* conn_graph, PhaseGVN* gvn, Unique_Node_List* delayed_worklist, Node* n, uint opcode) const { return false; }
|
||||
virtual bool escape_add_final_edges(ConnectionGraph* conn_graph, PhaseGVN* gvn, Node* n, uint opcode) const { return false; }
|
||||
|
@ -1118,7 +1118,7 @@ bool ShenandoahBarrierSetC2::has_only_shenandoah_wb_pre_uses(Node* n) {
|
||||
return n->outcnt() > 0;
|
||||
}
|
||||
|
||||
bool ShenandoahBarrierSetC2::final_graph_reshaping(Compile* compile, Node* n, uint opcode) const {
|
||||
bool ShenandoahBarrierSetC2::final_graph_reshaping(Compile* compile, Node* n, uint opcode, Unique_Node_List& dead_nodes) const {
|
||||
switch (opcode) {
|
||||
case Op_CallLeaf:
|
||||
case Op_CallLeafNoFP: {
|
||||
|
@ -138,7 +138,7 @@ public:
|
||||
#endif
|
||||
|
||||
virtual Node* ideal_node(PhaseGVN* phase, Node* n, bool can_reshape) const;
|
||||
virtual bool final_graph_reshaping(Compile* compile, Node* n, uint opcode) const;
|
||||
virtual bool final_graph_reshaping(Compile* compile, Node* n, uint opcode, Unique_Node_List& dead_nodes) const;
|
||||
|
||||
virtual bool escape_add_to_con_graph(ConnectionGraph* conn_graph, PhaseGVN* gvn, Unique_Node_List* delayed_worklist, Node* n, uint opcode) const;
|
||||
virtual bool escape_add_final_edges(ConnectionGraph* conn_graph, PhaseGVN* gvn, Node* n, uint opcode) const;
|
||||
|
@ -1551,6 +1551,7 @@ 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);
|
||||
}
|
||||
|
||||
@ -1577,54 +1578,6 @@ Node *AllocateNode::make_ideal_mark(PhaseGVN *phase, Node* obj, Node* control, N
|
||||
return mark_node;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
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");
|
||||
igvn->add_input_to(phase->C->root(), 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.
|
||||
|
@ -874,6 +874,7 @@ public:
|
||||
KlassNode, // type (maybe dynamic) of the obj.
|
||||
InitialTest, // slow-path test (may be constant)
|
||||
ALength, // array length (or TOP if none)
|
||||
ValidLengthTest,
|
||||
ParmLimit
|
||||
};
|
||||
|
||||
@ -883,6 +884,7 @@ public:
|
||||
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);
|
||||
|
||||
@ -977,18 +979,16 @@ public:
|
||||
//
|
||||
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
|
||||
)
|
||||
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)
|
||||
: 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() {
|
||||
|
@ -2693,6 +2693,17 @@ 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
|
||||
|
@ -3086,7 +3086,7 @@ void Compile::eliminate_redundant_card_marks(Node* n) {
|
||||
|
||||
//------------------------------final_graph_reshaping_impl----------------------
|
||||
// Implement items 1-5 from final_graph_reshaping below.
|
||||
void Compile::final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc) {
|
||||
void Compile::final_graph_reshaping_impl(Node *n, Final_Reshape_Counts& frc, Unique_Node_List& dead_nodes) {
|
||||
|
||||
if ( n->outcnt() == 0 ) return; // dead node
|
||||
uint nop = n->Opcode();
|
||||
@ -3137,9 +3137,9 @@ void Compile::final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc) {
|
||||
}
|
||||
#endif
|
||||
// Count FPU ops and common calls, implements item (3)
|
||||
bool gc_handled = BarrierSet::barrier_set()->barrier_set_c2()->final_graph_reshaping(this, n, nop);
|
||||
bool gc_handled = BarrierSet::barrier_set()->barrier_set_c2()->final_graph_reshaping(this, n, nop, dead_nodes);
|
||||
if (!gc_handled) {
|
||||
final_graph_reshaping_main_switch(n, frc, nop);
|
||||
final_graph_reshaping_main_switch(n, frc, nop, dead_nodes);
|
||||
}
|
||||
|
||||
// Collect CFG split points
|
||||
@ -3148,7 +3148,7 @@ void Compile::final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc) {
|
||||
}
|
||||
}
|
||||
|
||||
void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& frc, uint nop) {
|
||||
void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& frc, uint nop, Unique_Node_List& dead_nodes) {
|
||||
switch( nop ) {
|
||||
// Count all float operations that may use FPU
|
||||
case Op_AddF:
|
||||
@ -3770,22 +3770,8 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f
|
||||
// that input may be a chain of Phis. If those phis have no
|
||||
// other use, then the MemBarAcquire keeps them alive and
|
||||
// register allocation can be confused.
|
||||
ResourceMark rm;
|
||||
Unique_Node_List wq;
|
||||
wq.push(n->in(MemBarNode::Precedent));
|
||||
dead_nodes.push(n->in(MemBarNode::Precedent));
|
||||
n->set_req(MemBarNode::Precedent, top());
|
||||
while (wq.size() > 0) {
|
||||
Node* m = wq.pop();
|
||||
if (m->outcnt() == 0 && m != top()) {
|
||||
for (uint j = 0; j < m->req(); j++) {
|
||||
Node* in = m->in(j);
|
||||
if (in != NULL) {
|
||||
wq.push(in);
|
||||
}
|
||||
}
|
||||
m->disconnect_inputs(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -3858,7 +3844,7 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f
|
||||
//------------------------------final_graph_reshaping_walk---------------------
|
||||
// Replacing Opaque nodes with their input in final_graph_reshaping_impl(),
|
||||
// requires that the walk visits a node's inputs before visiting the node.
|
||||
void Compile::final_graph_reshaping_walk( Node_Stack &nstack, Node *root, Final_Reshape_Counts &frc ) {
|
||||
void Compile::final_graph_reshaping_walk(Node_Stack& nstack, Node* root, Final_Reshape_Counts& frc, Unique_Node_List& dead_nodes) {
|
||||
Unique_Node_List sfpt;
|
||||
|
||||
frc._visited.set(root->_idx); // first, mark node as visited
|
||||
@ -3884,7 +3870,7 @@ void Compile::final_graph_reshaping_walk( Node_Stack &nstack, Node *root, Final_
|
||||
}
|
||||
} else {
|
||||
// Now do post-visit work
|
||||
final_graph_reshaping_impl( n, frc );
|
||||
final_graph_reshaping_impl(n, frc, dead_nodes);
|
||||
if (nstack.is_empty())
|
||||
break; // finished
|
||||
n = nstack.node(); // Get node from stack
|
||||
@ -3984,7 +3970,8 @@ bool Compile::final_graph_reshaping() {
|
||||
// Visit everybody reachable!
|
||||
// Allocate stack of size C->live_nodes()/2 to avoid frequent realloc
|
||||
Node_Stack nstack(live_nodes() >> 1);
|
||||
final_graph_reshaping_walk(nstack, root(), frc);
|
||||
Unique_Node_List dead_nodes;
|
||||
final_graph_reshaping_walk(nstack, root(), frc, dead_nodes);
|
||||
|
||||
// Check for unreachable (from below) code (i.e., infinite loops).
|
||||
for( uint i = 0; i < frc._tests.size(); i++ ) {
|
||||
@ -3997,7 +3984,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 &&
|
||||
@ -4006,22 +3993,27 @@ 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->req() > TypeFunc::Parms+1 &&
|
||||
call->is_CallStaticJava()) {
|
||||
// Check for negative array length. In such case, the optimizer has
|
||||
} 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
|
||||
// detected that the allocation attempt will always result in an
|
||||
// exception. There is no fall-through projection of this CatchNode .
|
||||
Node *arg1 = call->in(TypeFunc::Parms+1);
|
||||
if (arg1->is_Type() &&
|
||||
arg1->as_Type()->type()->join(TypeInt::POS)->empty()) {
|
||||
assert(call->is_CallStaticJava(), "static call expected");
|
||||
assert(call->req() == call->jvms()->endoff() + 1, "missing extra input");
|
||||
uint valid_length_test_input = call->req() - 1;
|
||||
Node* valid_length_test = call->in(valid_length_test_input);
|
||||
call->del_req(valid_length_test_input);
|
||||
if (valid_length_test->find_int_con(1) == 0) {
|
||||
required_outcnt--;
|
||||
}
|
||||
dead_nodes.push(valid_length_test);
|
||||
assert(n->outcnt() == required_outcnt, "malformed control flow");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4030,6 +4022,16 @@ 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");
|
||||
uint valid_length_test_input = call->req() - 1;
|
||||
dead_nodes.push(call->in(valid_length_test_input));
|
||||
call->del_req(valid_length_test_input); // valid length test useless now
|
||||
}
|
||||
}
|
||||
// Check that I actually visited all kids. Unreached kids
|
||||
// must be infinite loops.
|
||||
@ -4048,6 +4050,19 @@ bool Compile::final_graph_reshaping() {
|
||||
}
|
||||
}
|
||||
|
||||
while (dead_nodes.size() > 0) {
|
||||
Node* m = dead_nodes.pop();
|
||||
if (m->outcnt() == 0 && m != top()) {
|
||||
for (uint j = 0; j < m->req(); j++) {
|
||||
Node* in = m->in(j);
|
||||
if (in != NULL) {
|
||||
dead_nodes.push(in);
|
||||
}
|
||||
}
|
||||
m->disconnect_inputs(this);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef IA32
|
||||
// If original bytecodes contained a mixture of floats and doubles
|
||||
// check if the optimizer has made it homogeneous, item (3).
|
||||
|
@ -1125,9 +1125,9 @@ class Compile : public Phase {
|
||||
#endif
|
||||
// Function calls made by the public function final_graph_reshaping.
|
||||
// No need to be made public as they are not called elsewhere.
|
||||
void final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc);
|
||||
void final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& frc, uint nop);
|
||||
void final_graph_reshaping_walk( Node_Stack &nstack, Node *root, Final_Reshape_Counts &frc );
|
||||
void final_graph_reshaping_impl(Node *n, Final_Reshape_Counts& frc, Unique_Node_List& dead_nodes);
|
||||
void final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& frc, uint nop, Unique_Node_List& dead_nodes);
|
||||
void final_graph_reshaping_walk(Node_Stack& nstack, Node* root, Final_Reshape_Counts& frc, Unique_Node_List& dead_nodes);
|
||||
void eliminate_redundant_card_marks(Node* n);
|
||||
|
||||
// Logic cone optimization.
|
||||
|
@ -2601,7 +2601,9 @@ 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 = _gvn.transform( new CatchProjNode(catc, CatchProjNode::fall_through_index, CatchProjNode::no_handler_bci) );
|
||||
Node* norm = new CatchProjNode(catc, CatchProjNode::fall_through_index, CatchProjNode::no_handler_bci);
|
||||
_gvn.set_type_bottom(norm);
|
||||
C->record_for_igvn(norm);
|
||||
Node* excp = _gvn.transform( new CatchProjNode(catc, CatchProjNode::catch_all_index, CatchProjNode::no_handler_bci) );
|
||||
|
||||
{ PreserveJVMState pjvms(this);
|
||||
@ -3852,20 +3854,28 @@ 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->isa_aryptr()) {
|
||||
BasicType bt = ary_type->isa_aryptr()->elem()->array_element_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);
|
||||
length, valid_length_test);
|
||||
|
||||
// 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);
|
||||
|
@ -2037,7 +2037,13 @@ void PhaseIdealLoop::clone_loop_handle_data_uses(Node* old, Node_List &old_new,
|
||||
// in the loop to break the loop, then test is again outside of the
|
||||
// loop to determine which way the loop exited.
|
||||
// Loop predicate If node connects to Bool node through Opaque1 node.
|
||||
if (use->is_If() || use->is_CMove() || C->is_predicate_opaq(use) || use->Opcode() == Op_Opaque4) {
|
||||
//
|
||||
// If the use is an AllocateArray through its ValidLengthTest input,
|
||||
// make sure the Bool/Cmp input is cloned down to avoid a Phi between
|
||||
// the AllocateArray node and its ValidLengthTest input that could cause
|
||||
// split if to break.
|
||||
if (use->is_If() || use->is_CMove() || C->is_predicate_opaq(use) || use->Opcode() == Op_Opaque4 ||
|
||||
(use->Opcode() == Op_AllocateArray && use->in(AllocateNode::ValidLengthTest) == old)) {
|
||||
// Since this code is highly unlikely, we lazily build the worklist
|
||||
// of such Nodes to go split.
|
||||
if (!split_if_set) {
|
||||
@ -2413,9 +2419,10 @@ void PhaseIdealLoop::finish_clone_loop(Node_List* split_if_set, Node_List* split
|
||||
if (split_if_set) {
|
||||
while (split_if_set->size()) {
|
||||
Node *iff = split_if_set->pop();
|
||||
if (iff->in(1)->is_Phi()) {
|
||||
Node *b = clone_iff(iff->in(1)->as_Phi());
|
||||
_igvn.replace_input_of(iff, 1, b);
|
||||
uint input = iff->Opcode() == Op_AllocateArray ? AllocateNode::ValidLengthTest : 1;
|
||||
if (iff->in(input)->is_Phi()) {
|
||||
Node *b = clone_iff(iff->in(input)->as_Phi());
|
||||
_igvn.replace_input_of(iff, input, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1192,7 +1192,8 @@ 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
|
||||
address slow_call_address, // Address of slow call
|
||||
Node* valid_length_test // whether length is valid or not
|
||||
)
|
||||
{
|
||||
Node* ctrl = alloc->in(TypeFunc::Control);
|
||||
@ -1377,6 +1378,12 @@ void PhaseMacroExpand::expand_allocate_common(
|
||||
// Copy debug information and adjust JVMState information, then replace
|
||||
// allocate node with the call
|
||||
call->copy_call_debug_info(&_igvn, alloc);
|
||||
// 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 (expand_fast_path) {
|
||||
call->set_cnt(PROB_UNLIKELY_MAG(4)); // Same effect as RC_UNCOMMON.
|
||||
} else {
|
||||
@ -1863,11 +1870,12 @@ 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());
|
||||
OptoRuntime::new_instance_Java(), NULL);
|
||||
}
|
||||
|
||||
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);
|
||||
const TypeAryKlassPtr* ary_klass_t = _igvn.type(klass_node)->isa_aryklassptr();
|
||||
@ -1882,7 +1890,7 @@ void PhaseMacroExpand::expand_allocate_array(AllocateArrayNode *alloc) {
|
||||
}
|
||||
expand_allocate_common(alloc, length,
|
||||
OptoRuntime::new_array_Type(),
|
||||
slow_call_address);
|
||||
slow_call_address, valid_length_test);
|
||||
}
|
||||
|
||||
//-------------------mark_eliminated_box----------------------------------
|
||||
|
@ -91,8 +91,8 @@ private:
|
||||
void expand_allocate_common(AllocateNode* alloc,
|
||||
Node* length,
|
||||
const TypeFunc* slow_call_type,
|
||||
address slow_call_address);
|
||||
void yank_initalize_node(InitializeNode* node);
|
||||
address slow_call_address,
|
||||
Node* valid_length_test);
|
||||
void yank_alloc_node(AllocateNode* alloc);
|
||||
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);
|
||||
|
@ -1640,6 +1640,16 @@ void PhaseIterGVN::add_users_to_worklist( Node *n ) {
|
||||
if (imem != NULL) add_users_to_worklist0(imem);
|
||||
}
|
||||
}
|
||||
// If the ValidLengthTest input changes then the fallthrough path out of the AllocateArray may have become dead.
|
||||
// CatchNode::Value() is responsible for killing that path. The CatchNode has to be explicitly enqueued for igvn
|
||||
// to guarantee the change is not missed.
|
||||
if (use_op == Op_AllocateArray && n == use->in(AllocateNode::ValidLengthTest)) {
|
||||
Node* p = use->as_AllocateArray()->proj_out_or_null(TypeFunc::Control);
|
||||
if (p != NULL) {
|
||||
add_users_to_worklist0(p);
|
||||
}
|
||||
}
|
||||
|
||||
if (use_op == Op_Initialize) {
|
||||
Node* imem = use->as_Initialize()->proj_out_or_null(TypeFunc::Memory);
|
||||
if (imem != NULL) add_users_to_worklist0(imem);
|
||||
@ -1851,6 +1861,7 @@ void PhaseCCP::push_phis(Unique_Node_List& worklist, const Node* use) const {
|
||||
|
||||
// If we changed the receiver type to a call, we need to revisit the Catch node following the call. It's looking for a
|
||||
// non-NULL receiver to know when to enable the regular fall-through path in addition to the NullPtrException path.
|
||||
// Same is true if the type of a ValidLengthTest input to an AllocateArrayNode changes.
|
||||
void PhaseCCP::push_catch(Unique_Node_List& worklist, const Node* use) {
|
||||
if (use->is_Call()) {
|
||||
for (DUIterator_Fast imax, i = use->fast_outs(imax); i < imax; i++) {
|
||||
|
@ -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, "unexpected node type");
|
||||
Node *use_c = use->is_If() ? use->in(0) : get_ctrl(use);
|
||||
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);
|
||||
if (use_c == blk1 || use_c == blk2) {
|
||||
assert(use->is_CMove(), "unexpected node type");
|
||||
continue;
|
||||
@ -166,14 +166,15 @@ 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, "unexpected node type");
|
||||
assert(u->in(1) == bol, "");
|
||||
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");
|
||||
// Get control block of either the CMove or the If input
|
||||
Node *u_ctrl = u->is_If() ? u->in(0) : get_ctrl(u);
|
||||
Node *u_ctrl = (u->is_If() || u->is_AllocateArray()) ? 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, 1, x);
|
||||
_igvn.replace_input_of(u, u->is_AllocateArray() ? AllocateNode::ValidLengthTest : 1, x);
|
||||
--j;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Red Hat, Inc. 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8279125
|
||||
* @summary fatal error: no reachable node should have no use
|
||||
* @requires vm.flavor == "server"
|
||||
*
|
||||
* @run main/othervm -XX:-BackgroundCompilation -XX:-DoEscapeAnalysis TestAllocArrayAfterAllocNoUse
|
||||
*
|
||||
*/
|
||||
|
||||
public class TestAllocArrayAfterAllocNoUse {
|
||||
private static Object field;
|
||||
|
||||
public static void main(String[] args) {
|
||||
for (int i = 0; i < 20_000; i++) {
|
||||
test();
|
||||
}
|
||||
}
|
||||
|
||||
private static void test() {
|
||||
try {
|
||||
final TestAllocArrayAfterAllocNoUse o = new TestAllocArrayAfterAllocNoUse();
|
||||
} catch (Exception e) {
|
||||
final int[] array = new int[100];
|
||||
field = array;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Red Hat, Inc. 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8279062
|
||||
* @summary C2: assert(t->meet(t0) == t) failed: Not monotonic after JDK-8278413
|
||||
*
|
||||
* @run main/othervm -XX:-BackgroundCompilation TestCCPAllocateArray
|
||||
*
|
||||
*/
|
||||
|
||||
public class TestCCPAllocateArray {
|
||||
public static void main(String[] args) {
|
||||
for (int i = 0; i < 20_000; i++) {
|
||||
try {
|
||||
test();
|
||||
} catch (OutOfMemoryError e) {
|
||||
}
|
||||
length(42);
|
||||
}
|
||||
}
|
||||
|
||||
private static int[] test() {
|
||||
int i = 2;
|
||||
for (; i < 4; i *= 2);
|
||||
return new int[length(i)];
|
||||
}
|
||||
|
||||
private static int length(int i) {
|
||||
return i == 4 ? Integer.MAX_VALUE : 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Red Hat, Inc. 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* bug 8279219
|
||||
* @summary C2 crash when allocating array of size too large
|
||||
* @requires vm.compiler2.enabled
|
||||
* @library /test/lib /
|
||||
* @build jdk.test.whitebox.WhiteBox
|
||||
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
|
||||
* @run main/othervm -ea -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:-BackgroundCompilation TestFailedAllocationBadGraph
|
||||
*/
|
||||
|
||||
import jdk.test.whitebox.WhiteBox;
|
||||
import java.lang.reflect.Method;
|
||||
import compiler.whitebox.CompilerWhiteBoxTest;
|
||||
|
||||
public class TestFailedAllocationBadGraph {
|
||||
private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
|
||||
|
||||
private static long[] array;
|
||||
private static int field;
|
||||
private static volatile int barrier;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
run("test1");
|
||||
run("test2");
|
||||
}
|
||||
|
||||
private static void run(String method) throws Exception {
|
||||
Method m = TestFailedAllocationBadGraph.class.getDeclaredMethod(method);
|
||||
WHITE_BOX.enqueueMethodForCompilation(m, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION);
|
||||
if (!WHITE_BOX.isMethodCompiled(m) || WHITE_BOX.getMethodCompilationLevel(m) != CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION) {
|
||||
throw new RuntimeException("should still be compiled");
|
||||
}
|
||||
}
|
||||
|
||||
private static int test1() {
|
||||
int length = Integer.MAX_VALUE;
|
||||
try {
|
||||
array = new long[length];
|
||||
} catch (OutOfMemoryError outOfMemoryError) {
|
||||
barrier = 0x42;
|
||||
length = field;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
private static int test2() {
|
||||
int length = -1;
|
||||
try {
|
||||
array = new long[length];
|
||||
} catch (OutOfMemoryError outOfMemoryError) {
|
||||
barrier = 0x42;
|
||||
length = field;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Red Hat, Inc. 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* bug 8288184
|
||||
* @summary C2 compilation asserts with "Bad graph detected in compute_lca_of_uses"
|
||||
* @run main/othervm -XX:-BackgroundCompilation TestNewArrayBadSize
|
||||
*/
|
||||
|
||||
public class TestNewArrayBadSize {
|
||||
long instanceCount;
|
||||
int iFld;
|
||||
|
||||
void vMeth(int i, long l) {
|
||||
int i1, i19 = -845;
|
||||
for (i1 = 5; i1 > 1; i1 -= 2)
|
||||
try {
|
||||
int ax$0 = i19;
|
||||
try {
|
||||
for (Object temp = new byte[i19]; ; i19 = "1".equals("0") ? 2 : 1) {}
|
||||
} finally {
|
||||
i19 = ax$0;
|
||||
}
|
||||
} catch (Throwable ax$3) {
|
||||
}
|
||||
}
|
||||
|
||||
void mainTest(String[] strArr1) {
|
||||
vMeth(iFld, instanceCount);
|
||||
}
|
||||
|
||||
public static void main(String[] strArr) {
|
||||
TestNewArrayBadSize _instance = new TestNewArrayBadSize();
|
||||
for (int i = 0; i < 10_000; ++i) _instance.mainTest(strArr);
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Red Hat, Inc. 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8291665
|
||||
* @summary C2: assert compiling SSLEngineInputRecord::decodeInputRecord
|
||||
* @run main/othervm -Xbatch TestNewArrayOutsideLoopValidLengthTestInLoop
|
||||
*/
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class TestNewArrayOutsideLoopValidLengthTestInLoop {
|
||||
private static volatile int barrier;
|
||||
|
||||
public static void main(String[] args) {
|
||||
boolean[] allFalse = new boolean[100];
|
||||
boolean[] allTrue = new boolean[100];
|
||||
Arrays.fill(allTrue, true);
|
||||
for (int i = 0; i < 20_000; i++) {
|
||||
test1(allFalse, allFalse, true);
|
||||
test1(allTrue, allFalse, true);
|
||||
test1(allFalse, allTrue, true);
|
||||
test1(allFalse, allFalse, false);
|
||||
}
|
||||
}
|
||||
|
||||
private static int[] test1(boolean[] flags1, boolean[] flags2, boolean flag) {
|
||||
for (int i = 1; i < 100; i *= 2) {
|
||||
boolean f = false;
|
||||
int j = i;
|
||||
if (flags1[i]) {
|
||||
barrier = 1;
|
||||
f = true;
|
||||
j = i / 2;
|
||||
}
|
||||
if (flag) {
|
||||
barrier = 1;
|
||||
}
|
||||
if (f) {
|
||||
return new int[j];
|
||||
}
|
||||
if (flags2[i]) {
|
||||
return new int[j];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user