8130847: Cloned object's fields observed as null after C2 escape analysis

Eliminated instance/array written to by an array copy variant must be correctly initialized when reallocated at a deopt

Reviewed-by: kvn, vlivanov
This commit is contained in:
Roland Westrelin 2015-08-15 02:54:18 +02:00
parent 63a173e2c4
commit 0baf2f7e8a
8 changed files with 382 additions and 79 deletions

View file

@ -626,3 +626,75 @@ bool ArrayCopyNode::may_modify(const TypeOopPtr *t_oop, PhaseTransform *phase) {
return CallNode::may_modify_arraycopy_helper(dest_t, t_oop, phase); return CallNode::may_modify_arraycopy_helper(dest_t, t_oop, phase);
} }
bool ArrayCopyNode::may_modify_helper(const TypeOopPtr *t_oop, Node* n, PhaseTransform *phase) {
if (n->is_Proj()) {
n = n->in(0);
if (n->is_Call() && n->as_Call()->may_modify(t_oop, phase)) {
return true;
}
}
return false;
}
bool ArrayCopyNode::may_modify(const TypeOopPtr *t_oop, MemBarNode* mb, PhaseTransform *phase) {
Node* mem = mb->in(TypeFunc::Memory);
if (mem->is_MergeMem()) {
Node* n = mem->as_MergeMem()->memory_at(Compile::AliasIdxRaw);
if (may_modify_helper(t_oop, n, phase)) {
return true;
} else if (n->is_Phi()) {
for (uint i = 1; i < n->req(); i++) {
if (n->in(i) != NULL) {
if (may_modify_helper(t_oop, n->in(i), phase)) {
return true;
}
}
}
}
}
return false;
}
// Does this array copy modify offsets between offset_lo and offset_hi
// in the destination array
// if must_modify is false, return true if the copy could write
// between offset_lo and offset_hi
// if must_modify is true, return true if the copy is guaranteed to
// write between offset_lo and offset_hi
bool ArrayCopyNode::modifies(intptr_t offset_lo, intptr_t offset_hi, PhaseTransform* phase, bool must_modify) {
assert(_kind == ArrayCopy || _kind == CopyOf || _kind == CopyOfRange, "only for real array copies");
Node* dest = in(ArrayCopyNode::Dest);
Node* src_pos = in(ArrayCopyNode::SrcPos);
Node* dest_pos = in(ArrayCopyNode::DestPos);
Node* len = in(ArrayCopyNode::Length);
const TypeInt *dest_pos_t = phase->type(dest_pos)->isa_int();
const TypeInt *len_t = phase->type(len)->isa_int();
const TypeAryPtr* ary_t = phase->type(dest)->isa_aryptr();
if (dest_pos_t != NULL && len_t != NULL && ary_t != NULL) {
BasicType ary_elem = ary_t->klass()->as_array_klass()->element_type()->basic_type();
uint header = arrayOopDesc::base_offset_in_bytes(ary_elem);
uint elemsize = type2aelembytes(ary_elem);
intptr_t dest_pos_plus_len_lo = (((intptr_t)dest_pos_t->_lo) + len_t->_lo) * elemsize + header;
intptr_t dest_pos_plus_len_hi = (((intptr_t)dest_pos_t->_hi) + len_t->_hi) * elemsize + header;
intptr_t dest_pos_lo = ((intptr_t)dest_pos_t->_lo) * elemsize + header;
intptr_t dest_pos_hi = ((intptr_t)dest_pos_t->_hi) * elemsize + header;
if (must_modify) {
if (offset_lo >= dest_pos_hi && offset_hi < dest_pos_plus_len_lo) {
return true;
}
} else {
if (offset_hi >= dest_pos_lo && offset_lo < dest_pos_plus_len_hi) {
return true;
}
}
}
return false;
}

View file

