8307927: C2: "malformed control flow" with irreducible loop

Reviewed-by: thartmann, epeter
This commit is contained in:
Roland Westrelin 2023-06-29 07:43:46 +00:00
parent be64d3ac3c
commit 690d626995
3 changed files with 189 additions and 49 deletions

View File

@ -3531,13 +3531,13 @@ bool IdealLoopTree::beautify_loops( PhaseIdealLoop *phase ) {
} }
//------------------------------allpaths_check_safepts---------------------------- //------------------------------allpaths_check_safepts----------------------------
// Allpaths backwards scan from loop tail, terminating each path at first safepoint // Allpaths backwards scan. Starting at the head, traversing all backedges, and the body. Terminating each path at first
// encountered. Helper for check_safepts. // safepoint encountered. Helper for check_safepts.
void IdealLoopTree::allpaths_check_safepts(VectorSet &visited, Node_List &stack) { void IdealLoopTree::allpaths_check_safepts(VectorSet &visited, Node_List &stack) {
assert(stack.size() == 0, "empty stack"); assert(stack.size() == 0, "empty stack");
stack.push(_tail); stack.push(_head);
visited.clear(); visited.clear();
visited.set(_tail->_idx); visited.set(_head->_idx);
while (stack.size() > 0) { while (stack.size() > 0) {
Node* n = stack.pop(); Node* n = stack.pop();
if (n->is_Call() && n->as_Call()->guaranteed_safepoint()) { if (n->is_Call() && n->as_Call()->guaranteed_safepoint()) {
@ -3545,12 +3545,13 @@ void IdealLoopTree::allpaths_check_safepts(VectorSet &visited, Node_List &stack)
} else if (n->Opcode() == Op_SafePoint) { } else if (n->Opcode() == Op_SafePoint) {
if (_phase->get_loop(n) != this) { if (_phase->get_loop(n) != this) {
if (_required_safept == nullptr) _required_safept = new Node_List(); if (_required_safept == nullptr) _required_safept = new Node_List();
_required_safept->push(n); // save the one closest to the tail // save the first we run into on that path: closest to the tail if the head has a single backedge
_required_safept->push(n);
} }
// Terminate this path // Terminate this path
} else { } else {
uint start = n->is_Region() ? 1 : 0; uint start = n->is_Region() ? 1 : 0;
uint end = n->is_Region() && !n->is_Loop() ? n->req() : start + 1; uint end = n->is_Region() && (!n->is_Loop() || n == _head) ? n->req() : start + 1;
for (uint i = start; i < end; i++) { for (uint i = start; i < end; i++) {
Node* in = n->in(i); Node* in = n->in(i);
assert(in->is_CFG(), "must be"); assert(in->is_CFG(), "must be");
@ -3622,53 +3623,55 @@ void IdealLoopTree::check_safepts(VectorSet &visited, Node_List &stack) {
if (_child) _child->check_safepts(visited, stack); if (_child) _child->check_safepts(visited, stack);
if (_next) _next ->check_safepts(visited, stack); if (_next) _next ->check_safepts(visited, stack);
if (!_head->is_CountedLoop() && !_has_sfpt && _parent != nullptr && !_irreducible) { if (!_head->is_CountedLoop() && !_has_sfpt && _parent != nullptr) {
bool has_call = false; // call on dom-path bool has_call = false; // call on dom-path
bool has_local_ncsfpt = false; // ncsfpt on dom-path at this loop depth bool has_local_ncsfpt = false; // ncsfpt on dom-path at this loop depth
Node* nonlocal_ncsfpt = nullptr; // ncsfpt on dom-path at a deeper depth Node* nonlocal_ncsfpt = nullptr; // ncsfpt on dom-path at a deeper depth
// Scan the dom-path nodes from tail to head if (!_irreducible) {
for (Node* n = tail(); n != _head; n = _phase->idom(n)) { // Scan the dom-path nodes from tail to head
if (n->is_Call() && n->as_Call()->guaranteed_safepoint()) { for (Node* n = tail(); n != _head; n = _phase->idom(n)) {
has_call = true; if (n->is_Call() && n->as_Call()->guaranteed_safepoint()) {
_has_sfpt = 1; // Then no need for a safept! has_call = true;
break; _has_sfpt = 1; // Then no need for a safept!
} else if (n->Opcode() == Op_SafePoint) {
if (_phase->get_loop(n) == this) {
has_local_ncsfpt = true;
break; break;
} } else if (n->Opcode() == Op_SafePoint) {
if (nonlocal_ncsfpt == nullptr) { if (_phase->get_loop(n) == this) {
nonlocal_ncsfpt = n; // save the one closest to the tail has_local_ncsfpt = true;
} break;
} else { }
IdealLoopTree* nlpt = _phase->get_loop(n); if (nonlocal_ncsfpt == nullptr) {
if (this != nlpt) { nonlocal_ncsfpt = n; // save the one closest to the tail
// If at an inner loop tail, see if the inner loop has already }
// recorded seeing a call on the dom-path (and stop.) If not, } else {
// jump to the head of the inner loop. IdealLoopTree* nlpt = _phase->get_loop(n);
assert(is_member(nlpt), "nested loop"); if (this != nlpt) {
Node* tail = nlpt->_tail; // If at an inner loop tail, see if the inner loop has already
if (tail->in(0)->is_If()) tail = tail->in(0); // recorded seeing a call on the dom-path (and stop.) If not,
if (n == tail) { // jump to the head of the inner loop.
// If inner loop has call on dom-path, so does outer loop assert(is_member(nlpt), "nested loop");
if (nlpt->_has_sfpt) { Node* tail = nlpt->_tail;
has_call = true; if (tail->in(0)->is_If()) tail = tail->in(0);
_has_sfpt = 1; if (n == tail) {
break; // If inner loop has call on dom-path, so does outer loop
} if (nlpt->_has_sfpt) {
// Skip to head of inner loop has_call = true;
assert(_phase->is_dominator(_head, nlpt->_head), "inner head dominated by outer head"); _has_sfpt = 1;
n = nlpt->_head; break;
if (_head == n) { }
// this and nlpt (inner loop) have the same loop head. This should not happen because // Skip to head of inner loop
// during beautify_loops we call merge_many_backedges. However, infinite loops may not assert(_phase->is_dominator(_head, nlpt->_head), "inner head dominated by outer head");
// have been attached to the loop-tree during build_loop_tree before beautify_loops, n = nlpt->_head;
// but then attached in the build_loop_tree afterwards, and so still have unmerged if (_head == n) {
// backedges. Check if we are indeed in an infinite subgraph, and terminate the scan, // this and nlpt (inner loop) have the same loop head. This should not happen because
// since we have reached the loop head of this. // during beautify_loops we call merge_many_backedges. However, infinite loops may not
assert(_head->as_Region()->is_in_infinite_subgraph(), // have been attached to the loop-tree during build_loop_tree before beautify_loops,
"only expect unmerged backedges in infinite loops"); // but then attached in the build_loop_tree afterwards, and so still have unmerged
break; // backedges. Check if we are indeed in an infinite subgraph, and terminate the scan,
// since we have reached the loop head of this.
assert(_head->as_Region()->is_in_infinite_subgraph(),
"only expect unmerged backedges in infinite loops");
break;
}
} }
} }
} }

View File

@ -0,0 +1,91 @@
/*
* Copyright (c) 2023, Red Hat, Inc. 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.
*/
super public class MalformedControlIrreducibleLoop
version 52:0
{
public Method "<init>":"()V"
stack 1 locals 1
{
aload_0;
invokespecial Method java/lang/Object."<init>":"()V";
return;
}
/* same as:
public static void actualTest(int flag) {
int i = 1;
int j = 1;
if (flag == 2) {
// goto second_entry;
}
loop:
for (;;) {
i = 1;
// second_entry:
do {
if (i == 1) {
continue loop; // goto loop
}
i *= 2;
j *= 2;
} while (j < 2);
break;
}
}
*/
public static Method actualTest:"(I)V"
stack 2 locals 3
{
iconst_1;
istore_1;
iconst_1;
istore_2;
iload_0;
iconst_2;
if_icmpne L11;
L9: stack_frame_type append;
locals_map int, int;
iconst_1;
istore_1;
L11: stack_frame_type same;
iload_1;
iconst_1;
if_icmpeq L9;
L19: stack_frame_type same;
iload_1;
iconst_2;
imul;
istore_1;
iload_2;
iconst_2;
imul;
istore_2;
iload_2;
iconst_2;
if_icmplt L11;
goto L35;
L35: stack_frame_type same;
return;
}
} // end Class TestMalformedControlIrreducibleLoop

View File

@ -0,0 +1,46 @@
/*
* Copyright (c) 2023, Red Hat, Inc. 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 8307927
* @summary C2: "malformed control flow" with irreducible loop
* @compile MalformedControlIrreducibleLoop.jasm
* @run main/othervm -Xcomp -XX:-TieredCompilation -XX:CompileOnly=TestMalformedControlIrreducibleLoop::test TestMalformedControlIrreducibleLoop
*/
public class TestMalformedControlIrreducibleLoop {
public static void main(String[] args) {
new MalformedControlIrreducibleLoop();
test(false);
}
private static void test(boolean flag) {
int i;
for (i = 1; i < 2; i *= 2) {
}
if (flag) {
MalformedControlIrreducibleLoop.actualTest(i);
}
}
}