mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 12:09:14 +00:00
8346552: C2: Add IR tests to check that Predicate cloning in Loop Unswitching works as expected
Co-authored-by: Christian Hagedorn <chagedorn@openjdk.org> Reviewed-by: chagedorn, epeter
This commit is contained in:
parent
953eef4f11
commit
84e9264e76
@ -2219,13 +2219,13 @@ void ParsePredicateNode::dump_spec(outputStream* st) const {
|
||||
st->print("Loop ");
|
||||
break;
|
||||
case Deoptimization::DeoptReason::Reason_profile_predicate:
|
||||
st->print("Profiled Loop ");
|
||||
st->print("Profiled_Loop ");
|
||||
break;
|
||||
case Deoptimization::DeoptReason::Reason_auto_vectorization_check:
|
||||
st->print("Auto_Vectorization_Check ");
|
||||
break;
|
||||
case Deoptimization::DeoptReason::Reason_loop_limit_check:
|
||||
st->print("Loop Limit Check ");
|
||||
st->print("Loop_Limit_Check ");
|
||||
break;
|
||||
default:
|
||||
fatal("unknown kind");
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
#include "opto/node.hpp"
|
||||
#include "opto/predicates.hpp"
|
||||
#include "opto/rootnode.hpp"
|
||||
#include "runtime/deoptimization.hpp"
|
||||
|
||||
// Walk over all Initialized Assertion Predicates and return the entry into the first Initialized Assertion Predicate
|
||||
// (i.e. not belonging to an Initialized Assertion Predicate anymore)
|
||||
@ -1093,11 +1094,19 @@ CloneUnswitchedLoopPredicatesVisitor::CloneUnswitchedLoopPredicatesVisitor(
|
||||
PhaseIdealLoop* phase)
|
||||
: _clone_predicate_to_true_path_loop(true_path_loop_head, node_in_true_path_loop_body, phase),
|
||||
_clone_predicate_to_false_path_loop(false_path_loop_head, node_in_false_path_loop_body, phase),
|
||||
_phase(phase) {}
|
||||
_phase(phase),
|
||||
_is_counted_loop(true_path_loop_head->is_CountedLoop()) {}
|
||||
|
||||
// Keep track of whether we are in the correct Predicate Block where Template Assertion Predicates can be found.
|
||||
// The PredicateIterator will always start at the loop entry and first visits the Loop Limit Check Predicate Block.
|
||||
// Does not clone a Loop Limit Check Parse Predicate if a counted loop is unswitched, because it most likely will not be
|
||||
// used anymore (it could only be used when both unswitched loop versions die and the Loop Limit Check Parse Predicate
|
||||
// ends up at a LoopNode without Loop Limit Check Parse Predicate directly following the unswitched loop that can then
|
||||
// be speculatively converted to a counted loop - this is rather rare).
|
||||
void CloneUnswitchedLoopPredicatesVisitor::visit(const ParsePredicate& parse_predicate) {
|
||||
Deoptimization::DeoptReason deopt_reason = parse_predicate.head()->deopt_reason();
|
||||
if (_is_counted_loop && deopt_reason == Deoptimization::Reason_loop_limit_check) {
|
||||
return;
|
||||
}
|
||||
_clone_predicate_to_true_path_loop.clone_parse_predicate(parse_predicate, false);
|
||||
_clone_predicate_to_false_path_loop.clone_parse_predicate(parse_predicate, true);
|
||||
parse_predicate.kill(_phase->igvn());
|
||||
|
||||
@ -1176,6 +1176,7 @@ class CloneUnswitchedLoopPredicatesVisitor : public PredicateVisitor {
|
||||
ClonePredicateToTargetLoop _clone_predicate_to_false_path_loop;
|
||||
|
||||
PhaseIdealLoop* const _phase;
|
||||
const bool _is_counted_loop;
|
||||
|
||||
public:
|
||||
CloneUnswitchedLoopPredicatesVisitor(LoopNode* true_path_loop_head,
|
||||
|
||||
@ -1470,6 +1470,11 @@ public class IRNode {
|
||||
optoOnly(OOPMAP_WITH, regex);
|
||||
}
|
||||
|
||||
public static final String OPAQUE_TEMPLATE_ASSERTION_PREDICATE = PREFIX + "OPAQUE_TEMPLATE_ASSERTION_PREDICATE" + POSTFIX;
|
||||
static {
|
||||
duringLoopOpts(OPAQUE_TEMPLATE_ASSERTION_PREDICATE, "OpaqueTemplateAssertionPredicate");
|
||||
}
|
||||
|
||||
public static final String OR_I = PREFIX + "OR_I" + POSTFIX;
|
||||
static {
|
||||
beforeMatchingNameRegex(OR_I, "OrI");
|
||||
@ -1576,12 +1581,17 @@ public class IRNode {
|
||||
|
||||
public static final String LOOP_LIMIT_CHECK_PARSE_PREDICATE = PREFIX + "LOOP_LIMIT_CHECK_PARSE_PREDICATE" + POSTFIX;
|
||||
static {
|
||||
parsePredicateNodes(LOOP_LIMIT_CHECK_PARSE_PREDICATE, "Loop Limit Check");
|
||||
parsePredicateNodes(LOOP_LIMIT_CHECK_PARSE_PREDICATE, "Loop_Limit_Check");
|
||||
}
|
||||
|
||||
public static final String PROFILED_LOOP_PARSE_PREDICATE = PREFIX + "PROFILED_LOOP_PARSE_PREDICATE" + POSTFIX;
|
||||
static {
|
||||
parsePredicateNodes(PROFILED_LOOP_PARSE_PREDICATE, "Profiled Loop");
|
||||
parsePredicateNodes(PROFILED_LOOP_PARSE_PREDICATE, "Profiled_Loop");
|
||||
}
|
||||
|
||||
public static final String AUTO_VECTORIZATION_CHECK_PARSE_PREDICATE = PREFIX + "AUTO_VECTORIZATION_CHECK_PARSE_PREDICATE" + POSTFIX;
|
||||
static {
|
||||
parsePredicateNodes(AUTO_VECTORIZATION_CHECK_PARSE_PREDICATE, "Auto_Vectorization_Check");
|
||||
}
|
||||
|
||||
public static final String PREDICATE_TRAP = PREFIX + "PREDICATE_TRAP" + POSTFIX;
|
||||
@ -2826,16 +2836,26 @@ public class IRNode {
|
||||
CompilePhase.BEFORE_MATCHING));
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply {@code regex} on all ideal graph phases starting from {@link CompilePhase#BEFORE_LOOP_OPTS}
|
||||
* up to and including {@link CompilePhase#AFTER_LOOP_OPTS}.
|
||||
*/
|
||||
private static void duringLoopOpts(String irNodePlaceholder, String regex) {
|
||||
IR_NODE_MAPPINGS.put(irNodePlaceholder, new SinglePhaseRangeEntry(CompilePhase.AFTER_LOOP_OPTS, regex,
|
||||
CompilePhase.BEFORE_LOOP_OPTS,
|
||||
CompilePhase.AFTER_LOOP_OPTS));
|
||||
}
|
||||
|
||||
private static void trapNodes(String irNodePlaceholder, String trapReason) {
|
||||
String regex = START + "CallStaticJava" + MID + "uncommon_trap.*" + trapReason + END;
|
||||
beforeMatching(irNodePlaceholder, regex);
|
||||
}
|
||||
|
||||
private static void parsePredicateNodes(String irNodePlaceholder, String label) {
|
||||
String regex = START + "ParsePredicate" + MID + "#" + label + "[ ]*!jvms:" + END;
|
||||
String regex = START + "ParsePredicate" + MID + "#" + label + " " + END;
|
||||
IR_NODE_MAPPINGS.put(irNodePlaceholder, new SinglePhaseRangeEntry(CompilePhase.AFTER_PARSING, regex,
|
||||
CompilePhase.AFTER_PARSING,
|
||||
CompilePhase.PHASEIDEALLOOP_ITERATIONS));
|
||||
CompilePhase.AFTER_LOOP_OPTS));
|
||||
}
|
||||
|
||||
private static void loadOfNodes(String irNodePlaceholder, String irNodeRegex) {
|
||||
|
||||
@ -0,0 +1,183 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package compiler.loopopts;
|
||||
|
||||
import compiler.lib.ir_framework.*;
|
||||
import java.util.Random;
|
||||
import jdk.test.lib.Utils;
|
||||
import jdk.test.lib.Asserts;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8346552
|
||||
* @summary Test that all parse predicates are cloned after loop unswitching.
|
||||
* @key randomness
|
||||
* @library /test/lib /
|
||||
* @run driver compiler.loopopts.TestUnswitchPredicateCloning
|
||||
*/
|
||||
|
||||
public class TestUnswitchPredicateCloning {
|
||||
static final int SIZE = 100;
|
||||
|
||||
private static final Random random = Utils.getRandomInstance();
|
||||
|
||||
public static void main(String[] strArr) {
|
||||
TestFramework.run();
|
||||
}
|
||||
|
||||
@Run(test = {"testUnswitchingBeforePredication", "testPredicationBeforeUnswitching", "testUnswitchingUncounted"})
|
||||
@Warmup(0)
|
||||
private static void runNoWarmup() {
|
||||
final int idx = random.nextInt(SIZE);
|
||||
final boolean cond = random.nextBoolean();
|
||||
int res = testUnswitchingBeforePredication(idx);
|
||||
Asserts.assertEQ(SIZE * idx, res);
|
||||
res = testPredicationBeforeUnswitching(idx, cond);
|
||||
Asserts.assertEQ((SIZE * (SIZE - 1)) / 2 + (cond ? SIZE * idx : 0), res);
|
||||
res = testUnswitchingUncounted(cond);
|
||||
Asserts.assertEQ((SIZE * (SIZE - 1)) / 2 + (cond ? SIZE : 0), res);
|
||||
}
|
||||
|
||||
@DontInline
|
||||
private static int[] getArr() {
|
||||
int[] arr = new int[SIZE];
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
arr[i] = i;
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
@Test
|
||||
// Check that Loop Unswitching doubled the number of Parse Predicates: We have
|
||||
// them at the true- and false-path-loop. Note that the Loop Limit Check Parse
|
||||
// Predicate is not cloned when we already have a counted loop.
|
||||
@IR(counts = { IRNode.LOOP_PARSE_PREDICATE, "3",
|
||||
IRNode.PROFILED_LOOP_PARSE_PREDICATE, "3",
|
||||
IRNode.LOOP_LIMIT_CHECK_PARSE_PREDICATE, "3",
|
||||
IRNode.AUTO_VECTORIZATION_CHECK_PARSE_PREDICATE, "3" },
|
||||
phase = CompilePhase.BEFORE_LOOP_UNSWITCHING)
|
||||
// Since we know that Loop Predication happens after Loop Unswitching, we can test the
|
||||
// have already been removed in the beautify loop phase.
|
||||
@IR(counts = { IRNode.LOOP_PARSE_PREDICATE, "4",
|
||||
IRNode.PROFILED_LOOP_PARSE_PREDICATE, "4",
|
||||
IRNode.LOOP_LIMIT_CHECK_PARSE_PREDICATE, "3",
|
||||
IRNode.AUTO_VECTORIZATION_CHECK_PARSE_PREDICATE, "4" },
|
||||
phase = CompilePhase.BEFORE_LOOP_PREDICATION_RC)
|
||||
// Check that Opaque Template Assertion Predicates are added in Loop Predication
|
||||
// even if Loop Predication only happens after Loop Unswitching.
|
||||
@IR(failOn = { IRNode.OPAQUE_TEMPLATE_ASSERTION_PREDICATE },
|
||||
phase = CompilePhase.AFTER_LOOP_UNSWITCHING)
|
||||
@IR(counts = { IRNode.OPAQUE_TEMPLATE_ASSERTION_PREDICATE, "2" },
|
||||
phase = CompilePhase.AFTER_LOOP_PREDICATION_RC)
|
||||
static int testUnswitchingBeforePredication(int j) {
|
||||
int zero = 34;
|
||||
int limit = 2;
|
||||
|
||||
// Ensure zero == 0 is only known after CCP
|
||||
for (; limit < 4; limit *= 2) {
|
||||
}
|
||||
for (int i = 2; i < limit; i++) {
|
||||
zero = 0;
|
||||
}
|
||||
|
||||
int[] arr = getArr();
|
||||
int res = 0;
|
||||
for (int i = 0; i < arr.length; i++) {
|
||||
// Trigger unswitching only after CCP
|
||||
if (zero == 0) {
|
||||
// Trigger range check after loop unswitching
|
||||
res += arr[j];
|
||||
} else {
|
||||
res += arr[i];
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@Test
|
||||
// Check that Loop Unswitching doubled the number of Parse and Template
|
||||
// Assertion Predicates. Again, the Loop Limit Check Parse Predicate
|
||||
// remains at the Loop Selector since this is a counted loop.
|
||||
@IR(failOn = { IRNode.OPAQUE_TEMPLATE_ASSERTION_PREDICATE },
|
||||
phase = CompilePhase.BEFORE_LOOP_PREDICATION_RC)
|
||||
@IR(counts = { IRNode.OPAQUE_TEMPLATE_ASSERTION_PREDICATE, "2",
|
||||
IRNode.LOOP_PARSE_PREDICATE, "1",
|
||||
IRNode.PROFILED_LOOP_PARSE_PREDICATE, "1",
|
||||
IRNode.LOOP_LIMIT_CHECK_PARSE_PREDICATE, "1",
|
||||
IRNode.AUTO_VECTORIZATION_CHECK_PARSE_PREDICATE, "1" },
|
||||
phase = CompilePhase.BEFORE_LOOP_UNSWITCHING)
|
||||
// After Loop Unswitching and after removing the killed predicates.
|
||||
@IR(counts = { IRNode.OPAQUE_TEMPLATE_ASSERTION_PREDICATE, "4",
|
||||
IRNode.LOOP_PARSE_PREDICATE, "2",
|
||||
IRNode.PROFILED_LOOP_PARSE_PREDICATE, "2",
|
||||
IRNode.LOOP_LIMIT_CHECK_PARSE_PREDICATE, "1",
|
||||
IRNode.AUTO_VECTORIZATION_CHECK_PARSE_PREDICATE, "2" },
|
||||
phase = CompilePhase.PHASEIDEALLOOP2)
|
||||
static int testPredicationBeforeUnswitching(int j, boolean cond) {
|
||||
int[] arr = getArr();
|
||||
int res = 0;
|
||||
for (int i = 0; i < arr.length; i++) {
|
||||
if (cond) {
|
||||
res += arr[j];
|
||||
}
|
||||
res += arr[i];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@Test
|
||||
// Check that Loop Unswitching doubled the number of all Parse Predicates.
|
||||
// Since this is not counted loop, the Loop Limit Check Parse Predicate
|
||||
// has to be cloned to both unswitched loops.
|
||||
@IR(counts = { IRNode.LOOP_PARSE_PREDICATE, "1",
|
||||
IRNode.PROFILED_LOOP_PARSE_PREDICATE, "1",
|
||||
IRNode.LOOP_LIMIT_CHECK_PARSE_PREDICATE, "1",
|
||||
IRNode.AUTO_VECTORIZATION_CHECK_PARSE_PREDICATE, "1" },
|
||||
phase = CompilePhase.BEFORE_LOOP_UNSWITCHING)
|
||||
// After Loop Unswitching and after removing the killed predicates all
|
||||
// Parse Predicates are doubled.
|
||||
@IR(counts = { IRNode.LOOP_PARSE_PREDICATE, "2",
|
||||
IRNode.PROFILED_LOOP_PARSE_PREDICATE, "2",
|
||||
IRNode.LOOP_LIMIT_CHECK_PARSE_PREDICATE, "2",
|
||||
IRNode.AUTO_VECTORIZATION_CHECK_PARSE_PREDICATE, "2" },
|
||||
failOn = { IRNode.COUNTED_LOOP },
|
||||
phase = CompilePhase.PHASEIDEALLOOP1)
|
||||
@IR(failOn = { IRNode.COUNTED_LOOP })
|
||||
static int testUnswitchingUncounted(boolean cond) {
|
||||
int[] arr = getArr();
|
||||
int res = 0;
|
||||
int i = 0;
|
||||
while (i < arr.length) {
|
||||
if (cond) {
|
||||
res += 1;
|
||||
}
|
||||
res += arr[i];
|
||||
|
||||
i = arr[i] + 1; // effectively i += 1, but don't tell the compiler!
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user