From 80fcfaf41aa2d6af30f15877e4466647dbca424e Mon Sep 17 00:00:00 2001 From: Roland Westrelin Date: Thu, 30 Oct 2025 10:34:37 +0000 Subject: [PATCH] 8369435: C2: transform (LShiftX (SubX con0 a), con1) into (SubX con0< TestMemorySegment_SubOfShift.java} | 14 +++---- 4 files changed, 64 insertions(+), 11 deletions(-) rename test/hotspot/jtreg/compiler/loopopts/superword/{TestMemorySegment_8359688.java => TestMemorySegment_SubOfShift.java} (86%) diff --git a/src/hotspot/share/opto/mulnode.cpp b/src/hotspot/share/opto/mulnode.cpp index 6940538e155..280781f686b 100644 --- a/src/hotspot/share/opto/mulnode.cpp +++ b/src/hotspot/share/opto/mulnode.cpp @@ -1080,6 +1080,19 @@ Node* LShiftNode::IdealIL(PhaseGVN* phase, bool can_reshape, BasicType bt) { } } } + // Check for "(con0 - X) << con1" + // Transform is legal, but check for profit. Avoid breaking 'i2s' + // and 'i2b' patterns which typically fold into 'StoreC/StoreB'. + if (add1_op == Op_Sub(bt) && (bt != T_INT || con < 16)) { // Left input is a sub? + // Left input is a sub from a constant? + const TypeInteger* t11 = phase->type(add1->in(1))->isa_integer(bt); + if (t11 != nullptr && t11->is_con()) { + // Compute X << con0 + Node* lsh = phase->transform(LShiftNode::make(add1->in(2), in(2), bt)); + // Compute (con1<integercon(java_shift_left(t11->get_con_as_long(bt), con, bt), bt), lsh, bt); + } + } // Check for "(x >> C1) << C2" if (add1_op == Op_RShift(bt) || add1_op == Op_URShift(bt)) { diff --git a/test/hotspot/jtreg/compiler/c2/irTests/LShiftINodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/LShiftINodeIdealizationTests.java index 7db56e2a878..c7a35a5a265 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/LShiftINodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/LShiftINodeIdealizationTests.java @@ -65,6 +65,7 @@ public class LShiftINodeIdealizationTests { "testLShiftOfAndOfRShift", "testLShiftOfAndOfURShift", "testLShiftOfAndOfCon", + "testShiftOfSubConstant", }) public void runMethod() { int a = RunInfo.getRandom().nextInt(); @@ -122,6 +123,7 @@ public class LShiftINodeIdealizationTests { Asserts.assertEQ((a + a) << 31, testLargeShiftOfAddSameInput(a)); Asserts.assertEQ(((a + 1) << 1) + 1, testShiftOfAddConstant(a)); Asserts.assertEQ((a & ((1 << (32 - 10)) -1)) << 10, testLShiftOfAndOfCon(a)); + Asserts.assertEQ(((1 - a) << 1) + 1, testShiftOfSubConstant(a)); assertDoubleShiftResult(a); } @@ -359,17 +361,45 @@ public class LShiftINodeIdealizationTests { @Test @IR(counts = { IRNode.ADD_I, "1"} , failOn = { IRNode.LSHIFT_I, IRNode.RSHIFT_I } ) @Arguments( values = { Argument.NUMBER_42 }) - public void testStoreShort(int x) { + public void testStoreShort1(int x) { shortField = (short)(x + x); } @Test @IR(counts = { IRNode.ADD_I, "1"} , failOn = { IRNode.LSHIFT_I, IRNode.RSHIFT_I } ) @Arguments( values = { Argument.NUMBER_42 }) - public void testStoreByte(int x) { + public void testStoreByte1(int x) { byteField = (byte)(x + x); } + @Test + @IR(counts = { IRNode.ADD_I, "1"} , failOn = { IRNode.LSHIFT_I, IRNode.RSHIFT_I } ) + @Arguments( values = { Argument.NUMBER_42 }) + public void testStoreShort2(int x) { + shortField = (short)(x + 1); + } + + @Test + @IR(counts = { IRNode.ADD_I, "1"} , failOn = { IRNode.LSHIFT_I, IRNode.RSHIFT_I } ) + @Arguments( values = { Argument.NUMBER_42 }) + public void testStoreByte2(int x) { + byteField = (byte)(x + 1); + } + + @Test + @IR(counts = { IRNode.SUB_I, "1"} , failOn = { IRNode.LSHIFT_I, IRNode.RSHIFT_I } ) + @Arguments( values = { Argument.NUMBER_42 }) + public void testStoreShort3(int x) { + shortField = (short)(1 - x); + } + + @Test + @IR(counts = { IRNode.SUB_I, "1"} , failOn = { IRNode.LSHIFT_I, IRNode.RSHIFT_I } ) + @Arguments( values = { Argument.NUMBER_42 }) + public void testStoreByte3(int x) { + byteField = (byte)(1 - x); + } + static int otherInput; @Test @@ -409,4 +439,10 @@ public class LShiftINodeIdealizationTests { public int testLShiftOfAndOfCon(int x) { return (x & ((1 << (32 - 10)) -1)) << 10; } + + @Test + @IR(counts = { IRNode.LSHIFT_I, "1", IRNode.SUB_I, "1" }, failOn = { IRNode.ADD_I }) + public int testShiftOfSubConstant(int x) { + return ((1 - x) << 1) + 1; + } } diff --git a/test/hotspot/jtreg/compiler/c2/irTests/LShiftLNodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/LShiftLNodeIdealizationTests.java index f489a348eef..6e58d6a04fe 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/LShiftLNodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/LShiftLNodeIdealizationTests.java @@ -63,6 +63,7 @@ public class LShiftLNodeIdealizationTests { "testLShiftOfAndOfRShift", "testLShiftOfAndOfURShift", "testLShiftOfAndOfCon", + "testShiftOfSubConstant", }) public void runMethod() { long a = RunInfo.getRandom().nextLong(); @@ -118,6 +119,7 @@ public class LShiftLNodeIdealizationTests { Asserts.assertEQ((a + a) << 63, testLargeShiftOfAddSameInput(a)); Asserts.assertEQ(((a + 1) << 1) + 1, testShiftOfAddConstant(a)); Asserts.assertEQ((a & ((1L << (64 - 10)) -1)) << 10, testLShiftOfAndOfCon(a)); + Asserts.assertEQ(((1L - a) << 1) + 1, testShiftOfSubConstant(a)); assertDoubleShiftResult(a); } @@ -358,4 +360,10 @@ public class LShiftLNodeIdealizationTests { public long testLShiftOfAndOfCon(long x) { return (x & ((1L << (64 - 10)) -1)) << 10; } + + @Test + @IR(counts = { IRNode.LSHIFT_L, "1", IRNode.SUB_L, "1" }, failOn = { IRNode.ADD_L }) + public long testShiftOfSubConstant(long x) { + return ((1 - x) << 1) + 1; + } } diff --git a/test/hotspot/jtreg/compiler/loopopts/superword/TestMemorySegment_8359688.java b/test/hotspot/jtreg/compiler/loopopts/superword/TestMemorySegment_SubOfShift.java similarity index 86% rename from test/hotspot/jtreg/compiler/loopopts/superword/TestMemorySegment_8359688.java rename to test/hotspot/jtreg/compiler/loopopts/superword/TestMemorySegment_SubOfShift.java index cac69901a32..34741d0f2a7 100644 --- a/test/hotspot/jtreg/compiler/loopopts/superword/TestMemorySegment_8359688.java +++ b/test/hotspot/jtreg/compiler/loopopts/superword/TestMemorySegment_SubOfShift.java @@ -30,17 +30,17 @@ import compiler.lib.ir_framework.*; /* * @test - * @bug 8324751 + * @bug 8324751 8369435 * @summary Reported issue: JDK-8359688: C2 SuperWord: missing RCE with MemorySegment * The examples are generated from TestAliasingFuzzer.java * So if you see something change here, you may want to investigate if we * can also tighten up the IR rules there. * @library /test/lib / - * @run driver compiler.loopopts.superword.TestMemorySegment_8359688 + * @run driver compiler.loopopts.superword.TestMemorySegment_SubOfShift */ -public class TestMemorySegment_8359688 { +public class TestMemorySegment_SubOfShift { public static MemorySegment b = MemorySegment.ofArray(new long[4 * 30_000]); @@ -61,17 +61,13 @@ public class TestMemorySegment_8359688 { @Test @Arguments(setup = "setup") - @IR(counts = {IRNode.STORE_VECTOR, "= 0", - IRNode.REPLICATE_L, "= 0", + @IR(counts = {IRNode.STORE_VECTOR, "> 0", + IRNode.REPLICATE_L, "= 1", ".*multiversion.*", "= 0"}, // AutoVectorization Predicate SUFFICES, there is no aliasing phase = CompilePhase.PRINT_IDEAL, applyIfPlatform = {"64-bit", "true"}, applyIf = {"AlignVector", "false"}, applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"}) - // Does not manage to remove all RangeChecks -> no vectorization - // If you see this IR rule fail: investigate JDK-8359688, possibly close it and fix this IR rule! - // Also: consider renaming the file to something more descriptive: what have you fixed with this? - // And: you may now be able to tighten IR rules in TestAliasingFuzzer.java public static void test1(MemorySegment b, int ivLo, int ivHi, int invar) { for (int i = ivLo; i < ivHi; i++) { b.setAtIndex(ValueLayout.JAVA_LONG_UNALIGNED, 30_000L - (long)i + (long)invar, 42);