7059047: EA: can't find initializing store with several CheckCastPP
Split adjust_escape_state() method into two methods to find initializing stores. Reviewed-by: never
This commit is contained in:
parent
93fbade63d
commit
0bc2963b7d
@ -378,16 +378,17 @@ void ConnectionGraph::add_edge_from_fields(uint adr_i, uint to_i, int offs) {
|
|||||||
// whose offset matches "offset".
|
// whose offset matches "offset".
|
||||||
void ConnectionGraph::add_deferred_edge_to_fields(uint from_i, uint adr_i, int offs) {
|
void ConnectionGraph::add_deferred_edge_to_fields(uint from_i, uint adr_i, int offs) {
|
||||||
PointsToNode* an = ptnode_adr(adr_i);
|
PointsToNode* an = ptnode_adr(adr_i);
|
||||||
|
bool is_alloc = an->_node->is_Allocate();
|
||||||
for (uint fe = 0; fe < an->edge_count(); fe++) {
|
for (uint fe = 0; fe < an->edge_count(); fe++) {
|
||||||
assert(an->edge_type(fe) == PointsToNode::FieldEdge, "expecting a field edge");
|
assert(an->edge_type(fe) == PointsToNode::FieldEdge, "expecting a field edge");
|
||||||
int fi = an->edge_target(fe);
|
int fi = an->edge_target(fe);
|
||||||
PointsToNode* pf = ptnode_adr(fi);
|
PointsToNode* pf = ptnode_adr(fi);
|
||||||
int po = pf->offset();
|
int offset = pf->offset();
|
||||||
if (pf->edge_count() == 0) {
|
if (!is_alloc) {
|
||||||
// we have not seen any stores to this field, assume it was set outside this method
|
// Assume the field was set outside this method if it is not Allocation
|
||||||
add_pointsto_edge(fi, _phantom_object);
|
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);
|
add_deferred_edge(from_i, fi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1041,7 +1042,7 @@ void ConnectionGraph::split_unique_types(GrowableArray<Node *> &alloc_worklist)
|
|||||||
PointsToNode::EscapeState es = escape_state(alloc);
|
PointsToNode::EscapeState es = escape_state(alloc);
|
||||||
// We have an allocation or call which returns a Java object,
|
// We have an allocation or call which returns a Java object,
|
||||||
// see if it is unescaped.
|
// see if it is unescaped.
|
||||||
if (es != PointsToNode::NoEscape || !ptn->_scalar_replaceable)
|
if (es != PointsToNode::NoEscape || !ptn->scalar_replaceable())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Find CheckCastPP for the allocate or for the return value of a call
|
// Find CheckCastPP for the allocate or for the return value of a call
|
||||||
@ -1090,7 +1091,7 @@ void ConnectionGraph::split_unique_types(GrowableArray<Node *> &alloc_worklist)
|
|||||||
// so it could be eliminated.
|
// so it could be eliminated.
|
||||||
alloc->as_Allocate()->_is_scalar_replaceable = true;
|
alloc->as_Allocate()->_is_scalar_replaceable = true;
|
||||||
}
|
}
|
||||||
set_escape_state(n->_idx, es);
|
set_escape_state(n->_idx, es); // CheckCastPP escape state
|
||||||
// in order for an object to be scalar-replaceable, it must be:
|
// in order for an object to be scalar-replaceable, it must be:
|
||||||
// - a direct allocation (not a call returning an object)
|
// - a direct allocation (not a call returning an object)
|
||||||
// - non-escaping
|
// - non-escaping
|
||||||
@ -1102,15 +1103,14 @@ void ConnectionGraph::split_unique_types(GrowableArray<Node *> &alloc_worklist)
|
|||||||
set_map(n->_idx, alloc);
|
set_map(n->_idx, alloc);
|
||||||
const TypeOopPtr *t = igvn->type(n)->isa_oopptr();
|
const TypeOopPtr *t = igvn->type(n)->isa_oopptr();
|
||||||
if (t == NULL)
|
if (t == NULL)
|
||||||
continue; // not a TypeInstPtr
|
continue; // not a TypeOopPtr
|
||||||
tinst = t->cast_to_exactness(true)->is_oopptr()->cast_to_instance_id(ni);
|
tinst = t->cast_to_exactness(true)->is_oopptr()->cast_to_instance_id(ni);
|
||||||
igvn->hash_delete(n);
|
igvn->hash_delete(n);
|
||||||
igvn->set_type(n, tinst);
|
igvn->set_type(n, tinst);
|
||||||
n->raise_bottom_type(tinst);
|
n->raise_bottom_type(tinst);
|
||||||
igvn->hash_insert(n);
|
igvn->hash_insert(n);
|
||||||
record_for_optimizer(n);
|
record_for_optimizer(n);
|
||||||
if (alloc->is_Allocate() && ptn->_scalar_replaceable &&
|
if (alloc->is_Allocate() && (t->isa_instptr() || t->isa_aryptr())) {
|
||||||
(t->isa_instptr() || t->isa_aryptr())) {
|
|
||||||
|
|
||||||
// First, put on the worklist all Field edges from Connection Graph
|
// First, put on the worklist all Field edges from Connection Graph
|
||||||
// which is more accurate then putting immediate users from Ideal Graph.
|
// which is more accurate then putting immediate users from Ideal Graph.
|
||||||
@ -1538,7 +1538,8 @@ bool ConnectionGraph::compute_escape() {
|
|||||||
worklist_init.push(C->root());
|
worklist_init.push(C->root());
|
||||||
}
|
}
|
||||||
|
|
||||||
GrowableArray<int> cg_worklist;
|
GrowableArray<Node*> alloc_worklist;
|
||||||
|
GrowableArray<Node*> addp_worklist;
|
||||||
PhaseGVN* igvn = _igvn;
|
PhaseGVN* igvn = _igvn;
|
||||||
bool has_allocations = false;
|
bool has_allocations = false;
|
||||||
|
|
||||||
@ -1551,11 +1552,13 @@ bool ConnectionGraph::compute_escape() {
|
|||||||
if (n->is_Allocate() || n->is_CallStaticJava() &&
|
if (n->is_Allocate() || n->is_CallStaticJava() &&
|
||||||
ptnode_adr(n->_idx)->node_type() == PointsToNode::JavaObject) {
|
ptnode_adr(n->_idx)->node_type() == PointsToNode::JavaObject) {
|
||||||
has_allocations = true;
|
has_allocations = true;
|
||||||
|
if (n->is_Allocate())
|
||||||
|
alloc_worklist.append(n);
|
||||||
}
|
}
|
||||||
if(n->is_AddP()) {
|
if(n->is_AddP()) {
|
||||||
// Collect address nodes. Use them during stage 3 below
|
// Collect address nodes. Use them during stage 3 below
|
||||||
// to build initial connection graph field edges.
|
// to build initial connection graph field edges.
|
||||||
cg_worklist.append(n->_idx);
|
addp_worklist.append(n);
|
||||||
} else if (n->is_MergeMem()) {
|
} else if (n->is_MergeMem()) {
|
||||||
// Collect all MergeMem nodes to add memory slices for
|
// Collect all MergeMem nodes to add memory slices for
|
||||||
// scalar replaceable objects in split_unique_types().
|
// scalar replaceable objects in split_unique_types().
|
||||||
@ -1581,10 +1584,9 @@ bool ConnectionGraph::compute_escape() {
|
|||||||
|
|
||||||
// 3. Pass to create initial fields edges (JavaObject -F-> AddP)
|
// 3. Pass to create initial fields edges (JavaObject -F-> AddP)
|
||||||
// to reduce number of iterations during stage 4 below.
|
// to reduce number of iterations during stage 4 below.
|
||||||
uint cg_length = cg_worklist.length();
|
uint addp_length = addp_worklist.length();
|
||||||
for( uint next = 0; next < cg_length; ++next ) {
|
for( uint next = 0; next < addp_length; ++next ) {
|
||||||
int ni = cg_worklist.at(next);
|
Node* n = addp_worklist.at(next);
|
||||||
Node* n = ptnode_adr(ni)->_node;
|
|
||||||
Node* base = get_addp_base(n);
|
Node* base = get_addp_base(n);
|
||||||
if (base->is_Proj())
|
if (base->is_Proj())
|
||||||
base = base->in(0);
|
base = base->in(0);
|
||||||
@ -1594,7 +1596,7 @@ bool ConnectionGraph::compute_escape() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cg_worklist.clear();
|
GrowableArray<int> cg_worklist;
|
||||||
cg_worklist.append(_phantom_object);
|
cg_worklist.append(_phantom_object);
|
||||||
GrowableArray<uint> worklist;
|
GrowableArray<uint> worklist;
|
||||||
|
|
||||||
@ -1653,73 +1655,44 @@ bool ConnectionGraph::compute_escape() {
|
|||||||
|
|
||||||
Arena* arena = Thread::current()->resource_area();
|
Arena* arena = Thread::current()->resource_area();
|
||||||
VectorSet visited(arena);
|
VectorSet visited(arena);
|
||||||
|
|
||||||
|
// 5. 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) {
|
||||||
|
find_init_values(n, &visited, igvn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
worklist.clear();
|
worklist.clear();
|
||||||
|
|
||||||
// 5. Remove deferred edges from the graph and adjust
|
// 6. Remove deferred edges from the graph.
|
||||||
// escape state of nonescaping objects.
|
uint cg_length = cg_worklist.length();
|
||||||
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);
|
int ni = cg_worklist.at(next);
|
||||||
PointsToNode* ptn = ptnode_adr(ni);
|
PointsToNode* ptn = ptnode_adr(ni);
|
||||||
PointsToNode::NodeType nt = ptn->node_type();
|
PointsToNode::NodeType nt = ptn->node_type();
|
||||||
if (nt == PointsToNode::LocalVar || nt == PointsToNode::Field) {
|
if (nt == PointsToNode::LocalVar || nt == PointsToNode::Field) {
|
||||||
remove_deferred(ni, &worklist, &visited);
|
remove_deferred(ni, &worklist, &visited);
|
||||||
Node *n = ptn->_node;
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. Propagate escape states.
|
// 7. 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();
|
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
|
// mark all nodes reachable from GlobalEscape nodes
|
||||||
while(worklist.length() > 0) {
|
(void)propagate_escape_state(&cg_worklist, &worklist, PointsToNode::GlobalEscape);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
// mark all nodes reachable from ArgEscape nodes
|
||||||
while(worklist.length() > 0) {
|
bool has_non_escaping_obj = propagate_escape_state(&cg_worklist, &worklist, PointsToNode::ArgEscape);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GrowableArray<Node*> alloc_worklist;
|
|
||||||
|
|
||||||
// push all NoEscape nodes on the worklist
|
// push all NoEscape nodes on the worklist
|
||||||
for( uint next = 0; next < cg_length; ++next ) {
|
for( uint next = 0; next < cg_length; ++next ) {
|
||||||
@ -1727,6 +1700,7 @@ bool ConnectionGraph::compute_escape() {
|
|||||||
if (ptnode_adr(nk)->escape_state() == PointsToNode::NoEscape)
|
if (ptnode_adr(nk)->escape_state() == PointsToNode::NoEscape)
|
||||||
worklist.push(nk);
|
worklist.push(nk);
|
||||||
}
|
}
|
||||||
|
alloc_worklist.clear();
|
||||||
// mark all nodes reachable from NoEscape nodes
|
// mark all nodes reachable from NoEscape nodes
|
||||||
while(worklist.length() > 0) {
|
while(worklist.length() > 0) {
|
||||||
uint nk = worklist.pop();
|
uint nk = worklist.pop();
|
||||||
@ -1735,9 +1709,11 @@ bool ConnectionGraph::compute_escape() {
|
|||||||
!(nk == _noop_null || nk == _oop_null))
|
!(nk == _noop_null || nk == _oop_null))
|
||||||
has_non_escaping_obj = true; // Non Escape
|
has_non_escaping_obj = true; // Non Escape
|
||||||
Node* n = ptn->_node;
|
Node* n = ptn->_node;
|
||||||
if (n->is_Allocate() && ptn->_scalar_replaceable ) {
|
bool scalar_replaceable = ptn->scalar_replaceable();
|
||||||
|
if (n->is_Allocate() && scalar_replaceable) {
|
||||||
// Push scalar replaceable allocations on alloc_worklist
|
// Push scalar replaceable allocations on alloc_worklist
|
||||||
// for processing in split_unique_types().
|
// for processing in split_unique_types(). Note,
|
||||||
|
// following code may change scalar_replaceable value.
|
||||||
alloc_worklist.append(n);
|
alloc_worklist.append(n);
|
||||||
}
|
}
|
||||||
uint e_cnt = ptn->edge_count();
|
uint e_cnt = ptn->edge_count();
|
||||||
@ -1746,6 +1722,13 @@ bool ConnectionGraph::compute_escape() {
|
|||||||
PointsToNode *np = ptnode_adr(npi);
|
PointsToNode *np = ptnode_adr(npi);
|
||||||
if (np->escape_state() < PointsToNode::NoEscape) {
|
if (np->escape_state() < PointsToNode::NoEscape) {
|
||||||
set_escape_state(npi, 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);
|
worklist.push(npi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1759,7 +1742,7 @@ bool ConnectionGraph::compute_escape() {
|
|||||||
assert(ptnode_adr(_noop_null)->escape_state() == PointsToNode::NoEscape, "sanity");
|
assert(ptnode_adr(_noop_null)->escape_state() == PointsToNode::NoEscape, "sanity");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (EliminateLocks) {
|
if (EliminateLocks && has_non_escaping_obj) {
|
||||||
// Mark locks before changing ideal graph.
|
// Mark locks before changing ideal graph.
|
||||||
int cnt = C->macro_count();
|
int cnt = C->macro_count();
|
||||||
for( int i=0; i < cnt; i++ ) {
|
for( int i=0; i < cnt; i++ ) {
|
||||||
@ -1784,7 +1767,18 @@ bool ConnectionGraph::compute_escape() {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool has_scalar_replaceable_candidates = alloc_worklist.length() > 0;
|
bool has_scalar_replaceable_candidates = false;
|
||||||
|
alloc_length = alloc_worklist.length();
|
||||||
|
for (uint next = 0; next < alloc_length; ++next) {
|
||||||
|
Node* n = alloc_worklist.at(next);
|
||||||
|
PointsToNode* ptn = ptnode_adr(n->_idx);
|
||||||
|
assert(ptn->escape_state() == PointsToNode::NoEscape, "sanity");
|
||||||
|
if (ptn->scalar_replaceable()) {
|
||||||
|
has_scalar_replaceable_candidates = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ( has_scalar_replaceable_candidates &&
|
if ( has_scalar_replaceable_candidates &&
|
||||||
C->AliasLevel() >= 3 && EliminateAllocations ) {
|
C->AliasLevel() >= 3 && EliminateAllocations ) {
|
||||||
|
|
||||||
@ -1813,53 +1807,32 @@ bool ConnectionGraph::compute_escape() {
|
|||||||
return has_non_escaping_obj;
|
return has_non_escaping_obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adjust escape state after Connection Graph is built.
|
// Find fields initializing values for allocations.
|
||||||
void ConnectionGraph::adjust_escape_state(int nidx, PhaseTransform* phase) {
|
void ConnectionGraph::find_init_values(Node* alloc, VectorSet* visited, PhaseTransform* phase) {
|
||||||
PointsToNode* ptn = ptnode_adr(nidx);
|
assert(alloc->is_Allocate(), "Should be called for Allocate nodes only");
|
||||||
Node* n = ptn->_node;
|
PointsToNode* pta = ptnode_adr(alloc->_idx);
|
||||||
assert(n->is_AddP(), "Should be called for AddP nodes only");
|
assert(pta->escape_state() == PointsToNode::NoEscape, "Not escaped Allocate nodes only");
|
||||||
// Search for objects which are not scalar replaceable.
|
InitializeNode* ini = alloc->as_Allocate()->initialization();
|
||||||
// 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.
|
|
||||||
|
|
||||||
Compile* C = _compile;
|
Compile* C = _compile;
|
||||||
|
visited->Reset();
|
||||||
int offset = ptn->offset();
|
|
||||||
Node* base = get_addp_base(n);
|
|
||||||
VectorSet* ptset = PointsTo(base);
|
|
||||||
int ptset_size = ptset->Size();
|
|
||||||
|
|
||||||
// Check if a oop field's initializing value is recorded and add
|
// Check if a oop field's initializing value is recorded and add
|
||||||
// a corresponding NULL field's value if it is not recorded.
|
// a corresponding NULL field's value if it is not recorded.
|
||||||
// Connection Graph does not record a default initialization by NULL
|
// Connection Graph does not record a default initialization by NULL
|
||||||
// captured by Initialize node.
|
// captured by Initialize node.
|
||||||
//
|
//
|
||||||
// Note: it will disable scalar replacement in some cases:
|
uint ae_cnt = pta->edge_count();
|
||||||
//
|
for (uint ei = 0; ei < ae_cnt; ei++) {
|
||||||
// Point p[] = new Point[1];
|
uint nidx = pta->edge_target(ei); // Field (AddP)
|
||||||
// p[0] = new Point(); // Will be not scalar replaced
|
PointsToNode* ptn = ptnode_adr(nidx);
|
||||||
//
|
assert(ptn->_node->is_AddP(), "Should be AddP nodes only");
|
||||||
// but it will save us from incorrect optimizations in next cases:
|
int offset = ptn->offset();
|
||||||
//
|
if (offset != Type::OffsetBot &&
|
||||||
// Point p[] = new Point[1];
|
offset != oopDesc::klass_offset_in_bytes() &&
|
||||||
// if ( x ) p[0] = new Point(); // Will be not scalar replaced
|
!visited->test_set(offset)) {
|
||||||
//
|
|
||||||
// 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();
|
|
||||||
|
|
||||||
// Check only oop fields.
|
// Check only oop fields.
|
||||||
const Type* adr_type = n->as_AddP()->bottom_type();
|
const Type* adr_type = ptn->_node->as_AddP()->bottom_type();
|
||||||
BasicType basic_field_type = T_INT;
|
BasicType basic_field_type = T_INT;
|
||||||
if (adr_type->isa_instptr()) {
|
if (adr_type->isa_instptr()) {
|
||||||
ciField* field = C->alias_type(adr_type->isa_instptr())->field();
|
ciField* field = C->alias_type(adr_type->isa_instptr())->field();
|
||||||
@ -1869,12 +1842,20 @@ void ConnectionGraph::adjust_escape_state(int nidx, PhaseTransform* phase) {
|
|||||||
// Ignore non field load (for example, klass load)
|
// Ignore non field load (for example, klass load)
|
||||||
}
|
}
|
||||||
} else if (adr_type->isa_aryptr()) {
|
} else if (adr_type->isa_aryptr()) {
|
||||||
|
if (offset != arrayOopDesc::length_offset_in_bytes()) {
|
||||||
const Type* elemtype = adr_type->isa_aryptr()->elem();
|
const Type* elemtype = adr_type->isa_aryptr()->elem();
|
||||||
basic_field_type = elemtype->array_element_basic_type();
|
basic_field_type = elemtype->array_element_basic_type();
|
||||||
} else {
|
} 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() &&
|
assert(adr_type->isa_rawptr() && base->is_Proj() &&
|
||||||
(base->in(0) == alloc),"unexpected pointer type");
|
(base->in(0) == alloc),"unexpected pointer type");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
if (basic_field_type == T_OBJECT ||
|
if (basic_field_type == T_OBJECT ||
|
||||||
basic_field_type == T_NARROWOOP ||
|
basic_field_type == T_NARROWOOP ||
|
||||||
@ -1889,8 +1870,21 @@ void ConnectionGraph::adjust_escape_state(int nidx, PhaseTransform* phase) {
|
|||||||
// Check for a store which follows allocation without branches.
|
// Check for a store which follows allocation without branches.
|
||||||
// For example, a volatile field store is not collected
|
// For example, a volatile field store is not collected
|
||||||
// by Initialize node. TODO: it would be nice to use idom() here.
|
// 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) {
|
if (store->is_Store() && store->in(0) != NULL) {
|
||||||
Node* ctrl = store->in(0);
|
Node* ctrl = store->in(0);
|
||||||
while(!(ctrl == ini || ctrl == alloc || ctrl == NULL ||
|
while(!(ctrl == ini || ctrl == alloc || ctrl == NULL ||
|
||||||
@ -1906,24 +1900,40 @@ void ConnectionGraph::adjust_escape_state(int nidx, PhaseTransform* phase) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (value == NULL || value != ptnode_adr(value->_idx)->_node) {
|
if (value == NULL || value != ptnode_adr(value->_idx)->_node) {
|
||||||
// A field's initializing value was not recorded. Add NULL.
|
// A field's initializing value was not recorded. Add NULL.
|
||||||
uint null_idx = UseCompressedOops ? _noop_null : _oop_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
|
||||||
|
// and mark them 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
|
// An object is not scalar replaceable if the field which may point
|
||||||
// to it has unknown offset (unknown element of an array of objects).
|
// to it has unknown offset (unknown element of an array of objects).
|
||||||
//
|
//
|
||||||
|
|
||||||
if (offset == Type::OffsetBot) {
|
if (offset == Type::OffsetBot) {
|
||||||
uint e_cnt = ptn->edge_count();
|
uint e_cnt = ptn->edge_count();
|
||||||
for (uint ei = 0; ei < e_cnt; ei++) {
|
for (uint ei = 0; ei < e_cnt; ei++) {
|
||||||
uint npi = ptn->edge_target(ei);
|
uint npi = ptn->edge_target(ei);
|
||||||
set_escape_state(npi, PointsToNode::ArgEscape);
|
ptnode_adr(npi)->set_scalar_replaceable(false);
|
||||||
ptnode_adr(npi)->_scalar_replaceable = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1942,20 +1952,62 @@ void ConnectionGraph::adjust_escape_state(int nidx, PhaseTransform* phase) {
|
|||||||
// to unknown field (unknown element for arrays, offset is OffsetBot).
|
// to unknown field (unknown element for arrays, offset is OffsetBot).
|
||||||
//
|
//
|
||||||
// Or the address may point to more then one object. This may produce
|
// 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 not scalar replaceable)
|
||||||
// since the flow-insensitive escape analysis can't separate
|
// since the flow-insensitive escape analysis can't separate
|
||||||
// the case when stores overwrite the field's value from the case
|
// the case when stores overwrite the field's value from the case
|
||||||
// when stores happened on different control branches.
|
// 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 &&
|
if (ptset_size > 1 || ptset_size != 0 &&
|
||||||
(has_LoadStore || offset == Type::OffsetBot)) {
|
(has_LoadStore || offset == Type::OffsetBot)) {
|
||||||
for( VectorSetI j(ptset); j.test(); ++j ) {
|
for( VectorSetI j(ptset); j.test(); ++j ) {
|
||||||
set_escape_state(j.elem, PointsToNode::ArgEscape);
|
ptnode_adr(j.elem)->set_scalar_replaceable(false);
|
||||||
ptnode_adr(j.elem)->_scalar_replaceable = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
void ConnectionGraph::process_call_arguments(CallNode *call, PhaseTransform *phase) {
|
||||||
|
|
||||||
switch (call->Opcode()) {
|
switch (call->Opcode()) {
|
||||||
@ -2112,6 +2164,7 @@ void ConnectionGraph::process_call_result(ProjNode *resproj, PhaseTransform *pha
|
|||||||
} else {
|
} else {
|
||||||
es = PointsToNode::NoEscape;
|
es = PointsToNode::NoEscape;
|
||||||
edge_to = call_idx;
|
edge_to = call_idx;
|
||||||
|
assert(ptnode_adr(call_idx)->scalar_replaceable(), "sanity");
|
||||||
}
|
}
|
||||||
set_escape_state(call_idx, es);
|
set_escape_state(call_idx, es);
|
||||||
add_pointsto_edge(resproj_idx, edge_to);
|
add_pointsto_edge(resproj_idx, edge_to);
|
||||||
@ -2135,10 +2188,11 @@ void ConnectionGraph::process_call_result(ProjNode *resproj, PhaseTransform *pha
|
|||||||
} else {
|
} else {
|
||||||
es = PointsToNode::NoEscape;
|
es = PointsToNode::NoEscape;
|
||||||
edge_to = call_idx;
|
edge_to = call_idx;
|
||||||
|
assert(ptnode_adr(call_idx)->scalar_replaceable(), "sanity");
|
||||||
int length = call->in(AllocateNode::ALength)->find_int_con(-1);
|
int length = call->in(AllocateNode::ALength)->find_int_con(-1);
|
||||||
if (length < 0 || length > EliminateAllocationArraySizeLimit) {
|
if (length < 0 || length > EliminateAllocationArraySizeLimit) {
|
||||||
// Not scalar replaceable if the length is not constant or too big.
|
// Not scalar replaceable if the length is not constant or too big.
|
||||||
ptnode_adr(call_idx)->_scalar_replaceable = false;
|
ptnode_adr(call_idx)->set_scalar_replaceable(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
set_escape_state(call_idx, es);
|
set_escape_state(call_idx, es);
|
||||||
@ -2180,11 +2234,12 @@ void ConnectionGraph::process_call_result(ProjNode *resproj, PhaseTransform *pha
|
|||||||
// Mark it as NoEscape so that objects referenced by
|
// Mark it as NoEscape so that objects referenced by
|
||||||
// it's fields will be marked as NoEscape at least.
|
// it's fields will be marked as NoEscape at least.
|
||||||
set_escape_state(call_idx, PointsToNode::NoEscape);
|
set_escape_state(call_idx, PointsToNode::NoEscape);
|
||||||
|
ptnode_adr(call_idx)->set_scalar_replaceable(false);
|
||||||
add_pointsto_edge(resproj_idx, call_idx);
|
add_pointsto_edge(resproj_idx, call_idx);
|
||||||
copy_dependencies = true;
|
copy_dependencies = true;
|
||||||
} else if (call_analyzer->is_return_local()) {
|
} else if (call_analyzer->is_return_local()) {
|
||||||
// determine whether any arguments are returned
|
// determine whether any arguments are returned
|
||||||
set_escape_state(call_idx, PointsToNode::NoEscape);
|
set_escape_state(call_idx, PointsToNode::ArgEscape);
|
||||||
bool ret_arg = false;
|
bool ret_arg = false;
|
||||||
for (uint i = TypeFunc::Parms; i < d->cnt(); i++) {
|
for (uint i = TypeFunc::Parms; i < d->cnt(); i++) {
|
||||||
const Type* at = d->field_at(i);
|
const Type* at = d->field_at(i);
|
||||||
@ -2201,7 +2256,6 @@ void ConnectionGraph::process_call_result(ProjNode *resproj, PhaseTransform *pha
|
|||||||
add_pointsto_edge(resproj_idx, arg->_idx);
|
add_pointsto_edge(resproj_idx, arg->_idx);
|
||||||
else
|
else
|
||||||
add_deferred_edge(resproj_idx, arg->_idx);
|
add_deferred_edge(resproj_idx, arg->_idx);
|
||||||
arg_esp->_hidden_alias = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2210,18 +2264,12 @@ void ConnectionGraph::process_call_result(ProjNode *resproj, PhaseTransform *pha
|
|||||||
set_escape_state(call_idx, PointsToNode::GlobalEscape);
|
set_escape_state(call_idx, PointsToNode::GlobalEscape);
|
||||||
add_pointsto_edge(resproj_idx, _phantom_object);
|
add_pointsto_edge(resproj_idx, _phantom_object);
|
||||||
}
|
}
|
||||||
|
if (done) {
|
||||||
copy_dependencies = true;
|
copy_dependencies = true;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
set_escape_state(call_idx, PointsToNode::GlobalEscape);
|
set_escape_state(call_idx, PointsToNode::GlobalEscape);
|
||||||
add_pointsto_edge(resproj_idx, _phantom_object);
|
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)
|
if (copy_dependencies)
|
||||||
call_analyzer->copy_dependencies(_compile->dependencies());
|
call_analyzer->copy_dependencies(_compile->dependencies());
|
||||||
|
@ -74,7 +74,7 @@
|
|||||||
// C2 does not have local variables. However for the purposes of constructing
|
// C2 does not have local variables. However for the purposes of constructing
|
||||||
// the connection graph, the following IR nodes are treated as local variables:
|
// the connection graph, the following IR nodes are treated as local variables:
|
||||||
// Phi (pointer values)
|
// Phi (pointer values)
|
||||||
// LoadP
|
// LoadP, LoadN
|
||||||
// Proj#5 (value returned from callnodes including allocations)
|
// Proj#5 (value returned from callnodes including allocations)
|
||||||
// CheckCastPP, CastPP
|
// CheckCastPP, CastPP
|
||||||
//
|
//
|
||||||
@ -84,7 +84,7 @@
|
|||||||
//
|
//
|
||||||
// The following node types are JavaObject:
|
// The following node types are JavaObject:
|
||||||
//
|
//
|
||||||
// top()
|
// phantom_object (general globally escaped object)
|
||||||
// Allocate
|
// Allocate
|
||||||
// AllocateArray
|
// AllocateArray
|
||||||
// Parm (for incoming arguments)
|
// Parm (for incoming arguments)
|
||||||
@ -93,6 +93,7 @@
|
|||||||
// ConP
|
// ConP
|
||||||
// LoadKlass
|
// LoadKlass
|
||||||
// ThreadLocal
|
// ThreadLocal
|
||||||
|
// CallStaticJava (which returns Object)
|
||||||
//
|
//
|
||||||
// AddP nodes are fields.
|
// AddP nodes are fields.
|
||||||
//
|
//
|
||||||
@ -130,10 +131,12 @@ public:
|
|||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
UnknownEscape = 0,
|
UnknownEscape = 0,
|
||||||
NoEscape = 1, // A scalar replaceable object with unique type.
|
NoEscape = 1, // An object does not escape method or thread and it is
|
||||||
ArgEscape = 2, // An object passed as argument or referenced by
|
// not passed to call. It could be replaced with scalar.
|
||||||
// argument (and not globally escape during call).
|
ArgEscape = 2, // An object does not escape method or thread but it is
|
||||||
GlobalEscape = 3 // An object escapes the method and thread.
|
// passed as argument to call or referenced by argument
|
||||||
|
// and it does not escape during call.
|
||||||
|
GlobalEscape = 3 // An object escapes the method or thread.
|
||||||
} EscapeState;
|
} EscapeState;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@ -154,27 +157,24 @@ private:
|
|||||||
NodeType _type;
|
NodeType _type;
|
||||||
EscapeState _escape;
|
EscapeState _escape;
|
||||||
GrowableArray<uint>* _edges; // outgoing edges
|
GrowableArray<uint>* _edges; // outgoing edges
|
||||||
|
|
||||||
public:
|
|
||||||
Node* _node; // Ideal node corresponding to this PointsTo node.
|
Node* _node; // Ideal node corresponding to this PointsTo node.
|
||||||
int _offset; // Object fields offsets.
|
int _offset; // Object fields offsets.
|
||||||
bool _scalar_replaceable;// Not escaped object could be replaced with scalar
|
bool _scalar_replaceable; // Not escaped object could be replaced with scalar
|
||||||
bool _hidden_alias; // This node is an argument to a function.
|
|
||||||
// which may return it creating a hidden alias.
|
|
||||||
|
|
||||||
|
public:
|
||||||
PointsToNode():
|
PointsToNode():
|
||||||
_type(UnknownType),
|
_type(UnknownType),
|
||||||
_escape(UnknownEscape),
|
_escape(UnknownEscape),
|
||||||
_edges(NULL),
|
_edges(NULL),
|
||||||
_node(NULL),
|
_node(NULL),
|
||||||
_offset(-1),
|
_offset(-1),
|
||||||
_scalar_replaceable(true),
|
_scalar_replaceable(true) {}
|
||||||
_hidden_alias(false) {}
|
|
||||||
|
|
||||||
|
|
||||||
EscapeState escape_state() const { return _escape; }
|
EscapeState escape_state() const { return _escape; }
|
||||||
NodeType node_type() const { return _type;}
|
NodeType node_type() const { return _type;}
|
||||||
int offset() { return _offset;}
|
int offset() { return _offset;}
|
||||||
|
bool scalar_replaceable() { return _scalar_replaceable;}
|
||||||
|
|
||||||
void set_offset(int offs) { _offset = offs;}
|
void set_offset(int offs) { _offset = offs;}
|
||||||
void set_escape_state(EscapeState state) { _escape = state; }
|
void set_escape_state(EscapeState state) { _escape = state; }
|
||||||
@ -182,6 +182,7 @@ public:
|
|||||||
assert(_type == UnknownType || _type == ntype, "Can't change node type");
|
assert(_type == UnknownType || _type == ntype, "Can't change node type");
|
||||||
_type = ntype;
|
_type = ntype;
|
||||||
}
|
}
|
||||||
|
void set_scalar_replaceable(bool v) { _scalar_replaceable = v; }
|
||||||
|
|
||||||
// count of outgoing edges
|
// count of outgoing edges
|
||||||
uint edge_count() const { return (_edges == NULL) ? 0 : _edges->length(); }
|
uint edge_count() const { return (_edges == NULL) ? 0 : _edges->length(); }
|
||||||
@ -233,8 +234,8 @@ private:
|
|||||||
// that pointer values loaded from
|
// that pointer values loaded from
|
||||||
// a field which has not been set
|
// a field which has not been set
|
||||||
// are assumed to point to.
|
// are assumed to point to.
|
||||||
uint _oop_null; // ConP(#NULL)
|
uint _oop_null; // ConP(#NULL)->_idx
|
||||||
uint _noop_null; // ConN(#NULL)
|
uint _noop_null; // ConN(#NULL)->_idx
|
||||||
|
|
||||||
Compile * _compile; // Compile object for current compilation
|
Compile * _compile; // Compile object for current compilation
|
||||||
PhaseIterGVN * _igvn; // Value numbering
|
PhaseIterGVN * _igvn; // Value numbering
|
||||||
@ -339,8 +340,16 @@ private:
|
|||||||
// Set the escape state of a node
|
// Set the escape state of a node
|
||||||
void set_escape_state(uint ni, PointsToNode::EscapeState es);
|
void set_escape_state(uint ni, PointsToNode::EscapeState es);
|
||||||
|
|
||||||
|
// Find fields initializing values for allocations.
|
||||||
|
void find_init_values(Node* n, VectorSet* visited, PhaseTransform* phase);
|
||||||
|
|
||||||
// Adjust escape state after Connection Graph is built.
|
// Adjust escape state after Connection Graph is built.
|
||||||
void adjust_escape_state(int nidx, PhaseTransform* phase);
|
void adjust_escape_state(Node* n);
|
||||||
|
|
||||||
|
// Propagate escape states to referenced nodes.
|
||||||
|
bool propagate_escape_state(GrowableArray<int>* cg_worklist,
|
||||||
|
GrowableArray<uint>* worklist,
|
||||||
|
PointsToNode::EscapeState esc_state);
|
||||||
|
|
||||||
// Compute the escape information
|
// Compute the escape information
|
||||||
bool compute_escape();
|
bool compute_escape();
|
||||||
@ -357,21 +366,6 @@ public:
|
|||||||
// escape state of a node
|
// escape state of a node
|
||||||
PointsToNode::EscapeState escape_state(Node *n);
|
PointsToNode::EscapeState escape_state(Node *n);
|
||||||
|
|
||||||
// other information we have collected
|
|
||||||
bool is_scalar_replaceable(Node *n) {
|
|
||||||
if (_collecting || (n->_idx >= nodes_size()))
|
|
||||||
return false;
|
|
||||||
PointsToNode* ptn = ptnode_adr(n->_idx);
|
|
||||||
return ptn->escape_state() == PointsToNode::NoEscape && ptn->_scalar_replaceable;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool hidden_alias(Node *n) {
|
|
||||||
if (_collecting || (n->_idx >= nodes_size()))
|
|
||||||
return true;
|
|
||||||
PointsToNode* ptn = ptnode_adr(n->_idx);
|
|
||||||
return (ptn->escape_state() != PointsToNode::NoEscape) || ptn->_hidden_alias;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef PRODUCT
|
#ifndef PRODUCT
|
||||||
void dump();
|
void dump();
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user