From c6afd3ced68d231a1d466bd62403409db9e8a96e Mon Sep 17 00:00:00 2001 From: Kerem Kat Date: Thu, 12 Mar 2026 09:24:55 +0000 Subject: [PATCH] 8379460: C2: Notify AddI/AddL to URShiftI/URShiftL users Reviewed-by: qamai, dfenacci, mchevalier --- src/hotspot/share/opto/phaseX.cpp | 7 ++ .../c2/igvn/TestURShiftAddNotification.java | 89 +++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 test/hotspot/jtreg/compiler/c2/igvn/TestURShiftAddNotification.java diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index c77316e7d0b..082746f80b7 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -2581,6 +2581,13 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ return u->Opcode() == Op_CmpU; }); } + // If changed AddI/AddL inputs, check URShift users for + // "((X << z) + Y) >>> z" optimization in URShift{I,L}Node::Ideal. + if (use_op == Op_AddI || use_op == Op_AddL) { + add_users_to_worklist_if(worklist, use, [](Node* u) { + return u->Opcode() == Op_URShiftI || u->Opcode() == Op_URShiftL; + }); + } // If changed AndI/AndL inputs, check RShift/URShift users for "(x & mask) >> shift" optimization opportunity if (use_op == Op_AndI || use_op == Op_AndL) { add_users_to_worklist_if(worklist, use, [](Node* u) { diff --git a/test/hotspot/jtreg/compiler/c2/igvn/TestURShiftAddNotification.java b/test/hotspot/jtreg/compiler/c2/igvn/TestURShiftAddNotification.java new file mode 100644 index 00000000000..8ca192ea38b --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/igvn/TestURShiftAddNotification.java @@ -0,0 +1,89 @@ +/* + * Copyright Amazon.com Inc. 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.igvn; + +import compiler.lib.ir_framework.*; +import jdk.test.lib.Asserts; +import jdk.test.lib.Platform; +import jdk.test.lib.Utils; +import java.util.Random; + +/* + * @test + * @bug 8379460 + * @key randomness + * @summary When AddI/AddL inputs change during IGVN, URShift users must be re-added + * to the IGVN worklist so they can re-check the ((X << z) + Y) >>> z optimization. + * @library /test/lib / + * @run driver ${test.main.class} + */ +public class TestURShiftAddNotification { + + private static final Random RANDOM = Utils.getRandomInstance(); + + public static void main(String[] args) { + var framework = new TestFramework(); + framework.addScenarios(new Scenario(0)); + if (Platform.isDebugBuild()) { + framework.addScenarios(new Scenario(1, "-XX:VerifyIterativeGVN=1110")); + } + framework.start(); + } + + // The trick: a loop whose exit value is only known after loop optimization. + // During initial GVN, i is a Phi, so (x << C) * i stays as MulI — URShift + // can't see the LShiftI input through the MulI. After loop opts resolve + // i = 1, MulI identity-folds to LShiftI (same type, no cascade), and + // without the fix URShift is never re-notified about the new LShiftI input. + + @Run(test = {"testI", "testL"}) + public void runTests() { + int xi = RANDOM.nextInt(); + int yi = RANDOM.nextInt(); + long xl = RANDOM.nextLong(); + long yl = RANDOM.nextLong(); + + Asserts.assertEQ(((xi << 3) + yi) >>> 3, testI(xi, yi)); + Asserts.assertEQ(((xl << 9) + yl) >>> 9, testL(xl, yl)); + } + + @Test + @IR(failOn = {IRNode.LSHIFT_I, IRNode.MUL_I}, + counts = {IRNode.URSHIFT_I, "1", IRNode.AND_I, "1"}) + static int testI(int x, int y) { + int i; + for (i = -10; i < 1; i++) { } + int c = (x << 3) * i; + return (c + y) >>> 3; + } + + @Test + @IR(failOn = {IRNode.LSHIFT_L, IRNode.MUL_L}, + counts = {IRNode.URSHIFT_L, "1", IRNode.AND_L, "1"}) + static long testL(long x, long y) { + int i; + for (i = -10; i < 1; i++) { } + long c = (x << 9) * i; + return (c + y) >>> 9; + } +}