diff --git a/src/hotspot/share/opto/mulnode.cpp b/src/hotspot/share/opto/mulnode.cpp index cc204897f91..9f2fb9a3d1a 100644 --- a/src/hotspot/share/opto/mulnode.cpp +++ b/src/hotspot/share/opto/mulnode.cpp @@ -813,6 +813,8 @@ Node *LShiftINode::Ideal(PhaseGVN *phase, bool can_reshape) { // Left input is an add of the same number? if (add1->in(1) == add1->in(2)) { // Convert "(x + x) << c0" into "x << (c0 + 1)" + // In general, this optimization cannot be applied for c0 == 31 since + // 2x << 31 != x << 32 = x << 0 = x (e.g. x = 1: 2 << 31 = 0 != 1) return new LShiftINode(add1->in(1), phase->intcon(con + 1)); } @@ -930,8 +932,13 @@ Node *LShiftLNode::Ideal(PhaseGVN *phase, bool can_reshape) { assert( add1 != add1->in(1), "dead loop in LShiftLNode::Ideal" ); // Left input is an add of the same number? - if (add1->in(1) == add1->in(2)) { + if (con != (BitsPerJavaLong - 1) && add1->in(1) == add1->in(2)) { // Convert "(x + x) << c0" into "x << (c0 + 1)" + // Can only be applied if c0 != 63 because: + // (x + x) << 63 = 2x << 63, while + // (x + x) << 63 --transform--> x << 64 = x << 0 = x (!= 2x << 63, for example for x = 1) + // According to the Java spec, chapter 15.19, we only consider the six lowest-order bits of the right-hand operand + // (i.e. "right-hand operand" & 0b111111). Therefore, x << 64 is the same as x << 0 (64 = 0b10000000 & 0b0111111 = 0). return new LShiftLNode(add1->in(1), phase->intcon(con + 1)); } diff --git a/test/hotspot/jtreg/compiler/c2/irTests/TestIRLShiftIdeal_XPlusX_LShiftC.java b/test/hotspot/jtreg/compiler/c2/irTests/TestIRLShiftIdeal_XPlusX_LShiftC.java index 3c82b0060fd..1f411cd3fe6 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/TestIRLShiftIdeal_XPlusX_LShiftC.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/TestIRLShiftIdeal_XPlusX_LShiftC.java @@ -28,7 +28,7 @@ /* * @test - * @bug 8278114 + * @bug 8278114 8288564 * @summary Test that transformation from (x + x) >> c to x >> (c + 1) works as intended. * @library /test/lib / * @requires vm.compiler2.enabled @@ -149,4 +149,34 @@ private void assertC2Compiled(RunInfo info) { Asserts.assertTrue(info.isTestC2Compiled()); } } + + @Test + @IR(failOn = {IRNode.MUL_I}) + @IR(counts = {IRNode.LSHIFT_I, "1", + IRNode.ADD_I, "1"}) + public int testIntRandom(int x) { + return (x + x) << 31; + } + + @Run(test = "testIntRandom") + public void runTestIntRandom() { + int random = RunInfo.getRandom().nextInt(); + int interpreterResult = (random + random) << 31; + Asserts.assertEQ(testIntRandom(random), interpreterResult); + } + + @Test + @IR(failOn = {IRNode.MUL_L}) + @IR(counts = {IRNode.LSHIFT_L, "1", + IRNode.ADD_L, "1"}) + public long testLongRandom(long x) { + return (x + x) << 63; + } + + @Run(test = "testLongRandom") + public void runTestLongRandom() { + long random = RunInfo.getRandom().nextLong(); + long interpreterResult = (random + random) << 63; + Asserts.assertEQ(testLongRandom(random), interpreterResult); + } }