8246203: Segmentation fault in verification due to stack overflow with -XX:+VerifyIterativeGVN

Replace the recursive verification algorithm with an iterative one to avoid a stack overflow for large graphs.

Reviewed-by: kvn, thartmann
This commit is contained in:
Christian Hagedorn 2020-06-15 09:50:11 +02:00
parent 3341d36131
commit 08df6a1f15
4 changed files with 164 additions and 61 deletions

View File

@ -2152,63 +2152,79 @@ void Node::verify_edges(Unique_Node_List &visited) {
}
}
void Node::verify_recur(const Node *n, int verify_depth,
VectorSet &old_space, VectorSet &new_space) {
if ( verify_depth == 0 ) return;
if (verify_depth > 0) --verify_depth;
// Verify all nodes if verify_depth is negative
void Node::verify(Node* n, int verify_depth) {
assert(verify_depth != 0, "depth should not be 0");
ResourceMark rm;
ResourceArea* area = Thread::current()->resource_area();
VectorSet old_space(area);
VectorSet new_space(area);
Node_List worklist(area);
worklist.push(n);
Compile* C = Compile::current();
uint last_index_on_current_depth = 0;
verify_depth--; // Visiting the first node on depth 1
// Only add nodes to worklist if verify_depth is negative (visit all nodes) or greater than 0
bool add_to_worklist = verify_depth != 0;
// Contained in new_space or old_space?
VectorSet *v = C->node_arena()->contains(n) ? &new_space : &old_space;
// Check for visited in the proper space. Numberings are not unique
// across spaces so we need a separate VectorSet for each space.
if( v->test_set(n->_idx) ) return;
if (n->is_Con() && n->bottom_type() == Type::TOP) {
if (C->cached_top_node() == NULL)
C->set_cached_top_node((Node*)n);
assert(C->cached_top_node() == n, "TOP node must be unique");
}
for (uint list_index = 0; list_index < worklist.size(); list_index++) {
n = worklist[list_index];
for( uint i = 0; i < n->len(); i++ ) {
Node *x = n->in(i);
if (!x || x->is_top()) continue;
// Verify my input has a def-use edge to me
if (true /*VerifyDefUse*/) {
// Count use-def edges from n to x
int cnt = 0;
for( uint j = 0; j < n->len(); j++ )
if( n->in(j) == x )
cnt++;
// Count def-use edges from x to n
uint max = x->_outcnt;
for( uint k = 0; k < max; k++ )
if (x->_out[k] == n)
cnt--;
assert( cnt == 0, "mismatched def-use edge counts" );
if (n->is_Con() && n->bottom_type() == Type::TOP) {
if (C->cached_top_node() == NULL) {
C->set_cached_top_node((Node*)n);
}
assert(C->cached_top_node() == n, "TOP node must be unique");
}
verify_recur(x, verify_depth, old_space, new_space);
for (uint i = 0; i < n->len(); i++) {
Node* x = n->in(i);
if (!x || x->is_top()) {
continue;
}
// Verify my input has a def-use edge to me
// Count use-def edges from n to x
int cnt = 0;
for (uint j = 0; j < n->len(); j++) {
if (n->in(j) == x) {
cnt++;
}
}
// Count def-use edges from x to n
uint max = x->_outcnt;
for (uint k = 0; k < max; k++) {
if (x->_out[k] == n) {
cnt--;
}
}
assert(cnt == 0, "mismatched def-use edge counts");
// Contained in new_space or old_space?
VectorSet* v = C->node_arena()->contains(x) ? &new_space : &old_space;
// Check for visited in the proper space. Numberings are not unique
// across spaces so we need a separate VectorSet for each space.
if (add_to_worklist && !v->test_set(x->_idx)) {
worklist.push(x);
}
}
if (verify_depth > 0 && list_index == last_index_on_current_depth) {
// All nodes on this depth were processed and its inputs are on the worklist. Decrement verify_depth and
// store the current last list index which is the last node in the list with the new depth. All nodes
// added afterwards will have a new depth again. Stop adding new nodes if depth limit is reached (=0).
verify_depth--;
if (verify_depth == 0) {
add_to_worklist = false;
}
last_index_on_current_depth = worklist.size() - 1;
}
}
}
//------------------------------verify-----------------------------------------
// Check Def-Use info for my subgraph
void Node::verify() const {
Compile* C = Compile::current();
Node* old_top = C->cached_top_node();
ResourceMark rm;
ResourceArea *area = Thread::current()->resource_area();
VectorSet old_space(area), new_space(area);
verify_recur(this, -1, old_space, new_space);
C->set_cached_top_node(old_top);
}
#endif
//------------------------------walk-------------------------------------------
// Graph walk, with both pre-order and post-order functions
void Node::walk(NFunc pre, NFunc post, void *env) {

View File

@ -1150,8 +1150,7 @@ public:
void collect_nodes_out_all_ctrl_boundary(GrowableArray<Node*> *ns) const;
void verify_edges(Unique_Node_List &visited); // Verify bi-directional edges
void verify() const; // Check Def-Use info for my subgraph
static void verify_recur(const Node *n, int verify_depth, VectorSet &old_space, VectorSet &new_space);
static void verify(Node* n, int verify_depth);
// This call defines a class-unique string used to identify class instances
virtual const char *Name() const;

View File

@ -1005,24 +1005,22 @@ void PhaseIterGVN::verify_step(Node* n) {
if (VerifyIterativeGVN) {
_verify_window[_verify_counter % _verify_window_size] = n;
++_verify_counter;
ResourceMark rm;
ResourceArea* area = Thread::current()->resource_area();
VectorSet old_space(area), new_space(area);
if (C->unique() < 1000 ||
0 == _verify_counter % (C->unique() < 10000 ? 10 : 100)) {
if (C->unique() < 1000 || 0 == _verify_counter % (C->unique() < 10000 ? 10 : 100)) {
++_verify_full_passes;
Node::verify_recur(C->root(), -1, old_space, new_space);
Node::verify(C->root(), -1);
}
const int verify_depth = 4;
for ( int i = 0; i < _verify_window_size; i++ ) {
for (int i = 0; i < _verify_window_size; i++) {
Node* n = _verify_window[i];
if ( n == NULL ) continue;
if( n->in(0) == NodeSentinel ) { // xform_idom
if (n == NULL) {
continue;
}
if (n->in(0) == NodeSentinel) { // xform_idom
_verify_window[i] = n->in(1);
--i; continue;
--i;
continue;
}
// Typical fanout is 1-2, so this call visits about 6 nodes.
Node::verify_recur(n, verify_depth, old_space, new_space);
Node::verify(n, 4);
}
}
}

View File

@ -0,0 +1,90 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 8246203
* @requires vm.debug == true & vm.flavor == "server"
* @summary Test which causes a stack overflow segmentation fault with -XX:+VerifyIterativeGVN due to a too deep recursion in Node::verify_recur().
*
* @run main/othervm/timeout=600 -Xcomp -XX:+VerifyIterativeGVN -XX:CompileCommand=compileonly,compiler.loopopts.TestDeepGraphVerifyIterativeGVN::*
* compiler.loopopts.TestDeepGraphVerifyIterativeGVN
*/
package compiler.loopopts;
public class TestDeepGraphVerifyIterativeGVN
{
static volatile int[] iArr;
static volatile int x;
public static void main(String[] arr) {
/*
* Just enough statements to create a deep enough graph (i.e. many nodes in one chain). The current recursive verification in Node::verify_recur() will follow this chain
* and call itself again for each newly discovered input node. The current implementation can only handle up to around 10000 recursive calls and will then crash with a
* stack overflow segementation fault. The iterative fix needs much less memory and does not result in a segementation fault anymore.
*/
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 }; iArr = new int[] { x % 2 };
}
}