@ -108,6 +108,7 @@ private:
BasicType copy_type, const Type* value_type, int count); BasicType copy_type, const Type* value_type, int count);
bool finish_transform(PhaseGVN *phase, bool can_reshape, bool finish_transform(PhaseGVN *phase, bool can_reshape,
Node* ctl, Node *mem); Node* ctl, Node *mem);
static bool may_modify_helper(const TypeOopPtr *t_oop, Node* n, PhaseTransform *phase);
public: public:
@ -162,6 +163,9 @@ public:
bool is_alloc_tightly_coupled() const { return _alloc_tightly_coupled; } bool is_alloc_tightly_coupled() const { return _alloc_tightly_coupled; }
static bool may_modify(const TypeOopPtr *t_oop, MemBarNode* mb, PhaseTransform *phase);
bool modifies(intptr_t offset_lo, intptr_t offset_hi, PhaseTransform* phase, bool must_modify);
#ifndef PRODUCT #ifndef PRODUCT
virtual void dump_spec(outputStream *st) const; virtual void dump_spec(outputStream *st) const;
virtual void dump_compact_spec(outputStream* st) const; virtual void dump_compact_spec(outputStream* st) const;

View file

@ -742,7 +742,7 @@ uint CallNode::match_edge(uint idx) const {
// //
bool CallNode::may_modify(const TypeOopPtr *t_oop, PhaseTransform *phase) { bool CallNode::may_modify(const TypeOopPtr *t_oop, PhaseTransform *phase) {
assert((t_oop != NULL), "sanity"); assert((t_oop != NULL), "sanity");
if (is_call_to_arraycopystub()) { if (is_call_to_arraycopystub() && strcmp(_name, "unsafe_arraycopy") != 0) {
const TypeTuple* args = _tf->domain(); const TypeTuple* args = _tf->domain();
Node* dest = NULL; Node* dest = NULL;
// Stubs that can be called once an ArrayCopyNode is expanded have // Stubs that can be called once an ArrayCopyNode is expanded have

View file

@ -324,18 +324,28 @@ static Node *scan_mem_chain(Node *mem, int alias_idx, int offset, Node *start_me
return in; return in;
} else if (in->is_Call()) { } else if (in->is_Call()) {
CallNode *call = in->as_Call(); CallNode *call = in->as_Call();
if (!call->may_modify(tinst, phase)) { if (call->may_modify(tinst, phase)) {
mem = call->in(TypeFunc::Memory); assert(call->is_ArrayCopy(), "ArrayCopy is the only call node that doesn't make allocation escape");
if (call->as_ArrayCopy()->modifies(offset, offset, phase, false)) {
return in;
}
} }
mem = in->in(TypeFunc::Memory); mem = in->in(TypeFunc::Memory);
} else if (in->is_MemBar()) { } else if (in->is_MemBar()) {
if (ArrayCopyNode::may_modify(tinst, in->as_MemBar(), phase)) {
assert(in->in(0)->is_Proj() && in->in(0)->in(0)->is_ArrayCopy(), "should be arraycopy");
ArrayCopyNode* ac = in->in(0)->in(0)->as_ArrayCopy();
assert(ac->is_clonebasic(), "Only basic clone is a non escaping clone");
return ac;
}
mem = in->in(TypeFunc::Memory); mem = in->in(TypeFunc::Memory);
} else { } else {
assert(false, "unexpected projection"); assert(false, "unexpected projection");
} }
} else if (mem->is_Store()) { } else if (mem->is_Store()) {
const TypePtr* atype = mem->as_Store()->adr_type(); const TypePtr* atype = mem->as_Store()->adr_type();
int adr_idx = Compile::current()->get_alias_index(atype); int adr_idx = phase->C->get_alias_index(atype);
if (adr_idx == alias_idx) { if (adr_idx == alias_idx) {
assert(atype->isa_oopptr(), "address type must be oopptr"); assert(atype->isa_oopptr(), "address type must be oopptr");
int adr_offset = atype->offset(); int adr_offset = atype->offset();
@ -373,7 +383,7 @@ static Node *scan_mem_chain(Node *mem, int alias_idx, int offset, Node *start_me
adr = mem->in(3); // Destination array adr = mem->in(3); // Destination array
} }
const TypePtr* atype = adr->bottom_type()->is_ptr(); const TypePtr* atype = adr->bottom_type()->is_ptr();
int adr_idx = Compile::current()->get_alias_index(atype); int adr_idx = phase->C->get_alias_index(atype);
if (adr_idx == alias_idx) { if (adr_idx == alias_idx) {
assert(false, "Object is not scalar replaceable if a LoadStore node access its field"); assert(false, "Object is not scalar replaceable if a LoadStore node access its field");
return NULL; return NULL;
@ -386,12 +396,63 @@ static Node *scan_mem_chain(Node *mem, int alias_idx, int offset, Node *start_me
} }
} }
// Generate loads from source of the arraycopy for fields of
// destination needed at a deoptimization point
Node* PhaseMacroExpand::make_arraycopy_load(ArrayCopyNode* ac, intptr_t offset, Node* ctl, BasicType ft, const Type *ftype, AllocateNode *alloc) {
BasicType bt = ft;
const Type *type = ftype;
if (ft == T_NARROWOOP) {
bt = T_OBJECT;
type = ftype->make_oopptr();
}
Node* res = NULL;
if (ac->is_clonebasic()) {
Node* base = ac->in(ArrayCopyNode::Src)->in(AddPNode::Base);
Node* adr = _igvn.transform(new AddPNode(base, base, MakeConX(offset)));
const TypePtr* adr_type = _igvn.type(base)->is_ptr()->add_offset(offset);
Node* m = ac->in(TypeFunc::Memory);
while (m->is_MergeMem()) {
m = m->as_MergeMem()->memory_at(C->get_alias_index(adr_type));
if (m->is_Proj() && m->in(0)->is_MemBar()) {
m = m->in(0)->in(TypeFunc::Memory);
}
}
res = LoadNode::make(_igvn, ctl, m, adr, adr_type, type, bt, MemNode::unordered, LoadNode::Pinned);
} else {
if (ac->modifies(offset, offset, &_igvn, true)) {
assert(ac->in(ArrayCopyNode::Dest) == alloc->result_cast(), "arraycopy destination should be allocation's result");
uint shift = exact_log2(type2aelembytes(bt));
Node* diff = _igvn.transform(new SubINode(ac->in(ArrayCopyNode::SrcPos), ac->in(ArrayCopyNode::DestPos)));
#ifdef _LP64
diff = _igvn.transform(new ConvI2LNode(diff));
#endif
diff = _igvn.transform(new LShiftXNode(diff, intcon(shift)));
Node* off = _igvn.transform(new AddXNode(MakeConX(offset), diff));
Node* base = ac->in(ArrayCopyNode::Src);
Node* adr = _igvn.transform(new AddPNode(base, base, off));
const TypePtr* adr_type = _igvn.type(base)->is_ptr()->add_offset(offset);
Node* m = ac->in(TypeFunc::Memory);
res = LoadNode::make(_igvn, ctl, m, adr, adr_type, type, bt, MemNode::unordered, LoadNode::Pinned);
}
}
if (res != NULL) {
res = _igvn.transform(res);
if (ftype->isa_narrowoop()) {
// PhaseMacroExpand::scalar_replacement adds DecodeN nodes
res = _igvn.transform(new EncodePNode(res, ftype));
}
return res;
}
return NULL;
}
// //
// Given a Memory Phi, compute a value Phi containing the values from stores // Given a Memory Phi, compute a value Phi containing the values from stores
// on the input paths. // on the input paths.
// Note: this function is recursive, its depth is limied by the "level" argument // Note: this function is recursive, its depth is limited by the "level" argument
// Returns the computed Phi, or NULL if it cannot compute it. // Returns the computed Phi, or NULL if it cannot compute it.
Node *PhaseMacroExpand::value_from_mem_phi(Node *mem, BasicType ft, const Type *phi_type, const TypeOopPtr *adr_t, Node *alloc, Node_Stack *value_phis, int level) { Node *PhaseMacroExpand::value_from_mem_phi(Node *mem, BasicType ft, const Type *phi_type, const TypeOopPtr *adr_t, AllocateNode *alloc, Node_Stack *value_phis, int level) {
assert(mem->is_Phi(), "sanity"); assert(mem->is_Phi(), "sanity");
int alias_idx = C->get_alias_index(adr_t); int alias_idx = C->get_alias_index(adr_t);
int offset = adr_t->offset(); int offset = adr_t->offset();
@ -458,6 +519,9 @@ Node *PhaseMacroExpand::value_from_mem_phi(Node *mem, BasicType ft, const Type *
assert(val->in(0)->is_LoadStore() || val->in(0)->Opcode() == Op_EncodeISOArray, "sanity"); assert(val->in(0)->is_LoadStore() || val->in(0)->Opcode() == Op_EncodeISOArray, "sanity");
assert(false, "Object is not scalar replaceable if a LoadStore node access its field"); assert(false, "Object is not scalar replaceable if a LoadStore node access its field");
return NULL; return NULL;
} else if (val->is_ArrayCopy()) {
Node* res = make_arraycopy_load(val->as_ArrayCopy(), offset, val->in(0), ft, phi_type, alloc);
values.at_put(j, res);
} else { } else {
#ifdef ASSERT #ifdef ASSERT
val->dump(); val->dump();
@ -479,7 +543,7 @@ Node *PhaseMacroExpand::value_from_mem_phi(Node *mem, BasicType ft, const Type *
} }
// Search the last value stored into the object's field. // Search the last value stored into the object's field.
Node *PhaseMacroExpand::value_from_mem(Node *sfpt_mem, BasicType ft, const Type *ftype, const TypeOopPtr *adr_t, Node *alloc) { Node *PhaseMacroExpand::value_from_mem(Node *sfpt_mem, Node *sfpt_ctl, BasicType ft, const Type *ftype, const TypeOopPtr *adr_t, AllocateNode *alloc) {
assert(adr_t->is_known_instance_field(), "instance required"); assert(adr_t->is_known_instance_field(), "instance required");
int instance_id = adr_t->instance_id(); int instance_id = adr_t->instance_id();
assert((uint)instance_id == alloc->_idx, "wrong allocation"); assert((uint)instance_id == alloc->_idx, "wrong allocation");
@ -538,6 +602,8 @@ Node *PhaseMacroExpand::value_from_mem(Node *sfpt_mem, BasicType ft, const Type
} else { } else {
done = true; done = true;
} }
} else if (mem->is_ArrayCopy()) {
done = true;
} else { } else {
assert(false, "unexpected node"); assert(false, "unexpected node");
} }
@ -562,6 +628,13 @@ Node *PhaseMacroExpand::value_from_mem(Node *sfpt_mem, BasicType ft, const Type
value_phis.pop(); value_phis.pop();
} }
} }
} else if (mem->is_ArrayCopy()) {
Node* ctl = mem->in(0);
if (sfpt_ctl->is_Proj() && sfpt_ctl->as_Proj()->is_uncommon_trap_proj(Deoptimization::Reason_none)) {
// pin the loads in the uncommon trap path
ctl = sfpt_ctl;
}
return make_arraycopy_load(mem->as_ArrayCopy(), offset, ctl, ft, ftype, alloc);
} }
} }
// Something go wrong. // Something go wrong.
@ -738,6 +811,7 @@ bool PhaseMacroExpand::scalar_replacement(AllocateNode *alloc, GrowableArray <Sa
while (safepoints.length() > 0) { while (safepoints.length() > 0) {
SafePointNode* sfpt = safepoints.pop(); SafePointNode* sfpt = safepoints.pop();
Node* mem = sfpt->memory(); Node* mem = sfpt->memory();
Node* ctl = sfpt->control();
assert(sfpt->jvms() != NULL, "missed JVMS"); assert(sfpt->jvms() != NULL, "missed JVMS");
// Fields of scalar objs are referenced only at the end // Fields of scalar objs are referenced only at the end
// of regular debuginfo at the last (youngest) JVMS. // of regular debuginfo at the last (youngest) JVMS.
@ -789,7 +863,7 @@ bool PhaseMacroExpand::scalar_replacement(AllocateNode *alloc, GrowableArray <Sa
const TypeOopPtr *field_addr_type = res_type->add_offset(offset)->isa_oopptr(); const TypeOopPtr *field_addr_type = res_type->add_offset(offset)->isa_oopptr();
Node *field_val = value_from_mem(mem, basic_elem_type, field_type, field_addr_type, alloc); Node *field_val = value_from_mem(mem, ctl, basic_elem_type, field_type, field_addr_type, alloc);
if (field_val == NULL) { if (field_val == NULL) {
// We weren't able to find a value for this field, // We weren't able to find a value for this field,
// give up on eliminating this allocation. // give up on eliminating this allocation.

View file

@ -85,8 +85,8 @@ private:
Node* length, Node* length,
const TypeFunc* slow_call_type, const TypeFunc* slow_call_type,
address slow_call_address); address slow_call_address);
Node *value_from_mem(Node *mem, BasicType ft, const Type *ftype, const TypeOopPtr *adr_t, Node *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, Node *alloc, Node_Stack *value_phis, int level); Node *value_from_mem_phi(Node *mem, BasicType ft, const Type *ftype, const TypeOopPtr *adr_t, AllocateNode *alloc, Node_Stack *value_phis, int level);
bool eliminate_boxing_node(CallStaticJavaNode *boxing); bool eliminate_boxing_node(CallStaticJavaNode *boxing);
bool eliminate_allocate_node(AllocateNode *alloc); bool eliminate_allocate_node(AllocateNode *alloc);
@ -200,6 +200,8 @@ private:
Node* old_eden_top, Node* new_eden_top, Node* old_eden_top, Node* new_eden_top,
Node* length); Node* length);
Node* make_arraycopy_load(ArrayCopyNode* ac, intptr_t offset, Node* ctl, BasicType ft, const Type *ftype, AllocateNode *alloc);
public: public:
PhaseMacroExpand(PhaseIterGVN &igvn) : Phase(Macro_Expand), _igvn(igvn), _has_locks(false) { PhaseMacroExpand(PhaseIterGVN &igvn) : Phase(Macro_Expand), _igvn(igvn), _has_locks(false) {
_igvn.set_delay_transform(true); _igvn.set_delay_transform(true);

View file

@ -108,37 +108,6 @@ extern void print_alias_types();
#endif #endif
static bool membar_for_arraycopy_helper(const TypeOopPtr *t_oop, Node* n, PhaseTransform *phase) {
if (n->is_Proj()) {
n = n->in(0);
if (n->is_Call() && n->as_Call()->may_modify(t_oop, phase)) {
return true;
}
}
return false;
}
static bool membar_for_arraycopy(const TypeOopPtr *t_oop, MemBarNode* mb, PhaseTransform *phase) {
Node* mem = mb->in(TypeFunc::Memory);
if (mem->is_MergeMem()) {
Node* n = mem->as_MergeMem()->memory_at(Compile::AliasIdxRaw);
if (membar_for_arraycopy_helper(t_oop, n, phase)) {
return true;
} else if (n->is_Phi()) {
for (uint i = 1; i < n->req(); i++) {
if (n->in(i) != NULL) {
if (membar_for_arraycopy_helper(t_oop, n->in(i), phase)) {
return true;
}
}
}
}
}
return false;
}
Node *MemNode::optimize_simple_memory_chain(Node *mchain, const TypeOopPtr *t_oop, Node *load, PhaseGVN *phase) { Node *MemNode::optimize_simple_memory_chain(Node *mchain, const TypeOopPtr *t_oop, Node *load, PhaseGVN *phase) {
assert((t_oop != NULL), "sanity"); assert((t_oop != NULL), "sanity");
bool is_instance = t_oop->is_known_instance_field(); bool is_instance = t_oop->is_known_instance_field();
@ -183,7 +152,7 @@ Node *MemNode::optimize_simple_memory_chain(Node *mchain, const TypeOopPtr *t_oo
} }
} }
} else if (proj_in->is_MemBar()) { } else if (proj_in->is_MemBar()) {
if (membar_for_arraycopy(t_oop, proj_in->as_MemBar(), phase)) { if (ArrayCopyNode::may_modify(t_oop, proj_in->as_MemBar(), phase)) {
break; break;
} }
result = proj_in->in(TypeFunc::Memory); result = proj_in->in(TypeFunc::Memory);
@ -545,36 +514,13 @@ Node* LoadNode::find_previous_arraycopy(PhaseTransform* phase, Node* ld_alloc, N
Node* dest = ac->in(ArrayCopyNode::Dest); Node* dest = ac->in(ArrayCopyNode::Dest);
if (dest == ld_base) { if (dest == ld_base) {
Node* src_pos = ac->in(ArrayCopyNode::SrcPos);
Node* dest_pos = ac->in(ArrayCopyNode::DestPos);
Node* len = ac->in(ArrayCopyNode::Length);
const TypeInt *dest_pos_t = phase->type(dest_pos)->isa_int();
const TypeX *ld_offs_t = phase->type(ld_offs)->isa_intptr_t(); const TypeX *ld_offs_t = phase->type(ld_offs)->isa_intptr_t();
const TypeInt *len_t = phase->type(len)->isa_int(); if (ac->modifies(ld_offs_t->_lo, ld_offs_t->_hi, phase, can_see_stored_value)) {
const TypeAryPtr* ary_t = phase->type(dest)->isa_aryptr();
if (dest_pos_t != NULL && ld_offs_t != NULL && len_t != NULL && ary_t != NULL) {
BasicType ary_elem = ary_t->klass()->as_array_klass()->element_type()->basic_type();
uint header = arrayOopDesc::base_offset_in_bytes(ary_elem);
uint elemsize = type2aelembytes(ary_elem);
intptr_t dest_pos_plus_len_lo = (((intptr_t)dest_pos_t->_lo) + len_t->_lo) * elemsize + header;
intptr_t dest_pos_plus_len_hi = (((intptr_t)dest_pos_t->_hi) + len_t->_hi) * elemsize + header;
intptr_t dest_pos_lo = ((intptr_t)dest_pos_t->_lo) * elemsize + header;
intptr_t dest_pos_hi = ((intptr_t)dest_pos_t->_hi) * elemsize + header;
if (can_see_stored_value) {
if (ld_offs_t->_lo >= dest_pos_hi && ld_offs_t->_hi < dest_pos_plus_len_lo) {
return ac; return ac;
} }
} else { if (!can_see_stored_value) {
if (ld_offs_t->_hi < dest_pos_lo || ld_offs_t->_lo >= dest_pos_plus_len_hi) {
mem = ac->in(TypeFunc::Memory); mem = ac->in(TypeFunc::Memory);
} }
return ac;
}
}
} }
} }
} }
@ -703,7 +649,7 @@ Node* MemNode::find_previous_store(PhaseTransform* phase) {
continue; // (a) advance through independent call memory continue; // (a) advance through independent call memory
} }
} else if (mem->is_Proj() && mem->in(0)->is_MemBar()) { } else if (mem->is_Proj() && mem->in(0)->is_MemBar()) {
if (membar_for_arraycopy(addr_t, mem->in(0)->as_MemBar(), phase)) { if (ArrayCopyNode::may_modify(addr_t, mem->in(0)->as_MemBar(), phase)) {
break; break;
} }
mem = mem->in(0)->in(TypeFunc::Memory); mem = mem->in(0)->in(TypeFunc::Memory);
@ -883,18 +829,17 @@ static bool skip_through_membars(Compile::AliasType* atp, const TypeInstPtr* tp,
// Is the value loaded previously stored by an arraycopy? If so return // Is the value loaded previously stored by an arraycopy? If so return
// a load node that reads from the source array so we may be able to // a load node that reads from the source array so we may be able to
// optimize out the ArrayCopy node later. // optimize out the ArrayCopy node later.
Node* MemNode::can_see_arraycopy_value(Node* st, PhaseTransform* phase) const { Node* LoadNode::can_see_arraycopy_value(Node* st, PhaseTransform* phase) const {
Node* ld_adr = in(MemNode::Address); Node* ld_adr = in(MemNode::Address);
intptr_t ld_off = 0; intptr_t ld_off = 0;
AllocateNode* ld_alloc = AllocateNode::Ideal_allocation(ld_adr, phase, ld_off); AllocateNode* ld_alloc = AllocateNode::Ideal_allocation(ld_adr, phase, ld_off);
Node* ac = find_previous_arraycopy(phase, ld_alloc, st, true); Node* ac = find_previous_arraycopy(phase, ld_alloc, st, true);
if (ac != NULL) { if (ac != NULL) {
assert(ac->is_ArrayCopy(), "what kind of node can this be?"); assert(ac->is_ArrayCopy(), "what kind of node can this be?");
assert(is_Load(), "only for loads");
Node* ld = clone();
if (ac->as_ArrayCopy()->is_clonebasic()) { if (ac->as_ArrayCopy()->is_clonebasic()) {
assert(ld_alloc != NULL, "need an alloc"); assert(ld_alloc != NULL, "need an alloc");
Node* ld = clone();
Node* addp = in(MemNode::Address)->clone(); Node* addp = in(MemNode::Address)->clone();
assert(addp->is_AddP(), "address must be addp"); assert(addp->is_AddP(), "address must be addp");
assert(addp->in(AddPNode::Base) == ac->in(ArrayCopyNode::Dest)->in(AddPNode::Base), "strange pattern"); assert(addp->in(AddPNode::Base) == ac->in(ArrayCopyNode::Dest)->in(AddPNode::Base), "strange pattern");
@ -906,9 +851,7 @@ Node* MemNode::can_see_arraycopy_value(Node* st, PhaseTransform* phase) const {
assert(ld_alloc->in(0) != NULL, "alloc must have control"); assert(ld_alloc->in(0) != NULL, "alloc must have control");
ld->set_req(0, ld_alloc->in(0)); ld->set_req(0, ld_alloc->in(0));
} }
return ld;
} else { } else {
Node* ld = clone();
Node* addp = in(MemNode::Address)->clone(); Node* addp = in(MemNode::Address)->clone();
assert(addp->in(AddPNode::Base) == addp->in(AddPNode::Address), "should be"); assert(addp->in(AddPNode::Base) == addp->in(AddPNode::Address), "should be");
addp->set_req(AddPNode::Base, ac->in(ArrayCopyNode::Src)); addp->set_req(AddPNode::Base, ac->in(ArrayCopyNode::Src));
@ -933,8 +876,10 @@ Node* MemNode::can_see_arraycopy_value(Node* st, PhaseTransform* phase) const {
assert(ac->in(0) != NULL, "alloc must have control"); assert(ac->in(0) != NULL, "alloc must have control");
ld->set_req(0, ac->in(0)); ld->set_req(0, ac->in(0));
} }
return ld;
} }
// load depends on the tests that validate the arraycopy
ld->as_Load()->_depends_only_on_test = Pinned;
return ld;
} }
return NULL; return NULL;
} }

