8281548: Add escape analysis tracing flag

Reviewed-by: kvn, thartmann, xliu
This commit is contained in:
Jorn Vernee 2022-03-01 16:26:19 +00:00
parent b03d66c501
commit 8fec7b87c1
4 changed files with 141 additions and 45 deletions

View File

@ -66,6 +66,7 @@
cflags(PrintIntrinsics, bool, PrintIntrinsics, PrintIntrinsics) \ cflags(PrintIntrinsics, bool, PrintIntrinsics, PrintIntrinsics) \
NOT_PRODUCT(cflags(TraceOptoPipelining, bool, TraceOptoPipelining, TraceOptoPipelining)) \ NOT_PRODUCT(cflags(TraceOptoPipelining, bool, TraceOptoPipelining, TraceOptoPipelining)) \
NOT_PRODUCT(cflags(TraceOptoOutput, bool, TraceOptoOutput, TraceOptoOutput)) \ NOT_PRODUCT(cflags(TraceOptoOutput, bool, TraceOptoOutput, TraceOptoOutput)) \
NOT_PRODUCT(cflags(TraceEscapeAnalysis, bool, false, TraceEscapeAnalysis)) \
NOT_PRODUCT(cflags(PrintIdeal, bool, PrintIdeal, PrintIdeal)) \ NOT_PRODUCT(cflags(PrintIdeal, bool, PrintIdeal, PrintIdeal)) \
NOT_PRODUCT(cflags(PrintIdealPhase, ccstrlist, "", PrintIdealPhase)) \ NOT_PRODUCT(cflags(PrintIdealPhase, ccstrlist, "", PrintIdealPhase)) \
cflags(TraceSpilling, bool, TraceSpilling, TraceSpilling) \ cflags(TraceSpilling, bool, TraceSpilling, TraceSpilling) \

View File

@ -79,6 +79,7 @@ class methodHandle;
option(TraceOptoPipelining, "TraceOptoPipelining", Bool) \ option(TraceOptoPipelining, "TraceOptoPipelining", Bool) \
option(TraceOptoOutput, "TraceOptoOutput", Bool) \ option(TraceOptoOutput, "TraceOptoOutput", Bool) \
option(TraceSpilling, "TraceSpilling", Bool) \ option(TraceSpilling, "TraceSpilling", Bool) \
NOT_PRODUCT(option(TraceEscapeAnalysis, "TraceEscapeAnalysis", Bool)) \
NOT_PRODUCT(option(PrintIdeal, "PrintIdeal", Bool)) \ NOT_PRODUCT(option(PrintIdeal, "PrintIdeal", Bool)) \
NOT_PRODUCT(option(PrintIdealPhase, "PrintIdealPhase", Ccstrlist)) \ NOT_PRODUCT(option(PrintIdealPhase, "PrintIdealPhase", Ccstrlist)) \
NOT_PRODUCT(option(IGVPrintLevel, "IGVPrintLevel", Intx)) \ NOT_PRODUCT(option(IGVPrintLevel, "IGVPrintLevel", Intx)) \

View File

