8360510: C2: Template Assertion Predicates are not cloned to the inner counted loop with -XX:+StressDuplicateBackedge

Reviewed-by: epeter, roland
This commit is contained in:
Christian Hagedorn 2025-11-26 07:55:43 +00:00
parent 46ee8d550e
commit 275cb9f287
4 changed files with 149 additions and 4 deletions

View File

@ -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);

View File

@ -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 {

View File

@ -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

View File

@ -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;
}
}
}
}