diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index 3ef6a085b1c..ee3f138b8af 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -4180,6 +4180,33 @@ bool PhaseIdealLoop::partial_peel( IdealLoopTree *loop, Node_List &old_new ) { return true; } +#ifdef ASSERT + +// Moves Template Assertion Predicates to a target loop by cloning and killing the old ones. The target loop is the +// original, not-cloned loop. This is currently only used with StressLoopBackedge which is a develop flag only and +// false with product builds. We can therefore guard it with an ifdef. More details can be found at the use-site. +class MoveAssertionPredicatesVisitor : public PredicateVisitor { + ClonePredicateToTargetLoop _clone_predicate_to_loop; + PhaseIdealLoop* const _phase; + +public: + MoveAssertionPredicatesVisitor(LoopNode* target_loop_head, + const NodeInSingleLoopBody &node_in_loop_body, + PhaseIdealLoop* phase) + : _clone_predicate_to_loop(target_loop_head, node_in_loop_body, phase), + _phase(phase) { + } + NONCOPYABLE(MoveAssertionPredicatesVisitor); + + using PredicateVisitor::visit; + + void visit(const TemplateAssertionPredicate& template_assertion_predicate) override { + _clone_predicate_to_loop.clone_template_assertion_predicate(template_assertion_predicate); + template_assertion_predicate.kill(_phase->igvn()); + } +}; +#endif // ASSERT + // Transform: // // loop<-----------------+ @@ -4248,6 +4275,7 @@ bool PhaseIdealLoop::duplicate_loop_backedge(IdealLoopTree *loop, Node_List &old IfNode* exit_test = nullptr; uint inner; float f; +#ifdef ASSERT if (StressDuplicateBackedge) { if (head->is_strip_mined()) { return false; @@ -4266,7 +4294,9 @@ bool PhaseIdealLoop::duplicate_loop_backedge(IdealLoopTree *loop, Node_List &old } inner = 1; - } else { + } else +#endif //ASSERT + { // Is the shape of the loop that of a counted loop... Node* back_control = loop_exit_control(head, loop); if (back_control == nullptr) { @@ -4457,6 +4487,19 @@ bool PhaseIdealLoop::duplicate_loop_backedge(IdealLoopTree *loop, Node_List &old } } +#ifdef ASSERT + if (StressDuplicateBackedge && head->is_CountedLoop()) { + // The Template Assertion Predicates from the old counted loop are now at the new outer loop - clone them to + // the inner counted loop and kill the old ones. We only need to do this with debug builds because + // StressDuplicateBackedge is a devlop flag and false by default. Without StressDuplicateBackedge 'head' will be a + // non-counted loop, and thus we have no Template Assertion Predicates above the old loop to move down. + PredicateIterator predicate_iterator(outer_head->in(LoopNode::EntryControl)); + NodeInSingleLoopBody node_in_body(this, loop); + MoveAssertionPredicatesVisitor move_assertion_predicates_visitor(head, node_in_body, this); + predicate_iterator.for_each(move_assertion_predicates_visitor); + } +#endif // ASSERT + C->set_major_progress(); C->print_method(PHASE_AFTER_DUPLICATE_LOOP_BACKEDGE, 4, outer_head); diff --git a/test/hotspot/jtreg/compiler/loopopts/TestVerifyLoopOptimizationsHitsMemLimit.java b/test/hotspot/jtreg/compiler/loopopts/TestVerifyLoopOptimizationsHitsMemLimit.java index a2834c26dcc..fbe69ad0e04 100644 --- a/test/hotspot/jtreg/compiler/loopopts/TestVerifyLoopOptimizationsHitsMemLimit.java +++ b/test/hotspot/jtreg/compiler/loopopts/TestVerifyLoopOptimizationsHitsMemLimit.java @@ -37,15 +37,15 @@ package compiler.loopopts; * -XX:CompileCommand=memlimit,compiler.loopopts.TestVerifyLoopOptimizationsHitsMemLimit::test,100M~crash * -XX:-TieredCompilation -Xcomp -XX:PerMethodTrapLimit=0 * -XX:+StressLoopPeeling -XX:+VerifyLoopOptimizations - * -XX:StressSeed=1870557292 + * -XX:StressSeed=1870557292 -XX:-StressDuplicateBackedge * compiler.loopopts.TestVerifyLoopOptimizationsHitsMemLimit * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions * -XX:CompileCommand=compileonly,compiler.loopopts.TestVerifyLoopOptimizationsHitsMemLimit::test * -XX:CompileCommand=memlimit,compiler.loopopts.TestVerifyLoopOptimizationsHitsMemLimit::test,100M~crash * -XX:-TieredCompilation -Xcomp -XX:PerMethodTrapLimit=0 - * -XX:+StressLoopPeeling -XX:+VerifyLoopOptimizations + * -XX:+StressLoopPeeling -XX:+VerifyLoopOptimizations -XX:-StressDuplicateBackedge * compiler.loopopts.TestVerifyLoopOptimizationsHitsMemLimit - * @run main compiler.loopopts.TestVerifyLoopOptimizationsHitsMemLimit + * @run main/othervm -XX:-StressDuplicateBackedge compiler.loopopts.TestVerifyLoopOptimizationsHitsMemLimit */ public class TestVerifyLoopOptimizationsHitsMemLimit { diff --git a/test/hotspot/jtreg/compiler/predicates/assertion/TestAssertionPredicates.java b/test/hotspot/jtreg/compiler/predicates/assertion/TestAssertionPredicates.java index 8f30dce15bc..c3ccf4eff43 100644 --- a/test/hotspot/jtreg/compiler/predicates/assertion/TestAssertionPredicates.java +++ b/test/hotspot/jtreg/compiler/predicates/assertion/TestAssertionPredicates.java @@ -144,6 +144,19 @@ * compiler.predicates.assertion.TestAssertionPredicates DataUpdate */ +/* + * @test id=DataUpdateZGC + * @key randomness + * @bug 8288981 8350577 0360510 + * @requires vm.compiler2.enabled + * @requires vm.gc.Z + * @run main/othervm -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:+StressGCM -XX:+AbortVMOnCompilationFailure -XX:+UseZGC + * -XX:CompileCommand=compileonly,compiler.predicates.assertion.TestAssertionPredicates::* + * -XX:CompileCommand=dontinline,compiler.predicates.assertion.TestAssertionPredicates::* + * -XX:+IgnoreUnrecognizedVMOptions -XX:-KillPathsReachableByDeadTypeNode + * compiler.predicates.assertion.TestAssertionPredicates DataUpdate + */ + /* * @test id=CloneDown * @bug 8288981 8350577 diff --git a/test/hotspot/jtreg/compiler/predicates/assertion/TestStressDuplicateBackedgeWithAssertionPredicate.java b/test/hotspot/jtreg/compiler/predicates/assertion/TestStressDuplicateBackedgeWithAssertionPredicate.java new file mode 100644 index 00000000000..02082137bf0 --- /dev/null +++ b/test/hotspot/jtreg/compiler/predicates/assertion/TestStressDuplicateBackedgeWithAssertionPredicate.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2025, 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 8360510 + * @summary Test that StressDuplicateBackedge correctly clones Template Assertion Predicates to the inner counted loop. + * @run main/othervm -Xbatch -XX:+IgnoreUnrecognizedVMOptions -XX:+StressDuplicateBackedge + * compiler.predicates.assertion.TestStressDuplicateBackedgeWithAssertionPredicate + * @run main compiler.predicates.assertion.TestStressDuplicateBackedgeWithAssertionPredicate + */ + +package compiler.predicates.assertion; + +public class TestStressDuplicateBackedgeWithAssertionPredicate { + static int[] iArr = new int[100]; + static int iFld; + static long lFld; + + public static void main(String[] strArr) { + for (int i = 0; i < 10000; i++) { + test(); + } + } + + static void test() { + // 5) Once the inner empty loop is removed (step 4), we can apply the "duplicate backedge + // optimization" to the initial outer counted loop which is now the only loop left. Note + // that we can do that even though it is a counted loop: This is stressed with + // StressDuplicateLoopBackedge. + // 6) We do the following "duplicate loop backedge" transformation with current mainline: + // + // Template Assertion + // Template Assertion Predicates + // Predicates | + // | ====> ... + // ... | + // | Loop # Outer Non-Counted Loop (newly added) + // CountedLoop | + // CountedLoop # Inner Counted Loop (old) + // + // 7) After the transformation, the Template Assertion Predicates are still at the Outer Non-Counted Loop. + // As a result, we find them to be useless in the next predicate elimination call with + // EliminateUselessPredicates because they cannot be found from the Inner Counted Loop (we stop at + // Loop which is not a predicate). However, we have verification code in place that checks that we + // can only find useless Template Assertion Predicates if the associated counted loop node is dead. + // This is not the case and we crash with an assertion failure. + // + // The fix is to move the Template Assertion Predicates to the Inner Counted Loop again. + for (int i = 0; i < 100; i++) { + // 3) Loop Predication will hoist this range checkout out of the loop with Template + // Assertion Predicates. + iArr[i] = 34; + + // 1) We need an inner empty loop to make sure the outer counter loop is not strip mined. + // Otherwise, we cannot apply the duplicate backedge optimization to the outer loop. + // 4) Found to be empty and removed. + for (int j = 0; j < 10; j++) {} + + // 2) We need some region inside the outer loop, otherwise, we cannot apply the duplicate + // backedge optimization. + if (i == 3) { + lFld = 34; + } else { + iFld = 2; + } + } + } +}