7092905: C2: Keep track of the number of dead nodes

Keep an (almost) accurate running count of the reachable (live) flow graph nodes.

Reviewed-by: kvn, twisti, jrose, vlivanov
This commit is contained in:
Bharadwaj Yadavalli 2012-11-27 17:24:15 -08:00
parent 96562be9e5
commit 2d2532e740
28 changed files with 306 additions and 120 deletions

View file

@ -13,6 +13,6 @@ Adding the -i option with also report inlining like PrintInlining.
More information about the LogCompilation output can be found at
http://wikis.sun.com/display/HotSpotInternals/LogCompilation+overview
http://wikis.sun.com/display/HotSpotInternals/PrintCompilation
http://wikis.sun.com/display/HotSpotInternals/LogCompilation+tool
https://wikis.oracle.com/display/HotSpotInternals/LogCompilation+overview
https://wikis.oracle.com/display/HotSpotInternals/PrintCompilation
https://wikis.oracle.com/display/HotSpotInternals/LogCompilation+tool

View file

@ -38,6 +38,7 @@ public class CallSite {
private String reason;
private List<CallSite> calls;
private int endNodes;
private int endLiveNodes;
private double timeStamp;
CallSite() {
@ -106,7 +107,7 @@ public class CallSite {
}
}
if (getEndNodes() > 0) {
stream.printf(" (end time: %6.4f nodes: %d)", getTimeStamp(), getEndNodes());
stream.printf(" (end time: %6.4f nodes: %d live: %d)", getTimeStamp(), getEndNodes(), getEndLiveNodes());
}
stream.println("");
if (getReceiver() != null) {
@ -195,6 +196,14 @@ public class CallSite {
return endNodes;
}
void setEndLiveNodes(int n) {
endLiveNodes = n;
}
public int getEndLiveNodes() {
return endLiveNodes;
}
void setTimeStamp(double time) {
timeStamp = time;
}

View file

@ -43,7 +43,7 @@ public class LogCompilation extends DefaultHandler implements ErrorHandler, Cons
System.out.println(" -S: print compilation statistics");
System.out.println(" -s: sort events by start time");
System.out.println(" -e: sort events by elapsed time");
System.out.println(" -N: sort events by name and start");
System.out.println(" -n: sort events by name and start");
System.exit(exitcode);
}
@ -137,7 +137,11 @@ public class LogCompilation extends DefaultHandler implements ErrorHandler, Cons
v2 = Integer.valueOf(0);
}
phaseNodes.put(phase.getName(), Integer.valueOf(v2.intValue() + phase.getNodes()));
out.printf("\t%s %6.4f %d %d\n", phase.getName(), phase.getElapsedTime(), phase.getStartNodes(), phase.getNodes());
/* Print phase name, elapsed time, nodes at the start of the phase,
nodes created in the phase, live nodes at the start of the phase,
live nodes added in the phase.
*/
out.printf("\t%s %6.4f %d %d %d %d\n", phase.getName(), phase.getElapsedTime(), phase.getStartNodes(), phase.getNodes(), phase.getStartLiveNodes(), phase.getLiveNodes());
}
} else if (e instanceof MakeNotEntrantEvent) {
MakeNotEntrantEvent mne = (MakeNotEntrantEvent) e;

View file

@ -268,12 +268,18 @@ public class LogParser extends DefaultHandler implements ErrorHandler, Constants
if (qname.equals("phase")) {
Phase p = new Phase(search(atts, "name"),
Double.parseDouble(search(atts, "stamp")),
Integer.parseInt(search(atts, "nodes")));
Integer.parseInt(search(atts, "nodes")),
Integer.parseInt(search(atts, "live")));
phaseStack.push(p);
} else if (qname.equals("phase_done")) {
Phase p = phaseStack.pop();
p.setEndNodes(Integer.parseInt(search(atts, "nodes")));
if (! p.getId().equals(search(atts, "name"))) {
System.out.println("phase: " + p.getId());
throw new InternalError("phase name mismatch");
}
p.setEnd(Double.parseDouble(search(atts, "stamp")));
p.setEndNodes(Integer.parseInt(search(atts, "nodes")));
p.setEndLiveNodes(Integer.parseInt(search(atts, "live")));
compile.getPhases().add(p);
} else if (qname.equals("task")) {
compile = new Compilation(Integer.parseInt(search(atts, "compile_id", "-1")));
@ -406,6 +412,7 @@ public class LogParser extends DefaultHandler implements ErrorHandler, Constants
} else if (qname.equals("parse_done")) {
CallSite call = scopes.pop();
call.setEndNodes(Integer.parseInt(search(atts, "nodes", "1")));
call.setEndLiveNodes(Integer.parseInt(search(atts, "live", "1")));
call.setTimeStamp(Double.parseDouble(search(atts, "stamp")));
scopes.push(call);
}

View file