View file

@ -126,7 +126,6 @@ public:
// Can this node (load or store) accurately see a stored value in // Can this node (load or store) accurately see a stored value in
// the given memory state? (The state may or may not be in(Memory).) // the given memory state? (The state may or may not be in(Memory).)
Node* can_see_stored_value(Node* st, PhaseTransform* phase) const; Node* can_see_stored_value(Node* st, PhaseTransform* phase) const;
Node* can_see_arraycopy_value(Node* st, PhaseTransform* phase) const;
#ifndef PRODUCT #ifndef PRODUCT
static void dump_adr_type(const Node* mem, const TypePtr* adr_type, outputStream *st); static void dump_adr_type(const Node* mem, const TypePtr* adr_type, outputStream *st);
@ -252,6 +251,9 @@ public:
protected: protected:
const Type* load_array_final_field(const TypeKlassPtr *tkls, const Type* load_array_final_field(const TypeKlassPtr *tkls,
ciKlass* klass) const; ciKlass* klass) const;
Node* can_see_arraycopy_value(Node* st, PhaseTransform* phase) const;
// depends_only_on_test is almost always true, and needs to be almost always // depends_only_on_test is almost always true, and needs to be almost always
// true to enable key hoisting & commoning optimizations. However, for the // true to enable key hoisting & commoning optimizations. However, for the
// special case of RawPtr loads from TLS top & end, and other loads performed by // special case of RawPtr loads from TLS top & end, and other loads performed by

