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 Fri Nov 18 18:16:30 2011
--- new/src/share/vm/opto/escape.cpp Fri Nov 18 18:16:30 2011
*** 128,137 ****
--- 128,144 ----
PointsToNode *t = ptnode_adr(to_i);
assert(f->node_type() != PointsToNode::UnknownType && t->node_type() != PointsToNode::UnknownType, "node types must be set");
assert(f->node_type() == PointsToNode::LocalVar || f->node_type() == PointsToNode::Field, "invalid source of PointsTo edge");
assert(t->node_type() == PointsToNode::JavaObject, "invalid destination of PointsTo edge");
+ if (to_i == _phantom_object) { // Quick test for most common object
+ if (f->has_unknown_ptr()) {
+ return;
+ } else {
+ f->set_has_unknown_ptr();
+ }
+ }
add_edge(f, to_i, PointsToNode::PointsToEdge);
}
void ConnectionGraph::add_deferred_edge(uint from_i, uint to_i) {
PointsToNode *f = ptnode_adr(from_i);
*** 163,172 ****
--- 170,182 ----
assert(t_ptr != NULL, "must be a pointer type");
return t_ptr->offset();
}
void ConnectionGraph::add_field_edge(uint from_i, uint to_i, int offset) {
+ // Don't add fields to NULL pointer.
+ if (is_null_ptr(from_i))
+ return;
PointsToNode *f = ptnode_adr(from_i);
PointsToNode *t = ptnode_adr(to_i);
assert(f->node_type() != PointsToNode::UnknownType && t->node_type() != PointsToNode::UnknownType, "node types must be set");
assert(f->node_type() == PointsToNode::JavaObject, "invalid destination of Field edge");
*** 177,187 ****
--- 187,197 ----
add_edge(f, to_i, PointsToNode::FieldEdge);
}
void ConnectionGraph::set_escape_state(uint ni, PointsToNode::EscapeState es) {
// Don't change non-escaping state of NULL pointer.
! if (ni == _noop_null || ni == _oop_null)
! if (is_null_ptr(ni))
return;
PointsToNode *npt = ptnode_adr(ni);
PointsToNode::EscapeState old_es = npt->escape_state();
if (es > old_es)
npt->set_escape_state(es);
*** 309,323 ****
--- 319,331 ----
deferred_edges->clear();
visited->Reset();
visited->set(ni);
PointsToNode *ptn = ptnode_adr(ni);
if (ptn->edge_count() == 0) {
// No deferred or pointsto edges found. Assume the value was set
// outside this method. Add edge to phantom object.
add_pointsto_edge(ni, _phantom_object);
}
+ assert(ptn->node_type() == PointsToNode::LocalVar ||
+ ptn->node_type() == PointsToNode::Field, "sanity");
+ assert(ptn->edge_count() != 0, "should have at least phantom_object");
// Mark current edges as visited and move deferred edges to separate array.
for (uint i = 0; i < ptn->edge_count(); ) {
uint t = ptn->edge_target(i);
#ifdef ASSERT
*** 334,383 ****
--- 342,408 ----
}
for (int next = 0; next < deferred_edges->length(); ++next) {
uint t = deferred_edges->at(next);
PointsToNode *ptt = ptnode_adr(t);
uint e_cnt = ptt->edge_count();
if (e_cnt == 0) {
// No deferred or pointsto edges found. Assume the value was set
// outside this method. Add edge to phantom object.
add_pointsto_edge(t, _phantom_object);
add_pointsto_edge(ni, _phantom_object);
}
+ assert(e_cnt != 0, "should have at least phantom_object");
for (uint e = 0; e < e_cnt; e++) {
uint etgt = ptt->edge_target(e);
if (visited->test_set(etgt))
continue;
PointsToNode::EdgeType et = ptt->edge_type(e);
if (et == PointsToNode::PointsToEdge) {
add_pointsto_edge(ni, etgt);
if(etgt == _phantom_object) {
// Special case - field set outside (globally escaping).
set_escape_state(ni, PointsToNode::GlobalEscape);
}
} else if (et == PointsToNode::DeferredEdge) {
deferred_edges->append(etgt);
} else {
assert(false,"invalid connection graph");
}
}
}
+ if (ptn->edge_count() == 0) {
+ // No pointsto edges found after deferred edges are removed.
+ // For example, in the next case where call is replaced
+ // with uncommon trap and as result array's load references
+ // itself through deferred edges:
+ //
+ // A a = b[i];
+ // if (c!=null) a = c.foo();
+ // b[i] = a;
+ //
+ // Assume the value was set outside this method and
+ // add edge to phantom object.
+ add_pointsto_edge(ni, _phantom_object);
+ }
}
// Add an edge to node given by "to_i" from any field of adr_i whose offset
// matches "offset" A deferred edge is added if to_i is a LocalVar, and
// a pointsto edge is added if it is a JavaObject
void ConnectionGraph::add_edge_from_fields(uint adr_i, uint to_i, int offs) {
+ // No fields for NULL pointer.
+ if (is_null_ptr(adr_i)) {
+ return;
+ }
PointsToNode* an = ptnode_adr(adr_i);
PointsToNode* to = ptnode_adr(to_i);
bool deferred = (to->node_type() == PointsToNode::LocalVar);
+ bool escaped = (to_i == _phantom_object) && (offs == Type::OffsetTop);
+ if (escaped) {
+ // Values in fields escaped during call.
+ assert(an->escape_state() >= PointsToNode::ArgEscape, "sanity");
+ offs = Type::OffsetBot;
+ }
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);
+ if (escaped) {
+ set_escape_state(fi, PointsToNode::GlobalEscape);
+ }
PointsToNode* pf = ptnode_adr(fi);
int po = pf->offset();
if (po == offs || po == Type::OffsetBot || offs == Type::OffsetBot) {
if (deferred)
add_deferred_edge(fi, to_i);
*** 388,397 ****
--- 413,426 ----
}
// 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) {
+ // No fields for NULL pointer.
+ if (is_null_ptr(adr_i)) {
+ return;
+ }
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);
*** 1560,1581 ****
--- 1589,1607 ----
GrowableArray<Node*> alloc_worklist;
GrowableArray<Node*> addp_worklist;
GrowableArray<Node*> ptr_cmp_worklist;
PhaseGVN* igvn = _igvn;
bool has_allocations = false;
// Push all useful nodes onto CG list and set their type.
for( uint next = 0; next < worklist_init.size(); ++next ) {
Node* n = worklist_init.at(next);
record_for_escape_analysis(n, igvn);
// 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);
} else if(n->is_AddP()) {
// Collect address nodes. Use them during stage 3 below
// to build initial connection graph field edges.
addp_worklist.append(n);
*** 1592,1602 ****
--- 1618,1628 ----
Node* m = n->fast_out(i); // Get user
worklist_init.push(m);
}
}
! if (!has_allocations) {
! if (alloc_worklist.length() == 0) {
_collecting = false;
return false; // Nothing to do.
}
// 2. First pass to create simple CG edges (doesn't require to walk CG).
*** 1675,1738 ****
--- 1701,1785 ----
_collecting = false;
return false;
}
#undef CG_BUILD_ITER_LIMIT
+ // 5. Propagate escaped states.
+ worklist.clear();
+
+ // mark all nodes reachable from GlobalEscape nodes
+ (void)propagate_escape_state(&cg_worklist, &worklist, PointsToNode::GlobalEscape);
+
+ // mark all nodes reachable from ArgEscape nodes
+ bool has_non_escaping_obj = propagate_escape_state(&cg_worklist, &worklist, PointsToNode::ArgEscape);
+
Arena* arena = Thread::current()->resource_area();
VectorSet visited(arena);
! // 5. Find fields initializing values for not escaped allocations
! // 6. Find fields initializing values for not escaped allocations
uint alloc_length = alloc_worklist.length();
for (uint next = 0; next < alloc_length; ++next) {
Node* n = alloc_worklist.at(next);
if (ptnode_adr(n->_idx)->escape_state() == PointsToNode::NoEscape) {
+ has_non_escaping_obj = true;
+ if (n->is_Allocate()) {
find_init_values(n, &visited, igvn);
}
}
+ }
worklist.clear();
// 6. Remove deferred edges from the graph.
uint cg_length = cg_worklist.length();
+
+ // Skip the rest of code if all objects escaped.
+ if (!has_non_escaping_obj) {
+ cg_length = 0;
+ addp_length = 0;
+ }
+
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) {
+ if (ptn->edge_count() == 0) {
+ // No values were found. Assume the value was set
+ // outside this method - add edge to phantom object.
+ add_pointsto_edge(ni, _phantom_object);
+ }
+ }
+ }
+
+ // 7. Remove deferred edges from the graph.
+ 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);
}
}
! // 7. Adjust escape state of nonescaping objects.
! // 8. Adjust escape state of nonescaping objects.
for (uint next = 0; next < addp_length; ++next) {
Node* n = addp_worklist.at(next);
adjust_escape_state(n);
}
// 8. Propagate escape states.
worklist.clear();
// mark all nodes reachable from GlobalEscape nodes
(void)propagate_escape_state(&cg_worklist, &worklist, PointsToNode::GlobalEscape);
// mark all nodes reachable from ArgEscape nodes
bool has_non_escaping_obj = propagate_escape_state(&cg_worklist, &worklist, PointsToNode::ArgEscape);
// push all NoEscape nodes on the worklist
+ worklist.clear();
for( uint next = 0; next < cg_length; ++next ) {
int nk = cg_worklist.at(next);
! if (ptnode_adr(nk)->escape_state() == PointsToNode::NoEscape)
! if (ptnode_adr(nk)->escape_state() == PointsToNode::NoEscape &&
+ !is_null_ptr(nk))
worklist.push(nk);
}
+
alloc_worklist.clear();
// mark all nodes reachable from NoEscape nodes
+ // Propagate scalar_replaceable value.
while(worklist.length() > 0) {
uint nk = worklist.pop();
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;
bool scalar_replaceable = ptn->scalar_replaceable();
if (n->is_Allocate() && scalar_replaceable) {
// Push scalar replaceable allocations on alloc_worklist
// for processing in split_unique_types(). Note,
*** 1740,1770 ****
--- 1787,1820 ----
alloc_worklist.append(n);
}
uint e_cnt = ptn->edge_count();
for (uint ei = 0; ei < e_cnt; ei++) {
uint npi = ptn->edge_target(ei);
+ if (is_null_ptr(npi))
+ continue;
PointsToNode *np = ptnode_adr(npi);
if (np->escape_state() < PointsToNode::NoEscape) {
set_escape_state(npi, PointsToNode::NoEscape);
if (!scalar_replaceable) {
np->set_scalar_replaceable(false);
}
worklist.push(npi);
} else if (np->scalar_replaceable() && !scalar_replaceable) {
// Propagate scalar_replaceable value.
np->set_scalar_replaceable(false);
worklist.push(npi);
}
}
}
_collecting = false;
assert(C->unique() == nodes_size(), "there should be no new ideal nodes during ConnectionGraph build");
! assert(ptnode_adr(_oop_null)->escape_state() == PointsToNode::NoEscape, "sanity");
! assert(ptnode_adr(_oop_null)->escape_state() == PointsToNode::NoEscape &&
+ ptnode_adr(_oop_null)->edge_count() == 0, "sanity");
if (UseCompressedOops) {
! assert(ptnode_adr(_noop_null)->escape_state() == PointsToNode::NoEscape, "sanity");
! assert(ptnode_adr(_noop_null)->escape_state() == PointsToNode::NoEscape &&
+ ptnode_adr(_noop_null)->edge_count() == 0, "sanity");
}
if (EliminateLocks && has_non_escaping_obj) {
// Mark locks before changing ideal graph.
int cnt = C->macro_count();
*** 1877,1894 ****
--- 1927,1959 ----
// 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.
//
+ uint null_idx = UseCompressedOops ? _noop_null : _oop_null;
uint ae_cnt = pta->edge_count();
+ bool visited_bottom_offset = false;
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() &&
! if (offset == Type::OffsetBot) {
+ if (!visited_bottom_offset) {
+ visited_bottom_offset = true;
+ // Check only oop fields.
+ const Type* adr_type = ptn->_node->as_AddP()->bottom_type();
+ if (!adr_type->isa_aryptr() ||
+ (adr_type->isa_aryptr()->klass() == NULL) ||
+ adr_type->isa_aryptr()->klass()->is_obj_array_klass()) {
+ // OffsetBot is used to reference array's element,
+ // always add reference to NULL since we don't
+ // known which element if referenced.
+ add_edge_from_fields(alloc->_idx, null_idx, offset);
+ }
+ }
+ } else if (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;
*** 1960,1970 ****
--- 2025,2034 ----
}
}
}
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_edge_from_fields(alloc->_idx, null_idx, offset);
}
}
}
}
*** 2046,2062 ****
--- 2110,2134 ----
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) {
! int pt = worklist->pop();
+ PointsToNode* ptn = ptnode_adr(pt);
+ if (ptn->node_type() == PointsToNode::JavaObject &&
+ !is_null_ptr(pt)) {
has_java_obj = true;
+ if (esc_state > PointsToNode::NoEscape) {
+ // fields values are unknown if object escapes
+ add_edge_from_fields(pt, _phantom_object, Type::OffsetBot);
}
+ }
uint e_cnt = ptn->edge_count();
for (uint ei = 0; ei < e_cnt; ei++) {
uint npi = ptn->edge_target(ei);
+ if (is_null_ptr(npi))
+ continue;
PointsToNode *np = ptnode_adr(npi);
if (np->escape_state() < esc_state) {
set_escape_state(npi, esc_state);
worklist->push(npi);
}
*** 2157,2220 ****
--- 2229,2363 ----
}
return NULL;
}
void ConnectionGraph::process_call_arguments(CallNode *call, PhaseTransform *phase) {
+ bool is_arraycopy = false;
switch (call->Opcode()) {
#ifdef ASSERT
case Op_Allocate:
case Op_AllocateArray:
case Op_Lock:
case Op_Unlock:
assert(false, "should be done already");
break;
#endif
case Op_CallLeaf:
case Op_CallLeafNoFP:
+ is_arraycopy = (call->as_CallLeaf()->_name != NULL &&
+ strstr(call->as_CallLeaf()->_name, "arraycopy") != 0);
+ // fall through
+ case Op_CallLeaf:
{
// Stub calls, objects do not escape but they are not scale replaceable.
// Adjust escape state for outgoing arguments.
const TypeTuple * d = call->tf()->domain();
+ bool src_has_oops = false;
for (uint i = TypeFunc::Parms; i < d->cnt(); i++) {
const Type* at = d->field_at(i);
Node *arg = call->in(i)->uncast();
const Type *aat = phase->type(arg);
+ PointsToNode::EscapeState arg_esc = ptnode_adr(arg->_idx)->escape_state();
if (!arg->is_top() && at->isa_ptr() && aat->isa_ptr() &&
! ptnode_adr(arg->_idx)->escape_state() < PointsToNode::ArgEscape) {
! (is_arraycopy || arg_esc < PointsToNode::ArgEscape)) {
assert(aat == Type::TOP || aat == TypePtr::NULL_PTR ||
aat->isa_ptr() != NULL, "expecting an Ptr");
+ bool arg_has_oops = aat->isa_oopptr() &&
+ (aat->isa_oopptr()->klass() == NULL || aat->isa_instptr() ||
+ (aat->isa_aryptr() && aat->isa_aryptr()->klass()->is_obj_array_klass()));
+ if (i == TypeFunc::Parms) {
+ src_has_oops = arg_has_oops;
+ }
+ //
+ // src or dst could be j.l.Object when other is basic type array:
+ //
+ // arraycopy(char[],0,Object*,0,size);
+ // arraycopy(Object*,0,char[],0,size);
+ //
+ // Don't add edges from dst's fields in such cases.
+ //
+ bool arg_is_arraycopy_dest = src_has_oops && is_arraycopy &&
+ arg_has_oops && (i > TypeFunc::Parms);
#ifdef ASSERT
! if (!(call->Opcode() == Op_CallLeafNoFP &&
! if (!(is_arraycopy ||
call->as_CallLeaf()->_name != NULL &&
(strstr(call->as_CallLeaf()->_name, "arraycopy") != 0) ||
call->as_CallLeaf()->_name != NULL &&
(strcmp(call->as_CallLeaf()->_name, "g1_wb_pre") == 0 ||
strcmp(call->as_CallLeaf()->_name, "g1_wb_post") == 0 ))
) {
call->dump();
assert(false, "EA: unexpected CallLeaf");
}
#endif
+ // Process arraycopy's destination regardless its escape state
+ // since we need to add all posible edges to references in
+ // source object.
+ if (arg_esc >= PointsToNode::ArgEscape &&
+ !arg_is_arraycopy_dest) {
+ continue;
+ }
set_escape_state(arg->_idx, PointsToNode::ArgEscape);
+ Node* arg_base = arg;
if (arg->is_AddP()) {
//
// The inline_native_clone() case when the arraycopy stub is called
// after the allocation before Initialize and CheckCastPP nodes.
+ // Or normal arraycopy for object arrays case.
//
// Set AddP's base (Allocate) as not scalar replaceable since
// pointer to the base (with offset) is passed as argument.
//
! arg_base = get_addp_base(arg);
}
for( VectorSetI j(PointsTo(arg)); j.test(); ++j ) {
uint pt = j.elem;
set_escape_state(pt, PointsToNode::ArgEscape);
+ VectorSet argset = *PointsTo(arg_base); // Clone set
+ for( VectorSetI j(&argset); j.test(); ++j ) {
+ uint pd = j.elem; // Destination object
+ set_escape_state(pd, PointsToNode::ArgEscape);
+
+ if (arg_is_arraycopy_dest) {
+ PointsToNode* ptd = ptnode_adr(pd);
+ // Conservatively reference an unknown object since
+ // not all source's fields/elements may be known.
+ add_edge_from_fields(pd, _phantom_object, Type::OffsetBot);
+
+ Node *src = call->in(TypeFunc::Parms)->uncast();
+ Node* src_base = src;
+ if (src->is_AddP()) {
+ src_base = get_addp_base(src);
}
+ // Create edges from destination's fields to
+ // everything known source's fields could point to.
+ for( VectorSetI s(PointsTo(src_base)); s.test(); ++s ) {
+ uint ps = s.elem;
+ bool has_bottom_offset = false;
+ for (uint fd = 0; fd < ptd->edge_count(); fd++) {
+ assert(ptd->edge_type(fd) == PointsToNode::FieldEdge, "expecting a field edge");
+ int fdi = ptd->edge_target(fd);
+ PointsToNode* pfd = ptnode_adr(fdi);
+ int offset = pfd->offset();
+ if (offset == Type::OffsetBot)
+ has_bottom_offset = true;
+ assert(offset != -1, "offset should be set");
+ add_deferred_edge_to_fields(fdi, ps, offset);
}
+ // Destination object may not have access (no field edge)
+ // to fields which are accessed in source object.
+ // As result no edges will be created to those source's
+ // fields and escape state of destination object will
+ // not be propagated to those fields.
+ //
+ // Mark source object as global escape except in
+ // the case with Type::OffsetBot field (which is
+ // common case for array elements access) when
+ // edges are created to all source's fields.
+ if (!has_bottom_offset) {
+ set_escape_state(ps, PointsToNode::GlobalEscape);
}
+ }
+ }
+ }
+ }
+ }
break;
}
case Op_CallStaticJava:
// For a static call, we know exactly what method is being called.
*** 2250,2267 ****
--- 2393,2411 ----
}
for( VectorSetI j(PointsTo(arg)); j.test(); ++j ) {
uint pt = j.elem;
if (global_escapes) {
! // The argument global escapes, mark everything it could point to
set_escape_state(pt, PointsToNode::GlobalEscape);
+ add_edge_from_fields(pt, _phantom_object, Type::OffsetBot);
} else {
+ set_escape_state(pt, PointsToNode::ArgEscape);
if (fields_escapes) {
// The argument itself doesn't escape, but any fields might
! add_edge_from_fields(pt, _phantom_object, Type::OffsetBot);
! add_edge_from_fields(pt, _phantom_object, Type::OffsetTop);
}
set_escape_state(pt, PointsToNode::ArgEscape);
}
}
}
}
if (copy_dependencies)
*** 2283,2292 ****
--- 2427,2437 ----
Node *arg = call->in(i)->uncast();
set_escape_state(arg->_idx, PointsToNode::GlobalEscape);
for( VectorSetI j(PointsTo(arg)); j.test(); ++j ) {
uint pt = j.elem;
set_escape_state(pt, PointsToNode::GlobalEscape);
+ add_edge_from_fields(pt, _phantom_object, Type::OffsetBot);
}
}
}
}
}
*** 2383,2401 ****
--- 2528,2547 ----
// 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);
ptnode_adr(call_idx)->set_scalar_replaceable(false);
+ // Fields values are unknown
+ add_edge_from_fields(call_idx, _phantom_object, Type::OffsetBot);
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::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) {
Node *arg = call->in(i)->uncast();
if (call_analyzer->is_arg_returned(i - TypeFunc::Parms)) {
ret_arg = true;
*** 2407,2427 ****
--- 2553,2570 ----
else
add_deferred_edge(resproj_idx, arg->_idx);
}
}
}
- if (done && !ret_arg) {
+ copy_dependencies = true;
+ // is_return_local() is true when only arguments are returned.
+ if (!ret_arg || !call_analyzer->is_return_local()) {
// 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);
}
if (copy_dependencies)
call_analyzer->copy_dependencies(_compile->dependencies());
}
if (done)
src/share/vm/opto/escape.cpp
Index
Unified diffs
Context diffs
Sdiffs
Wdiffs
Patch
New
Old
Previous File
Next File