src/share/vm/opto/escape.cpp
Index Unified diffs Context diffs Sdiffs Wdiffs Patch New Old Previous File Next File
*** old/src/share/vm/opto/escape.cpp	Tue Nov  1 11:32:53 2011
--- new/src/share/vm/opto/escape.cpp	Tue Nov  1 11:32:53 2011

*** 58,67 **** --- 58,68 ---- }; static const char *esc_names[] = { "UnknownEscape", "NoEscape", + "ControlEscape", "ArgEscape", "GlobalEscape" }; static const char *edge_type_suffix[] = {
*** 74,84 **** --- 75,85 ---- void PointsToNode::dump(bool print_state) const { NodeType nt = node_type(); tty->print("%s ", node_type_names[(int) nt]); if (print_state) { EscapeState es = escape_state(); ! tty->print("%s %s ", esc_names[(int) es], _scalar_replaceable ? "":"NSR"); } tty->print("[["); for (uint i = 0; i < edge_count(); i++) { tty->print(" %d%s", edge_target(i), edge_type_suffix[(int) edge_type(i)]); }
*** 376,395 **** --- 377,397 ---- // Add a deferred edge from node given by "from_i" to any field of adr_i // whose offset matches "offset". void ConnectionGraph::add_deferred_edge_to_fields(uint from_i, uint adr_i, int offs) { PointsToNode* an = ptnode_adr(adr_i); + bool is_alloc = an->_node->is_Allocate(); for (uint fe = 0; fe < an->edge_count(); fe++) { assert(an->edge_type(fe) == PointsToNode::FieldEdge, "expecting a field edge"); int fi = an->edge_target(fe); PointsToNode* pf = ptnode_adr(fi); ! int po = pf->offset(); ! if (pf->edge_count() == 0) { // we have not seen any stores to this field, assume it was set outside this method ! int offset = pf->offset(); ! if (!is_alloc) { + // Assume the field was set outside this method if it is not Allocation add_pointsto_edge(fi, _phantom_object); } ! if (po == offs || po == Type::OffsetBot || offs == Type::OffsetBot) { ! if (offset == offs || offset == Type::OffsetBot || offs == Type::OffsetBot) { add_deferred_edge(from_i, fi); } } }
*** 1039,1049 **** --- 1041,1051 ---- // copy escape information to call node PointsToNode* ptn = ptnode_adr(alloc->_idx); PointsToNode::EscapeState es = escape_state(alloc); // We have an allocation or call which returns a Java object, // see if it is unescaped. - 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 == NULL) { // No uses except Initialize node
*** 1100,1118 **** --- 1102,1119 ---- ptnode_adr(n->_idx)->_node != NULL, "should be registered"); set_map(alloc->_idx, n); set_map(n->_idx, alloc); const TypeOopPtr *t = igvn->type(n)->isa_oopptr(); if (t == NULL) ! continue; // not a TypeInstPtr ! continue; // not a TypeOopPtr tinst = t->cast_to_exactness(true)->is_oopptr()->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); ! if (alloc->is_Allocate() && ptn->_scalar_replaceable && (t->isa_instptr() || t->isa_aryptr())) { ! if (alloc->is_Allocate() && (t->isa_instptr() || t->isa_aryptr())) { // First, put on the worklist all Field edges from Connection Graph // which is more accurate then putting immediate users from Ideal Graph. for (uint e = 0; e < ptn->edge_count(); e++) { Node *use = ptnode_adr(ptn->edge_target(e))->_node;
*** 1536,1545 **** --- 1537,1547 ---- // Initialize worklist if (C->root() != NULL) { worklist_init.push(C->root()); } + GrowableArray<Node*> alloc_worklist; GrowableArray<int> cg_worklist; PhaseGVN* igvn = _igvn; bool has_allocations = false; // Push all useful nodes onto CG list and set their type.
*** 1549,1558 **** --- 1551,1562 ---- // Only allocations and java static calls results are checked // for an escape status. See process_call_result() below. if (n->is_Allocate() || n->is_CallStaticJava() && ptnode_adr(n->_idx)->node_type() == PointsToNode::JavaObject) { has_allocations = true; + if (n->is_Allocate()) + alloc_worklist.append(n); } if(n->is_AddP()) { // Collect address nodes. Use them during stage 3 below // to build initial connection graph field edges. cg_worklist.append(n->_idx);
*** 1651,1728 **** --- 1655,1715 ---- } #undef CG_BUILD_ITER_LIMIT Arena* arena = Thread::current()->resource_area(); VectorSet visited(arena); + + // 5. Find fields initializing values for not escaped allocations + length = alloc_worklist.length(); + for (uint next = 0; next < length; ++next) { + Node* n = alloc_worklist.at(next); + if (ptnode_adr(n->_idx)->escape_state() == PointsToNode::NoEscape) { + find_init_values(n, &visited, igvn); + } + } + worklist.clear(); + worklist_init.clear(); + alloc_worklist.clear(); // 5. Remove deferred edges from the graph and adjust // escape state of nonescaping objects. + // 6. Remove deferred edges from the graph. cg_length = cg_worklist.length(); ! for( uint next = 0; next < cg_length; ++next ) { ! for (uint next = 0; next < cg_length; ++next) { int ni = cg_worklist.at(next); PointsToNode* ptn = ptnode_adr(ni); PointsToNode::NodeType nt = ptn->node_type(); if (nt == PointsToNode::LocalVar || nt == PointsToNode::Field) { remove_deferred(ni, &worklist, &visited); Node *n = ptn->_node; if (n->is_AddP()) { // Search for objects which are not scalar replaceable // and adjust their escape state. adjust_escape_state(ni, igvn); + alloc_worklist.append(n); } } } ! // 6. Propagate escape states. ! // 7. Adjust escape state of nonescaping objects. + length = alloc_worklist.length(); + for (uint next = 0; next < length; ++next) { + Node* n = alloc_worklist.at(next); + adjust_escape_state(n); + } + + // 8. Propagate escape states. worklist.clear(); bool has_non_escaping_obj = false; // push all GlobalEscape nodes on the worklist for( uint next = 0; next < cg_length; ++next ) { int nk = cg_worklist.at(next); if (ptnode_adr(nk)->escape_state() == PointsToNode::GlobalEscape) worklist.push(nk); } // mark all nodes reachable from GlobalEscape nodes while(worklist.length() > 0) { PointsToNode* ptn = ptnode_adr(worklist.pop()); uint e_cnt = ptn->edge_count(); for (uint ei = 0; ei < e_cnt; ei++) { uint npi = ptn->edge_target(ei); PointsToNode *np = ptnode_adr(npi); if (np->escape_state() < PointsToNode::GlobalEscape) { set_escape_state(npi, PointsToNode::GlobalEscape); worklist.push(npi); } } } + (void)propagate_escape_state(&cg_worklist, &worklist, PointsToNode::GlobalEscape); // push all ArgEscape nodes on the worklist for( uint next = 0; next < cg_length; ++next ) { int nk = cg_worklist.at(next); if (ptnode_adr(nk)->escape_state() == PointsToNode::ArgEscape) worklist.push(nk); } // mark all nodes reachable from ArgEscape nodes while(worklist.length() > 0) { PointsToNode* ptn = ptnode_adr(worklist.pop()); if (ptn->node_type() == PointsToNode::JavaObject) has_non_escaping_obj = true; // Non GlobalEscape uint e_cnt = ptn->edge_count(); for (uint ei = 0; ei < e_cnt; ei++) { uint npi = ptn->edge_target(ei); PointsToNode *np = ptnode_adr(npi); if (np->escape_state() < PointsToNode::ArgEscape) { set_escape_state(npi, PointsToNode::ArgEscape); worklist.push(npi); } } } + has_non_escaping_obj = propagate_escape_state(&cg_worklist, &worklist, PointsToNode::ArgEscape); GrowableArray<Node*> alloc_worklist; + // mark all nodes reachable from ControlEscape nodes + has_non_escaping_obj = propagate_escape_state(&cg_worklist, &worklist, PointsToNode::ControlEscape); + alloc_worklist.clear(); + // push all NoEscape nodes on the worklist for( uint next = 0; next < cg_length; ++next ) { int nk = cg_worklist.at(next); if (ptnode_adr(nk)->escape_state() == PointsToNode::NoEscape) worklist.push(nk);
*** 1733,1743 **** --- 1720,1730 ---- PointsToNode* ptn = ptnode_adr(nk); if (ptn->node_type() == PointsToNode::JavaObject && !(nk == _noop_null || nk == _oop_null)) has_non_escaping_obj = true; // Non Escape Node* n = ptn->_node; - if (n->is_Allocate() && ptn->_scalar_replaceable ) { // Push scalar replaceable allocations on alloc_worklist // for processing in split_unique_types(). alloc_worklist.append(n); } uint e_cnt = ptn->edge_count();
*** 1757,1767 **** --- 1744,1754 ---- assert(ptnode_adr(_oop_null)->escape_state() == PointsToNode::NoEscape, "sanity"); if (UseCompressedOops) { assert(ptnode_adr(_noop_null)->escape_state() == PointsToNode::NoEscape, "sanity"); } ! if (EliminateLocks && has_non_escaping_obj) { // Mark locks before changing ideal graph. int cnt = C->macro_count(); for( int i=0; i < cnt; i++ ) { Node *n = C->macro_node(i); if (n->is_AbstractLock()) { // Lock and Unlock nodes
*** 1811,1882 **** --- 1798,1856 ---- #endif } return has_non_escaping_obj; } ! // Adjust escape state after Connection Graph is built. ! void ConnectionGraph::adjust_escape_state(int nidx, PhaseTransform* phase) { ! PointsToNode* ptn = ptnode_adr(nidx); ! Node* n = ptn->_node; ! assert(n->is_AddP(), "Should be called for AddP nodes only"); // Search for objects which are not scalar replaceable. // Mark their escape state as ArgEscape to propagate the state // to referenced objects. // Note: currently there are no difference in compiler optimizations // for ArgEscape objects and NoEscape objects which are not // scalar replaceable. ! // Find fields initializing values for allocations. ! void ConnectionGraph::find_init_values(Node* alloc, VectorSet* visited, PhaseTransform* phase) { ! assert(alloc->is_Allocate(), "Should be called for Allocate nodes only"); ! PointsToNode* pta = ptnode_adr(alloc->_idx); ! assert(pta->escape_state() == PointsToNode::NoEscape, "Not escaped Allocate nodes only"); + InitializeNode* ini = alloc->as_Allocate()->initialization(); Compile* C = _compile; int offset = ptn->offset(); Node* base = get_addp_base(n); VectorSet* ptset = PointsTo(base); int ptset_size = ptset->Size(); + visited->Reset(); // Check if a oop field's initializing value is recorded and add // a corresponding NULL field's value if it is not recorded. // Connection Graph does not record a default initialization by NULL // captured by Initialize node. // // Note: it will disable scalar replacement in some cases: // // Point p[] = new Point[1]; // p[0] = new Point(); // Will be not scalar replaced // // but it will save us from incorrect optimizations in next cases: // // Point p[] = new Point[1]; // if ( x ) p[0] = new Point(); // Will be not scalar replaced // // Do a simple control flow analysis to distinguish above cases. // if (offset != Type::OffsetBot && ptset_size == 1) { uint elem = ptset->getelem(); // Allocation node's index // It does not matter if it is not Allocation node since // only non-escaping allocations are scalar replaced. if (ptnode_adr(elem)->_node->is_Allocate() && ptnode_adr(elem)->escape_state() == PointsToNode::NoEscape) { AllocateNode* alloc = ptnode_adr(elem)->_node->as_Allocate(); InitializeNode* ini = alloc->initialization(); + uint ae_cnt = pta->edge_count(); + for (uint ei = 0; ei < ae_cnt; ei++) { + uint nidx = pta->edge_target(ei); // Field (AddP) + PointsToNode* ptn = ptnode_adr(nidx); + assert(ptn->_node->is_AddP(), "Should be AddP nodes only"); + int offset = ptn->offset(); + if (offset != Type::OffsetBot && + offset != oopDesc::klass_offset_in_bytes() && + !visited->test_set(offset)) { // Check only oop fields. ! const Type* adr_type = ptn->_node->as_AddP()->bottom_type(); BasicType basic_field_type = T_INT; if (adr_type->isa_instptr()) { ciField* field = C->alias_type(adr_type->isa_instptr())->field(); if (field != NULL) { basic_field_type = field->layout_type(); } else { // Ignore non field load (for example, klass load) } } else if (adr_type->isa_aryptr()) { + if (offset != arrayOopDesc::length_offset_in_bytes()) { const Type* elemtype = adr_type->isa_aryptr()->elem(); basic_field_type = elemtype->array_element_basic_type(); } else { // Raw pointers are used for initializing stores so skip it. + // Ignore array length load + } + #ifdef ASSERT + } else { + // Raw pointers are used for initializing stores so skip it + // since it should be recorded already + Node* base = get_addp_base(ptn->_node); assert(adr_type->isa_rawptr() && base->is_Proj() && (base->in(0) == alloc),"unexpected pointer type"); + #endif } if (basic_field_type == T_OBJECT || basic_field_type == T_NARROWOOP || basic_field_type == T_ARRAY) { Node* value = NULL;
*** 1887,1898 **** --- 1861,1885 ---- value = store->in(MemNode::ValueIn); } else if (ptn->edge_count() > 0) { // Are there oop stores? // Check for a store which follows allocation without branches. // For example, a volatile field store is not collected // by Initialize node. TODO: it would be nice to use idom() here. for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { store = n->fast_out(i); + // + // Search all references to the same field which use different + // AddP nodes, for example, in the next case: + // + // Point p[] = new Point[1]; + // if ( x ) { p[0] = new Point(); p[0].x = x; } + // if ( p[0] != null ) { y = p[0].x; } // has CastPP + // + for (uint next = ei; (next < ae_cnt) && (value == NULL); next++) { + uint fpi = pta->edge_target(next); // Field (AddP) + PointsToNode *ptf = ptnode_adr(fpi); + if (ptf->offset() == offset) { + Node* nf = ptf->_node; + for (DUIterator_Fast imax, i = nf->fast_outs(imax); i < imax; i++) { + store = nf->fast_out(i); if (store->is_Store() && store->in(0) != NULL) { Node* ctrl = store->in(0); while(!(ctrl == ini || ctrl == alloc || ctrl == NULL || ctrl == C->root() || ctrl == C->top() || ctrl->is_Region() || ctrl->is_IfTrue() || ctrl->is_IfFalse())) {
*** 1904,1931 **** --- 1891,1934 ---- } } } } } + } + } if (value == NULL || value != ptnode_adr(value->_idx)->_node) { // A field's initializing value was not recorded. Add NULL. uint null_idx = UseCompressedOops ? _noop_null : _oop_null; ! add_pointsto_edge(nidx, null_idx); ! add_edge_from_fields(alloc->_idx, null_idx, offset); } } } } + } + // Adjust escape state after Connection Graph is built. + void ConnectionGraph::adjust_escape_state(Node* n) { + PointsToNode* ptn = ptnode_adr(n->_idx); + assert(n->is_AddP(), "Should be called for AddP nodes only"); + // Search for objects which are not scalar replaceable. + // Mark their escape state as ControlEscape to propagate the state + // to referenced objects. + + int offset = ptn->offset(); + Node* base = get_addp_base(n); + VectorSet* ptset = PointsTo(base); + int ptset_size = ptset->Size(); + // An object is not scalar replaceable if the field which may point // to it has unknown offset (unknown element of an array of objects). // + if (offset == Type::OffsetBot) { uint e_cnt = ptn->edge_count(); for (uint ei = 0; ei < e_cnt; ei++) { uint npi = ptn->edge_target(ei); ! set_escape_state(npi, PointsToNode::ArgEscape); ptnode_adr(npi)->_scalar_replaceable = false; ! set_escape_state(npi, PointsToNode::ControlEscape); } } // Currently an object is not scalar replaceable if a LoadStore node // access its field since the field value is unknown after it.
*** 1940,1963 **** --- 1943,2008 ---- } // An object is not scalar replaceable if the address points // to unknown field (unknown element for arrays, offset is OffsetBot). // // Or the address may point to more then one object. This may produce ! // the false positive result (set scalar_replaceable to false) ! // the false positive result (set escape to ControlEscape) // since the flow-insensitive escape analysis can't separate // the case when stores overwrite the field's value from the case // when stores happened on different control branches. // + // Note: it will disable scalar replacement in some cases: + // + // Point p[] = new Point[1]; + // p[0] = new Point(); // Will be not scalar replaced + // + // but it will save us from incorrect optimizations in next cases: + // + // Point p[] = new Point[1]; + // if ( x ) p[0] = new Point(); // Will be not scalar replaced + // if (ptset_size > 1 || ptset_size != 0 && (has_LoadStore || offset == Type::OffsetBot)) { for( VectorSetI j(ptset); j.test(); ++j ) { ! set_escape_state(j.elem, PointsToNode::ArgEscape); ptnode_adr(j.elem)->_scalar_replaceable = false; ! set_escape_state(j.elem, PointsToNode::ControlEscape); } } } + // Propagate escape states to referenced nodes. + bool ConnectionGraph::propagate_escape_state(GrowableArray<int>* cg_worklist, + GrowableArray<uint>* worklist, + PointsToNode::EscapeState esc_state) { + bool has_java_obj = false; + + // push all nodes with the same escape state on the worklist + uint cg_length = cg_worklist->length(); + for (uint next = 0; next < cg_length; ++next) { + int nk = cg_worklist->at(next); + if (ptnode_adr(nk)->escape_state() == esc_state) + worklist->push(nk); + } + // mark all reachable nodes + while (worklist->length() > 0) { + PointsToNode* ptn = ptnode_adr(worklist->pop()); + if (ptn->node_type() == PointsToNode::JavaObject) { + has_java_obj = true; + } + uint e_cnt = ptn->edge_count(); + for (uint ei = 0; ei < e_cnt; ei++) { + uint npi = ptn->edge_target(ei); + PointsToNode *np = ptnode_adr(npi); + if (np->escape_state() < esc_state) { + set_escape_state(npi, esc_state); + worklist->push(npi); + } + } + } + // Has not escaping java objects + return has_java_obj && (esc_state < PointsToNode::GlobalEscape); + } + void ConnectionGraph::process_call_arguments(CallNode *call, PhaseTransform *phase) { switch (call->Opcode()) { #ifdef ASSERT case Op_Allocate:
*** 2136,2146 **** --- 2181,2191 ---- es = PointsToNode::NoEscape; edge_to = call_idx; int length = call->in(AllocateNode::ALength)->find_int_con(-1); if (length < 0 || length > EliminateAllocationArraySizeLimit) { // Not scalar replaceable if the length is not constant or too big. ! ptnode_adr(call_idx)->_scalar_replaceable = false; ! es = PointsToNode::ControlEscape; } } set_escape_state(call_idx, es); add_pointsto_edge(resproj_idx, edge_to); _processed.set(resproj_idx);
*** 2175,2192 **** --- 2220,2237 ---- bool copy_dependencies = false; if (call_analyzer->is_return_allocated()) { // Returns a newly allocated unescaped object, simply // update dependency information. ! // Mark it as NoEscape so that objects referenced by ! // it's fields will be marked as NoEscape at least. ! set_escape_state(call_idx, PointsToNode::NoEscape); ! // Mark it as ControlEscape so that objects referenced by ! // it's fields will be marked as ControlEscape at least. ! set_escape_state(call_idx, PointsToNode::ControlEscape); add_pointsto_edge(resproj_idx, call_idx); copy_dependencies = true; } else if (call_analyzer->is_return_local()) { // determine whether any arguments are returned ! set_escape_state(call_idx, PointsToNode::NoEscape); ! set_escape_state(call_idx, PointsToNode::ArgEscape); bool ret_arg = false; for (uint i = TypeFunc::Parms; i < d->cnt(); i++) { const Type* at = d->field_at(i); if (at->isa_oopptr() != NULL) {
*** 2199,2230 **** --- 2244,2268 ---- done = false; else if (arg_esp->node_type() == PointsToNode::JavaObject) add_pointsto_edge(resproj_idx, arg->_idx); else add_deferred_edge(resproj_idx, arg->_idx); arg_esp->_hidden_alias = true; } } } if (done && !ret_arg) { // Returns unknown object. set_escape_state(call_idx, PointsToNode::GlobalEscape); add_pointsto_edge(resproj_idx, _phantom_object); } + if (done) { copy_dependencies = true; + } } else { set_escape_state(call_idx, PointsToNode::GlobalEscape); add_pointsto_edge(resproj_idx, _phantom_object); for (uint i = TypeFunc::Parms; i < d->cnt(); i++) { const Type* at = d->field_at(i); if (at->isa_oopptr() != NULL) { Node *arg = call->in(i)->uncast(); PointsToNode *arg_esp = ptnode_adr(arg->_idx); arg_esp->_hidden_alias = true; } } } if (copy_dependencies) call_analyzer->copy_dependencies(_compile->dependencies()); } if (done) _processed.set(resproj_idx);

src/share/vm/opto/escape.cpp
Index Unified diffs Context diffs Sdiffs Wdiffs Patch New Old Previous File Next File