View file

@ -0,0 +1,204 @@
/*
* Copyright (c) 2015, 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.
*/
/*
* @test
* @bug 8130847
* @summary Eliminated instance/array written to by an array copy variant must be correctly initialized when reallocated at a deopt
* @run main/othervm -XX:-BackgroundCompilation -XX:-UseOnStackReplacement TestEliminatedArrayCopyDeopt
*
*/
// Test that if an ArrayCopy node is eliminated because it doesn't
// escape, then the correct field/array element values are captured so
// on a deoptimization, when the object/array is reallocated, it is
// correctly initialized
public class TestEliminatedArrayCopyDeopt {
static class A implements Cloneable {
int f0;
int f1;
int f2;
int f3;
int f4;
int f5;
int f6;
int f7;
int f8;
int f9;
int f10;
int f11;
int f12;
int f13;
int f14;
int f15;
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
// Clone
static boolean m1(A a, boolean flag) throws CloneNotSupportedException {
A c = (A)a.clone();
if (flag) {
// never taken branch that causes the deoptimization
if (c.f0 != 0x42) {
return false;
}
}
return true;
}
// Array clone
static int[] m2_src = null;
static boolean m2(boolean flag) throws CloneNotSupportedException {
int[] src = new int[10];
m2_src = src;
for (int i = 0; i < src.length; i++) {
src[i] = 0x42+i;
}
int[] c = (int[])src.clone();
if (flag) {
for (int i = 0; i < c.length; i++) {
if (c[i] != src[i]) {
return false;
}
}
}
return true;
}
// Array copy
static boolean m3(int[] src, boolean flag) {
int[] dst = new int[10];
System.arraycopy(src, 0, dst, 0, 10);
if (flag) {
for (int i = 0; i < dst.length; i++) {
if (dst[i] != src[i]) {
return false;
}
}
}
return true;
}
// Array copy of subrange
static boolean m4(int[] src, boolean flag) {
int[] dst = new int[10];
dst[0] = 0x42;
dst[1] = 0x42 - 1;
dst[2] = 0x42 - 2;
dst[8] = 0x42 - 8;
dst[9] = 0x42 - 9;
int src_off = 2;
int dst_off = 3;
int len = 5;
System.arraycopy(src, src_off, dst, dst_off, len);
if (flag) {
for (int i = 0; i < dst.length; i++) {
if (i >= dst_off && i < dst_off + len) {
if (dst[i] != src[i - dst_off + src_off]) {
return false;
}
} else {
if (dst[i] != 0x42-i) {
return false;
}
}
}
}
return true;
}
// Array copy with Phi
static boolean m5(int[] src, boolean flag1, boolean flag2) {
int[] dst = new int[10];
if (flag1) {
System.arraycopy(src, 0, dst, 0, 10);
}
if (flag2) {
for (int i = 0; i < dst.length; i++) {
if (dst[i] != src[i]) {
return false;
}
}
}
return true;
}
static public void main(String[] args) throws Exception {
boolean success = true;
A a = new A();
a.f0 = 0x42;
for (int i = 0; i < 20000; i++) {
m1(a, false);
}
if (!m1(a, true)) {
System.out.println("m1 failed");
success = false;
}
for (int i = 0; i < 20000; i++) {
m2(false);
}
if (!m2(true)) {
System.out.println("m2 failed");
success = false;
}
int[] src = new int[10];
for (int i = 0; i < src.length; i++) {
src[i] = 0x42+i;
}
for (int i = 0; i < 20000; i++) {
m3(src, false);
}
if (!m3(src, true)) {
System.out.println("m3 failed");
success = false;
}
for (int i = 0; i < 20000; i++) {
m4(src, false);
}
if (!m4(src, true)) {
System.out.println("m4 failed");
success = false;
}
for (int i = 0; i < 20000; i++) {
m5(src, i%2 == 0, false);
}
if (!m5(src, true, true)) {
System.out.println("m4 failed");
success = false;
}
if (!success) {
throw new RuntimeException("Test failed");
}
}
}