in src/hotspot/share/opto/escape.cpp [4403:5053]
void ConnectionGraph::split_unique_types(GrowableArray<Node *> &alloc_worklist,
GrowableArray<ArrayCopyNode*> &arraycopy_worklist,
GrowableArray<MergeMemNode*> &mergemem_worklist,
Unique_Node_List &reducible_merges) {
DEBUG_ONLY(Unique_Node_List reduced_merges;)
GrowableArray<Node *> memnode_worklist;
GrowableArray<PhiNode *> orig_phis;
PhaseIterGVN *igvn = _igvn;
uint new_index_start = (uint) _compile->num_alias_types();
VectorSet visited;
ideal_nodes.clear(); // Reset for use with set_map/get_map.
uint unique_old = _compile->unique();
// Phase 1: Process possible allocations from alloc_worklist.
// Create instance types for the CheckCastPP for allocations where possible.
//
// (Note: don't forget to change the order of the second AddP node on
// the alloc_worklist if the order of the worklist processing is changed,
// see the comment in find_second_addp().)
//
while (alloc_worklist.length() != 0) {
Node *n = alloc_worklist.pop();
uint ni = n->_idx;
if (n->is_Call()) {
CallNode *alloc = n->as_Call();
// copy escape information to call node
PointsToNode* ptn = ptnode_adr(alloc->_idx);
PointsToNode::EscapeState es = ptn->escape_state();
// We have an allocation or call which returns a Java object,
// see if it is non-escaped.
if (es != PointsToNode::NoEscape || !ptn->scalar_replaceable()) {
continue;
}
// Find CheckCastPP for the allocate or for the return value of a call
n = alloc->result_cast();
if (n == nullptr) { // No uses except Initialize node
if (alloc->is_Allocate()) {
// Set the scalar_replaceable flag for allocation
// so it could be eliminated if it has no uses.
alloc->as_Allocate()->_is_scalar_replaceable = true;
}
continue;
}
if (!n->is_CheckCastPP()) { // not unique CheckCastPP.
// we could reach here for allocate case if one init is associated with many allocs.
if (alloc->is_Allocate()) {
alloc->as_Allocate()->_is_scalar_replaceable = false;
}
continue;
}
// The inline code for Object.clone() casts the allocation result to
// java.lang.Object and then to the actual type of the allocated
// object. Detect this case and use the second cast.
// Also detect j.l.reflect.Array.newInstance(jobject, jint) case when
// the allocation result is cast to java.lang.Object and then
// to the actual Array type.
if (alloc->is_Allocate() && n->as_Type()->type() == TypeInstPtr::NOTNULL
&& (alloc->is_AllocateArray() ||
igvn->type(alloc->in(AllocateNode::KlassNode)) != TypeInstKlassPtr::OBJECT)) {
Node *cast2 = nullptr;
for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) {
Node *use = n->fast_out(i);
if (use->is_CheckCastPP()) {
cast2 = use;
break;
}
}
if (cast2 != nullptr) {
n = cast2;
} else {
// Non-scalar replaceable if the allocation type is unknown statically
// (reflection allocation), the object can't be restored during
// deoptimization without precise type.
continue;
}
}
const TypeOopPtr *t = igvn->type(n)->isa_oopptr();
if (t == nullptr) {
continue; // not a TypeOopPtr
}
if (!t->klass_is_exact()) {
continue; // not an unique type
}
if (alloc->is_Allocate()) {
// Set the scalar_replaceable flag for allocation
// so it could be eliminated.
alloc->as_Allocate()->_is_scalar_replaceable = true;
}
set_escape_state(ptnode_adr(n->_idx), es NOT_PRODUCT(COMMA trace_propagate_message(ptn))); // CheckCastPP escape state
// in order for an object to be scalar-replaceable, it must be:
// - a direct allocation (not a call returning an object)
// - non-escaping
// - eligible to be a unique type
// - not determined to be ineligible by escape analysis
set_map(alloc, n);
set_map(n, alloc);
const TypeOopPtr* tinst = t->cast_to_instance_id(ni);
igvn->hash_delete(n);
igvn->set_type(n, tinst);
n->raise_bottom_type(tinst);
igvn->hash_insert(n);
record_for_optimizer(n);
// Allocate an alias index for the header fields. Accesses to
// the header emitted during macro expansion wouldn't have
// correct memory state otherwise.
_compile->get_alias_index(tinst->add_offset(oopDesc::mark_offset_in_bytes()));
_compile->get_alias_index(tinst->add_offset(oopDesc::klass_offset_in_bytes()));
if (alloc->is_Allocate() && (t->isa_instptr() || t->isa_aryptr())) {
// Add a new NarrowMem projection for each existing NarrowMem projection with new adr type
InitializeNode* init = alloc->as_Allocate()->initialization();
assert(init != nullptr, "can't find Initialization node for this Allocate node");
auto process_narrow_proj = [&](NarrowMemProjNode* proj) {
const TypePtr* adr_type = proj->adr_type();
const TypePtr* new_adr_type = tinst->add_offset(adr_type->offset());
if (adr_type != new_adr_type && !init->already_has_narrow_mem_proj_with_adr_type(new_adr_type)) {
DEBUG_ONLY( uint alias_idx = _compile->get_alias_index(new_adr_type); )
assert(_compile->get_general_index(alias_idx) == _compile->get_alias_index(adr_type), "new adr type should be narrowed down from existing adr type");
NarrowMemProjNode* new_proj = new NarrowMemProjNode(init, new_adr_type);
igvn->set_type(new_proj, new_proj->bottom_type());
record_for_optimizer(new_proj);
set_map(proj, new_proj); // record it so ConnectionGraph::find_inst_mem() can find it
}
};
init->for_each_narrow_mem_proj_with_new_uses(process_narrow_proj);
// First, put on the worklist all Field edges from Connection Graph
// which is more accurate than putting immediate users from Ideal Graph.
for (EdgeIterator e(ptn); e.has_next(); e.next()) {
PointsToNode* tgt = e.get();
if (tgt->is_Arraycopy()) {
continue;
}
Node* use = tgt->ideal_node();
assert(tgt->is_Field() && use->is_AddP(),
"only AddP nodes are Field edges in CG");
if (use->outcnt() > 0) { // Don't process dead nodes
Node* addp2 = find_second_addp(use, use->in(AddPNode::Base));
if (addp2 != nullptr) {
assert(alloc->is_AllocateArray(),"array allocation was expected");
alloc_worklist.append_if_missing(addp2);
}
alloc_worklist.append_if_missing(use);
}
}
// An allocation may have an Initialize which has raw stores. Scan
// the users of the raw allocation result and push AddP users
// on alloc_worklist.
Node *raw_result = alloc->proj_out_or_null(TypeFunc::Parms);
assert (raw_result != nullptr, "must have an allocation result");
for (DUIterator_Fast imax, i = raw_result->fast_outs(imax); i < imax; i++) {
Node *use = raw_result->fast_out(i);
if (use->is_AddP() && use->outcnt() > 0) { // Don't process dead nodes
Node* addp2 = find_second_addp(use, raw_result);
if (addp2 != nullptr) {
assert(alloc->is_AllocateArray(),"array allocation was expected");
alloc_worklist.append_if_missing(addp2);
}
alloc_worklist.append_if_missing(use);
} else if (use->is_MemBar()) {
memnode_worklist.append_if_missing(use);
}
}
}
} else if (n->is_AddP()) {
if (has_reducible_merge_base(n->as_AddP(), reducible_merges)) {
// This AddP will go away when we reduce the Phi
continue;
}
Node* addp_base = get_addp_base(n);
JavaObjectNode* jobj = unique_java_object(addp_base);
if (jobj == nullptr || jobj == phantom_obj) {
#ifdef ASSERT
ptnode_adr(get_addp_base(n)->_idx)->dump();
ptnode_adr(n->_idx)->dump();
assert(jobj != nullptr && jobj != phantom_obj, "escaped allocation");
#endif
_compile->record_failure(_invocation > 0 ? C2Compiler::retry_no_iterative_escape_analysis() : C2Compiler::retry_no_escape_analysis());
return;
}
Node *base = get_map(jobj->idx()); // CheckCastPP node
if (!split_AddP(n, base)) continue; // wrong type from dead path
} else if (n->is_Phi() ||
n->is_CheckCastPP() ||
n->is_EncodeP() ||
n->is_DecodeN() ||
(n->is_ConstraintCast() && n->Opcode() == Op_CastPP)) {
if (visited.test_set(n->_idx)) {
assert(n->is_Phi(), "loops only through Phi's");
continue; // already processed
}
// Reducible Phi's will be removed from the graph after split_unique_types
// finishes. For now we just try to split out the SR inputs of the merge.
Node* parent = n->in(1);
if (reducible_merges.member(n)) {
reduce_phi(n->as_Phi(), alloc_worklist);
#ifdef ASSERT
if (VerifyReduceAllocationMerges) {
reduced_merges.push(n);
}
#endif
continue;
} else if (reducible_merges.member(parent)) {
// 'n' is an user of a reducible merge (a Phi). It will be simplified as
// part of reduce_merge.
continue;
}
JavaObjectNode* jobj = unique_java_object(n);
if (jobj == nullptr || jobj == phantom_obj) {
#ifdef ASSERT
ptnode_adr(n->_idx)->dump();
assert(jobj != nullptr && jobj != phantom_obj, "escaped allocation");
#endif
_compile->record_failure(_invocation > 0 ? C2Compiler::retry_no_iterative_escape_analysis() : C2Compiler::retry_no_escape_analysis());
return;
} else {
Node *val = get_map(jobj->idx()); // CheckCastPP node
TypeNode *tn = n->as_Type();
const TypeOopPtr* tinst = igvn->type(val)->isa_oopptr();
assert(tinst != nullptr && tinst->is_known_instance() &&
tinst->instance_id() == jobj->idx() , "instance type expected.");
const Type *tn_type = igvn->type(tn);
const TypeOopPtr *tn_t;
if (tn_type->isa_narrowoop()) {
tn_t = tn_type->make_ptr()->isa_oopptr();
} else {
tn_t = tn_type->isa_oopptr();
}
if (tn_t != nullptr && tinst->maybe_java_subtype_of(tn_t)) {
if (tn_type->isa_narrowoop()) {
tn_type = tinst->make_narrowoop();
} else {
tn_type = tinst;
}
igvn->hash_delete(tn);
igvn->set_type(tn, tn_type);
tn->set_type(tn_type);
igvn->hash_insert(tn);
record_for_optimizer(n);
} else {
assert(tn_type == TypePtr::NULL_PTR ||
(tn_t != nullptr && !tinst->maybe_java_subtype_of(tn_t)),
"unexpected type");
continue; // Skip dead path with different type
}
}
} else {
DEBUG_ONLY(n->dump();)
assert(false, "EA: unexpected node");
continue;
}
// push allocation's users on appropriate worklist
for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) {
Node *use = n->fast_out(i);
if(use->is_Mem() && use->in(MemNode::Address) == n) {
// Load/store to instance's field
memnode_worklist.append_if_missing(use);
} else if (use->is_MemBar()) {
if (use->in(TypeFunc::Memory) == n) { // Ignore precedent edge
memnode_worklist.append_if_missing(use);
}
} else if (use->is_AddP() && use->outcnt() > 0) { // No dead nodes
Node* addp2 = find_second_addp(use, n);
if (addp2 != nullptr) {
alloc_worklist.append_if_missing(addp2);
}
alloc_worklist.append_if_missing(use);
} else if (use->is_Phi() ||
use->is_CheckCastPP() ||
use->is_EncodeNarrowPtr() ||
use->is_DecodeNarrowPtr() ||
(use->is_ConstraintCast() && use->Opcode() == Op_CastPP)) {
alloc_worklist.append_if_missing(use);
#ifdef ASSERT
} else if (use->is_Mem()) {
assert(use->in(MemNode::Address) != n, "EA: missing allocation reference path");
} else if (use->is_MergeMem()) {
assert(mergemem_worklist.contains(use->as_MergeMem()), "EA: missing MergeMem node in the worklist");
} else if (use->is_SafePoint()) {
// Look for MergeMem nodes for calls which reference unique allocation
// (through CheckCastPP nodes) even for debug info.
Node* m = use->in(TypeFunc::Memory);
if (m->is_MergeMem()) {
assert(mergemem_worklist.contains(m->as_MergeMem()), "EA: missing MergeMem node in the worklist");
}
} else if (use->Opcode() == Op_EncodeISOArray) {
if (use->in(MemNode::Memory) == n || use->in(3) == n) {
// EncodeISOArray overwrites destination array
memnode_worklist.append_if_missing(use);
}
} else {
uint op = use->Opcode();
if ((op == Op_StrCompressedCopy || op == Op_StrInflatedCopy) &&
(use->in(MemNode::Memory) == n)) {
// They overwrite memory edge corresponding to destination array,
memnode_worklist.append_if_missing(use);
} else if (!(op == Op_CmpP || op == Op_Conv2B ||
op == Op_CastP2X ||
op == Op_FastLock || op == Op_AryEq ||
op == Op_StrComp || op == Op_CountPositives ||
op == Op_StrCompressedCopy || op == Op_StrInflatedCopy ||
op == Op_StrEquals || op == Op_VectorizedHashCode ||
op == Op_StrIndexOf || op == Op_StrIndexOfChar ||
op == Op_SubTypeCheck ||
op == Op_ReinterpretS2HF ||
BarrierSet::barrier_set()->barrier_set_c2()->is_gc_barrier_node(use))) {
n->dump();
use->dump();
assert(false, "EA: missing allocation reference path");
}
#endif
}
}
}
#ifdef ASSERT
if (VerifyReduceAllocationMerges) {
for (uint i = 0; i < reducible_merges.size(); i++) {
Node* phi = reducible_merges.at(i);
if (!reduced_merges.member(phi)) {
phi->dump(2);
phi->dump(-2);
assert(false, "This reducible merge wasn't reduced.");
}
// At this point reducible Phis shouldn't have AddP users anymore; only SafePoints or Casts.
for (DUIterator_Fast jmax, j = phi->fast_outs(jmax); j < jmax; j++) {
Node* use = phi->fast_out(j);
if (!use->is_SafePoint() && !use->is_CastPP()) {
phi->dump(2);
phi->dump(-2);
assert(false, "Unexpected user of reducible Phi -> %d:%s:%d", use->_idx, use->Name(), use->outcnt());
}
}
}
}
#endif
// Go over all ArrayCopy nodes and if one of the inputs has a unique
// type, record it in the ArrayCopy node so we know what memory this
// node uses/modified.
for (int next = 0; next < arraycopy_worklist.length(); next++) {
ArrayCopyNode* ac = arraycopy_worklist.at(next);
Node* dest = ac->in(ArrayCopyNode::Dest);
if (dest->is_AddP()) {
dest = get_addp_base(dest);
}
JavaObjectNode* jobj = unique_java_object(dest);
if (jobj != nullptr) {
Node *base = get_map(jobj->idx());
if (base != nullptr) {
const TypeOopPtr *base_t = _igvn->type(base)->isa_oopptr();
ac->_dest_type = base_t;
}
}
Node* src = ac->in(ArrayCopyNode::Src);
if (src->is_AddP()) {
src = get_addp_base(src);
}
jobj = unique_java_object(src);
if (jobj != nullptr) {
Node* base = get_map(jobj->idx());
if (base != nullptr) {
const TypeOopPtr *base_t = _igvn->type(base)->isa_oopptr();
ac->_src_type = base_t;
}
}
}
// New alias types were created in split_AddP().
uint new_index_end = (uint) _compile->num_alias_types();
_compile->print_method(PHASE_EA_AFTER_SPLIT_UNIQUE_TYPES_1, 5);
// Phase 2: Process MemNode's from memnode_worklist. compute new address type and
// compute new values for Memory inputs (the Memory inputs are not
// actually updated until phase 4.)
if (memnode_worklist.length() == 0)
return; // nothing to do
while (memnode_worklist.length() != 0) {
Node *n = memnode_worklist.pop();
if (visited.test_set(n->_idx)) {
continue;
}
if (n->is_Phi() || n->is_ClearArray()) {
// we don't need to do anything, but the users must be pushed
} else if (n->is_MemBar()) { // MemBar nodes
if (!n->is_Initialize()) { // memory projections for Initialize pushed below (so we get to all their uses)
// we don't need to do anything, but the users must be pushed
n = n->as_MemBar()->proj_out_or_null(TypeFunc::Memory);
if (n == nullptr) {
continue;
}
}
} else if (n->is_CallLeaf()) {
// Runtime calls with narrow memory input (no MergeMem node)
// get the memory projection
n = n->as_Call()->proj_out_or_null(TypeFunc::Memory);
if (n == nullptr) {
continue;
}
} else if (n->Opcode() == Op_StrInflatedCopy) {
// Check direct uses of StrInflatedCopy.
// It is memory type Node - no special SCMemProj node.
} else if (n->Opcode() == Op_StrCompressedCopy ||
n->Opcode() == Op_EncodeISOArray) {
// get the memory projection
n = n->find_out_with(Op_SCMemProj);
assert(n != nullptr && n->Opcode() == Op_SCMemProj, "memory projection required");
} else if (n->is_Proj()) {
assert(n->in(0)->is_Initialize(), "we only push memory projections for Initialize");
} else {
#ifdef ASSERT
if (!n->is_Mem()) {
n->dump();
}
assert(n->is_Mem(), "memory node required.");
#endif
Node *addr = n->in(MemNode::Address);
const Type *addr_t = igvn->type(addr);
if (addr_t == Type::TOP) {
continue;
}
assert (addr_t->isa_ptr() != nullptr, "pointer type required.");
int alias_idx = _compile->get_alias_index(addr_t->is_ptr());
assert ((uint)alias_idx < new_index_end, "wrong alias index");
Node *mem = find_inst_mem(n->in(MemNode::Memory), alias_idx, orig_phis);
if (_compile->failing()) {
return;
}
if (mem != n->in(MemNode::Memory)) {
// We delay the memory edge update since we need old one in
// MergeMem code below when instances memory slices are separated.
set_map(n, mem);
}
if (n->is_Load()) {
continue; // don't push users
} else if (n->is_LoadStore()) {
// get the memory projection
n = n->find_out_with(Op_SCMemProj);
assert(n != nullptr && n->Opcode() == Op_SCMemProj, "memory projection required");
}
}
// push user on appropriate worklist
for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) {
Node *use = n->fast_out(i);
if (use->is_Phi() || use->is_ClearArray()) {
memnode_worklist.append_if_missing(use);
} else if (use->is_Mem() && use->in(MemNode::Memory) == n) {
memnode_worklist.append_if_missing(use);
} else if (use->is_MemBar() || use->is_CallLeaf()) {
if (use->in(TypeFunc::Memory) == n) { // Ignore precedent edge
memnode_worklist.append_if_missing(use);
}
} else if (use->is_Proj()) {
assert(n->is_Initialize(), "We only push projections of Initialize");
if (use->as_Proj()->_con == TypeFunc::Memory) { // Ignore precedent edge
memnode_worklist.append_if_missing(use);
}
#ifdef ASSERT
} else if(use->is_Mem()) {
assert(use->in(MemNode::Memory) != n, "EA: missing memory path");
} else if (use->is_MergeMem()) {
assert(mergemem_worklist.contains(use->as_MergeMem()), "EA: missing MergeMem node in the worklist");
} else if (use->Opcode() == Op_EncodeISOArray) {
if (use->in(MemNode::Memory) == n || use->in(3) == n) {
// EncodeISOArray overwrites destination array
memnode_worklist.append_if_missing(use);
}
} else {
uint op = use->Opcode();
if ((use->in(MemNode::Memory) == n) &&
(op == Op_StrCompressedCopy || op == Op_StrInflatedCopy)) {
// They overwrite memory edge corresponding to destination array,
memnode_worklist.append_if_missing(use);
} else if (!(BarrierSet::barrier_set()->barrier_set_c2()->is_gc_barrier_node(use) ||
op == Op_AryEq || op == Op_StrComp || op == Op_CountPositives ||
op == Op_StrCompressedCopy || op == Op_StrInflatedCopy || op == Op_VectorizedHashCode ||
op == Op_StrEquals || op == Op_StrIndexOf || op == Op_StrIndexOfChar)) {
n->dump();
use->dump();
assert(false, "EA: missing memory path");
}
#endif
}
}
}
// Phase 3: Process MergeMem nodes from mergemem_worklist.
// Walk each memory slice moving the first node encountered of each
// instance type to the input corresponding to its alias index.
uint length = mergemem_worklist.length();
for( uint next = 0; next < length; ++next ) {
MergeMemNode* nmm = mergemem_worklist.at(next);
assert(!visited.test_set(nmm->_idx), "should not be visited before");
// Note: we don't want to use MergeMemStream here because we only want to
// scan inputs which exist at the start, not ones we add during processing.
// Note 2: MergeMem may already contains instance memory slices added
// during find_inst_mem() call when memory nodes were processed above.
igvn->hash_delete(nmm);
uint nslices = MIN2(nmm->req(), new_index_start);
for (uint i = Compile::AliasIdxRaw+1; i < nslices; i++) {
Node* mem = nmm->in(i);
Node* cur = nullptr;
if (mem == nullptr || mem->is_top()) {
continue;
}
// First, update mergemem by moving memory nodes to corresponding slices
// if their type became more precise since this mergemem was created.
while (mem->is_Mem()) {
const Type* at = igvn->type(mem->in(MemNode::Address));
if (at != Type::TOP) {
assert (at->isa_ptr() != nullptr, "pointer type required.");
uint idx = (uint)_compile->get_alias_index(at->is_ptr());
if (idx == i) {
if (cur == nullptr) {
cur = mem;
}
} else {
if (idx >= nmm->req() || nmm->is_empty_memory(nmm->in(idx))) {
nmm->set_memory_at(idx, mem);
}
}
}
mem = mem->in(MemNode::Memory);
}
nmm->set_memory_at(i, (cur != nullptr) ? cur : mem);
// Find any instance of the current type if we haven't encountered
// already a memory slice of the instance along the memory chain.
for (uint ni = new_index_start; ni < new_index_end; ni++) {
if((uint)_compile->get_general_index(ni) == i) {
Node *m = (ni >= nmm->req()) ? nmm->empty_memory() : nmm->in(ni);
if (nmm->is_empty_memory(m)) {
Node* result = find_inst_mem(mem, ni, orig_phis);
if (_compile->failing()) {
return;
}
nmm->set_memory_at(ni, result);
}
}
}
}
// Find the rest of instances values
for (uint ni = new_index_start; ni < new_index_end; ni++) {
const TypeOopPtr *tinst = _compile->get_adr_type(ni)->isa_oopptr();
Node* result = step_through_mergemem(nmm, ni, tinst);
if (result == nmm->base_memory()) {
// Didn't find instance memory, search through general slice recursively.
result = nmm->memory_at(_compile->get_general_index(ni));
result = find_inst_mem(result, ni, orig_phis);
if (_compile->failing()) {
return;
}
nmm->set_memory_at(ni, result);
}
}
// If we have crossed the 3/4 point of max node limit it's too risky
// to continue with EA/SR because we might hit the max node limit.
if (_compile->live_nodes() >= _compile->max_node_limit() * 0.75) {
if (_compile->do_reduce_allocation_merges()) {
_compile->record_failure(C2Compiler::retry_no_reduce_allocation_merges());
} else if (_invocation > 0) {
_compile->record_failure(C2Compiler::retry_no_iterative_escape_analysis());
} else {
_compile->record_failure(C2Compiler::retry_no_escape_analysis());
}
return;
}
igvn->hash_insert(nmm);
record_for_optimizer(nmm);
}
_compile->print_method(PHASE_EA_AFTER_SPLIT_UNIQUE_TYPES_3, 5);
// Phase 4: Update the inputs of non-instance memory Phis and
// the Memory input of memnodes
// First update the inputs of any non-instance Phi's from
// which we split out an instance Phi. Note we don't have
// to recursively process Phi's encountered on the input memory
// chains as is done in split_memory_phi() since they will
// also be processed here.
for (int j = 0; j < orig_phis.length(); j++) {
PhiNode *phi = orig_phis.at(j);
int alias_idx = _compile->get_alias_index(phi->adr_type());
igvn->hash_delete(phi);
for (uint i = 1; i < phi->req(); i++) {
Node *mem = phi->in(i);
Node *new_mem = find_inst_mem(mem, alias_idx, orig_phis);
if (_compile->failing()) {
return;
}
if (mem != new_mem) {
phi->set_req(i, new_mem);
}
}
igvn->hash_insert(phi);
record_for_optimizer(phi);
}
// Update the memory inputs of MemNodes with the value we computed
// in Phase 2 and move stores memory users to corresponding memory slices.
// Disable memory split verification code until the fix for 6984348.
// Currently it produces false negative results since it does not cover all cases.
#if 0 // ifdef ASSERT
visited.Reset();
Node_Stack old_mems(arena, _compile->unique() >> 2);
#endif
for (uint i = 0; i < ideal_nodes.size(); i++) {
Node* n = ideal_nodes.at(i);
Node* nmem = get_map(n->_idx);
assert(nmem != nullptr, "sanity");
if (n->is_Mem()) {
#if 0 // ifdef ASSERT
Node* old_mem = n->in(MemNode::Memory);
if (!visited.test_set(old_mem->_idx)) {
old_mems.push(old_mem, old_mem->outcnt());
}
#endif
assert(n->in(MemNode::Memory) != nmem, "sanity");
if (!n->is_Load()) {
// Move memory users of a store first.
move_inst_mem(n, orig_phis);
}
// Now update memory input
igvn->hash_delete(n);
n->set_req(MemNode::Memory, nmem);
igvn->hash_insert(n);
record_for_optimizer(n);
} else {
assert(n->is_Allocate() || n->is_CheckCastPP() ||
n->is_AddP() || n->is_Phi() || n->is_NarrowMemProj(), "unknown node used for set_map()");
}
}
#if 0 // ifdef ASSERT
// Verify that memory was split correctly
while (old_mems.is_nonempty()) {
Node* old_mem = old_mems.node();
uint old_cnt = old_mems.index();
old_mems.pop();
assert(old_cnt == old_mem->outcnt(), "old mem could be lost");
}
#endif
_compile->print_method(PHASE_EA_AFTER_SPLIT_UNIQUE_TYPES_4, 5);
}