From d562d3fcbe22a0443037c5b447e1a41401275814 Mon Sep 17 00:00:00 2001 From: Emanuel Peter Date: Thu, 8 Dec 2022 15:29:26 +0000 Subject: [PATCH] 8297642: PhaseIdealLoop::only_has_infinite_loops must detect all loops that never lead to termination Reviewed-by: thartmann, roland --- src/hotspot/share/opto/loopnode.cpp | 57 +++++------ .../loopopts/TestOnlyInfiniteLoops.jasm | 96 +++++++++++++++++++ .../loopopts/TestOnlyInfiniteLoopsMain.java | 53 ++++++++++ 3 files changed, 175 insertions(+), 31 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/loopopts/TestOnlyInfiniteLoops.jasm create mode 100644 test/hotspot/jtreg/compiler/loopopts/TestOnlyInfiniteLoopsMain.java diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index 2c244496727..f8250bf59b4 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -4190,45 +4190,40 @@ bool PhaseIdealLoop::process_expensive_nodes() { } #ifdef ASSERT -// Goes over all children of the root of the loop tree, collects all controls for the loop and its inner loops then -// checks whether any control is a branch out of the loop and if it is, whether it's not a NeverBranch. +// Goes over all children of the root of the loop tree. Check if any of them have a path +// down to Root, that does not go via a NeverBranch exit. bool PhaseIdealLoop::only_has_infinite_loops() { + ResourceMark rm; + Unique_Node_List worklist; + // start traversal at all loop heads of first-level loops for (IdealLoopTree* l = _ltree_root->_child; l != NULL; l = l->_next) { - Unique_Node_List wq; Node* head = l->_head; assert(head->is_Region(), ""); - for (uint i = 1; i < head->req(); ++i) { - Node* in = head->in(i); - if (get_loop(in) != _ltree_root) { - wq.push(in); - } - } - for (uint i = 0; i < wq.size(); ++i) { - Node* c = wq.at(i); - if (c == head) { - continue; - } else if (c->is_Region()) { - for (uint j = 1; j < c->req(); ++j) { - wq.push(c->in(j)); - } - } else { - wq.push(c->in(0)); - } - } - assert(wq.member(head), ""); - for (uint i = 0; i < wq.size(); ++i) { - Node* c = wq.at(i); - if (c->is_MultiBranch()) { - for (DUIterator_Fast jmax, j = c->fast_outs(jmax); j < jmax; j++) { - Node* u = c->fast_out(j); - assert(u->is_CFG(), ""); - if (!wq.member(u) && c->Opcode() != Op_NeverBranch) { - return false; - } + worklist.push(head); + } + // BFS traversal down the CFG, except through NeverBranch exits + for (uint i = 0; i < worklist.size(); ++i) { + Node* n = worklist.at(i); + assert(n->is_CFG(), "only traverse CFG"); + if (n->is_Root()) { + // Found root -> there was an exit! + return false; + } else if (n->Opcode() == Op_NeverBranch) { + // Only follow the loop-internal projection, not the NeverBranch exit + ProjNode* proj = n->as_Multi()->proj_out_or_null(0); + assert(proj != nullptr, "must find loop-internal projection of NeverBranch"); + worklist.push(proj); + } else { + // Traverse all CFG outputs + for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { + Node* use = n->fast_out(i); + if (use->is_CFG()) { + worklist.push(use); } } } } + // No exit found for any loop -> all are infinite return true; } #endif diff --git a/test/hotspot/jtreg/compiler/loopopts/TestOnlyInfiniteLoops.jasm b/test/hotspot/jtreg/compiler/loopopts/TestOnlyInfiniteLoops.jasm new file mode 100644 index 00000000000..c9cb0e50d8e --- /dev/null +++ b/test/hotspot/jtreg/compiler/loopopts/TestOnlyInfiniteLoops.jasm @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2022, 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. + */ + + +super public class TestOnlyInfiniteLoops +{ + public Method "":"()V" + stack 2 locals 1 + { + aload_0; + invokespecial Method java/lang/Object."":"()V"; + return; + } + + static Method test_simple:"(III)I" + stack 200 locals 10 + { + // Nested infinite loop, where inner loop eventually + // looses exit to outer loop. Then, the inner loop + // floats outside the inner loop. The entry from + // outer to inner loop now becomes an exit for the + // outer loop, where it now enters the next loop, that + // used to be the inner loop. + iconst_0; + istore 9; + + iload 0; + ifeq LEND; // skip + + LOOP1: + iload 1; + ifeq LOOP1; // dominates + LOOP2: + // SKIP: prevent loop-exit from becoming zero-trip guard + iload 2; + ifeq SKIP; + iinc 9, 1; + SKIP: + iload 1; + ifeq LOOP1; // is dominated + goto LOOP2; + + LEND: + iload 9; + ireturn; + } + static Method test_irreducible:"(IIII)V" + stack 200 locals 200 + { + iload_0; + ifeq LEND; // skip + + L1: + iload 1; + ifgt MERGE; + L2: + iload 2; + ifge MERGE; + goto L1; + + MERGE: + nop; + LOOP: + iload 3; + ifle L2; + iconst_0; // always true + ifeq LOOP; + iconst_0; // always true + ifeq LOOP; + INFTY: + goto INFTY; // infinite loop + + LEND: + return; + } +} diff --git a/test/hotspot/jtreg/compiler/loopopts/TestOnlyInfiniteLoopsMain.java b/test/hotspot/jtreg/compiler/loopopts/TestOnlyInfiniteLoopsMain.java new file mode 100644 index 00000000000..e85e9a572fb --- /dev/null +++ b/test/hotspot/jtreg/compiler/loopopts/TestOnlyInfiniteLoopsMain.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2022, 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 8297642 + * @compile TestOnlyInfiniteLoops.jasm + * @summary Nested irreducible loops, where the inner loop floats out of the outer + * @run main/othervm + * -XX:CompileCommand=compileonly,TestOnlyInfiniteLoops::test* + * -XX:-TieredCompilation -Xcomp + * TestOnlyInfiniteLoopsMain + * + * @test + * @bug 8297642 + * @compile TestOnlyInfiniteLoops.jasm + * @summary Nested irreducible loops, where the inner loop floats out of the outer + * @run main/othervm + * -XX:CompileCommand=compileonly,TestOnlyInfiniteLoops::test* + * -XX:-TieredCompilation -Xcomp + * -XX:PerMethodTrapLimit=0 + * TestOnlyInfiniteLoopsMain +*/ + +public class TestOnlyInfiniteLoopsMain { + public static void main(String[] args) { + TestOnlyInfiniteLoops t = new TestOnlyInfiniteLoops(); + System.out.println("test_simple"); + t.test_simple(0, 0, 0); + System.out.println("test_irreducible"); + t.test_irreducible(0, 0, 0, 0); + } +}