diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index 0699255a59d..3cbbc114778 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -2007,19 +2007,6 @@ void PhaseIterGVN::verify_Identity_for(Node* n) { case Op_ConvI2L: return; - // AddINode::Identity - // Converts (x-y)+y to x - // Could be issue with notification - // - // Turns out AddL does the same. - // - // Found with: - // compiler/c2/Test6792161.java - // -ea -esa -XX:CompileThreshold=100 -XX:+UnlockExperimentalVMOptions -server -XX:-TieredCompilation -XX:+IgnoreUnrecognizedVMOptions -XX:VerifyIterativeGVN=1110 - case Op_AddI: - case Op_AddL: - return; - // AbsINode::Identity // Not investigated yet. case Op_AbsI: @@ -2778,6 +2765,18 @@ void PhaseIterGVN::add_users_of_use_to_worklist(Node* n, Node* use, Unique_Node_ }; use->visit_uses(push_and_to_worklist, is_boundary); } + + // If changed Sub inputs, check Add for identity. + // e.g., (x - y) + y -> x; x + (y - x) -> y. + if (use_op == Op_SubI || use_op == Op_SubL) { + const int add_op = (use_op == Op_SubI) ? Op_AddI : Op_AddL; + for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) { + Node* u = use->fast_out(i2); + if (u->Opcode() == add_op) { + worklist.push(u); + } + } + } } /** diff --git a/test/hotspot/jtreg/compiler/c2/igvn/TestMissingAddSubElimination.java b/test/hotspot/jtreg/compiler/c2/igvn/TestMissingAddSubElimination.java new file mode 100644 index 00000000000..1679e131505 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/igvn/TestMissingAddSubElimination.java @@ -0,0 +1,117 @@ +/* + * 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.igvn; + +import compiler.lib.ir_framework.*; +import java.util.Random; +import jdk.test.lib.Asserts; +import jdk.test.lib.Utils; + +/* + * @test + * @bug 8373731 + * @summary C2 IGVN should eliminate a + (b - c) after c = a * i folds to a, + * i.e. a + (b - a) -> b + * @library /test/lib / + * @run driver ${test.main.class} + */ + +public class TestMissingAddSubElimination { + private static final Random R = Utils.getRandomInstance(); + + public static void main(String[] args) { + TestFramework.runWithFlags("-XX:+IgnoreUnrecognizedVMOptions", + "-XX:VerifyIterativeGVN=1000", + "-XX:CompileCommand=compileonly,compiler.c2.igvn.TestMissingAddSubElimination::*"); + } + + @Run(test = {"testAddI1", "testAddI2", "testAddL1", "testAddL2"}) + public void runTestAdd(){ + int x = R.nextInt(); + int y = R.nextInt(); + Asserts.assertEQ(testAddI1(x, y), y); + Asserts.assertEQ(testAddI2(x, y), x); + + long xl = R.nextLong(); + long yl = R.nextLong(); + Asserts.assertEQ(testAddL1(xl, yl), yl); + Asserts.assertEQ(testAddL2(xl, yl), xl); + } + + // int: x + (y - x) -> y + @Test + @IR(counts = { + IRNode.ADD_I, "0", + IRNode.SUB_I, "0", + IRNode.MUL_I, "0" + }) + int testAddI1(int x, int y) { + int i; + for (i = -10; i < 1; i++) { } + int c = x * i; + return x + (y - c); + } + + // int: (x - y) + y -> x + @Test + @IR(counts = { + IRNode.ADD_I, "0", + IRNode.SUB_I, "0", + IRNode.MUL_I, "0" + }) + int testAddI2(int x, int y) { + int i; + for (i = -10; i < 1; i++) { } + int c = y * i; + return (x - c) + y; + } + + // long: x + (y - x) -> y + @Test + @IR(counts = { + IRNode.ADD_L, "0", + IRNode.SUB_L, "0", + IRNode.MUL_L, "0" + }) + long testAddL1(long x, long y) { + int i; + for (i = -10; i < 1; i++) { } + long c = x * i; + return x + (y - c); + } + + // long: (x - y) + y -> x + @Test + @IR(counts = { + IRNode.ADD_L, "0", + IRNode.SUB_L, "0", + IRNode.MUL_L, "0" + }) + long testAddL2(long x, long y) { + int i; + for (i = -10; i < 1; i++) { } + long c = y * i; + return (x - c) + y; + } +} \ No newline at end of file