From b2cd3b0d48bdabacfd421dee9b9f87a003e0e09d Mon Sep 17 00:00:00 2001 From: Roland Westrelin Date: Wed, 28 Jan 2026 08:00:11 +0000 Subject: [PATCH] 8350330: C2: PhaseIdealLoop::add_parse_predicate() should mirror GraphKit::add_parse_predicate() Reviewed-by: chagedorn, qamai --- src/hotspot/share/opto/loopnode.cpp | 44 +++++------ src/hotspot/share/opto/loopnode.hpp | 3 + .../TestLoopNestTooManyTraps.java | 79 +++++++++++++++++++ 3 files changed, 103 insertions(+), 23 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/longcountedloops/TestLoopNestTooManyTraps.java diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index 8dc34af9c19..fab354e3e3d 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -590,7 +590,7 @@ Node* PhaseIdealLoop::loop_nest_replace_iv(Node* iv_to_replace, Node* inner_iv, // Add a Parse Predicate with an uncommon trap on the failing/false path. Normal control will continue on the true path. void PhaseIdealLoop::add_parse_predicate(Deoptimization::DeoptReason reason, Node* inner_head, IdealLoopTree* loop, SafePointNode* sfpt) { - if (!C->too_many_traps(reason)) { + if (!C->too_many_traps(sfpt->jvms()->method(), sfpt->jvms()->bci(), reason)) { ParsePredicateNode* parse_predicate = new ParsePredicateNode(inner_head->in(LoopNode::EntryControl), reason, &_igvn); register_control(parse_predicate, loop, inner_head->in(LoopNode::EntryControl)); Node* if_false = new IfFalseNode(parse_predicate); @@ -760,6 +760,24 @@ SafePointNode* PhaseIdealLoop::find_safepoint(Node* back_control, Node* x, Ideal return safepoint; } +void PhaseIdealLoop::add_parse_predicates(IdealLoopTree* outer_ilt, LoopNode* inner_head, SafePointNode* cloned_sfpt) { + if (ShortRunningLongLoop) { + add_parse_predicate(Deoptimization::Reason_short_running_long_loop, inner_head, outer_ilt, cloned_sfpt); + } + if (UseLoopPredicate) { + add_parse_predicate(Deoptimization::Reason_predicate, inner_head, outer_ilt, cloned_sfpt); + if (UseProfiledLoopPredicate) { + add_parse_predicate(Deoptimization::Reason_profile_predicate, inner_head, outer_ilt, cloned_sfpt); + } + } + + if (UseAutoVectorizationPredicate) { + add_parse_predicate(Deoptimization::Reason_auto_vectorization_check, inner_head, outer_ilt, cloned_sfpt); + } + + add_parse_predicate(Deoptimization::Reason_loop_limit_check, inner_head, outer_ilt, cloned_sfpt); +} + // If the loop has the shape of a counted loop but with a long // induction variable, transform the loop in a loop nest: an inner // loop that iterates for at most max int iterations with an integer @@ -1123,26 +1141,7 @@ bool PhaseIdealLoop::create_loop_nest(IdealLoopTree* loop, Node_List &old_new) { if (safepoint != nullptr) { SafePointNode* cloned_sfpt = old_new[safepoint->_idx]->as_SafePoint(); - if (ShortRunningLongLoop) { - add_parse_predicate(Deoptimization::Reason_short_running_long_loop, inner_head, outer_ilt, cloned_sfpt); - } - if (UseLoopPredicate) { - add_parse_predicate(Deoptimization::Reason_predicate, inner_head, outer_ilt, cloned_sfpt); - if (UseProfiledLoopPredicate) { - add_parse_predicate(Deoptimization::Reason_profile_predicate, inner_head, outer_ilt, cloned_sfpt); - } - } - - if (UseAutoVectorizationPredicate) { - // We only want to use the auto-vectorization check as a trap once per bci. And - // PhaseIdealLoop::add_parse_predicate only checks trap limits per method, so - // we do a custom check here. - if (!C->too_many_traps(cloned_sfpt->jvms()->method(), cloned_sfpt->jvms()->bci(), Deoptimization::Reason_auto_vectorization_check)) { - add_parse_predicate(Deoptimization::Reason_auto_vectorization_check, inner_head, outer_ilt, cloned_sfpt); - } - } - - add_parse_predicate(Deoptimization::Reason_loop_limit_check, inner_head, outer_ilt, cloned_sfpt); + add_parse_predicates(outer_ilt, inner_head, cloned_sfpt); } #ifndef PRODUCT @@ -1893,7 +1892,7 @@ bool PhaseIdealLoop::convert_to_long_loop(Node* cmp, Node* phi, IdealLoopTree* l #endif //------------------------------is_counted_loop-------------------------------- -bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*&loop, BasicType iv_bt) { +bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*& loop, BasicType iv_bt) { PhaseGVN *gvn = &_igvn; Node* back_control = loop_exit_control(x, loop); @@ -3528,7 +3527,6 @@ void OuterStripMinedLoopNode::transform_to_counted_loop(PhaseIterGVN* igvn, Phas CountedLoopEndNode* cle = inner_cl->loopexit(); Node* inner_test = cle->in(1); IfNode* outer_le = outer_loop_end(); - CountedLoopEndNode* inner_cle = inner_cl->loopexit(); Node* safepoint = outer_safepoint(); fix_sunk_stores_when_back_to_counted_loop(igvn, iloop); diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp index ffc283ac941..24976d76a51 100644 --- a/src/hotspot/share/opto/loopnode.hpp +++ b/src/hotspot/share/opto/loopnode.hpp @@ -1368,6 +1368,9 @@ public: #endif void add_parse_predicate(Deoptimization::DeoptReason reason, Node* inner_head, IdealLoopTree* loop, SafePointNode* sfpt); SafePointNode* find_safepoint(Node* back_control, Node* x, IdealLoopTree* loop); + + void add_parse_predicates(IdealLoopTree* outer_ilt, LoopNode* inner_head, SafePointNode* cloned_sfpt); + IdealLoopTree* insert_outer_loop(IdealLoopTree* loop, LoopNode* outer_l, Node* outer_ift); IdealLoopTree* create_outer_strip_mined_loop(Node* init_control, IdealLoopTree* loop, float cl_prob, float le_fcnt, diff --git a/test/hotspot/jtreg/compiler/longcountedloops/TestLoopNestTooManyTraps.java b/test/hotspot/jtreg/compiler/longcountedloops/TestLoopNestTooManyTraps.java new file mode 100644 index 00000000000..502afc56548 --- /dev/null +++ b/test/hotspot/jtreg/compiler/longcountedloops/TestLoopNestTooManyTraps.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2026 IBM Corporation. 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 8350330 + * @summary C2: PhaseIdealLoop::add_parse_predicate() should mirror GraphKit::add_parse_predicate() + * @library /test/lib / + * @build jdk.test.whitebox.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:-BackgroundCompilation -XX:-ShortRunningLongLoop -XX:-UseOnStackReplacement + * -XX:CompileOnly=*TestLoopNestTooManyTraps::test1 -XX:LoopMaxUnroll=0 + * compiler.longcountedloops.TestLoopNestTooManyTraps + */ + +package compiler.longcountedloops; + +import java.lang.reflect.Method; +import java.util.Objects; +import jdk.test.whitebox.WhiteBox; + +public class TestLoopNestTooManyTraps { + private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); + + public static void main(String[] args) throws NoSuchMethodException { + Method methodTest1 = TestLoopNestTooManyTraps.class.getDeclaredMethod("test1", int.class, long.class); + + for (int j = 0; j < 10; j++) { + System.out.println("iteration " + j); + for (int i = 0; i < 20_000; i++) { + test1(1000, 1000); + } + if (!WHITE_BOX.isMethodCompiled(methodTest1)) { + throw new RuntimeException("test1 should be compiled"); + } + System.out.println("iteration 2 " + j); + try { + test1(10000, 1000); + } catch (IndexOutOfBoundsException ioobe) { + } + if (j <= 1) { + if (WHITE_BOX.isMethodCompiled(methodTest1)) { + throw new RuntimeException("test1 should have deoptimized at iteration " + j); + } + } else { + if (!WHITE_BOX.isMethodCompiled(methodTest1)) { + throw new RuntimeException("test1 shouldn't have deoptimized"); + } + } + } + } + + private static void test1(int stop, long length) { + for (int i = 0; i < stop; i++) { + Objects.checkIndex(i, length); + } + } +}