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