@ -30,10 +30,13 @@ public class Phase extends BasicLogEvent {
private final int startNodes;
private int endNodes;
private final int startLiveNodes;
private int endLiveNodes;
Phase(String n, double s, int nodes) {
Phase(String n, double s, int nodes, int live) {
super(s, n);
startNodes = nodes;
startLiveNodes = live;
}
int getNodes() {
@ -55,6 +58,22 @@ public class Phase extends BasicLogEvent {
public int getEndNodes() {
return endNodes;
}
/* Number of live nodes added by the phase */
int getLiveNodes() {
return getEndLiveNodes() - getStartLiveNodes();
}
void setEndLiveNodes(int n) {
endLiveNodes = n;
}
public int getStartLiveNodes() {
return startLiveNodes;
}
public int getEndLiveNodes() {
return endLiveNodes;
}
@Override
public void print(PrintStream stream) {

View file

@ -292,7 +292,7 @@ class Block : public CFGElement {
void needed_for_next_call(Node *this_call, VectorSet &next_call, Block_Array &bbs);
bool schedule_local(PhaseCFG *cfg, Matcher &m, GrowableArray<int> &ready_cnt, VectorSet &next_call);
// Cleanup if any code lands between a Call and his Catch
void call_catch_cleanup(Block_Array &bbs);
void call_catch_cleanup(Block_Array &bbs, Compile *C);
// Detect implicit-null-check opportunities. Basically, find NULL checks
// with suitable memory ops nearby. Use the memory op to do the NULL check.
// I can generate a memory op if there is not one nearby.

View file

@ -115,6 +115,12 @@
notproduct(bool, VerifyOpto, false, \
"Apply more time consuming verification during compilation") \
\
notproduct(bool, VerifyIdealNodeCount, false, \
"Verify that tracked dead ideal node count is accurate") \
\
notproduct(bool, PrintIdealNodeCount, false, \
"Print liveness counts of ideal nodes") \
\
notproduct(bool, VerifyOptoOopOffsets, false, \
"Check types of base addresses in field references") \
\

View file

@ -1495,7 +1495,7 @@ void PhaseChaitin::fixup_spills() {
cisc->ins_req(1,src); // Requires a memory edge
}
b->_nodes.map(j,cisc); // Insert into basic block
n->subsume_by(cisc); // Correct graph
n->subsume_by(cisc, C); // Correct graph
//
++_used_cisc_instructions;
#ifndef PRODUCT

View file

@ -316,7 +316,12 @@ void Compile::gvn_replace_by(Node* n, Node* nn) {
}
static inline bool not_a_node(const Node* n) {
if (n == NULL) return true;
if (((intptr_t)n & 1) != 0) return true; // uninitialized, etc.
if (*(address*)n == badAddress) return true; // kill by Node::destruct
return false;
}
// Identify all nodes that are reachable from below, useful.
// Use breadth-first pass that records state in a Unique_Node_List,
@ -337,12 +342,27 @@ void Compile::identify_useful_nodes(Unique_Node_List &useful) {
uint max = n->len();
for( uint i = 0; i < max; ++i ) {
Node *m = n->in(i);
if( m == NULL ) continue;
if (not_a_node(m)) continue;
useful.push(m);
}
}
}
// Update dead_node_list with any missing dead nodes using useful
// list. Consider all non-useful nodes to be useless i.e., dead nodes.
void Compile::update_dead_node_list(Unique_Node_List &useful) {
uint max_idx = unique();
VectorSet& useful_node_set = useful.member_set();
for (uint node_idx = 0; node_idx < max_idx; node_idx++) {
// If node with index node_idx is not in useful set,
// mark it as dead in dead node list.
if (! useful_node_set.test(node_idx) ) {
record_dead_node(node_idx);
}
}
}
// Disconnect all useless nodes by disconnecting those at the boundary.
void Compile::remove_useless_nodes(Unique_Node_List &useful) {
uint next = 0;
@ -582,6 +602,8 @@ Compile::Compile( ciEnv* ci_env, C2Compiler* compiler, ciMethod* target, int osr
_inner_loops(0),
_scratch_const_size(-1),
_in_scratch_emit_size(false),
_dead_node_list(comp_arena()),
_dead_node_count(0),
#ifndef PRODUCT
_trace_opto_output(TraceOptoOutput || method()->has_option("TraceOptoOutput")),
_printer(IdealGraphPrinter::printer()),
@ -873,6 +895,8 @@ Compile::Compile( ciEnv* ci_env,
_trace_opto_output(TraceOptoOutput),
_printer(NULL),
#endif
_dead_node_list(comp_arena()),
_dead_node_count(0),
_congraph(NULL) {
C = this;
@ -1069,6 +1093,72 @@ void Compile::set_cached_top_node(Node* tn) {
assert(_top == NULL || top()->is_top(), "");
}
#ifdef ASSERT
uint Compile::count_live_nodes_by_graph_walk() {
Unique_Node_List useful(comp_arena());
// Get useful node list by walking the graph.
identify_useful_nodes(useful);
return useful.size();
}
void Compile::print_missing_nodes() {
// Return if CompileLog is NULL and PrintIdealNodeCount is false.
if ((_log == NULL) && (! PrintIdealNodeCount)) {
return;
}
// This is an expensive function. It is executed only when the user
// specifies VerifyIdealNodeCount option or otherwise knows the
// additional work that needs to be done to identify reachable nodes
// by walking the flow graph and find the missing ones using
// _dead_node_list.
Unique_Node_List useful(comp_arena());
// Get useful node list by walking the graph.
identify_useful_nodes(useful);
uint l_nodes = C->live_nodes();
uint l_nodes_by_walk = useful.size();
if (l_nodes != l_nodes_by_walk) {
if (_log != NULL) {
_log->begin_head("mismatched_nodes count='%d'", abs((int) (l_nodes - l_nodes_by_walk)));
_log->stamp();
_log->end_head();
}
VectorSet& useful_member_set = useful.member_set();
int last_idx = l_nodes_by_walk;
for (int i = 0; i < last_idx; i++) {
if (useful_member_set.test(i)) {
if (_dead_node_list.test(i)) {
if (_log != NULL) {
_log->elem("mismatched_node_info node_idx='%d' type='both live and dead'", i);
}
if (PrintIdealNodeCount) {
// Print the log message to tty
tty->print_cr("mismatched_node idx='%d' both live and dead'", i);
useful.at(i)->dump();
}
}
}
else if (! _dead_node_list.test(i)) {
if (_log != NULL) {
_log->elem("mismatched_node_info node_idx='%d' type='neither live nor dead'", i);
}
if (PrintIdealNodeCount) {
// Print the log message to tty
tty->print_cr("mismatched_node idx='%d' type='neither live nor dead'", i);
}
}
}
if (_log != NULL) {
_log->tail("mismatched_nodes");
}
}
}
#endif
#ifndef PRODUCT
void Compile::verify_top(Node* tn) const {
if (tn != NULL) {
@ -2087,7 +2177,7 @@ static bool oop_offset_is_sane(const TypeInstPtr* tp) {
// Eliminate trivially redundant StoreCMs and accumulate their
// precedence edges.
static void eliminate_redundant_card_marks(Node* n) {
void Compile::eliminate_redundant_card_marks(Node* n) {
assert(n->Opcode() == Op_StoreCM, "expected StoreCM");
if (n->in(MemNode::Address)->outcnt() > 1) {
// There are multiple users of the same address so it might be
@ -2122,7 +2212,7 @@ static void eliminate_redundant_card_marks(Node* n) {
// Eliminate the previous StoreCM
prev->set_req(MemNode::Memory, mem->in(MemNode::Memory));
assert(mem->outcnt() == 0, "should be dead");
mem->disconnect_inputs(NULL);
mem->disconnect_inputs(NULL, this);
} else {
prev = mem;
}
@ -2133,7 +2223,7 @@ static void eliminate_redundant_card_marks(Node* n) {
//------------------------------final_graph_reshaping_impl----------------------
// Implement items 1-5 from final_graph_reshaping below.
static void final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc ) {
void Compile::final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc) {
if ( n->outcnt() == 0 ) return; // dead node
uint nop = n->Opcode();
@ -2163,8 +2253,7 @@ static void final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc ) {
#ifdef ASSERT
if( n->is_Mem() ) {
Compile* C = Compile::current();
int alias_idx = C->get_alias_index(n->as_Mem()->adr_type());
int alias_idx = get_alias_index(n->as_Mem()->adr_type());
assert( n->in(0) != NULL || alias_idx != Compile::AliasIdxRaw ||
// oop will be recorded in oop map if load crosses safepoint
n->is_Load() && (n->as_Load()->bottom_type()->isa_oopptr() ||
@ -2213,7 +2302,7 @@ static void final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc ) {
break;
case Op_Opaque1: // Remove Opaque Nodes before matching
case Op_Opaque2: // Remove Opaque Nodes before matching
n->subsume_by(n->in(1));
n->subsume_by(n->in(1), this);
break;
case Op_CallStaticJava:
case Op_CallJava:
@ -2337,8 +2426,7 @@ static void final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc ) {
int op = t->isa_oopptr() ? Op_ConN : Op_ConNKlass;
// Look for existing ConN node of the same exact type.
Compile* C = Compile::current();
Node* r = C->root();
Node* r = root();
uint cnt = r->outcnt();
for (uint i = 0; i < cnt; i++) {
Node* m = r->raw_out(i);
@ -2352,14 +2440,14 @@ static void final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc ) {
// Decode a narrow oop to match address
// [R12 + narrow_oop_reg<<3 + offset]
if (t->isa_oopptr()) {
nn = new (C) DecodeNNode(nn, t);
nn = new (this) DecodeNNode(nn, t);
} else {
nn = new (C) DecodeNKlassNode(nn, t);
nn = new (this) DecodeNKlassNode(nn, t);
}
n->set_req(AddPNode::Base, nn);
n->set_req(AddPNode::Address, nn);
if (addp->outcnt() == 0) {
addp->disconnect_inputs(NULL);
addp->disconnect_inputs(NULL, this);
}
}
}
@ -2371,7 +2459,6 @@ static void final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc ) {
#ifdef _LP64
case Op_CastPP:
if (n->in(1)->is_DecodeN() && Matcher::gen_narrow_oop_implicit_null_checks()) {
Compile* C = Compile::current();
Node* in1 = n->in(1);
const Type* t = n->bottom_type();
Node* new_in1 = in1->clone();
@ -2400,9 +2487,9 @@ static void final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc ) {
new_in1->set_req(0, n->in(0));
}
n->subsume_by(new_in1);
n->subsume_by(new_in1, this);
if (in1->outcnt() == 0) {
in1->disconnect_inputs(NULL);
in1->disconnect_inputs(NULL, this);
}
}
break;
@ -2419,7 +2506,6 @@ static void final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc ) {
}
assert(in1->is_DecodeNarrowPtr(), "sanity");
Compile* C = Compile::current();
Node* new_in2 = NULL;
if (in2->is_DecodeNarrowPtr()) {
assert(in2->Opcode() == in1->Opcode(), "must be same node type");
@ -2432,7 +2518,7 @@ static void final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc ) {
// oops implicit null check is not generated.
// This will allow to generate normal oop implicit null check.
if (Matcher::gen_narrow_oop_implicit_null_checks())
new_in2 = ConNode::make(C, TypeNarrowOop::NULL_PTR);
new_in2 = ConNode::make(this, TypeNarrowOop::NULL_PTR);
//
// This transformation together with CastPP transformation above
// will generated code for implicit NULL checks for compressed oops.
@ -2471,19 +2557,19 @@ static void final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc ) {
// NullCheck base_reg
//
} else if (t->isa_oopptr()) {
new_in2 = ConNode::make(C, t->make_narrowoop());
new_in2 = ConNode::make(this, t->make_narrowoop());
} else if (t->isa_klassptr()) {
new_in2 = ConNode::make(C, t->make_narrowklass());
new_in2 = ConNode::make(this, t->make_narrowklass());
}
}
if (new_in2 != NULL) {
Node* cmpN = new (C) CmpNNode(in1->in(1), new_in2);
n->subsume_by( cmpN );
Node* cmpN = new (this) CmpNNode(in1->in(1), new_in2);
n->subsume_by(cmpN, this);
if (in1->outcnt() == 0) {
in1->disconnect_inputs(NULL);
in1->disconnect_inputs(NULL, this);
}
if (in2->outcnt() == 0) {
in2->disconnect_inputs(NULL);
in2->disconnect_inputs(NULL, this);
}
}
}
@ -2501,21 +2587,20 @@ static void final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc ) {
case Op_EncodePKlass: {
Node* in1 = n->in(1);
if (in1->is_DecodeNarrowPtr()) {
n->subsume_by(in1->in(1));
n->subsume_by(in1->in(1), this);
} else if (in1->Opcode() == Op_ConP) {
Compile* C = Compile::current();
const Type* t = in1->bottom_type();
if (t == TypePtr::NULL_PTR) {
assert(t->isa_oopptr(), "null klass?");
n->subsume_by(ConNode::make(C, TypeNarrowOop::NULL_PTR));
n->subsume_by(ConNode::make(this, TypeNarrowOop::NULL_PTR), this);
} else if (t->isa_oopptr()) {
n->subsume_by(ConNode::make(C, t->make_narrowoop()));
n->subsume_by(ConNode::make(this, t->make_narrowoop()), this);
} else if (t->isa_klassptr()) {
n->subsume_by(ConNode::make(C, t->make_narrowklass()));
n->subsume_by(ConNode::make(this, t->make_narrowklass()), this);
}
}
if (in1->outcnt() == 0) {
in1->disconnect_inputs(NULL);
in1->disconnect_inputs(NULL, this);
}
break;
}
@ -2538,7 +2623,7 @@ static void final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc ) {
}
}
assert(proj != NULL, "must be found");
p->subsume_by(proj);
p->subsume_by(proj, this);
}
}
break;
@ -2558,7 +2643,7 @@ static void final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc ) {
unique_in = NULL;
}
if (unique_in != NULL) {
n->subsume_by(unique_in);
n->subsume_by(unique_in, this);
}
}
break;
@ -2571,16 +2656,15 @@ static void final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc ) {
Node* d = n->find_similar(Op_DivI);
if (d) {
// Replace them with a fused divmod if supported
Compile* C = Compile::current();
if (Matcher::has_match_rule(Op_DivModI)) {
DivModINode* divmod = DivModINode::make(C, n);
d->subsume_by(divmod->div_proj());
n->subsume_by(divmod->mod_proj());
DivModINode* divmod = DivModINode::make(this, n);
d->subsume_by(divmod->div_proj(), this);
n->subsume_by(divmod->mod_proj(), this);
} else {
// replace a%b with a-((a/b)*b)
Node* mult = new (C) MulINode(d, d->in(2));
Node* sub = new (C) SubINode(d->in(1), mult);
n->subsume_by( sub );
Node* mult = new (this) MulINode(d, d->in(2));
Node* sub = new (this) SubINode(d->in(1), mult);
n->subsume_by(sub, this);
}
}
}
@ -2592,16 +2676,15 @@ static void final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc ) {
Node* d = n->find_similar(Op_DivL);
if (d) {
// Replace them with a fused divmod if supported
Compile* C = Compile::current();
if (Matcher::has_match_rule(Op_DivModL)) {
DivModLNode* divmod = DivModLNode::make(C, n);
d->subsume_by(divmod->div_proj());
n->subsume_by(divmod->mod_proj());
DivModLNode* divmod = DivModLNode::make(this, n);
d->subsume_by(divmod->div_proj(), this);
n->subsume_by(divmod->mod_proj(), this);
} else {
// replace a%b with a-((a/b)*b)
Node* mult = new (C) MulLNode(d, d->in(2));
Node* sub = new (C) SubLNode(d->in(1), mult);
n->subsume_by( sub );
Node* mult = new (this) MulLNode(d, d->in(2));
Node* sub = new (this) SubLNode(d->in(1), mult);
n->subsume_by(sub, this);
}
}
}
@ -2620,8 +2703,8 @@ static void final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc ) {
if (n->req()-1 > 2) {
// Replace many operand PackNodes with a binary tree for matching
PackNode* p = (PackNode*) n;
Node* btp = p->binary_tree_pack(Compile::current(), 1, n->req());
n->subsume_by(btp);
Node* btp = p->binary_tree_pack(this, 1, n->req());
n->subsume_by(btp, this);
}
break;
case Op_Loop:
@ -2645,18 +2728,16 @@ static void final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc ) {
if (t != NULL && t->is_con()) {
juint shift = t->get_con();
if (shift > mask) { // Unsigned cmp
Compile* C = Compile::current();
n->set_req(2, ConNode::make(C, TypeInt::make(shift & mask)));
n->set_req(2, ConNode::make(this, TypeInt::make(shift & mask)));
}
} else {
if (t == NULL || t->_lo < 0 || t->_hi > (int)mask) {
Compile* C = Compile::current();
Node* shift = new (C) AndINode(in2, ConNode::make(C, TypeInt::make(mask)));
Node* shift = new (this) AndINode(in2, ConNode::make(this, TypeInt::make(mask)));
n->set_req(2, shift);
}
}
if (in2->outcnt() == 0) { // Remove dead node
in2->disconnect_inputs(NULL);
in2->disconnect_inputs(NULL, this);
}
}
break;
@ -2674,7 +2755,7 @@ static void final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc ) {
//------------------------------final_graph_reshaping_walk---------------------
// Replacing Opaque nodes with their input in final_graph_reshaping_impl(),
// requires that the walk visits a node's inputs before visiting the node.
static void final_graph_reshaping_walk( Node_Stack &nstack, Node *root, Final_Reshape_Counts &frc ) {
void Compile::final_graph_reshaping_walk( Node_Stack &nstack, Node *root, Final_Reshape_Counts &frc ) {
ResourceArea *area = Thread::current()->resource_area();
Unique_Node_List sfpt(area);
@ -2741,7 +2822,7 @@ static void final_graph_reshaping_walk( Node_Stack &nstack, Node *root, Final_Re
n->set_req(j, in->in(1));
}
if (in->outcnt() == 0) {
in->disconnect_inputs(NULL);
in->disconnect_inputs(NULL, this);
}
}
}
@ -3014,7 +3095,8 @@ void Compile::record_failure(const char* reason) {
}
Compile::TracePhase::TracePhase(const char* name, elapsedTimer* accumulator, bool dolog)
: TraceTime(NULL, accumulator, false NOT_PRODUCT( || TimeCompiler ), false)
: TraceTime(NULL, accumulator, false NOT_PRODUCT( || TimeCompiler ), false),
_phase_name(name), _dolog(dolog)
{
if (dolog) {
C = Compile::current();
@ -3024,15 +3106,34 @@ Compile::TracePhase::TracePhase(const char* name, elapsedTimer* accumulator, boo
_log = NULL;
}
if (_log != NULL) {
_log->begin_head("phase name='%s' nodes='%d'", name, C->unique());
_log->begin_head("phase name='%s' nodes='%d' live='%d'", _phase_name, C->unique(), C->live_nodes());
_log->stamp();
_log->end_head();
}
}
Compile::TracePhase::~TracePhase() {
C = Compile::current();
if (_dolog) {
_log = C->log();
} else {
_log = NULL;
}
#ifdef ASSERT
if (PrintIdealNodeCount) {
tty->print_cr("phase name='%s' nodes='%d' live='%d' live_graph_walk='%d'",
_phase_name, C->unique(), C->live_nodes(), C->count_live_nodes_by_graph_walk());
}
if (VerifyIdealNodeCount) {
Compile::current()->print_missing_nodes();
}
#endif
if (_log != NULL) {
_log->done("phase nodes='%d'", C->unique());
_log->done("phase name='%s' nodes='%d' live='%d'", _phase_name, C->unique(), C->live_nodes());
}
}

View file

@ -75,6 +75,8 @@ class TypeFunc;
class Unique_Node_List;
class nmethod;
class WarmCallInfo;
class Node_Stack;
struct Final_Reshape_Counts;
//------------------------------Compile----------------------------------------
// This class defines a top-level Compiler invocation.
@ -98,6 +100,8 @@ class Compile : public Phase {
private:
Compile* C;
CompileLog* _log;
const char* _phase_name;
bool _dolog;
public:
TracePhase(const char* name, elapsedTimer* accumulator, bool dolog);
~TracePhase();
@ -313,6 +317,9 @@ class Compile : public Phase {
// Node management
uint _unique; // Counter for unique Node indices
VectorSet _dead_node_list; // Set of dead nodes
uint _dead_node_count; // Number of dead nodes; VectorSet::Size() is O(N).
// So use this to keep count and make the call O(1).
debug_only(static int _debug_idx;) // Monotonic counter (not reset), use -XX:BreakAtNode=<idx>
Arena _node_arena; // Arena for new-space Nodes
Arena _old_arena; // Arena for old-space Nodes, lifetime during xform
@ -534,7 +541,7 @@ class Compile : public Phase {
ciEnv* env() const { return _env; }
CompileLog* log() const { return _log; }
bool failing() const { return _env->failing() || _failure_reason != NULL; }
const char* failure_reason() { return _failure_reason; }
const char* failure_reason() { return _failure_reason; }
bool failure_reason_is(const char* r) { return (r==_failure_reason) || (r!=NULL && _failure_reason!=NULL && strcmp(r, _failure_reason)==0); }
void record_failure(const char* reason);
@ -549,7 +556,7 @@ class Compile : public Phase {
record_method_not_compilable(reason, true);
}
bool check_node_count(uint margin, const char* reason) {
if (unique() + margin > (uint)MaxNodeLimit) {
if (live_nodes() + margin > (uint)MaxNodeLimit) {
record_method_not_compilable(reason);
return true;
} else {
@ -558,25 +565,41 @@ class Compile : public Phase {
}
// Node management
uint unique() const { return _unique; }
uint next_unique() { return _unique++; }
void set_unique(uint i) { _unique = i; }
static int debug_idx() { return debug_only(_debug_idx)+0; }
static void set_debug_idx(int i) { debug_only(_debug_idx = i); }
Arena* node_arena() { return &_node_arena; }
Arena* old_arena() { return &_old_arena; }
RootNode* root() const { return _root; }
void set_root(RootNode* r) { _root = r; }
StartNode* start() const; // (Derived from root.)
uint unique() const { return _unique; }
uint next_unique() { return _unique++; }
void set_unique(uint i) { _unique = i; }
static int debug_idx() { return debug_only(_debug_idx)+0; }
static void set_debug_idx(int i) { debug_only(_debug_idx = i); }
Arena* node_arena() { return &_node_arena; }
Arena* old_arena() { return &_old_arena; }
RootNode* root() const { return _root; }
void set_root(RootNode* r) { _root = r; }
StartNode* start() const; // (Derived from root.)
void init_start(StartNode* s);
Node* immutable_memory();
Node* immutable_memory();
Node* recent_alloc_ctl() const { return _recent_alloc_ctl; }
Node* recent_alloc_obj() const { return _recent_alloc_obj; }
void set_recent_alloc(Node* ctl, Node* obj) {
Node* recent_alloc_ctl() const { return _recent_alloc_ctl; }
Node* recent_alloc_obj() const { return _recent_alloc_obj; }
void set_recent_alloc(Node* ctl, Node* obj) {
_recent_alloc_ctl = ctl;
_recent_alloc_obj = obj;
}
}
void record_dead_node(uint idx) { if (_dead_node_list.test_set(idx)) return;
_dead_node_count++;
}
uint dead_node_count() { return _dead_node_count; }
void reset_dead_node_list() { _dead_node_list.Reset();
_dead_node_count = 0;
}
uint live_nodes() {
int val = _unique - _dead_node_count;
assert (val >= 0, err_msg_res("number of tracked dead nodes %d more than created nodes %d", _unique, _dead_node_count));
return (uint) val;
}
#ifdef ASSERT
uint count_live_nodes_by_graph_walk();
void print_missing_nodes();
#endif
// Constant table
ConstantTable& constant_table() { return _constant_table; }
@ -678,6 +701,7 @@ class Compile : public Phase {
void identify_useful_nodes(Unique_Node_List &useful);
void update_dead_node_list(Unique_Node_List &useful);
void remove_useless_nodes (Unique_Node_List &useful);
WarmCallInfo* warm_calls() const { return _warm_calls; }
@ -892,6 +916,11 @@ class Compile : public Phase {
static juint _intrinsic_hist_count[vmIntrinsics::ID_LIMIT];
static jubyte _intrinsic_hist_flags[vmIntrinsics::ID_LIMIT];
#endif
// Function calls made by the public function final_graph_reshaping.
// No need to be made public as they are not called elsewhere.
void final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc);
void final_graph_reshaping_walk( Node_Stack &nstack, Node *root, Final_Reshape_Counts &frc );
void eliminate_redundant_card_marks(Node* n);
public:

View file

@ -2320,7 +2320,7 @@ PhiNode *ConnectionGraph::create_split_phi(PhiNode *orig_phi, int alias_idx, Gro
}
}
}
if ((int)C->unique() + 2*NodeLimitFudgeFactor > MaxNodeLimit) {
if ((int) (C->live_nodes() + 2*NodeLimitFudgeFactor) > MaxNodeLimit) {
if (C->do_escape_analysis() == true && !C->failing()) {
// Retry compilation without escape analysis.
// If this is the first failure, the sentinel string will "stick"

View file

@ -1359,7 +1359,7 @@ void PhaseCFG::GlobalCodeMotion( Matcher &matcher, uint unique, Node_List &proj_
// If we inserted any instructions between a Call and his CatchNode,
// clone the instructions on all paths below the Catch.
for( i=0; i < _num_blocks; i++ )
_blocks[i]->call_catch_cleanup(_bbs);
_blocks[i]->call_catch_cleanup(_bbs, C);
#ifndef PRODUCT
if (trace_opto_pipelining()) {

View file

@ -153,7 +153,7 @@ void GraphKit::verify_exception_state(SafePointNode* ex_map) {
void GraphKit::stop_and_kill_map() {
SafePointNode* dead_map = stop();
if (dead_map != NULL) {
dead_map->disconnect_inputs(NULL); // Mark the map as killed.
dead_map->disconnect_inputs(NULL, C); // Mark the map as killed.
assert(dead_map->is_killed(), "must be so marked");
}
}
@ -1811,7 +1811,7 @@ void GraphKit::replace_call(CallNode* call, Node* result) {
}
// Disconnect the call from the graph
call->disconnect_inputs(NULL);
call->disconnect_inputs(NULL, C);
C->gvn_replace_by(call, C->top());
// Clean up any MergeMems that feed other MergeMems since the

View file

@ -573,7 +573,7 @@ uint PhaseChaitin::build_ifg_physical( ResourceArea *a ) {
(n2lidx(def) && !liveout.member(n2lidx(def)) ) ) {
b->_nodes.remove(j - 1);
if( lrgs(r)._def == n ) lrgs(r)._def = 0;
n->disconnect_inputs(NULL);
n->disconnect_inputs(NULL, C);
_cfg._bbs.map(n->_idx,NULL);
n->replace_by(C->top());
// Since yanking a Node from block, high pressure moves up one

View file

@ -1006,7 +1006,7 @@ static void catch_cleanup_inter_block(Node *use, Block *use_blk, Node *def, Bloc
//------------------------------call_catch_cleanup-----------------------------
// If we inserted any instructions between a Call and his CatchNode,
// clone the instructions on all paths below the Catch.
void Block::call_catch_cleanup(Block_Array &bbs) {
void Block::call_catch_cleanup(Block_Array &bbs, Compile* C) {
// End of region to clone
uint end = end_idx();
@ -1068,7 +1068,7 @@ void Block::call_catch_cleanup(Block_Array &bbs) {
// Remove the now-dead cloned ops
for(uint i3 = beg; i3 < end; i3++ ) {
_nodes[beg]->disconnect_inputs(NULL);
_nodes[beg]->disconnect_inputs(NULL, C);
_nodes.remove(beg);
}
@ -1081,7 +1081,7 @@ void Block::call_catch_cleanup(Block_Array &bbs) {
Node *n = sb->_nodes[j];
if (n->outcnt() == 0 &&
(!n->is_Proj() || n->as_Proj()->in(0)->outcnt() == 1) ){
n->disconnect_inputs(NULL);
n->disconnect_inputs(NULL, C);
sb->_nodes.remove(j);
new_cnt--;
}

View file

@ -269,10 +269,10 @@ void IdealLoopTree::reassociate_invariants(PhaseIdealLoop *phase) {
bool IdealLoopTree::policy_peeling( PhaseIdealLoop *phase ) const {
Node *test = ((IdealLoopTree*)this)->tail();
int body_size = ((IdealLoopTree*)this)->_body.size();
int uniq = phase->C->unique();
int live_node_count = phase->C->live_nodes();
// Peeling does loop cloning which can result in O(N^2) node construction
if( body_size > 255 /* Prevent overflow for large body_size */
|| (body_size * body_size + uniq > MaxNodeLimit) ) {
|| (body_size * body_size + live_node_count > MaxNodeLimit) ) {
return false; // too large to safely clone
}
while( test != _head ) { // Scan till run off top of loop
@ -601,7 +601,7 @@ bool IdealLoopTree::policy_maximally_unroll( PhaseIdealLoop *phase ) const {
return false;
if (new_body_size > unroll_limit ||
// Unrolling can result in a large amount of node construction
new_body_size >= MaxNodeLimit - phase->C->unique()) {
new_body_size >= MaxNodeLimit - (uint) phase->C->live_nodes()) {
return false;
}
@ -2268,7 +2268,7 @@ bool IdealLoopTree::iteration_split_impl( PhaseIdealLoop *phase, Node_List &old_
// Skip next optimizations if running low on nodes. Note that
// policy_unswitching and policy_maximally_unroll have this check.
uint nodes_left = MaxNodeLimit - phase->C->unique();
uint nodes_left = MaxNodeLimit - (uint) phase->C->live_nodes();
if ((2 * _body.size()) > nodes_left) {
return true;
}

View file

@ -59,7 +59,7 @@ bool IdealLoopTree::policy_unswitching( PhaseIdealLoop *phase ) const {
if (!_head->is_Loop()) {
return false;
}
uint nodes_left = MaxNodeLimit - phase->C->unique();
uint nodes_left = MaxNodeLimit - phase->C->live_nodes();
if (2 * _body.size() > nodes_left) {
return false; // Too speculative if running low on nodes.
}

View file

@ -729,7 +729,7 @@ static bool merge_point_too_heavy(Compile* C, Node* region) {
for (DUIterator_Fast imax, i = region->fast_outs(imax); i < imax; i++) {
weight += region->fast_out(i)->outcnt();
}
int nodes_left = MaxNodeLimit - C->unique();
int nodes_left = MaxNodeLimit - C->live_nodes();
if (weight * 8 > nodes_left) {
#ifndef PRODUCT
if (PrintOpto)

View file

@ -2262,7 +2262,7 @@ void PhaseMacroExpand::expand_lock_node(LockNode *lock) {
Node *slow_ctrl = _fallthroughproj->clone();
transform_later(slow_ctrl);
_igvn.hash_delete(_fallthroughproj);
_fallthroughproj->disconnect_inputs(NULL);
_fallthroughproj->disconnect_inputs(NULL, C);
region->init_req(1, slow_ctrl);
// region inputs are now complete
transform_later(region);
@ -2327,7 +2327,7 @@ void PhaseMacroExpand::expand_unlock_node(UnlockNode *unlock) {
Node *slow_ctrl = _fallthroughproj->clone();
transform_later(slow_ctrl);
_igvn.hash_delete(_fallthroughproj);
_fallthroughproj->disconnect_inputs(NULL);
_fallthroughproj->disconnect_inputs(NULL, C);
region->init_req(1, slow_ctrl);
// region inputs are now complete
transform_later(region);

View file

@ -342,6 +342,7 @@ void Matcher::match( ) {
// Reset node counter so MachNodes start with _idx at 0
int nodes = C->unique(); // save value
C->set_unique(0);
C->reset_dead_node_list();
// Recursively match trees from old space into new space.
// Correct leaves of new-space Nodes; they point to old-space.

View file

@ -57,7 +57,7 @@ void Node::verify_construction() {
int new_debug_idx = old_debug_idx+1;
if (new_debug_idx > 0) {
// Arrange that the lowest five decimal digits of _debug_idx
// will repeat thos of _idx. In case this is somehow pathological,
// will repeat those of _idx. In case this is somehow pathological,
// we continue to assign negative numbers (!) consecutively.
const int mod = 100000;
int bump = (int)(_idx - new_debug_idx) % mod;
@ -67,7 +67,7 @@ void Node::verify_construction() {
}
Compile::set_debug_idx(new_debug_idx);
set_debug_idx( new_debug_idx );
assert(Compile::current()->unique() < (uint)MaxNodeLimit, "Node limit exceeded");
assert(Compile::current()->unique() < (UINT_MAX - 1), "Node limit exceeded UINT_MAX");
if (BreakAtNode != 0 && (_debug_idx == BreakAtNode || (int)_idx == BreakAtNode)) {
tty->print_cr("BreakAtNode: _idx=%d _debug_idx=%d", _idx, _debug_idx);
BREAKPOINT;
@ -802,7 +802,7 @@ int Node::replace_edge(Node* old, Node* neww) {
//-------------------------disconnect_inputs-----------------------------------
// NULL out all inputs to eliminate incoming Def-Use edges.
// Return the number of edges between 'n' and 'this'
int Node::disconnect_inputs(Node *n) {
int Node::disconnect_inputs(Node *n, Compile* C) {
int edges_to_n = 0;
uint cnt = req();
@ -824,6 +824,9 @@ int Node::disconnect_inputs(Node *n) {
// Node::destruct requires all out edges be deleted first
// debug_only(destruct();) // no reuse benefit expected
if (edges_to_n == 0) {
C->record_dead_node(_idx);
}
return edges_to_n;
}

View file

@ -410,7 +410,7 @@ protected:
int replace_edge(Node* old, Node* neww);
// NULL out all inputs to eliminate incoming Def-Use edges.
// Return the number of edges between 'n' and 'this'
int disconnect_inputs(Node *n);
int disconnect_inputs(Node *n, Compile *c);
// Quickly, return true if and only if I am Compile::current()->top().
bool is_top() const {
@ -458,9 +458,9 @@ public:
void replace_by(Node* new_node);
// Globally replace this node by a given new node, updating all uses
// and cutting input edges of old node.
void subsume_by(Node* new_node) {
void subsume_by(Node* new_node, Compile* c) {
replace_by(new_node);
disconnect_inputs(NULL);
disconnect_inputs(NULL, c);
}
void set_req_X( uint i, Node *n, PhaseIterGVN *igvn );
// Find the one non-null required input. RegionNode only

View file

@ -513,7 +513,7 @@ void Compile::shorten_branches(uint* blk_starts, int& code_size, int& reloc_size
}
adjust_block_start += diff;
b->_nodes.map(idx, replacement);
mach->subsume_by(replacement);
mach->subsume_by(replacement, C);
mach = replacement;
progress = true;
@ -1425,7 +1425,7 @@ void Compile::fill_buffer(CodeBuffer* cb, uint* blk_starts) {
jmp_rule[i] = mach->rule();
#endif
b->_nodes.map(j, replacement);
mach->subsume_by(replacement);
mach->subsume_by(replacement, C);
n = replacement;
mach = replacement;
}

View file

@ -601,8 +601,8 @@ Parse::Parse(JVMState* caller, ciMethod* parse_method, float expected_uses)
set_map(entry_map);
do_exits();
if (log) log->done("parse nodes='%d' memory='%d'",
C->unique(), C->node_arena()->used());
if (log) log->done("parse nodes='%d' live='%d' memory='%d'",
C->unique(), C->live_nodes(), C->node_arena()->used());
}
//---------------------------do_all_blocks-------------------------------------

View file

@ -383,6 +383,8 @@ PhaseRemoveUseless::PhaseRemoveUseless( PhaseGVN *gvn, Unique_Node_List *worklis
// Identify nodes that are reachable from below, useful.
C->identify_useful_nodes(_useful);
// Update dead node list
C->update_dead_node_list(_useful);
// Remove all useless nodes from PhaseValues' recorded types
// Must be done before disconnecting nodes to preserve hash-table-invariant
@ -1190,7 +1192,7 @@ void PhaseIterGVN::remove_globally_dead_node( Node *dead ) {
}
}
}
C->record_dead_node(dead->_idx);
if (dead->is_macro()) {
C->remove_macro_node(dead);
}
@ -1199,6 +1201,11 @@ void PhaseIterGVN::remove_globally_dead_node( Node *dead ) {
continue;
}
}
// Constant node that has no out-edges and has only one in-edge from
// root is usually dead. However, sometimes reshaping walk makes
// it reachable by adding use edges. So, we will NOT count Con nodes
// as dead to be conservative about the dead node count at any
// given time.
}
// Aggressively kill globally dead uses

View file

@ -146,7 +146,7 @@ int PhaseChaitin::yank_if_dead_recurse(Node *old, Node *orig_old, Block *current
}
}
// Disconnect control and remove precedence edges if any exist
old->disconnect_inputs(NULL);
old->disconnect_inputs(NULL, C);
}
return blk_adjust;
}
@ -513,7 +513,7 @@ void PhaseChaitin::post_allocate_copy_removal() {
b->_nodes.remove(j--); phi_dex--;
_cfg._bbs.map(phi->_idx,NULL);
phi->replace_by(u);
phi->disconnect_inputs(NULL);
phi->disconnect_inputs(NULL, C);
continue;
}
// Note that if value[pidx] exists, then we merged no new values here

View file

@ -747,7 +747,7 @@ uint PhaseChaitin::Split(uint maxlrg, ResourceArea* split_arena) {
if( i >= cnt ) { // Found one unique input
assert(Find_id(n) == Find_id(u), "should be the same lrg");
n->replace_by(u); // Then replace with unique input
n->disconnect_inputs(NULL);
n->disconnect_inputs(NULL, C);
b->_nodes.remove(insidx);
insidx--;
b->_ihrp_index--;

View file

@ -241,13 +241,13 @@ class StringConcat : public ResourceObj {
_stringopts->gvn()->transform(call);
C->gvn_replace_by(uct, call);
uct->disconnect_inputs(NULL);
uct->disconnect_inputs(NULL, C);
}
}
void cleanup() {
// disconnect the hook node
_arguments->disconnect_inputs(NULL);
_arguments->disconnect_inputs(NULL, _stringopts->C);
}
};
@ -358,7 +358,7 @@ void StringConcat::eliminate_initialize(InitializeNode* init) {
C->gvn_replace_by(mem_proj, mem);
}
C->gvn_replace_by(init, C->top());
init->disconnect_inputs(NULL);
init->disconnect_inputs(NULL, C);
}
Node_List PhaseStringOpts::collect_toString_calls() {
@ -1477,6 +1477,6 @@ void PhaseStringOpts::replace_string_concat(StringConcat* sc) {
kit.replace_call(sc->end(), result);
// Unhook any hook nodes
string_sizes->disconnect_inputs(NULL);
string_sizes->disconnect_inputs(NULL, C);
sc->cleanup();
}