From e6b1cb897d9c75b34744c7d24f72abcec9986b0b Mon Sep 17 00:00:00 2001 From: Francisco Ferrari Bihurriet Date: Thu, 14 Aug 2025 07:49:26 +0200 Subject: [PATCH] Improve the IR test to add the new covered cases I also checked the test is now failing in the master branch (at f95af744b07a9ec87e2507b3d584cbcddc827bbd). --- .../compiler/c2/gvn/TestBoolNodeGVN.java | 251 +++++++++++++++++- 1 file changed, 239 insertions(+), 12 deletions(-) diff --git a/test/hotspot/jtreg/compiler/c2/gvn/TestBoolNodeGVN.java b/test/hotspot/jtreg/compiler/c2/gvn/TestBoolNodeGVN.java index d3d86e64164..841aa9a9867 100644 --- a/test/hotspot/jtreg/compiler/c2/gvn/TestBoolNodeGVN.java +++ b/test/hotspot/jtreg/compiler/c2/gvn/TestBoolNodeGVN.java @@ -41,16 +41,58 @@ public class TestBoolNodeGVN { } /** - * Test changing ((x & m) u<= m) or ((m & x) u<= m) to always true, same with ((x & m) u< m+1) and ((m & x) u< m+1) - * The test is only applicable to x64, aarch64 and riscv64 for having Integer.compareUnsigned - * intrinsified. + * Test CmpUNode::Value_cmpu_and_mask optimizations for cases 1a and 1b. + * The test is only applicable to x64, aarch64 and riscv64 for having + * Integer.compareUnsigned intrinsified. */ + + @Test + @Arguments(values = {Argument.DEFAULT, Argument.DEFAULT}) + @IR(counts = {IRNode.CMP_U, "1"}, + phase = CompilePhase.AFTER_PARSING, + applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}) + public static boolean testCase1aDoNotOptimizeForEQxm(int x, int m) { + // [BoolTest::eq] 1a) x & m =u m is unknown + return Integer.compareUnsigned((x & m), m) == 0; + } + + @Test + @Arguments(values = {Argument.DEFAULT, Argument.DEFAULT}) + @IR(counts = {IRNode.CMP_U, "1"}, + phase = CompilePhase.AFTER_PARSING, + applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}) + public static boolean testCase1aDoNotOptimizeForEQmx(int x, int m) { + // [BoolTest::eq] 1a) m & x =u m is unknown + return Integer.compareUnsigned((m & x), m) == 0; + } + + @Test + @Arguments(values = {Argument.DEFAULT, Argument.DEFAULT}) + @IR(counts = {IRNode.CMP_U, "1"}, + phase = CompilePhase.AFTER_PARSING, + applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}) + public static boolean testCase1aDoNotOptimizeForNExm(int x, int m) { + // [BoolTest::ne] 1a) x & m ≠u m is unknown + return Integer.compareUnsigned((x & m), m) != 0; + } + + @Test + @Arguments(values = {Argument.DEFAULT, Argument.DEFAULT}) + @IR(counts = {IRNode.CMP_U, "1"}, + phase = CompilePhase.AFTER_PARSING, + applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}) + public static boolean testCase1aDoNotOptimizeForNEmx(int x, int m) { + // [BoolTest::ne] 1a) m & x ≠u m is unknown + return Integer.compareUnsigned((m & x), m) != 0; + } + @Test @Arguments(values = {Argument.DEFAULT, Argument.DEFAULT}) @IR(failOn = IRNode.CMP_U, phase = CompilePhase.AFTER_PARSING, applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}) - public static boolean testShouldReplaceCpmUCase1(int x, int m) { + public static boolean testCase1aOptimizeAsTrueForLExm(int x, int m) { + // [BoolTest::le] 1a) x & m ≤u m is always true return Integer.compareUnsigned((x & m), m) <= 0; } @@ -59,16 +101,166 @@ public class TestBoolNodeGVN { @IR(failOn = IRNode.CMP_U, phase = CompilePhase.AFTER_PARSING, applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}) - public static boolean testShouldReplaceCpmUCase2(int x, int m) { + public static boolean testCase1aOptimizeAsTrueForLEmx(int x, int m) { + // [BoolTest::le] 1a) m & x ≤u m is always true return Integer.compareUnsigned((m & x), m) <= 0; } + @Test + @Arguments(values = {Argument.DEFAULT, Argument.DEFAULT}) + @IR(counts = {IRNode.CMP_U, "1"}, + phase = CompilePhase.AFTER_PARSING, + applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}) + public static boolean testCase1aDoNotOptimizeForGExm(int x, int m) { + // [BoolTest::ge] 1a) x & m ≥u m is unknown + return Integer.compareUnsigned((x & m), m) >= 0; + } + + @Test + @Arguments(values = {Argument.DEFAULT, Argument.DEFAULT}) + @IR(counts = {IRNode.CMP_U, "1"}, + phase = CompilePhase.AFTER_PARSING, + applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}) + public static boolean testCase1aDoNotOptimizeForGEmx(int x, int m) { + // [BoolTest::ge] 1a) m & x ≥u m is unknown + return Integer.compareUnsigned((m & x), m) >= 0; + } + + @Test + @Arguments(values = {Argument.DEFAULT, Argument.DEFAULT}) + @IR(counts = {IRNode.CMP_U, "1"}, + phase = CompilePhase.AFTER_PARSING, + applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}) + public static boolean testCase1aDoNotOptimizeForLTxm(int x, int m) { + // [BoolTest::lt] 1a) x & m u m is always false + return Integer.compareUnsigned((x & m), m) > 0; + } + + @Test + @Arguments(values = {Argument.DEFAULT, Argument.DEFAULT}) + @IR(failOn = IRNode.CMP_U, + phase = CompilePhase.AFTER_PARSING, + applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}) + public static boolean testCase1aOptimizeAsFalseForGTmx(int x, int m) { + // [BoolTest::gt] 1a) m & x >u m is always false + return Integer.compareUnsigned((m & x), m) > 0; + } + @Test @Arguments(values = {Argument.DEFAULT, Argument.RANDOM_EACH}) @IR(failOn = IRNode.CMP_U, phase = CompilePhase.AFTER_PARSING, applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}) - public static boolean testShouldReplaceCpmUCase3(int x, int m) { + public static boolean testCase1bOptimizeAsFalseForEQxm(int x, int m) { + // [BoolTest::eq] 1b) x & m =u m + 1 is always false (if m ≠ -1) + m = Math.max(0, m); + return Integer.compareUnsigned((x & m), m + 1) == 0; + } + + @Test + @Arguments(values = {Argument.DEFAULT, Argument.RANDOM_EACH}) + @IR(failOn = IRNode.CMP_U, + phase = CompilePhase.AFTER_PARSING, + applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}) + public static boolean testCase1bOptimizeAsFalseForEQmx(int x, int m) { + // [BoolTest::eq] 1b) m & x =u m + 1 is always false (if m ≠ -1) + m = Math.max(0, m); + return Integer.compareUnsigned((m & x), m + 1) == 0; + } + + @Test + @Arguments(values = {Argument.DEFAULT, Argument.RANDOM_EACH}) + @IR(failOn = IRNode.CMP_U, + phase = CompilePhase.AFTER_PARSING, + applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}) + public static boolean testCase1bOptimizeAsTrueForNExm(int x, int m) { + // [BoolTest::ne] 1b) x & m ≠u m + 1 is always true (if m ≠ -1) + m = Math.max(0, m); + return Integer.compareUnsigned((x & m), m + 1) != 0; + } + + @Test + @Arguments(values = {Argument.DEFAULT, Argument.RANDOM_EACH}) + @IR(failOn = IRNode.CMP_U, + phase = CompilePhase.AFTER_PARSING, + applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}) + public static boolean testCase1bOptimizeAsTrueForNEmx(int x, int m) { + // [BoolTest::ne] 1b) m & x ≠u m + 1 is always true (if m ≠ -1) + m = Math.max(0, m); + return Integer.compareUnsigned((m & x), m + 1) != 0; + } + + @Test + @Arguments(values = {Argument.DEFAULT, Argument.RANDOM_EACH}) + @IR(failOn = IRNode.CMP_U, + phase = CompilePhase.AFTER_PARSING, + applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}) + public static boolean testCase1bOptimizeAsTrueForLExm(int x, int m) { + // [BoolTest::le] 1b) x & m ≤u m + 1 is always true (if m ≠ -1) + m = Math.max(0, m); + return Integer.compareUnsigned((x & m), m + 1) <= 0; + } + + @Test + @Arguments(values = {Argument.DEFAULT, Argument.RANDOM_EACH}) + @IR(failOn = IRNode.CMP_U, + phase = CompilePhase.AFTER_PARSING, + applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}) + public static boolean testCase1bOptimizeAsTrueForLEmx(int x, int m) { + // [BoolTest::le] 1b) m & x ≤u m + 1 is always true (if m ≠ -1) + m = Math.max(0, m); + return Integer.compareUnsigned((m & x), m + 1) <= 0; + } + + @Test + @Arguments(values = {Argument.DEFAULT, Argument.RANDOM_EACH}) + @IR(failOn = IRNode.CMP_U, + phase = CompilePhase.AFTER_PARSING, + applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}) + public static boolean testCase1bOptimizeAsFalseForGExm(int x, int m) { + // [BoolTest::ge] 1b) x & m ≥u m + 1 is always false (if m ≠ -1) + m = Math.max(0, m); + return Integer.compareUnsigned((x & m), m + 1) >= 0; + } + + @Test + @Arguments(values = {Argument.DEFAULT, Argument.RANDOM_EACH}) + @IR(failOn = IRNode.CMP_U, + phase = CompilePhase.AFTER_PARSING, + applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}) + public static boolean testCase1bOptimizeAsFalseForGEmx(int x, int m) { + // [BoolTest::ge] 1b) m & x ≥u m + 1 is always false (if m ≠ -1) + m = Math.max(0, m); + return Integer.compareUnsigned((m & x), m + 1) >= 0; + } + + @Test + @Arguments(values = {Argument.DEFAULT, Argument.RANDOM_EACH}) + @IR(failOn = IRNode.CMP_U, + phase = CompilePhase.AFTER_PARSING, + applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}) + public static boolean testCase1bOptimizeAsTrueForLTxm(int x, int m) { + // [BoolTest::lt] 1b) x & m u m + 1 is always false (if m ≠ -1) + m = Math.max(0, m); + return Integer.compareUnsigned((x & m), m + 1) > 0; + } + + @Test + @Arguments(values = {Argument.DEFAULT, Argument.RANDOM_EACH}) + @IR(failOn = IRNode.CMP_U, + phase = CompilePhase.AFTER_PARSING, + applyIfPlatformOr = {"x64", "true", "aarch64", "true", "riscv64", "true"}) + public static boolean testCase1bOptimizeAsFalseForGTmx(int x, int m) { + // [BoolTest::gt] 1b) m & x >u m + 1 is always false (if m ≠ -1) + m = Math.max(0, m); + return Integer.compareUnsigned((m & x), m + 1) > 0; + } + @Test @Arguments(values = {Argument.DEFAULT, Argument.DEFAULT}) @IR(counts = {IRNode.CMP_U, "1"}, // m could be -1 and thus optimization cannot be applied @@ -145,11 +360,23 @@ public class TestBoolNodeGVN { for (int x : values) { for (int m : values) { - if (!testShouldReplaceCpmUCase1(x, m) || - !testShouldReplaceCpmUCase2(x, m) || - !testShouldReplaceCpmUCase3(x, m) || - !testShouldReplaceCpmUCase4(x, m)) { - throw new RuntimeException("Bad result for x = " + x + " and m = " + m + ", expected always true"); + if (!testCase1aOptimizeAsTrueForLExm(x, m) || + !testCase1aOptimizeAsTrueForLEmx(x, m) || + testCase1aOptimizeAsFalseForGTxm(x, m) || + testCase1aOptimizeAsFalseForGTmx(x, m) || + testCase1bOptimizeAsFalseForEQxm(x, m) || + testCase1bOptimizeAsFalseForEQmx(x, m) || + !testCase1bOptimizeAsTrueForNExm(x, m) || + !testCase1bOptimizeAsTrueForNEmx(x, m) || + !testCase1bOptimizeAsTrueForLExm(x, m) || + !testCase1bOptimizeAsTrueForLEmx(x, m) || + testCase1bOptimizeAsFalseForGExm(x, m) || + testCase1bOptimizeAsFalseForGEmx(x, m) || + !testCase1bOptimizeAsTrueForLTxm(x, m) || + !testCase1bOptimizeAsTrueForLTmx(x, m) || + testCase1bOptimizeAsFalseForGTxm(x, m) || + testCase1bOptimizeAsFalseForGTmx(x, m)) { + throw new RuntimeException("Bad result for x = " + x + " and m = " + m); } } }