8369435: C2: transform (LShiftX (SubX con0 a), con1) into (SubX con0<<con1 (LShiftX a con1))

Reviewed-by: epeter, qamai
This commit is contained in:
Roland Westrelin 2025-10-30 10:34:37 +00:00
parent 87e5341d78
commit 80fcfaf41a
4 changed files with 64 additions and 11 deletions

View File

@ -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<<con0) - (X<<con0)
return SubNode::make(phase->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)) {

View File

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

View File

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

View File

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