@ -217,6 +217,20 @@ bool ConnectionGraph::compute_escape() {
sfn_worklist.append(n->as_SafePoint()); sfn_worklist.append(n->as_SafePoint());
} }
} }
#ifndef PRODUCT
if (_compile->directive()->TraceEscapeAnalysisOption) {
tty->print("+++++ Initial worklist for ");
_compile->method()->print_name();
tty->print_cr(" (ea_inv=%d)", _invocation);
for (int i = 0; i < ptnodes_worklist.length(); i++) {
PointsToNode* ptn = ptnodes_worklist.at(i);
ptn->dump();
}
tty->print_cr("+++++ Calculating escape states and scalar replaceability");
}
#endif
if (non_escaped_allocs_worklist.length() == 0) { if (non_escaped_allocs_worklist.length() == 0) {
_collecting = false; _collecting = false;
return false; // Nothing to do. return false; // Nothing to do.
@ -865,7 +879,7 @@ bool ConnectionGraph::add_final_edges_unsafe_access(Node* n, uint opcode) {
Node* val = n->in(MemNode::ValueIn); Node* val = n->in(MemNode::ValueIn);
PointsToNode* ptn = ptnode_adr(val->_idx); PointsToNode* ptn = ptnode_adr(val->_idx);
assert(ptn != NULL, "node should be registered"); assert(ptn != NULL, "node should be registered");
set_escape_state(ptn, PointsToNode::GlobalEscape); set_escape_state(ptn, PointsToNode::GlobalEscape NOT_PRODUCT(COMMA "stored at raw address"));
// Add edge to object for unsafe access with offset. // Add edge to object for unsafe access with offset.
PointsToNode* adr_ptn = ptnode_adr(adr->_idx); PointsToNode* adr_ptn = ptnode_adr(adr->_idx);
assert(adr_ptn != NULL, "node should be registered"); assert(adr_ptn != NULL, "node should be registered");
@ -892,14 +906,20 @@ void ConnectionGraph::add_call_node(CallNode* call) {
ciKlass* cik = kt->klass(); ciKlass* cik = kt->klass();
PointsToNode::EscapeState es = PointsToNode::NoEscape; PointsToNode::EscapeState es = PointsToNode::NoEscape;
bool scalar_replaceable = true; bool scalar_replaceable = true;
NOT_PRODUCT(const char* nsr_reason = "");
if (call->is_AllocateArray()) { if (call->is_AllocateArray()) {
if (!cik->is_array_klass()) { // StressReflectiveCode if (!cik->is_array_klass()) { // StressReflectiveCode
es = PointsToNode::GlobalEscape; es = PointsToNode::GlobalEscape;
} else { } else {
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) {
// Not scalar replaceable if the length is not constant or too big. // Not scalar replaceable if the length is not constant.
scalar_replaceable = false; scalar_replaceable = false;
NOT_PRODUCT(nsr_reason = "has a non-constant length");
} else if (length > EliminateAllocationArraySizeLimit) {
// Not scalar replaceable if the length is too big.
scalar_replaceable = false;
NOT_PRODUCT(nsr_reason = "has a length that is too big");
} }
} }
} else { // Allocate instance } else { // Allocate instance
@ -914,13 +934,14 @@ void ConnectionGraph::add_call_node(CallNode* call) {
if (nfields > EliminateAllocationFieldsLimit) { if (nfields > EliminateAllocationFieldsLimit) {
// Not scalar replaceable if there are too many fields. // Not scalar replaceable if there are too many fields.
scalar_replaceable = false; scalar_replaceable = false;
NOT_PRODUCT(nsr_reason = "has too many fields");
} }
} }
} }
add_java_object(call, es); add_java_object(call, es);
PointsToNode* ptn = ptnode_adr(call_idx); PointsToNode* ptn = ptnode_adr(call_idx);
if (!scalar_replaceable && ptn->scalar_replaceable()) { if (!scalar_replaceable && ptn->scalar_replaceable()) {
ptn->set_scalar_replaceable(false); set_not_scalar_replaceable(ptn NOT_PRODUCT(COMMA nsr_reason));
} }
} else if (call->is_CallStaticJava()) { } else if (call->is_CallStaticJava()) {
// Call nodes could be different types: // Call nodes could be different types:
@ -951,7 +972,7 @@ void ConnectionGraph::add_call_node(CallNode* call) {
assert(strncmp(name, "_multianewarray", 15) == 0, "TODO: add failed case check"); assert(strncmp(name, "_multianewarray", 15) == 0, "TODO: add failed case check");
// Returns a newly allocated non-escaped object. // Returns a newly allocated non-escaped object.
add_java_object(call, PointsToNode::NoEscape); add_java_object(call, PointsToNode::NoEscape);
ptnode_adr(call_idx)->set_scalar_replaceable(false); set_not_scalar_replaceable(ptnode_adr(call_idx) NOT_PRODUCT(COMMA "is result of multinewarray"));
} else if (meth->is_boxing_method()) { } else if (meth->is_boxing_method()) {
// Returns boxing object // Returns boxing object
PointsToNode::EscapeState es; PointsToNode::EscapeState es;
@ -973,7 +994,7 @@ void ConnectionGraph::add_call_node(CallNode* call) {
// 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.
add_java_object(call, PointsToNode::NoEscape); add_java_object(call, PointsToNode::NoEscape);
ptnode_adr(call_idx)->set_scalar_replaceable(false); set_not_scalar_replaceable(ptnode_adr(call_idx) NOT_PRODUCT(COMMA "is result of call"));
} else { } else {
// Determine whether any arguments are returned. // Determine whether any arguments are returned.
const TypeTuple* d = call->tf()->domain(); const TypeTuple* d = call->tf()->domain();
@ -1128,7 +1149,7 @@ void ConnectionGraph::process_call_arguments(CallNode *call) {
es = PointsToNode::NoEscape; es = PointsToNode::NoEscape;
} }
} }
set_escape_state(arg_ptn, es); set_escape_state(arg_ptn, es NOT_PRODUCT(COMMA trace_arg_escape_message(call)));
if (arg_is_arraycopy_dest) { if (arg_is_arraycopy_dest) {
Node* src = call->in(TypeFunc::Parms); Node* src = call->in(TypeFunc::Parms);
if (src->is_AddP()) { if (src->is_AddP()) {
@ -1183,12 +1204,12 @@ void ConnectionGraph::process_call_arguments(CallNode *call) {
arg_ptn->escape_state() < PointsToNode::GlobalEscape) { arg_ptn->escape_state() < PointsToNode::GlobalEscape) {
if (!call_analyzer->is_arg_stack(k)) { if (!call_analyzer->is_arg_stack(k)) {
// The argument global escapes // The argument global escapes
set_escape_state(arg_ptn, PointsToNode::GlobalEscape); set_escape_state(arg_ptn, PointsToNode::GlobalEscape NOT_PRODUCT(COMMA trace_arg_escape_message(call)));
} else { } else {
set_escape_state(arg_ptn, PointsToNode::ArgEscape); set_escape_state(arg_ptn, PointsToNode::ArgEscape NOT_PRODUCT(COMMA trace_arg_escape_message(call)));
if (!call_analyzer->is_arg_local(k)) { if (!call_analyzer->is_arg_local(k)) {
// The argument itself doesn't escape, but any fields might // The argument itself doesn't escape, but any fields might
set_fields_escape_state(arg_ptn, PointsToNode::GlobalEscape); set_fields_escape_state(arg_ptn, PointsToNode::GlobalEscape NOT_PRODUCT(COMMA trace_arg_escape_message(call)));
} }
} }
} }
@ -1217,7 +1238,7 @@ void ConnectionGraph::process_call_arguments(CallNode *call) {
arg = get_addp_base(arg); arg = get_addp_base(arg);
} }
assert(ptnode_adr(arg->_idx) != NULL, "should be defined already"); assert(ptnode_adr(arg->_idx) != NULL, "should be defined already");
set_escape_state(ptnode_adr(arg->_idx), PointsToNode::GlobalEscape); set_escape_state(ptnode_adr(arg->_idx), PointsToNode::GlobalEscape NOT_PRODUCT(COMMA trace_arg_escape_message(call)));
} }
} }
} }
@ -1401,30 +1422,30 @@ bool ConnectionGraph::find_non_escaped_objects(GrowableArray<PointsToNode*>& ptn
assert(ptn->arraycopy_dst(), "sanity"); assert(ptn->arraycopy_dst(), "sanity");
// Propagate only fields escape state through arraycopy edge. // Propagate only fields escape state through arraycopy edge.
if (e->fields_escape_state() < field_es) { if (e->fields_escape_state() < field_es) {
set_fields_escape_state(e, field_es); set_fields_escape_state(e, field_es NOT_PRODUCT(COMMA trace_propagate_message(ptn)));
escape_worklist.push(e); escape_worklist.push(e);
} }
} else if (es >= field_es) { } else if (es >= field_es) {
// fields_escape_state is also set to 'es' if it is less than 'es'. // fields_escape_state is also set to 'es' if it is less than 'es'.
if (e->escape_state() < es) { if (e->escape_state() < es) {
set_escape_state(e, es); set_escape_state(e, es NOT_PRODUCT(COMMA trace_propagate_message(ptn)));
escape_worklist.push(e); escape_worklist.push(e);
} }
} else { } else {
// Propagate field escape state. // Propagate field escape state.
bool es_changed = false; bool es_changed = false;
if (e->fields_escape_state() < field_es) { if (e->fields_escape_state() < field_es) {
set_fields_escape_state(e, field_es); set_fields_escape_state(e, field_es NOT_PRODUCT(COMMA trace_propagate_message(ptn)));
es_changed = true; es_changed = true;
} }
if ((e->escape_state() < field_es) && if ((e->escape_state() < field_es) &&
e->is_Field() && ptn->is_JavaObject() && e->is_Field() && ptn->is_JavaObject() &&
e->as_Field()->is_oop()) { e->as_Field()->is_oop()) {
// Change escape state of referenced fields. // Change escape state of referenced fields.
set_escape_state(e, field_es); set_escape_state(e, field_es NOT_PRODUCT(COMMA trace_propagate_message(ptn)));
es_changed = true; es_changed = true;
} else if (e->escape_state() < es) { } else if (e->escape_state() < es) {
set_escape_state(e, es); set_escape_state(e, es NOT_PRODUCT(COMMA trace_propagate_message(ptn)));
es_changed = true; es_changed = true;
} }
if (es_changed) { if (es_changed) {
@ -1790,7 +1811,7 @@ void ConnectionGraph::adjust_scalar_replaceable_state(JavaObjectNode* jobj) {
// 1. An object is not scalar replaceable if the field into which it is // 1. An object is not scalar replaceable if the field into which it is
// stored has unknown offset (stored into unknown element of an array). // stored has unknown offset (stored into unknown element of an array).
if (field->offset() == Type::OffsetBot) { if (field->offset() == Type::OffsetBot) {
jobj->set_scalar_replaceable(false); set_not_scalar_replaceable(jobj NOT_PRODUCT(COMMA "is stored at unknown offset"));
return; return;
} }
// 2. An object is not scalar replaceable if the field into which it is // 2. An object is not scalar replaceable if the field into which it is
@ -1799,7 +1820,7 @@ void ConnectionGraph::adjust_scalar_replaceable_state(JavaObjectNode* jobj) {
for (BaseIterator i(field); i.has_next(); i.next()) { for (BaseIterator i(field); i.has_next(); i.next()) {
PointsToNode* base = i.get(); PointsToNode* base = i.get();
if (base == null_obj) { if (base == null_obj) {
jobj->set_scalar_replaceable(false); set_not_scalar_replaceable(jobj NOT_PRODUCT(COMMA "is stored into field with potentially null base"));
return; return;
} }
} }
@ -1811,8 +1832,8 @@ void ConnectionGraph::adjust_scalar_replaceable_state(JavaObjectNode* jobj) {
PointsToNode* ptn = j.get(); PointsToNode* ptn = j.get();
if (ptn->is_JavaObject() && ptn != jobj) { if (ptn->is_JavaObject() && ptn != jobj) {
// Mark all objects. // Mark all objects.
jobj->set_scalar_replaceable(false); set_not_scalar_replaceable(jobj NOT_PRODUCT(COMMA trace_merged_message(ptn)));
ptn->set_scalar_replaceable(false); set_not_scalar_replaceable(ptn NOT_PRODUCT(COMMA trace_merged_message(jobj)));
} }
} }
if (!jobj->scalar_replaceable()) { if (!jobj->scalar_replaceable()) {
@ -1832,7 +1853,7 @@ void ConnectionGraph::adjust_scalar_replaceable_state(JavaObjectNode* jobj) {
// 4. An object is not scalar replaceable if it has a field with unknown // 4. An object is not scalar replaceable if it has a field with unknown
// offset (array's element is accessed in loop). // offset (array's element is accessed in loop).
if (offset == Type::OffsetBot) { if (offset == Type::OffsetBot) {
jobj->set_scalar_replaceable(false); set_not_scalar_replaceable(jobj NOT_PRODUCT(COMMA "has field with unknown offset"));
return; return;
} }
// 5. Currently an object is not scalar replaceable if a LoadStore node // 5. Currently an object is not scalar replaceable if a LoadStore node
@ -1847,14 +1868,14 @@ void ConnectionGraph::adjust_scalar_replaceable_state(JavaObjectNode* jobj) {
n->in(AddPNode::Address)->Opcode() == Op_CheckCastPP) { n->in(AddPNode::Address)->Opcode() == Op_CheckCastPP) {
assert(n->in(AddPNode::Address)->bottom_type()->isa_rawptr(), "raw address so raw cast expected"); assert(n->in(AddPNode::Address)->bottom_type()->isa_rawptr(), "raw address so raw cast expected");
assert(_igvn->type(n->in(AddPNode::Address)->in(1))->isa_oopptr(), "cast pattern at unsafe access expected"); assert(_igvn->type(n->in(AddPNode::Address)->in(1))->isa_oopptr(), "cast pattern at unsafe access expected");
jobj->set_scalar_replaceable(false); set_not_scalar_replaceable(jobj NOT_PRODUCT(COMMA "is used as base of mixed unsafe access"));
return; return;
} }
for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) {
Node* u = n->fast_out(i); Node* u = n->fast_out(i);
if (u->is_LoadStore() || (u->is_Mem() && u->as_Mem()->is_mismatched_access())) { if (u->is_LoadStore() || (u->is_Mem() && u->as_Mem()->is_mismatched_access())) {
jobj->set_scalar_replaceable(false); set_not_scalar_replaceable(jobj NOT_PRODUCT(COMMA "is used in LoadStore or mismatched access"));
return; return;
} }
} }
@ -1883,8 +1904,8 @@ void ConnectionGraph::adjust_scalar_replaceable_state(JavaObjectNode* jobj) {
// this field's base by now. // this field's base by now.
if (base->is_JavaObject() && base != jobj) { if (base->is_JavaObject() && base != jobj) {
// Mark all bases. // Mark all bases.
jobj->set_scalar_replaceable(false); set_not_scalar_replaceable(jobj NOT_PRODUCT(COMMA "may point to more than one object"));
base->set_scalar_replaceable(false); set_not_scalar_replaceable(base NOT_PRODUCT(COMMA "may point to more than one object"));
} }
} }
} }
@ -3149,7 +3170,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(ptnode_adr(n->_idx), es); // CheckCastPP escape state set_escape_state(ptnode_adr(n->_idx), es NOT_PRODUCT(COMMA trace_propagate_message(ptn))); // CheckCastPP escape state
// in order for an object to be scalar-replaceable, it must be: // 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
@ -3624,38 +3645,42 @@ static const char *esc_names[] = {
"GlobalEscape" "GlobalEscape"
}; };
void PointsToNode::dump(bool print_state) const { void PointsToNode::dump_header(bool print_state, outputStream* out) const {
NodeType nt = node_type(); NodeType nt = node_type();
tty->print("%s ", node_type_names[(int) nt]); out->print("%s(%d) ", node_type_names[(int) nt], _pidx);
if (print_state) { if (print_state) {
EscapeState es = escape_state(); EscapeState es = escape_state();
EscapeState fields_es = fields_escape_state(); EscapeState fields_es = fields_escape_state();
tty->print("%s(%s) ", esc_names[(int)es], esc_names[(int)fields_es]); out->print("%s(%s) ", esc_names[(int)es], esc_names[(int)fields_es]);
if (nt == PointsToNode::JavaObject && !this->scalar_replaceable()) { if (nt == PointsToNode::JavaObject && !this->scalar_replaceable()) {
tty->print("NSR "); out->print("NSR ");
} }
} }
}
void PointsToNode::dump(bool print_state, outputStream* out, bool newline) const {
dump_header(print_state, out);
if (is_Field()) { if (is_Field()) {
FieldNode* f = (FieldNode*)this; FieldNode* f = (FieldNode*)this;
if (f->is_oop()) { if (f->is_oop()) {
tty->print("oop "); out->print("oop ");
} }
if (f->offset() > 0) { if (f->offset() > 0) {
tty->print("+%d ", f->offset()); out->print("+%d ", f->offset());
} }
tty->print("("); out->print("(");
for (BaseIterator i(f); i.has_next(); i.next()) { for (BaseIterator i(f); i.has_next(); i.next()) {
PointsToNode* b = i.get(); PointsToNode* b = i.get();
tty->print(" %d%s", b->idx(),(b->is_JavaObject() ? "P" : "")); out->print(" %d%s", b->idx(),(b->is_JavaObject() ? "P" : ""));
} }
tty->print(" )"); out->print(" )");
} }
tty->print("["); out->print("[");
for (EdgeIterator i(this); i.has_next(); i.next()) { for (EdgeIterator i(this); i.has_next(); i.next()) {
PointsToNode* e = i.get(); PointsToNode* e = i.get();
tty->print(" %d%s%s", e->idx(),(e->is_JavaObject() ? "P" : (e->is_Field() ? "F" : "")), e->is_Arraycopy() ? "cp" : ""); out->print(" %d%s%s", e->idx(),(e->is_JavaObject() ? "P" : (e->is_Field() ? "F" : "")), e->is_Arraycopy() ? "cp" : "");
} }
tty->print(" ["); out->print(" [");
for (UseIterator i(this); i.has_next(); i.next()) { for (UseIterator i(this); i.has_next(); i.next()) {
PointsToNode* u = i.get(); PointsToNode* u = i.get();
bool is_base = false; bool is_base = false;
@ -3663,13 +3688,13 @@ void PointsToNode::dump(bool print_state) const {
is_base = true; is_base = true;
u = PointsToNode::get_use_node(u)->as_Field(); u = PointsToNode::get_use_node(u)->as_Field();
} }
tty->print(" %d%s%s", u->idx(), is_base ? "b" : "", u->is_Arraycopy() ? "cp" : ""); out->print(" %d%s%s", u->idx(), is_base ? "b" : "", u->is_Arraycopy() ? "cp" : "");
} }
tty->print(" ]] "); out->print(" ]] ");
if (_node == NULL) { if (_node == NULL) {
tty->print_cr("<null>"); out->print("<null>%s", newline ? "\n" : "");
} else { } else {
_node->dump(); _node->dump(newline ? "\n" : "", false, out);
} }
} }
@ -3712,6 +3737,51 @@ void ConnectionGraph::dump(GrowableArray<PointsToNode*>& ptnodes_worklist) {
} }
} }
} }
void ConnectionGraph::trace_es_update_helper(PointsToNode* ptn, PointsToNode::EscapeState es, bool fields, const char* reason) const {
if (_compile->directive()->TraceEscapeAnalysisOption) {
assert(ptn != nullptr, "should not be null");
assert(reason != nullptr, "should not be null");
ptn->dump_header(true);
PointsToNode::EscapeState new_es = fields ? ptn->escape_state() : es;
PointsToNode::EscapeState new_fields_es = fields ? es : ptn->fields_escape_state();
tty->print_cr("-> %s(%s) %s", esc_names[(int)new_es], esc_names[(int)new_fields_es], reason);
}
}
const char* ConnectionGraph::trace_propagate_message(PointsToNode* from) const {
if (_compile->directive()->TraceEscapeAnalysisOption) {
stringStream ss;
ss.print("propagated from: ");
from->dump(true, &ss, false);
return ss.as_string();
} else {
return nullptr;
}
}
const char* ConnectionGraph::trace_arg_escape_message(CallNode* call) const {
if (_compile->directive()->TraceEscapeAnalysisOption) {
stringStream ss;
ss.print("escapes as arg to:");
call->dump("", false, &ss);
return ss.as_string();
} else {
return nullptr;
}
}
const char* ConnectionGraph::trace_merged_message(PointsToNode* other) const {
if (_compile->directive()->TraceEscapeAnalysisOption) {
stringStream ss;
ss.print("is merged with other object: ");
other->dump_header(true, &ss);
return ss.as_string();
} else {
return nullptr;
}
}
#endif #endif
void ConnectionGraph::record_for_optimizer(Node *n) { void ConnectionGraph::record_for_optimizer(Node *n) {

View File

@ -232,7 +232,8 @@ public:
#ifndef PRODUCT #ifndef PRODUCT
NodeType node_type() const { return (NodeType)_type;} NodeType node_type() const { return (NodeType)_type;}
void dump(bool print_state=true) const; void dump(bool print_state=true, outputStream* out=tty, bool newline=true) const;
void dump_header(bool print_state=true, outputStream* out=tty) const;
#endif #endif
}; };
@ -429,21 +430,26 @@ private:
int find_init_values_phantom(JavaObjectNode* ptn); int find_init_values_phantom(JavaObjectNode* ptn);
// Set the escape state of an object and its fields. // Set the escape state of an object and its fields.
void set_escape_state(PointsToNode* ptn, PointsToNode::EscapeState esc) { void set_escape_state(PointsToNode* ptn, PointsToNode::EscapeState esc
NOT_PRODUCT(COMMA const char* reason)) {
// Don't change non-escaping state of NULL pointer. // Don't change non-escaping state of NULL pointer.
if (ptn != null_obj) { if (ptn != null_obj) {
if (ptn->escape_state() < esc) { if (ptn->escape_state() < esc) {
NOT_PRODUCT(trace_es_update_helper(ptn, esc, false, reason));
ptn->set_escape_state(esc); ptn->set_escape_state(esc);
} }
if (ptn->fields_escape_state() < esc) { if (ptn->fields_escape_state() < esc) {
NOT_PRODUCT(trace_es_update_helper(ptn, esc, true, reason));
ptn->set_fields_escape_state(esc); ptn->set_fields_escape_state(esc);
} }
} }
} }
void set_fields_escape_state(PointsToNode* ptn, PointsToNode::EscapeState esc) { void set_fields_escape_state(PointsToNode* ptn, PointsToNode::EscapeState esc
NOT_PRODUCT(COMMA const char* reason)) {
// Don't change non-escaping state of NULL pointer. // Don't change non-escaping state of NULL pointer.
if (ptn != null_obj) { if (ptn != null_obj) {
if (ptn->fields_escape_state() < esc) { if (ptn->fields_escape_state() < esc) {
NOT_PRODUCT(trace_es_update_helper(ptn, esc, true, reason));
ptn->set_fields_escape_state(esc); ptn->set_fields_escape_state(esc);
} }
} }
@ -569,6 +575,24 @@ private:
// Compute the escape information // Compute the escape information
bool compute_escape(); bool compute_escape();
void set_not_scalar_replaceable(PointsToNode* ptn NOT_PRODUCT(COMMA const char* reason)) const {
#ifndef PRODUCT
if (_compile->directive()->TraceEscapeAnalysisOption) {
assert(ptn != nullptr, "should not be null");
ptn->dump_header(true);
tty->print_cr("is NSR. %s", reason);
}
#endif
ptn->set_scalar_replaceable(false);
}
#ifndef PRODUCT
void trace_es_update_helper(PointsToNode* ptn, PointsToNode::EscapeState es, bool fields, const char* reason) const;
const char* trace_propagate_message(PointsToNode* from) const;
const char* trace_arg_escape_message(CallNode* call) const;
const char* trace_merged_message(PointsToNode* other) const;
#endif
public: public:
ConnectionGraph(Compile *C, PhaseIterGVN *igvn, int iteration); ConnectionGraph(Compile *C, PhaseIterGVN *igvn, int iteration);