8378413: C2: Missed Ideal optimization opportunity in PhaseIterGVN for URShiftI still exists

Reviewed-by: qamai, dlong
This commit is contained in:
Kerem Kat 2026-03-05 11:05:01 +00:00 committed by Quan Anh Mai
parent ec3b58b5e0
commit 8a9b63f76f
2 changed files with 148 additions and 4 deletions

View File

@ -1539,15 +1539,20 @@ Node* URShiftINode::Ideal(PhaseGVN* phase, bool can_reshape) {
Node *add = in(1);
if (in1_op == Op_AddI) {
Node *lshl = add->in(1);
Node *y = add->in(2);
if (lshl->Opcode() != Op_LShiftI) {
lshl = add->in(2);
y = add->in(1);
}
// Compare shift counts by value, not by node pointer, to also match a not-yet-normalized
// negative constant (e.g. -1 vs 31)
int lshl_con = 0;
if (lshl->Opcode() == Op_LShiftI &&
const_shift_count(phase, lshl, &lshl_con) &&
(lshl_con & (BitsPerJavaInteger - 1)) == con) {
Node *y_z = phase->transform( new URShiftINode(add->in(2),in(2)) );
Node *sum = phase->transform( new AddINode( lshl->in(1), y_z ) );
return new AndINode( sum, phase->intcon(mask) );
Node *y_z = phase->transform(new URShiftINode(y, in(2)));
Node *sum = phase->transform(new AddINode(lshl->in(1), y_z));
return new AndINode(sum, phase->intcon(mask));
}
}
@ -1699,13 +1704,18 @@ Node* URShiftLNode::Ideal(PhaseGVN* phase, bool can_reshape) {
const TypeInt *t2 = phase->type(in(2))->isa_int();
if (add->Opcode() == Op_AddL) {
Node *lshl = add->in(1);
Node *y = add->in(2);
if (lshl->Opcode() != Op_LShiftL) {
lshl = add->in(2);
y = add->in(1);
}
// Compare shift counts by value, not by node pointer, to also match a not-yet-normalized
// negative constant (e.g. -1 vs 63)
int lshl_con = 0;
if (lshl->Opcode() == Op_LShiftL &&
const_shift_count(phase, lshl, &lshl_con) &&
(lshl_con & (BitsPerJavaLong - 1)) == con) {
Node* y_z = phase->transform(new URShiftLNode(add->in(2), in(2)));
Node* y_z = phase->transform(new URShiftLNode(y, in(2)));
Node* sum = phase->transform(new AddLNode(lshl->in(1), y_z));
return new AndLNode(sum, phase->longcon(mask));
}

View File

@ -0,0 +1,134 @@
/*
* Copyright (c) 2026, 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.c2.gvn;
import compiler.lib.ir_framework.*;
import jdk.test.lib.Asserts;
import jdk.test.lib.Utils;
import java.util.Random;
/*
* @test
* @bug 8378413
* @key randomness
* @summary Verify that URShift{I,L}Node::Ideal optimizes ((x << C) + y) >>> C
* regardless of Add input order, i.e. it is commutative w.r.t. the addition.
* @library /test/lib /
* @run driver ${test.main.class}
* @run driver ${test.main.class} -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions
* -XX:VerifyIterativeGVN=1110 -Xbatch -XX:CompileCommand=compileonly,${test.main.class}::*
*/
public class MissedURShiftIAddILShiftIdeal {
private static final Random RANDOM = Utils.getRandomInstance();
public static void main(String[] args) {
TestFramework.run();
}
@Run(test = {"testI", "testICommuted", "testIComputedY",
"testL", "testLCommuted", "testLComputedY"})
public void runMethod() {
int xi = RANDOM.nextInt();
int yi = RANDOM.nextInt();
int ai = RANDOM.nextInt();
int bi = RANDOM.nextInt();
long xl = RANDOM.nextLong();
long yl = RANDOM.nextLong();
long al = RANDOM.nextLong();
long bl = RANDOM.nextLong();
assertResultI(xi, yi, ai, bi);
assertResultL(xl, yl, al, bl);
}
@DontCompile
public void assertResultI(int x, int y, int a, int b) {
Asserts.assertEQ(((x << 3) + y) >>> 3, testI(x, y));
Asserts.assertEQ((y + (x << 5)) >>> 5, testICommuted(x, y));
Asserts.assertEQ(((x << 7) + (a ^ b)) >>> 7, testIComputedY(x, a, b));
}
@DontCompile
public void assertResultL(long x, long y, long a, long b) {
Asserts.assertEQ(((x << 9) + y) >>> 9, testL(x, y));
Asserts.assertEQ((y + (x << 11)) >>> 11, testLCommuted(x, y));
Asserts.assertEQ(((x << 13) + (a ^ b)) >>> 13, testLComputedY(x, a, b));
}
@Test
// ((x << 3) + y) >>> 3 => (x + (y >>> 3)) & mask
@IR(counts = {IRNode.LSHIFT_I, "0",
IRNode.URSHIFT_I, "1",
IRNode.AND_I, "1"})
static int testI(int x, int y) {
return ((x << 3) + y) >>> 3;
}
@Test
// (y + (x << 5)) >>> 5 => (x + (y >>> 5)) & mask (commuted Add)
@IR(counts = {IRNode.LSHIFT_I, "0",
IRNode.URSHIFT_I, "1",
IRNode.AND_I, "1"})
static int testICommuted(int x, int y) {
return (y + (x << 5)) >>> 5;
}
@Test
// ((x << 7) + (a ^ b)) >>> 7 => (x + ((a ^ b) >>> 7)) & mask
// Computed y (a ^ b) has higher _idx than LShift, so LShift stays in Add's in(1).
@IR(counts = {IRNode.LSHIFT_I, "0",
IRNode.URSHIFT_I, "1",
IRNode.AND_I, "1"})
static int testIComputedY(int x, int a, int b) {
return ((x << 7) + (a ^ b)) >>> 7;
}
@Test
// ((x << 9) + y) >>> 9 => (x + (y >>> 9)) & mask
@IR(counts = {IRNode.LSHIFT_L, "0",
IRNode.URSHIFT_L, "1",
IRNode.AND_L, "1"})
static long testL(long x, long y) {
return ((x << 9) + y) >>> 9;
}
@Test
// (y + (x << 11)) >>> 11 => (x + (y >>> 11)) & mask (commuted Add)
@IR(counts = {IRNode.LSHIFT_L, "0",
IRNode.URSHIFT_L, "1",
IRNode.AND_L, "1"})
static long testLCommuted(long x, long y) {
return (y + (x << 11)) >>> 11;
}
@Test
// ((x << 13) + (a ^ b)) >>> 13 => (x + ((a ^ b) >>> 13)) & mask
// Computed y (a ^ b) has higher _idx than LShift, so LShift stays in Add's in(1).
@IR(counts = {IRNode.LSHIFT_L, "0",
IRNode.URSHIFT_L, "1",
IRNode.AND_L, "1"})
static long testLComputedY(long x, long a, long b) {
return ((x << 13) + (a ^ b)) >>> 13;
}
}