7147464: Java crashed while executing method with over 8k of dneg operations
Summary: replace recursive method with iterative
Reviewed-by: kvn, twisti
Contributed-by: dean.long@oracle.com
--- a/hotspot/src/share/vm/opto/phaseX.cpp Fri Jul 13 20:14:27 2012 -0400
+++ b/hotspot/src/share/vm/opto/phaseX.cpp Mon Jul 16 15:31:18 2012 -0400
@@ -757,6 +757,7 @@
//------------------------------PhaseIterGVN-----------------------------------
// Initialize hash table to fresh and clean for +VerifyOpto
PhaseIterGVN::PhaseIterGVN( PhaseIterGVN *igvn, const char *dummy ) : PhaseGVN(igvn,dummy), _worklist( ),
+ _stack(C->unique() >> 1),
_delay_transform(false) {
}
@@ -764,6 +765,7 @@
// Initialize with previous PhaseIterGVN info; used by PhaseCCP
PhaseIterGVN::PhaseIterGVN( PhaseIterGVN *igvn ) : PhaseGVN(igvn),
_worklist( igvn->_worklist ),
+ _stack( igvn->_stack ),
_delay_transform(igvn->_delay_transform)
{
}
@@ -772,6 +774,7 @@
// Initialize with previous PhaseGVN info from Parser
PhaseIterGVN::PhaseIterGVN( PhaseGVN *gvn ) : PhaseGVN(gvn),
_worklist(*C->for_igvn()),
+ _stack(C->unique() >> 1),
_delay_transform(false)
{
uint max;
@@ -1138,51 +1141,77 @@
// Kill a globally dead Node. All uses are also globally dead and are
// aggressively trimmed.
void PhaseIterGVN::remove_globally_dead_node( Node *dead ) {
- assert(dead != C->root(), "killing root, eh?");
- if (dead->is_top()) return;
- NOT_PRODUCT( set_progress(); )
- // Remove from iterative worklist
- _worklist.remove(dead);
- if (!dead->is_Con()) { // Don't kill cons but uses
- // Remove from hash table
- _table.hash_delete( dead );
- // Smash all inputs to 'dead', isolating him completely
- for( uint i = 0; i < dead->req(); i++ ) {
- Node *in = dead->in(i);
- if( in ) { // Points to something?
- dead->set_req(i,NULL); // Kill the edge
- if (in->outcnt() == 0 && in != C->top()) {// Made input go dead?
- remove_dead_node(in); // Recursively remove
- } else if (in->outcnt() == 1 &&
- in->has_special_unique_user()) {
- _worklist.push(in->unique_out());
- } else if (in->outcnt() <= 2 && dead->is_Phi()) {
- if( in->Opcode() == Op_Region )
- _worklist.push(in);
- else if( in->is_Store() ) {
- DUIterator_Fast imax, i = in->fast_outs(imax);
- _worklist.push(in->fast_out(i));
- i++;
- if(in->outcnt() == 2) {
- _worklist.push(in->fast_out(i));
- i++;
+ enum DeleteProgress {
+ PROCESS_INPUTS,
+ PROCESS_OUTPUTS
+ };
+ assert(_stack.is_empty(), "not empty");
+ _stack.push(dead, PROCESS_INPUTS);
+
+ while (_stack.is_nonempty()) {
+ dead = _stack.node();
+ uint progress_state = _stack.index();
+ assert(dead != C->root(), "killing root, eh?");
+ assert(!dead->is_top(), "add check for top when pushing");
+ NOT_PRODUCT( set_progress(); )
+ if (progress_state == PROCESS_INPUTS) {
+ // After following inputs, continue to outputs
+ _stack.set_index(PROCESS_OUTPUTS);
+ // Remove from iterative worklist
+ _worklist.remove(dead);
+ if (!dead->is_Con()) { // Don't kill cons but uses
+ bool recurse = false;
+ // Remove from hash table
+ _table.hash_delete( dead );
+ // Smash all inputs to 'dead', isolating him completely
+ for( uint i = 0; i < dead->req(); i++ ) {
+ Node *in = dead->in(i);
+ if( in ) { // Points to something?
+ dead->set_req(i,NULL); // Kill the edge
+ if (in->outcnt() == 0 && in != C->top()) {// Made input go dead?
+ _stack.push(in, PROCESS_INPUTS); // Recursively remove
+ recurse = true;
+ } else if (in->outcnt() == 1 &&
+ in->has_special_unique_user()) {
+ _worklist.push(in->unique_out());
+ } else if (in->outcnt() <= 2 && dead->is_Phi()) {
+ if( in->Opcode() == Op_Region )
+ _worklist.push(in);
+ else if( in->is_Store() ) {
+ DUIterator_Fast imax, i = in->fast_outs(imax);
+ _worklist.push(in->fast_out(i));
+ i++;
+ if(in->outcnt() == 2) {
+ _worklist.push(in->fast_out(i));
+ i++;
+ }
+ assert(!(i < imax), "sanity");
+ }
}
- assert(!(i < imax), "sanity");
}
}
+
+ if (dead->is_macro()) {
+ C->remove_macro_node(dead);
+ }
+
+ if (recurse) {
+ continue;
+ }
}
}
- if (dead->is_macro()) {
- C->remove_macro_node(dead);
+ // Aggressively kill globally dead uses
+ // (Rather than pushing all the outs at once, we push one at a time,
+ // plus the parent to resume later, because of the indefinite number
+ // of edge deletions per loop trip.)
+ if (dead->outcnt() > 0) {
+ // Recursively remove
+ _stack.push(dead->raw_out(0), PROCESS_INPUTS);
+ } else {
+ _stack.pop();
}
}
- // Aggressively kill globally dead uses
- // (Cannot use DUIterator_Last because of the indefinite number
- // of edge deletions per loop trip.)
- while (dead->outcnt() > 0) {
- remove_globally_dead_node(dead->raw_out(0));
- }
}
//------------------------------subsume_node-----------------------------------
--- a/hotspot/src/share/vm/opto/phaseX.hpp Fri Jul 13 20:14:27 2012 -0400
+++ b/hotspot/src/share/vm/opto/phaseX.hpp Mon Jul 16 15:31:18 2012 -0400
@@ -403,6 +403,8 @@
// Subsume users of node 'old' into node 'nn'
void subsume_node( Node *old, Node *nn );
+ Node_Stack _stack; // Stack used to avoid recursion
+
protected:
// Idealize new Node 'n' with respect to its inputs and its value
@@ -438,8 +440,8 @@
// It is significant only for debugging and profiling.
Node* register_new_node_with_optimizer(Node* n, Node* orig = NULL);
- // Kill a globally dead Node. It is allowed to have uses which are
- // assumed dead and left 'in limbo'.
+ // Kill a globally dead Node. All uses are also globally dead and are
+ // aggressively trimmed.
void remove_globally_dead_node( Node *dead );
// Kill all inputs to a dead node, recursively making more dead nodes.