From 621f0d86992f012f000fc00a4f8091b64478f396 Mon Sep 17 00:00:00 2001 From: j3graham Date: Mon, 13 Jan 2025 16:50:49 -0500 Subject: [PATCH 01/57] test for xor const folding --- .../c2/irTests/ConstFoldingTests.java | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 test/hotspot/jtreg/compiler/c2/irTests/ConstFoldingTests.java diff --git a/test/hotspot/jtreg/compiler/c2/irTests/ConstFoldingTests.java b/test/hotspot/jtreg/compiler/c2/irTests/ConstFoldingTests.java new file mode 100644 index 0000000000000..37f1772c59cb7 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/irTests/ConstFoldingTests.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2025, 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.irTests; + +import compiler.lib.ir_framework.*; + +/* + * @test + * @summary verify that constant folding is done on xor + * @library /test/lib / + * @requires vm.compiler2.enabled + * @run driver compiler.c2.irTests.ConstFoldingTests + */ + +public class ConstFoldingTests { + + public static void main(String[] args) { + TestFramework.run(); + } + + @Test + @IR(failOn = {IRNode.XOR}) + @IR(counts = {IRNode.CON_I, "1"}) + // Checks (c1 ^c2) => c3 (constant folded) + public int testConstXorI() { + int c = 42; + return c ^ 2025; + } + + @Test + @IR(failOn = {IRNode.XOR}) + @IR(counts = {IRNode.CON_L, "1"}) + // Checks (c1 ^ c2) => c3 (constant folded) + public long testConstXorL() { + long c = 42; + return c ^ 2025; + } + +} From 6b4e80da6c993f45300eb65cc326b95999a2a7eb Mon Sep 17 00:00:00 2001 From: j3graham Date: Mon, 13 Jan 2025 17:01:25 -0500 Subject: [PATCH 02/57] xor const folding --- src/hotspot/share/opto/addnode.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index d9b62d2890dd3..6d43a2f3aee63 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -983,6 +983,11 @@ const Type* XorINode::Value(PhaseGVN* phase) const { // inputs have bits set. lo can always become 0. const TypeInt* t1i = t1->is_int(); const TypeInt* t2i = t2->is_int(); + + if( t1i->is_con() && t2i->is_con() ) { + return TypeInt::make( t1i->get_con() ^ t2i->get_con() ); + } + if ((t1i->_lo >= 0) && (t1i->_hi > 0) && (t2i->_lo >= 0) && @@ -1068,6 +1073,11 @@ const Type* XorLNode::Value(PhaseGVN* phase) const { // inputs have bits set. lo can always become 0. const TypeLong* t1l = t1->is_long(); const TypeLong* t2l = t2->is_long(); + + if( t1l->is_con() && t2l->is_con() ){ + return TypeLong::make( t1l->get_con() ^ t2l->get_con() ); + } + if ((t1l->_lo >= 0) && (t1l->_hi > 0) && (t2l->_lo >= 0) && From 21979d99f07d36bd300f310d310ab4e5b9aa9982 Mon Sep 17 00:00:00 2001 From: j3graham Date: Mon, 13 Jan 2025 19:43:00 -0500 Subject: [PATCH 03/57] format, copyright date --- src/hotspot/share/opto/addnode.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index 6d43a2f3aee63..9df7557c104da 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, 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 @@ -984,8 +984,8 @@ const Type* XorINode::Value(PhaseGVN* phase) const { const TypeInt* t1i = t1->is_int(); const TypeInt* t2i = t2->is_int(); - if( t1i->is_con() && t2i->is_con() ) { - return TypeInt::make( t1i->get_con() ^ t2i->get_con() ); + if (t1i->is_con() && t2i->is_con()) { + return TypeInt::make(t1i->get_con() ^ t2i->get_con()); } if ((t1i->_lo >= 0) && @@ -1074,8 +1074,8 @@ const Type* XorLNode::Value(PhaseGVN* phase) const { const TypeLong* t1l = t1->is_long(); const TypeLong* t2l = t2->is_long(); - if( t1l->is_con() && t2l->is_con() ){ - return TypeLong::make( t1l->get_con() ^ t2l->get_con() ); + if (t1l->is_con() && t2l->is_con()) { + return TypeLong::make(t1l->get_con() ^ t2l->get_con()); } if ((t1l->_lo >= 0) && From 7cd18f8994cde5de3af8494369365e5c7225fe78 Mon Sep 17 00:00:00 2001 From: j3graham Date: Tue, 14 Jan 2025 13:03:42 -0500 Subject: [PATCH 04/57] move logic from Value to add_node XorI --- src/hotspot/share/opto/addnode.cpp | 44 ++++++++++++++---------------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index 9df7557c104da..d6c3c41bf2266 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -979,24 +979,6 @@ const Type* XorINode::Value(PhaseGVN* phase) const { if (in1->eqv_uncast(in2)) { return add_id(); } - // result of xor can only have bits sets where any of the - // inputs have bits set. lo can always become 0. - const TypeInt* t1i = t1->is_int(); - const TypeInt* t2i = t2->is_int(); - - if (t1i->is_con() && t2i->is_con()) { - return TypeInt::make(t1i->get_con() ^ t2i->get_con()); - } - - if ((t1i->_lo >= 0) && - (t1i->_hi > 0) && - (t2i->_lo >= 0) && - (t2i->_hi > 0)) { - // hi - set all bits below the highest bit. Using round_down to avoid overflow. - const TypeInt* t1x = TypeInt::make(0, round_down_power_of_2(t1i->_hi) + (round_down_power_of_2(t1i->_hi) - 1), t1i->_widen); - const TypeInt* t2x = TypeInt::make(0, round_down_power_of_2(t2i->_hi) + (round_down_power_of_2(t2i->_hi) - 1), t2i->_widen); - return t1x->meet(t2x); - } return AddNode::Value(phase); } @@ -1010,14 +992,28 @@ const Type *XorINode::add_ring( const Type *t0, const Type *t1 ) const { const TypeInt *r0 = t0->is_int(); // Handy access const TypeInt *r1 = t1->is_int(); - // Complementing a boolean? - if( r0 == TypeInt::BOOL && ( r1 == TypeInt::ONE - || r1 == TypeInt::BOOL)) - return TypeInt::BOOL; + if( !r0->is_con() || !r1->is_con() ) { - if( !r0->is_con() || !r1->is_con() ) // Not constants - return TypeInt::INT; // Any integer, but still no symbols. + // result of xor can only have bits sets where any of the + // inputs have bits set. lo can always become 0. + + if ((r0->_lo >= 0) && + (r0->_hi > 0) && + (r1->_lo >= 0) && + (r1->_hi > 0)) { + // hi - set all bits below the highest bit. Using round_down to avoid overflow. + const TypeInt* t1x = TypeInt::make(0, round_down_power_of_2(r0->_hi) + (round_down_power_of_2(r0->_hi) - 1), r0->_widen); + const TypeInt* t2x = TypeInt::make(0, round_down_power_of_2(r1->_hi) + (round_down_power_of_2(r1->_hi) - 1), r1->_widen); + return t1x->meet(t2x); + } + // Complementing a boolean? + if( r0 == TypeInt::BOOL && ( r1 == TypeInt::ONE + || r1 == TypeInt::BOOL)) + return TypeInt::BOOL; + + return TypeInt::INT; // Any integer, but still no symbols. + } // Otherwise just XOR them bits. return TypeInt::make( r0->get_con() ^ r1->get_con() ); } From 8ec4313c55d9b0f34cb048de8fb2d4fa9635124a Mon Sep 17 00:00:00 2001 From: j3graham Date: Tue, 14 Jan 2025 18:57:58 -0500 Subject: [PATCH 05/57] move logic from Value to add_node XorL --- src/hotspot/share/opto/addnode.cpp | 39 +++++++++---------- .../c2/irTests/ConstFoldingTests.java | 19 ++++++++- 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index d6c3c41bf2266..f532e20180c0a 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -993,6 +993,7 @@ const Type *XorINode::add_ring( const Type *t0, const Type *t1 ) const { const TypeInt *r1 = t1->is_int(); if( !r0->is_con() || !r1->is_con() ) { + //not a constant // result of xor can only have bits sets where any of the // inputs have bits set. lo can always become 0. @@ -1024,11 +1025,26 @@ const Type *XorLNode::add_ring( const Type *t0, const Type *t1 ) const { const TypeLong *r0 = t0->is_long(); // Handy access const TypeLong *r1 = t1->is_long(); - // If either input is not a constant, just return all integers. - if( !r0->is_con() || !r1->is_con() ) + if( !r0->is_con() || !r1->is_con() ) { + //not a constant + + // result of xor can only have bits sets where any of the + // inputs have bits set. lo can always become 0. + + if ((r0->_lo >= 0) && + (r0->_hi > 0) && + (r1->_lo >= 0) && + (r1->_hi > 0)) { + // hi - set all bits below the highest bit. Using round_down to avoid overflow. + const TypeLong* t1x = TypeLong::make(0, round_down_power_of_2(r0->_hi) + (round_down_power_of_2(r0->_hi) - 1), r0->_widen); + const TypeLong* t2x = TypeLong::make(0, round_down_power_of_2(r1->_hi) + (round_down_power_of_2(r1->_hi) - 1), r1->_widen); + return t1x->meet(t2x); + } + return TypeLong::LONG; // Any integer, but still no symbols. + } - // Otherwise just OR them bits. + // Otherwise just XOR them bits. return TypeLong::make( r0->get_con() ^ r1->get_con() ); } @@ -1065,24 +1081,7 @@ const Type* XorLNode::Value(PhaseGVN* phase) const { if (in1->eqv_uncast(in2)) { return add_id(); } - // result of xor can only have bits sets where any of the - // inputs have bits set. lo can always become 0. - const TypeLong* t1l = t1->is_long(); - const TypeLong* t2l = t2->is_long(); - if (t1l->is_con() && t2l->is_con()) { - return TypeLong::make(t1l->get_con() ^ t2l->get_con()); - } - - if ((t1l->_lo >= 0) && - (t1l->_hi > 0) && - (t2l->_lo >= 0) && - (t2l->_hi > 0)) { - // hi - set all bits below the highest bit. Using round_down to avoid overflow. - const TypeLong* t1x = TypeLong::make(0, round_down_power_of_2(t1l->_hi) + (round_down_power_of_2(t1l->_hi) - 1), t1l->_widen); - const TypeLong* t2x = TypeLong::make(0, round_down_power_of_2(t2l->_hi) + (round_down_power_of_2(t2l->_hi) - 1), t2l->_widen); - return t1x->meet(t2x); - } return AddNode::Value(phase); } diff --git a/test/hotspot/jtreg/compiler/c2/irTests/ConstFoldingTests.java b/test/hotspot/jtreg/compiler/c2/irTests/ConstFoldingTests.java index 37f1772c59cb7..e768645dc18e0 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/ConstFoldingTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/ConstFoldingTests.java @@ -54,7 +54,24 @@ public int testConstXorI() { // Checks (c1 ^ c2) => c3 (constant folded) public long testConstXorL() { long c = 42; - return c ^ 2025; + return c ^ 2025L; + } + + @Test + @IR(failOn = {IRNode.XOR}) + @IR(counts = {IRNode.CON_I, "1"}) + // Checks (x ^ x) => c3 (constant folded) + @Arguments(values = Argument.RANDOM_EACH) + public int testConstXorISelf(int x) { + return x ^ x; } + @Test + @IR(failOn = {IRNode.XOR}) + @IR(counts = {IRNode.CON_L, "1"}) + // Checks (x ^ x) => c3 (constant folded) + @Arguments(values = Argument.RANDOM_EACH) + public long testConstXorLSelf(long x) { + return x ^ x; + } } From bbdad2c6108790451acc864a2bd8a8f0caf3295e Mon Sep 17 00:00:00 2001 From: j3graham Date: Mon, 13 Jan 2025 16:50:49 -0500 Subject: [PATCH 06/57] test for xor const folding --- .../c2/irTests/ConstFoldingTests.java | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 test/hotspot/jtreg/compiler/c2/irTests/ConstFoldingTests.java diff --git a/test/hotspot/jtreg/compiler/c2/irTests/ConstFoldingTests.java b/test/hotspot/jtreg/compiler/c2/irTests/ConstFoldingTests.java new file mode 100644 index 0000000000000..37f1772c59cb7 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/irTests/ConstFoldingTests.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2025, 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.irTests; + +import compiler.lib.ir_framework.*; + +/* + * @test + * @summary verify that constant folding is done on xor + * @library /test/lib / + * @requires vm.compiler2.enabled + * @run driver compiler.c2.irTests.ConstFoldingTests + */ + +public class ConstFoldingTests { + + public static void main(String[] args) { + TestFramework.run(); + } + + @Test + @IR(failOn = {IRNode.XOR}) + @IR(counts = {IRNode.CON_I, "1"}) + // Checks (c1 ^c2) => c3 (constant folded) + public int testConstXorI() { + int c = 42; + return c ^ 2025; + } + + @Test + @IR(failOn = {IRNode.XOR}) + @IR(counts = {IRNode.CON_L, "1"}) + // Checks (c1 ^ c2) => c3 (constant folded) + public long testConstXorL() { + long c = 42; + return c ^ 2025; + } + +} From 17dcb39ced64b97d0c4d7a16a403bbba9b07a8a2 Mon Sep 17 00:00:00 2001 From: j3graham Date: Mon, 13 Jan 2025 17:01:25 -0500 Subject: [PATCH 07/57] xor const folding --- src/hotspot/share/opto/addnode.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index 928e06ee938cf..60d6aa281e5ca 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -982,6 +982,11 @@ const Type* XorINode::Value(PhaseGVN* phase) const { // inputs have bits set. lo can always become 0. const TypeInt* t1i = t1->is_int(); const TypeInt* t2i = t2->is_int(); + + if( t1i->is_con() && t2i->is_con() ) { + return TypeInt::make( t1i->get_con() ^ t2i->get_con() ); + } + if ((t1i->_lo >= 0) && (t1i->_hi > 0) && (t2i->_lo >= 0) && @@ -1067,6 +1072,11 @@ const Type* XorLNode::Value(PhaseGVN* phase) const { // inputs have bits set. lo can always become 0. const TypeLong* t1l = t1->is_long(); const TypeLong* t2l = t2->is_long(); + + if( t1l->is_con() && t2l->is_con() ){ + return TypeLong::make( t1l->get_con() ^ t2l->get_con() ); + } + if ((t1l->_lo >= 0) && (t1l->_hi > 0) && (t2l->_lo >= 0) && From 3f5f5aa5213b5908ecdb88ea98ad448ad6d739df Mon Sep 17 00:00:00 2001 From: j3graham Date: Mon, 13 Jan 2025 19:43:00 -0500 Subject: [PATCH 08/57] format, copyright date --- src/hotspot/share/opto/addnode.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index 60d6aa281e5ca..9a5e7a229b25f 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -983,8 +983,8 @@ const Type* XorINode::Value(PhaseGVN* phase) const { const TypeInt* t1i = t1->is_int(); const TypeInt* t2i = t2->is_int(); - if( t1i->is_con() && t2i->is_con() ) { - return TypeInt::make( t1i->get_con() ^ t2i->get_con() ); + if (t1i->is_con() && t2i->is_con()) { + return TypeInt::make(t1i->get_con() ^ t2i->get_con()); } if ((t1i->_lo >= 0) && @@ -1073,8 +1073,8 @@ const Type* XorLNode::Value(PhaseGVN* phase) const { const TypeLong* t1l = t1->is_long(); const TypeLong* t2l = t2->is_long(); - if( t1l->is_con() && t2l->is_con() ){ - return TypeLong::make( t1l->get_con() ^ t2l->get_con() ); + if (t1l->is_con() && t2l->is_con()) { + return TypeLong::make(t1l->get_con() ^ t2l->get_con()); } if ((t1l->_lo >= 0) && From 862db731a59a540ae320e33379b7851a8f4edd2b Mon Sep 17 00:00:00 2001 From: j3graham Date: Tue, 14 Jan 2025 13:03:42 -0500 Subject: [PATCH 09/57] move logic from Value to add_node XorI --- src/hotspot/share/opto/addnode.cpp | 44 ++++++++++++++---------------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index 9a5e7a229b25f..bbc1891b21675 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -978,24 +978,6 @@ const Type* XorINode::Value(PhaseGVN* phase) const { if (in1->eqv_uncast(in2)) { return add_id(); } - // result of xor can only have bits sets where any of the - // inputs have bits set. lo can always become 0. - const TypeInt* t1i = t1->is_int(); - const TypeInt* t2i = t2->is_int(); - - if (t1i->is_con() && t2i->is_con()) { - return TypeInt::make(t1i->get_con() ^ t2i->get_con()); - } - - if ((t1i->_lo >= 0) && - (t1i->_hi > 0) && - (t2i->_lo >= 0) && - (t2i->_hi > 0)) { - // hi - set all bits below the highest bit. Using round_down to avoid overflow. - const TypeInt* t1x = TypeInt::make(0, round_down_power_of_2(t1i->_hi) + (round_down_power_of_2(t1i->_hi) - 1), t1i->_widen); - const TypeInt* t2x = TypeInt::make(0, round_down_power_of_2(t2i->_hi) + (round_down_power_of_2(t2i->_hi) - 1), t2i->_widen); - return t1x->meet(t2x); - } return AddNode::Value(phase); } @@ -1009,14 +991,28 @@ const Type *XorINode::add_ring( const Type *t0, const Type *t1 ) const { const TypeInt *r0 = t0->is_int(); // Handy access const TypeInt *r1 = t1->is_int(); - // Complementing a boolean? - if( r0 == TypeInt::BOOL && ( r1 == TypeInt::ONE - || r1 == TypeInt::BOOL)) - return TypeInt::BOOL; + if( !r0->is_con() || !r1->is_con() ) { - if( !r0->is_con() || !r1->is_con() ) // Not constants - return TypeInt::INT; // Any integer, but still no symbols. + // result of xor can only have bits sets where any of the + // inputs have bits set. lo can always become 0. + + if ((r0->_lo >= 0) && + (r0->_hi > 0) && + (r1->_lo >= 0) && + (r1->_hi > 0)) { + // hi - set all bits below the highest bit. Using round_down to avoid overflow. + const TypeInt* t1x = TypeInt::make(0, round_down_power_of_2(r0->_hi) + (round_down_power_of_2(r0->_hi) - 1), r0->_widen); + const TypeInt* t2x = TypeInt::make(0, round_down_power_of_2(r1->_hi) + (round_down_power_of_2(r1->_hi) - 1), r1->_widen); + return t1x->meet(t2x); + } + // Complementing a boolean? + if( r0 == TypeInt::BOOL && ( r1 == TypeInt::ONE + || r1 == TypeInt::BOOL)) + return TypeInt::BOOL; + + return TypeInt::INT; // Any integer, but still no symbols. + } // Otherwise just XOR them bits. return TypeInt::make( r0->get_con() ^ r1->get_con() ); } From 55f4b26a42bc83df04894343b4adfa587e909774 Mon Sep 17 00:00:00 2001 From: j3graham Date: Tue, 14 Jan 2025 18:57:58 -0500 Subject: [PATCH 10/57] move logic from Value to add_node XorL --- src/hotspot/share/opto/addnode.cpp | 39 +++++++++---------- .../c2/irTests/ConstFoldingTests.java | 19 ++++++++- 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index bbc1891b21675..abcfd3d865ea6 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -992,6 +992,7 @@ const Type *XorINode::add_ring( const Type *t0, const Type *t1 ) const { const TypeInt *r1 = t1->is_int(); if( !r0->is_con() || !r1->is_con() ) { + //not a constant // result of xor can only have bits sets where any of the // inputs have bits set. lo can always become 0. @@ -1023,11 +1024,26 @@ const Type *XorLNode::add_ring( const Type *t0, const Type *t1 ) const { const TypeLong *r0 = t0->is_long(); // Handy access const TypeLong *r1 = t1->is_long(); - // If either input is not a constant, just return all integers. - if( !r0->is_con() || !r1->is_con() ) + if( !r0->is_con() || !r1->is_con() ) { + //not a constant + + // result of xor can only have bits sets where any of the + // inputs have bits set. lo can always become 0. + + if ((r0->_lo >= 0) && + (r0->_hi > 0) && + (r1->_lo >= 0) && + (r1->_hi > 0)) { + // hi - set all bits below the highest bit. Using round_down to avoid overflow. + const TypeLong* t1x = TypeLong::make(0, round_down_power_of_2(r0->_hi) + (round_down_power_of_2(r0->_hi) - 1), r0->_widen); + const TypeLong* t2x = TypeLong::make(0, round_down_power_of_2(r1->_hi) + (round_down_power_of_2(r1->_hi) - 1), r1->_widen); + return t1x->meet(t2x); + } + return TypeLong::LONG; // Any integer, but still no symbols. + } - // Otherwise just OR them bits. + // Otherwise just XOR them bits. return TypeLong::make( r0->get_con() ^ r1->get_con() ); } @@ -1064,24 +1080,7 @@ const Type* XorLNode::Value(PhaseGVN* phase) const { if (in1->eqv_uncast(in2)) { return add_id(); } - // result of xor can only have bits sets where any of the - // inputs have bits set. lo can always become 0. - const TypeLong* t1l = t1->is_long(); - const TypeLong* t2l = t2->is_long(); - if (t1l->is_con() && t2l->is_con()) { - return TypeLong::make(t1l->get_con() ^ t2l->get_con()); - } - - if ((t1l->_lo >= 0) && - (t1l->_hi > 0) && - (t2l->_lo >= 0) && - (t2l->_hi > 0)) { - // hi - set all bits below the highest bit. Using round_down to avoid overflow. - const TypeLong* t1x = TypeLong::make(0, round_down_power_of_2(t1l->_hi) + (round_down_power_of_2(t1l->_hi) - 1), t1l->_widen); - const TypeLong* t2x = TypeLong::make(0, round_down_power_of_2(t2l->_hi) + (round_down_power_of_2(t2l->_hi) - 1), t2l->_widen); - return t1x->meet(t2x); - } return AddNode::Value(phase); } diff --git a/test/hotspot/jtreg/compiler/c2/irTests/ConstFoldingTests.java b/test/hotspot/jtreg/compiler/c2/irTests/ConstFoldingTests.java index 37f1772c59cb7..e768645dc18e0 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/ConstFoldingTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/ConstFoldingTests.java @@ -54,7 +54,24 @@ public int testConstXorI() { // Checks (c1 ^ c2) => c3 (constant folded) public long testConstXorL() { long c = 42; - return c ^ 2025; + return c ^ 2025L; + } + + @Test + @IR(failOn = {IRNode.XOR}) + @IR(counts = {IRNode.CON_I, "1"}) + // Checks (x ^ x) => c3 (constant folded) + @Arguments(values = Argument.RANDOM_EACH) + public int testConstXorISelf(int x) { + return x ^ x; } + @Test + @IR(failOn = {IRNode.XOR}) + @IR(counts = {IRNode.CON_L, "1"}) + // Checks (x ^ x) => c3 (constant folded) + @Arguments(values = Argument.RANDOM_EACH) + public long testConstXorLSelf(long x) { + return x ^ x; + } } From fe3b4282653a92606bc7910e86fb58705f00f680 Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Wed, 22 Jan 2025 12:07:25 -0500 Subject: [PATCH 11/57] move tests, add correctness checks --- .../TestXorInt.java} | 52 +++++++----- .../jtreg/compiler/c2/gvn/TestXorLong.java | 83 +++++++++++++++++++ 2 files changed, 112 insertions(+), 23 deletions(-) rename test/hotspot/jtreg/compiler/c2/{irTests/ConstFoldingTests.java => gvn/TestXorInt.java} (61%) create mode 100644 test/hotspot/jtreg/compiler/c2/gvn/TestXorLong.java diff --git a/test/hotspot/jtreg/compiler/c2/irTests/ConstFoldingTests.java b/test/hotspot/jtreg/compiler/c2/gvn/TestXorInt.java similarity index 61% rename from test/hotspot/jtreg/compiler/c2/irTests/ConstFoldingTests.java rename to test/hotspot/jtreg/compiler/c2/gvn/TestXorInt.java index e768645dc18e0..8a31600e9e6da 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/ConstFoldingTests.java +++ b/test/hotspot/jtreg/compiler/c2/gvn/TestXorInt.java @@ -21,19 +21,26 @@ * questions. */ -package compiler.c2.irTests; +package compiler.c2.gvn; +import compiler.lib.generators.Generator; +import compiler.lib.generators.Generators; import compiler.lib.ir_framework.*; +import jdk.test.lib.Asserts; /* * @test * @summary verify that constant folding is done on xor + * @bug 8347645 * @library /test/lib / * @requires vm.compiler2.enabled - * @run driver compiler.c2.irTests.ConstFoldingTests + * @run driver compiler.c2.gvn.TestXorInt */ -public class ConstFoldingTests { +public class TestXorInt { + private static final Generator G = Generators.G.ints(); + private static final int CONST_1 = G.next(); + private static final int CONST_2 = G.next(); public static void main(String[] args) { TestFramework.run(); @@ -42,36 +49,35 @@ public static void main(String[] args) { @Test @IR(failOn = {IRNode.XOR}) @IR(counts = {IRNode.CON_I, "1"}) - // Checks (c1 ^c2) => c3 (constant folded) - public int testConstXorI() { - int c = 42; - return c ^ 2025; + // Checks (c ^c) => c (constant folded) + public int testConstXor() { + return CONST_1 ^ CONST_2; } - @Test - @IR(failOn = {IRNode.XOR}) - @IR(counts = {IRNode.CON_L, "1"}) - // Checks (c1 ^ c2) => c3 (constant folded) - public long testConstXorL() { - long c = 42; - return c ^ 2025L; + @Check(test = "testConstXor") + public void checkTestConstXor(int result) { + Asserts.assertEquals(interpretedXor(CONST_1, CONST_2), result); + } + + @DontCompile + private static int interpretedXor(int x, int y) { + return x ^ y; } @Test @IR(failOn = {IRNode.XOR}) @IR(counts = {IRNode.CON_I, "1"}) - // Checks (x ^ x) => c3 (constant folded) + // Checks (x ^ x) => c (constant folded) @Arguments(values = Argument.RANDOM_EACH) - public int testConstXorISelf(int x) { + public int testConstXorSelf(int x) { return x ^ x; } - @Test - @IR(failOn = {IRNode.XOR}) - @IR(counts = {IRNode.CON_L, "1"}) - // Checks (x ^ x) => c3 (constant folded) - @Arguments(values = Argument.RANDOM_EACH) - public long testConstXorLSelf(long x) { - return x ^ x; + @Check(test = "testConstXorSelf") + public void checkTestConstXorSelf(int result) { + Asserts.assertEquals(0, result); } } + + + diff --git a/test/hotspot/jtreg/compiler/c2/gvn/TestXorLong.java b/test/hotspot/jtreg/compiler/c2/gvn/TestXorLong.java new file mode 100644 index 0000000000000..7fec0e4c0839e --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/gvn/TestXorLong.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2025, 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.generators.Generator; +import compiler.lib.generators.Generators; +import compiler.lib.ir_framework.*; +import jdk.test.lib.Asserts; + +/* + * @test + * @summary verify that constant folding is done on xor + * @bug 8347645 + * @library /test/lib / + * @requires vm.compiler2.enabled + * @run driver compiler.c2.gvn.TestXorLong + */ + +public class TestXorLong { + private static final Generator G = Generators.G.longs(); + private static final long CONST_1 = G.next(); + private static final long CONST_2 = G.next(); + + public static void main(String[] args) { + TestFramework.run(); + } + + @Test + @IR(failOn = {IRNode.XOR}) + @IR(counts = {IRNode.CON_L, "1"}) + // Checks (c ^ c) => c (constant folded) + public long testConstXor() { + return CONST_1 ^ CONST_2; + } + + @Check(test = "testConstXor") + public void checkTestConstXor(long result) { + Asserts.assertEquals(interpretedXor(CONST_1, CONST_2), result); + } + + @DontCompile + private static long interpretedXor(long x, long y) { + return x ^ y; + } + + @Test + @IR(failOn = {IRNode.XOR}) + @IR(counts = {IRNode.CON_L, "1"}) + // Checks (x ^ x) => c (constant folded) + @Arguments(values = Argument.RANDOM_EACH) + public long testConstXorSelf(long x) { + return x ^ x; + } + + @Check(test = "testConstXorSelf") + public void checkTestConstXorSelf(long result) { + Asserts.assertEquals(0L, result); + } +} + + + From 51da0c6d3e84209b3d98047ab4e044a20d462db6 Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Wed, 22 Jan 2025 15:20:10 -0500 Subject: [PATCH 12/57] fix test names --- test/hotspot/jtreg/compiler/c2/gvn/TestXorInt.java | 6 +++--- test/hotspot/jtreg/compiler/c2/gvn/TestXorLong.java | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/hotspot/jtreg/compiler/c2/gvn/TestXorInt.java b/test/hotspot/jtreg/compiler/c2/gvn/TestXorInt.java index 8a31600e9e6da..27982658c3ff3 100644 --- a/test/hotspot/jtreg/compiler/c2/gvn/TestXorInt.java +++ b/test/hotspot/jtreg/compiler/c2/gvn/TestXorInt.java @@ -69,12 +69,12 @@ private static int interpretedXor(int x, int y) { @IR(counts = {IRNode.CON_I, "1"}) // Checks (x ^ x) => c (constant folded) @Arguments(values = Argument.RANDOM_EACH) - public int testConstXorSelf(int x) { + public int testXorSelf(int x) { return x ^ x; } - @Check(test = "testConstXorSelf") - public void checkTestConstXorSelf(int result) { + @Check(test = "testXorSelf") + public void checkTestXorSelf(int result) { Asserts.assertEquals(0, result); } } diff --git a/test/hotspot/jtreg/compiler/c2/gvn/TestXorLong.java b/test/hotspot/jtreg/compiler/c2/gvn/TestXorLong.java index 7fec0e4c0839e..772c370765a34 100644 --- a/test/hotspot/jtreg/compiler/c2/gvn/TestXorLong.java +++ b/test/hotspot/jtreg/compiler/c2/gvn/TestXorLong.java @@ -69,12 +69,12 @@ private static long interpretedXor(long x, long y) { @IR(counts = {IRNode.CON_L, "1"}) // Checks (x ^ x) => c (constant folded) @Arguments(values = Argument.RANDOM_EACH) - public long testConstXorSelf(long x) { + public long testXorSelf(long x) { return x ^ x; } - @Check(test = "testConstXorSelf") - public void checkTestConstXorSelf(long result) { + @Check(test = "testXorSelf") + public void checkTestXorSelf(long result) { Asserts.assertEquals(0L, result); } } From 3886c58d4f6a565fed322da1838040070488bb39 Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Thu, 23 Jan 2025 11:56:54 -0500 Subject: [PATCH 13/57] invert logic in const check, formatting, add test for boolean values --- src/hotspot/share/opto/addnode.cpp | 77 ++++++++--------- .../jtreg/compiler/c2/gvn/TestXorBool.java | 85 +++++++++++++++++++ 2 files changed, 124 insertions(+), 38 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/c2/gvn/TestXorBool.java diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index abcfd3d865ea6..61b95313609de 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -991,31 +991,32 @@ const Type *XorINode::add_ring( const Type *t0, const Type *t1 ) const { const TypeInt *r0 = t0->is_int(); // Handy access const TypeInt *r1 = t1->is_int(); - if( !r0->is_con() || !r1->is_con() ) { - //not a constant - - // result of xor can only have bits sets where any of the - // inputs have bits set. lo can always become 0. - - if ((r0->_lo >= 0) && - (r0->_hi > 0) && - (r1->_lo >= 0) && - (r1->_hi > 0)) { - // hi - set all bits below the highest bit. Using round_down to avoid overflow. - const TypeInt* t1x = TypeInt::make(0, round_down_power_of_2(r0->_hi) + (round_down_power_of_2(r0->_hi) - 1), r0->_widen); - const TypeInt* t2x = TypeInt::make(0, round_down_power_of_2(r1->_hi) + (round_down_power_of_2(r1->_hi) - 1), r1->_widen); - return t1x->meet(t2x); - } + if( r0->is_con() && r1->is_con() ){ + // just XOR them bits. + return TypeInt::make( r0->get_con() ^ r1->get_con() ); + } - // Complementing a boolean? - if( r0 == TypeInt::BOOL && ( r1 == TypeInt::ONE - || r1 == TypeInt::BOOL)) - return TypeInt::BOOL; + // not constants - return TypeInt::INT; // Any integer, but still no symbols. + // result of xor can only have bits sets where any of the + // inputs have bits set. lo can always become 0. + + if ( (r0->_lo >= 0) && + (r0->_hi > 0) && + (r1->_lo >= 0) && + (r1->_hi > 0) ) { + // hi - set all bits below the highest bit. Using round_down to avoid overflow. + const TypeInt* t1x = TypeInt::make(0, round_down_power_of_2(r0->_hi) + (round_down_power_of_2(r0->_hi) - 1), r0->_widen); + const TypeInt* t2x = TypeInt::make(0, round_down_power_of_2(r1->_hi) + (round_down_power_of_2(r1->_hi) - 1), r1->_widen); + return t1x->meet(t2x); } - // Otherwise just XOR them bits. - return TypeInt::make( r0->get_con() ^ r1->get_con() ); + + // Complementing a boolean? + if( r0 == TypeInt::BOOL && ( r1 == TypeInt::ONE + || r1 == TypeInt::BOOL)) + return TypeInt::BOOL; + + return TypeInt::INT; // Any integer, but still no symbols. } //============================================================================= @@ -1024,27 +1025,27 @@ const Type *XorLNode::add_ring( const Type *t0, const Type *t1 ) const { const TypeLong *r0 = t0->is_long(); // Handy access const TypeLong *r1 = t1->is_long(); - if( !r0->is_con() || !r1->is_con() ) { - //not a constant + if( r0->is_con() && r1->is_con() ){ + // just XOR them bits. + return TypeLong::make( r0->get_con() ^ r1->get_con() ); + } - // result of xor can only have bits sets where any of the - // inputs have bits set. lo can always become 0. + // not constants - if ((r0->_lo >= 0) && - (r0->_hi > 0) && - (r1->_lo >= 0) && - (r1->_hi > 0)) { - // hi - set all bits below the highest bit. Using round_down to avoid overflow. - const TypeLong* t1x = TypeLong::make(0, round_down_power_of_2(r0->_hi) + (round_down_power_of_2(r0->_hi) - 1), r0->_widen); - const TypeLong* t2x = TypeLong::make(0, round_down_power_of_2(r1->_hi) + (round_down_power_of_2(r1->_hi) - 1), r1->_widen); - return t1x->meet(t2x); - } + // result of xor can only have bits sets where any of the + // inputs have bits set. lo can always become 0. - return TypeLong::LONG; // Any integer, but still no symbols. + if ( (r0->_lo >= 0) && + (r0->_hi > 0) && + (r1->_lo >= 0) && + (r1->_hi > 0) ) { + // hi - set all bits below the highest bit. Using round_down to avoid overflow. + const TypeLong* t1x = TypeLong::make(0, round_down_power_of_2(r0->_hi) + (round_down_power_of_2(r0->_hi) - 1), r0->_widen); + const TypeLong* t2x = TypeLong::make(0, round_down_power_of_2(r1->_hi) + (round_down_power_of_2(r1->_hi) - 1), r1->_widen); + return t1x->meet(t2x); } - // Otherwise just XOR them bits. - return TypeLong::make( r0->get_con() ^ r1->get_con() ); + return TypeLong::LONG; // Any integer, but still no symbols. } Node* XorLNode::Ideal(PhaseGVN* phase, bool can_reshape) { diff --git a/test/hotspot/jtreg/compiler/c2/gvn/TestXorBool.java b/test/hotspot/jtreg/compiler/c2/gvn/TestXorBool.java new file mode 100644 index 0000000000000..6a50ea9e69b89 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/gvn/TestXorBool.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2025, 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.generators.Generator; +import compiler.lib.generators.Generators; +import compiler.lib.ir_framework.*; +import jdk.test.lib.Asserts; + +import java.util.List; + +/* + * @test + * @summary verify that constant folding is done on xor + * @bug 8347645 + * @library /test/lib / + * @requires vm.compiler2.enabled + * @run driver compiler.c2.gvn.TestXorBool + */ + +public class TestXorBool { + private static final Generator G = Generators.G.randomElement(List.of(Boolean.TRUE, Boolean.FALSE)); + private static final boolean CONST_1 = G.next(); + private static final boolean CONST_2 = G.next(); + + public static void main(String[] args) { + TestFramework.run(); + } + + @Test + @IR(failOn = {IRNode.XOR}) + @IR(counts = {IRNode.CON_I, "1"}) + // Checks (c ^c) => c (constant folded) + public boolean testConstXor() { + return CONST_1 ^ CONST_2; + } + + @Check(test = "testConstXor") + public void checkTestConstXor(boolean result) { + Asserts.assertEquals(interpretedXor(CONST_1, CONST_2), result); + } + + @DontCompile + private static boolean interpretedXor(boolean x, boolean y) { + return x ^ y; + } + + @Test + @IR(failOn = {IRNode.XOR}) + @IR(counts = {IRNode.CON_I, "1"}) + // Checks (x ^ x) => c (constant folded) + @Arguments(values = Argument.RANDOM_EACH) + public boolean testXorSelf(boolean x) { + return x ^ x; + } + + @Check(test = "testXorSelf") + public void checkTestXorSelf(boolean result) { + Asserts.assertEquals(false, result); + } +} + + + From 002a53f9044714b75d2ba95ae58000f8dfced5e1 Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Thu, 23 Jan 2025 11:59:47 -0500 Subject: [PATCH 14/57] remove redundant boolean check --- src/hotspot/share/opto/addnode.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index 61b95313609de..166f3690429eb 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -1011,11 +1011,6 @@ const Type *XorINode::add_ring( const Type *t0, const Type *t1 ) const { return t1x->meet(t2x); } - // Complementing a boolean? - if( r0 == TypeInt::BOOL && ( r1 == TypeInt::ONE - || r1 == TypeInt::BOOL)) - return TypeInt::BOOL; - return TypeInt::INT; // Any integer, but still no symbols. } From 4d4eeb15f89529cb0dcb2e866713dab1a6e7e483 Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Thu, 23 Jan 2025 12:37:28 -0500 Subject: [PATCH 15/57] move tests to Xor*NodeIdealizationTests --- .../jtreg/compiler/c2/gvn/TestXorBool.java | 85 ------------------- .../jtreg/compiler/c2/gvn/TestXorInt.java | 83 ------------------ .../jtreg/compiler/c2/gvn/TestXorLong.java | 83 ------------------ .../c2/irTests/XorINodeIdealizationTests.java | 57 +++++++++++-- .../c2/irTests/XorLNodeIdealizationTests.java | 26 +++++- 5 files changed, 75 insertions(+), 259 deletions(-) delete mode 100644 test/hotspot/jtreg/compiler/c2/gvn/TestXorBool.java delete mode 100644 test/hotspot/jtreg/compiler/c2/gvn/TestXorInt.java delete mode 100644 test/hotspot/jtreg/compiler/c2/gvn/TestXorLong.java diff --git a/test/hotspot/jtreg/compiler/c2/gvn/TestXorBool.java b/test/hotspot/jtreg/compiler/c2/gvn/TestXorBool.java deleted file mode 100644 index 6a50ea9e69b89..0000000000000 --- a/test/hotspot/jtreg/compiler/c2/gvn/TestXorBool.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2025, 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.generators.Generator; -import compiler.lib.generators.Generators; -import compiler.lib.ir_framework.*; -import jdk.test.lib.Asserts; - -import java.util.List; - -/* - * @test - * @summary verify that constant folding is done on xor - * @bug 8347645 - * @library /test/lib / - * @requires vm.compiler2.enabled - * @run driver compiler.c2.gvn.TestXorBool - */ - -public class TestXorBool { - private static final Generator G = Generators.G.randomElement(List.of(Boolean.TRUE, Boolean.FALSE)); - private static final boolean CONST_1 = G.next(); - private static final boolean CONST_2 = G.next(); - - public static void main(String[] args) { - TestFramework.run(); - } - - @Test - @IR(failOn = {IRNode.XOR}) - @IR(counts = {IRNode.CON_I, "1"}) - // Checks (c ^c) => c (constant folded) - public boolean testConstXor() { - return CONST_1 ^ CONST_2; - } - - @Check(test = "testConstXor") - public void checkTestConstXor(boolean result) { - Asserts.assertEquals(interpretedXor(CONST_1, CONST_2), result); - } - - @DontCompile - private static boolean interpretedXor(boolean x, boolean y) { - return x ^ y; - } - - @Test - @IR(failOn = {IRNode.XOR}) - @IR(counts = {IRNode.CON_I, "1"}) - // Checks (x ^ x) => c (constant folded) - @Arguments(values = Argument.RANDOM_EACH) - public boolean testXorSelf(boolean x) { - return x ^ x; - } - - @Check(test = "testXorSelf") - public void checkTestXorSelf(boolean result) { - Asserts.assertEquals(false, result); - } -} - - - diff --git a/test/hotspot/jtreg/compiler/c2/gvn/TestXorInt.java b/test/hotspot/jtreg/compiler/c2/gvn/TestXorInt.java deleted file mode 100644 index 27982658c3ff3..0000000000000 --- a/test/hotspot/jtreg/compiler/c2/gvn/TestXorInt.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2025, 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.generators.Generator; -import compiler.lib.generators.Generators; -import compiler.lib.ir_framework.*; -import jdk.test.lib.Asserts; - -/* - * @test - * @summary verify that constant folding is done on xor - * @bug 8347645 - * @library /test/lib / - * @requires vm.compiler2.enabled - * @run driver compiler.c2.gvn.TestXorInt - */ - -public class TestXorInt { - private static final Generator G = Generators.G.ints(); - private static final int CONST_1 = G.next(); - private static final int CONST_2 = G.next(); - - public static void main(String[] args) { - TestFramework.run(); - } - - @Test - @IR(failOn = {IRNode.XOR}) - @IR(counts = {IRNode.CON_I, "1"}) - // Checks (c ^c) => c (constant folded) - public int testConstXor() { - return CONST_1 ^ CONST_2; - } - - @Check(test = "testConstXor") - public void checkTestConstXor(int result) { - Asserts.assertEquals(interpretedXor(CONST_1, CONST_2), result); - } - - @DontCompile - private static int interpretedXor(int x, int y) { - return x ^ y; - } - - @Test - @IR(failOn = {IRNode.XOR}) - @IR(counts = {IRNode.CON_I, "1"}) - // Checks (x ^ x) => c (constant folded) - @Arguments(values = Argument.RANDOM_EACH) - public int testXorSelf(int x) { - return x ^ x; - } - - @Check(test = "testXorSelf") - public void checkTestXorSelf(int result) { - Asserts.assertEquals(0, result); - } -} - - - diff --git a/test/hotspot/jtreg/compiler/c2/gvn/TestXorLong.java b/test/hotspot/jtreg/compiler/c2/gvn/TestXorLong.java deleted file mode 100644 index 772c370765a34..0000000000000 --- a/test/hotspot/jtreg/compiler/c2/gvn/TestXorLong.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2025, 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.generators.Generator; -import compiler.lib.generators.Generators; -import compiler.lib.ir_framework.*; -import jdk.test.lib.Asserts; - -/* - * @test - * @summary verify that constant folding is done on xor - * @bug 8347645 - * @library /test/lib / - * @requires vm.compiler2.enabled - * @run driver compiler.c2.gvn.TestXorLong - */ - -public class TestXorLong { - private static final Generator G = Generators.G.longs(); - private static final long CONST_1 = G.next(); - private static final long CONST_2 = G.next(); - - public static void main(String[] args) { - TestFramework.run(); - } - - @Test - @IR(failOn = {IRNode.XOR}) - @IR(counts = {IRNode.CON_L, "1"}) - // Checks (c ^ c) => c (constant folded) - public long testConstXor() { - return CONST_1 ^ CONST_2; - } - - @Check(test = "testConstXor") - public void checkTestConstXor(long result) { - Asserts.assertEquals(interpretedXor(CONST_1, CONST_2), result); - } - - @DontCompile - private static long interpretedXor(long x, long y) { - return x ^ y; - } - - @Test - @IR(failOn = {IRNode.XOR}) - @IR(counts = {IRNode.CON_L, "1"}) - // Checks (x ^ x) => c (constant folded) - @Arguments(values = Argument.RANDOM_EACH) - public long testXorSelf(long x) { - return x ^ x; - } - - @Check(test = "testXorSelf") - public void checkTestXorSelf(long result) { - Asserts.assertEquals(0L, result); - } -} - - - diff --git a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java index f1a0320330b14..10d46c04a460c 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java @@ -33,6 +33,11 @@ * @run driver compiler.c2.irTests.XorINodeIdealizationTests */ public class XorINodeIdealizationTests { + private static final int CONST_1 = RunInfo.getRandom().nextInt(); + private static final int CONST_2 = RunInfo.getRandom().nextInt(); + private static final boolean CONST_BOOL_1 = RunInfo.getRandom().nextBoolean(); + private static final boolean CONST_BOOL_2 = RunInfo.getRandom().nextBoolean(); + public static void main(String[] args) { TestFramework.run(); } @@ -42,24 +47,27 @@ public static void main(String[] args) { "test7", "test8", "test9", "test10", "test11", "test12", "test13", "test14", "test15", - "test16", "test17"}) + "test16", "test17", + "testConstXor", "testXorSelf", + "testConstXorBool", "testXorSelfBool" + }) public void runMethod() { int a = RunInfo.getRandom().nextInt(); int b = RunInfo.getRandom().nextInt(); int c = RunInfo.getRandom().nextInt(); - int d = RunInfo.getRandom().nextInt(); + boolean d = RunInfo.getRandom().nextBoolean(); int min = Integer.MIN_VALUE; int max = Integer.MAX_VALUE; - assertResult(0, 0, 0, 0); + assertResult(0, 0, 0, false); assertResult(a, b, c, d); - assertResult(min, min, min, min); - assertResult(max, max, max, max); + assertResult(min, min, min, false); + assertResult(max, max, max, true); } @DontCompile - public void assertResult(int a, int b, int c, int d) { + public void assertResult(int a, int b, int c, boolean d) { Asserts.assertEQ(b - a , test1(a, b)); Asserts.assertEQ(a - b , test2(a, b)); Asserts.assertEQ(b - a , test3(a, b)); @@ -76,7 +84,10 @@ public void assertResult(int a, int b, int c, int d) { Asserts.assertEQ(~a , test14(a)); Asserts.assertEQ(~a , test15(a)); Asserts.assertEQ((~a + b) + (~a | c), test16(a, b, c)); - Asserts.assertEQ(-2023 - a , test17(a)); + Asserts.assertEQ(CONST_1 ^ CONST_2 , testConstXor()); + Asserts.assertEQ(0 , testXorSelf(a)); + Asserts.assertEQ(CONST_BOOL_1 ^ CONST_BOOL_2 , testConstXorBool()); + Asserts.assertEQ(false , testXorSelfBool(d)); } @Test @@ -217,4 +228,36 @@ public int test16(int x, int y, int z) { public int test17(int x) { return ~(x + 2022); } + + @Test + @IR(failOn = {IRNode.XOR}) + @IR(counts = {IRNode.CON_I, "1"}) + // Checks (c ^c) => c (constant folded) + public int testConstXor() { + return CONST_1 ^ CONST_2; + } + + @Test + @IR(failOn = {IRNode.XOR}) + @IR(counts = {IRNode.CON_I, "1"}) + // Checks (x ^ x) => c (constant folded) + public int testXorSelf(int x) { + return x ^ x; + } + + @Test + @IR(failOn = {IRNode.XOR}) + @IR(counts = {IRNode.CON_I, "1"}) + // Checks (c ^c) => c (constant folded) + public boolean testConstXorBool() { + return CONST_BOOL_1 ^ CONST_BOOL_2; + } + + @Test + @IR(failOn = {IRNode.XOR}) + @IR(counts = {IRNode.CON_I, "1"}) + // Checks (x ^ x) => c (constant folded) + public boolean testXorSelfBool(boolean x) { + return x ^ x; + } } diff --git a/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java index 22f3997a15ca9..494e06f84e39d 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java @@ -33,6 +33,10 @@ * @run driver compiler.c2.irTests.XorLNodeIdealizationTests */ public class XorLNodeIdealizationTests { + + private static final long CONST_1 = RunInfo.getRandom().nextLong(); + private static final long CONST_2 = RunInfo.getRandom().nextLong(); + public static void main(String[] args) { TestFramework.run(); } @@ -42,7 +46,9 @@ public static void main(String[] args) { "test7", "test8", "test9", "test10", "test11", "test12", "test13", "test14", "test15", - "test16", "test17"}) + "test16", "test17", + "testConstXor", "testXorSelf" + }) public void runMethod() { long a = RunInfo.getRandom().nextLong(); long b = RunInfo.getRandom().nextLong(); @@ -77,6 +83,8 @@ public void assertResult(long a, long b, long c, long d) { Asserts.assertEQ(~a , test15(a)); Asserts.assertEQ((~a + b) + (~a | c), test16(a, b, c)); Asserts.assertEQ(-2023 - a , test17(a)); + Asserts.assertEQ(CONST_1 ^ CONST_2 , testConstXor()); + Asserts.assertEQ(0L , testXorSelf(a)); } @Test @@ -217,4 +225,20 @@ public long test16(long x, long y, long z) { public long test17(long x) { return ~(x + 2022); } + + @Test + @IR(failOn = {IRNode.XOR}) + @IR(counts = {IRNode.CON_L, "1"}) + // Checks (c ^c) => c (constant folded) + public long testConstXor() { + return CONST_1 ^ CONST_2; + } + + @Test + @IR(failOn = {IRNode.XOR}) + @IR(counts = {IRNode.CON_L, "1"}) + // Checks (x ^ x) => c (constant folded) + public long testXorSelf(long x) { + return x ^ x; + } } From 1ed7d338fb51fdfafa64b540e9a53068ffdd2474 Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Fri, 24 Jan 2025 19:45:29 -0500 Subject: [PATCH 16/57] tests for xor hi=power of 2 --- .../c2/irTests/XorINodeIdealizationTests.java | 45 ++++++++++++++++- .../c2/irTests/XorLNodeIdealizationTests.java | 49 +++++++++++++++++-- 2 files changed, 89 insertions(+), 5 deletions(-) diff --git a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java index 10d46c04a460c..61016fc7fbf11 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java @@ -37,6 +37,7 @@ public class XorINodeIdealizationTests { private static final int CONST_2 = RunInfo.getRandom().nextInt(); private static final boolean CONST_BOOL_1 = RunInfo.getRandom().nextBoolean(); private static final boolean CONST_BOOL_2 = RunInfo.getRandom().nextBoolean(); + private static final int CONST_POW_2 = Math.abs(1 << RunInfo.getRandom().nextInt()); public static void main(String[] args) { TestFramework.run(); @@ -49,7 +50,9 @@ public static void main(String[] args) { "test13", "test14", "test15", "test16", "test17", "testConstXor", "testXorSelf", - "testConstXorBool", "testXorSelfBool" + "testConstXorBool", "testXorSelfBool", + "testMaxPow2","testMaxPow2Folded" + }) public void runMethod() { int a = RunInfo.getRandom().nextInt(); @@ -88,6 +91,8 @@ public void assertResult(int a, int b, int c, boolean d) { Asserts.assertEQ(0 , testXorSelf(a)); Asserts.assertEQ(CONST_BOOL_1 ^ CONST_BOOL_2 , testConstXorBool()); Asserts.assertEQ(false , testXorSelfBool(d)); + Asserts.assertEQ(interpretedMaxPow2(a, b), testMaxPow2(a, b)); + Asserts.assertEQ(true, testMaxPow2Folded(a, b)); } @Test @@ -260,4 +265,42 @@ public boolean testConstXorBool() { public boolean testXorSelfBool(boolean x) { return x ^ x; } + + // clamp value to [1,CONST_POW_2] + @ForceInline + private static int forceMinMax(int value){ + // equivalent to Math.min(CONST_POW_2, Math.max(value, 1)) + return 1 + (value & (CONST_POW_2 - 1)); + } + + @Test + @IR(counts = {IRNode.XOR, "1"}) // must not be constant-folded + // checks that add_ring computes correct max on exact powers of 2 + public boolean testMaxPow2(int x, int y) { + x = forceMinMax(x); + y = forceMinMax(y); + + long xor = x ^ y; + return xor < CONST_POW_2; + } + + @DontCompile + public boolean interpretedMaxPow2(int x, int y) { + x = forceMinMax(x); + y = forceMinMax(y); + + long xor = x ^ y; + return xor < CONST_POW_2; + } + + @Test + @IR(failOn = {IRNode.XOR}) + @IR(counts = {IRNode.CON_I, "1"}) + public boolean testMaxPow2Folded(int x, int y) { + x = forceMinMax(x); + y = forceMinMax(y); + + long xor = x ^ y; + return xor < (CONST_POW_2*2L); + } } diff --git a/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java index 494e06f84e39d..bf58cdf281393 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java @@ -36,6 +36,7 @@ public class XorLNodeIdealizationTests { private static final long CONST_1 = RunInfo.getRandom().nextLong(); private static final long CONST_2 = RunInfo.getRandom().nextLong(); + private static final long CONST_POW_2 = Math.abs(1L << RunInfo.getRandom().nextInt()); public static void main(String[] args) { TestFramework.run(); @@ -47,17 +48,18 @@ public static void main(String[] args) { "test10", "test11", "test12", "test13", "test14", "test15", "test16", "test17", - "testConstXor", "testXorSelf" + "testConstXor", "testXorSelf", + "testMaxPow2","testMaxPow2Folded" }) public void runMethod() { + long min = Long.MIN_VALUE; + long max = Long.MAX_VALUE; + long a = RunInfo.getRandom().nextLong(); long b = RunInfo.getRandom().nextLong(); long c = RunInfo.getRandom().nextLong(); long d = RunInfo.getRandom().nextLong(); - long min = Long.MIN_VALUE; - long max = Long.MAX_VALUE; - assertResult(0, 0, 0, 0); assertResult(a, b, c, d); assertResult(min, min, min, min); @@ -85,6 +87,8 @@ public void assertResult(long a, long b, long c, long d) { Asserts.assertEQ(-2023 - a , test17(a)); Asserts.assertEQ(CONST_1 ^ CONST_2 , testConstXor()); Asserts.assertEQ(0L , testXorSelf(a)); + Asserts.assertEQ(interpretedMaxPow2(a, b), testMaxPow2(a, b)); + Asserts.assertEQ(true, testMaxPow2Folded(a, b)); } @Test @@ -241,4 +245,41 @@ public long testConstXor() { public long testXorSelf(long x) { return x ^ x; } + + // clamp value to [1,CONST_POW_2] + @ForceInline + private static long forceMinMax(long value) { + // equivalent to Math.min(CONST_POW_2, Math.max(value, 1)) + return 1L + (value & (CONST_POW_2 - 1L)); + } + + @Test + @IR(counts = {IRNode.XOR, "1"}) // must not be constant-folded + // checks that add_ring computes correct max on exact powers of 2 + public boolean testMaxPow2(long x, long y) { + x = forceMinMax(x); + y = forceMinMax(y); + long xor = x ^ y; + return xor < CONST_POW_2; + } + + @DontCompile + public boolean interpretedMaxPow2(long x, long y) { + x = forceMinMax(x); + y = forceMinMax(y); + + long xor = x ^ y; + return xor < CONST_POW_2; + } + + @Test + @IR(failOn = {IRNode.XOR}) + @IR(counts = {IRNode.CON_I, "1"}) + public boolean testMaxPow2Folded(long x, long y) { + x = forceMinMax(x); + y = forceMinMax(y); + + long xor = x ^ y; + return xor < (CONST_POW_2*2L); + } } From 0bf5aa7f391c7160a69f2043273720f23047d9d8 Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Sat, 25 Jan 2025 01:41:40 -0500 Subject: [PATCH 17/57] simplified version of bounds check --- src/hotspot/share/opto/addnode.cpp | 55 +++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index 166f3690429eb..8af0f9faccc1d 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -991,7 +991,7 @@ const Type *XorINode::add_ring( const Type *t0, const Type *t1 ) const { const TypeInt *r0 = t0->is_int(); // Handy access const TypeInt *r1 = t1->is_int(); - if( r0->is_con() && r1->is_con() ){ + if (r0->is_con() && r1->is_con()) { // just XOR them bits. return TypeInt::make( r0->get_con() ^ r1->get_con() ); } @@ -1001,14 +1001,16 @@ const Type *XorINode::add_ring( const Type *t0, const Type *t1 ) const { // result of xor can only have bits sets where any of the // inputs have bits set. lo can always become 0. - if ( (r0->_lo >= 0) && - (r0->_hi > 0) && - (r1->_lo >= 0) && - (r1->_hi > 0) ) { - // hi - set all bits below the highest bit. Using round_down to avoid overflow. - const TypeInt* t1x = TypeInt::make(0, round_down_power_of_2(r0->_hi) + (round_down_power_of_2(r0->_hi) - 1), r0->_widen); - const TypeInt* t2x = TypeInt::make(0, round_down_power_of_2(r1->_hi) + (round_down_power_of_2(r1->_hi) - 1), r1->_widen); - return t1x->meet(t2x); + if (r0->_lo >= 0 && r1->_lo >= 0) { + // x ^ y cannot have any bit set that is higher than both the highest bits set in x and y + // x cannot have any bit set that is higher than the highest bit set in r0->_hi + // y cannot have any bit set that is higher than the highest bit set in r1->_hi + + // note cast to unsigned happens before +1 to avoid signed overflow, and + // round_up is safe because high bit is unset (0 <= lo <= hi) + juint max = round_up_power_of_2(juint(r0->_hi | r1->_hi) + 1) - 1; + + return TypeInt::make(0, max, MAX2(r0->_widen, r1->_widen)); } return TypeInt::INT; // Any integer, but still no symbols. @@ -1030,14 +1032,33 @@ const Type *XorLNode::add_ring( const Type *t0, const Type *t1 ) const { // result of xor can only have bits sets where any of the // inputs have bits set. lo can always become 0. - if ( (r0->_lo >= 0) && - (r0->_hi > 0) && - (r1->_lo >= 0) && - (r1->_hi > 0) ) { - // hi - set all bits below the highest bit. Using round_down to avoid overflow. - const TypeLong* t1x = TypeLong::make(0, round_down_power_of_2(r0->_hi) + (round_down_power_of_2(r0->_hi) - 1), r0->_widen); - const TypeLong* t2x = TypeLong::make(0, round_down_power_of_2(r1->_hi) + (round_down_power_of_2(r1->_hi) - 1), r1->_widen); - return t1x->meet(t2x); + if (r0->_lo >= 0 && r1->_lo >= 0) { + // x ^ y cannot have any bit set that is higher than both the highest bits set in x and y + // x cannot have any bit set that is higher than the highest bit set in r0->_hi + // y cannot have any bit set that is higher than the highest bit set in r1->_hi + + // we want to find a value that has all 1 bits everywhere up to and including + // the highest bits set in r0->_hi as well as r1->_hi. For this,we can take the next + // power of 2 strictly greater than both hi values and subtract 1 from it. + + // Example 1: + // r0->_hi = 5 (0b0101) r1->_hi=1 (0b0001) + // (5|1)+1 = 0b0110 + // round_up_pow2 = 0b1000 + // -1 = 0b0111 = max + + // Example 2 - this demonstrates need for the +1: + // r0->_hi = 4 (0b0100) r1->_hi=4 (0b0100) + // (4|4)+1 = 0b0101 + // round_up_pow2 = 0b1000 + // -1 = 0b0111 = max + // without the +1, round_up_pow2 would be 0b0100, resulting in 0b0011 as max + + // note cast to unsigned happens before +1 to avoid signed overflow, and + // round_up is safe because high bit is unset (0 <= lo <= hi) + julong max = round_up_power_of_2(julong(r0->_hi | r1->_hi) + 1) - 1; + + return TypeLong::make(0, max, MAX2(r0->_widen, r1->_widen)); } return TypeLong::LONG; // Any integer, but still no symbols. From cafdbfd705d69ea62039bb0de9830d3c979d2b89 Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Sat, 25 Jan 2025 11:18:57 -0500 Subject: [PATCH 18/57] formatting --- src/hotspot/share/opto/addnode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index 8af0f9faccc1d..eea554e10716a 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -1022,7 +1022,7 @@ const Type *XorLNode::add_ring( const Type *t0, const Type *t1 ) const { const TypeLong *r0 = t0->is_long(); // Handy access const TypeLong *r1 = t1->is_long(); - if( r0->is_con() && r1->is_con() ){ + if (r0->is_con() && r1->is_con()) { // just XOR them bits. return TypeLong::make( r0->get_con() ^ r1->get_con() ); } From f882eba330a670f919fc31b9b932efec27f1f959 Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Mon, 27 Jan 2025 14:10:45 -0500 Subject: [PATCH 19/57] clean up comments --- src/hotspot/share/opto/addnode.cpp | 21 ++++++++++--------- .../c2/irTests/XorINodeIdealizationTests.java | 4 ++-- .../c2/irTests/XorLNodeIdealizationTests.java | 4 ++-- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index eea554e10716a..63d44873d5251 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -992,13 +992,14 @@ const Type *XorINode::add_ring( const Type *t0, const Type *t1 ) const { const TypeInt *r1 = t1->is_int(); if (r0->is_con() && r1->is_con()) { - // just XOR them bits. + // Constant fold: (c1 ^ c2) -> c3 return TypeInt::make( r0->get_con() ^ r1->get_con() ); } - // not constants + // At least one of the arguments is not constant - // result of xor can only have bits sets where any of the + + // Result of xor can only have bits sets where any of the // inputs have bits set. lo can always become 0. if (r0->_lo >= 0 && r1->_lo >= 0) { @@ -1006,7 +1007,7 @@ const Type *XorINode::add_ring( const Type *t0, const Type *t1 ) const { // x cannot have any bit set that is higher than the highest bit set in r0->_hi // y cannot have any bit set that is higher than the highest bit set in r1->_hi - // note cast to unsigned happens before +1 to avoid signed overflow, and + // Note: cast to unsigned happens before +1 to avoid signed overflow, and // round_up is safe because high bit is unset (0 <= lo <= hi) juint max = round_up_power_of_2(juint(r0->_hi | r1->_hi) + 1) - 1; @@ -1023,13 +1024,13 @@ const Type *XorLNode::add_ring( const Type *t0, const Type *t1 ) const { const TypeLong *r1 = t1->is_long(); if (r0->is_con() && r1->is_con()) { - // just XOR them bits. + // Constant fold: (c1 ^ c2) -> c3 return TypeLong::make( r0->get_con() ^ r1->get_con() ); } - // not constants + // At least one of the arguments is not constant - // result of xor can only have bits sets where any of the + // Result of xor can only have bits sets where any of the // inputs have bits set. lo can always become 0. if (r0->_lo >= 0 && r1->_lo >= 0) { @@ -1037,7 +1038,7 @@ const Type *XorLNode::add_ring( const Type *t0, const Type *t1 ) const { // x cannot have any bit set that is higher than the highest bit set in r0->_hi // y cannot have any bit set that is higher than the highest bit set in r1->_hi - // we want to find a value that has all 1 bits everywhere up to and including + // We want to find a value that has all 1 bits everywhere up to and including // the highest bits set in r0->_hi as well as r1->_hi. For this,we can take the next // power of 2 strictly greater than both hi values and subtract 1 from it. @@ -1052,9 +1053,9 @@ const Type *XorLNode::add_ring( const Type *t0, const Type *t1 ) const { // (4|4)+1 = 0b0101 // round_up_pow2 = 0b1000 // -1 = 0b0111 = max - // without the +1, round_up_pow2 would be 0b0100, resulting in 0b0011 as max + // Without the +1, round_up_pow2 would be 0b0100, resulting in 0b0011 as max - // note cast to unsigned happens before +1 to avoid signed overflow, and + // Note: cast to unsigned happens before +1 to avoid signed overflow, and // round_up is safe because high bit is unset (0 <= lo <= hi) julong max = round_up_power_of_2(julong(r0->_hi | r1->_hi) + 1) - 1; diff --git a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java index 61016fc7fbf11..580f9f7db80fb 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 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 @@ -253,7 +253,7 @@ public int testXorSelf(int x) { @Test @IR(failOn = {IRNode.XOR}) @IR(counts = {IRNode.CON_I, "1"}) - // Checks (c ^c) => c (constant folded) + // Checks (c1 ^ c2) => c3 (constant folded) public boolean testConstXorBool() { return CONST_BOOL_1 ^ CONST_BOOL_2; } diff --git a/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java index bf58cdf281393..6c866ea24186e 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 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 @@ -233,7 +233,7 @@ public long test17(long x) { @Test @IR(failOn = {IRNode.XOR}) @IR(counts = {IRNode.CON_L, "1"}) - // Checks (c ^c) => c (constant folded) + // Checks (c1 ^ c2) => c3 (constant folded) public long testConstXor() { return CONST_1 ^ CONST_2; } From db16d38c5ffb60f9c3e47664d65fda64ea01882d Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Mon, 27 Jan 2025 19:29:52 -0500 Subject: [PATCH 20/57] test cleanup, stub of a test with gtest --- test/hotspot/gtest/opto/test_xor_node.cpp | 33 +++++++++++++++++++ .../c2/irTests/XorINodeIdealizationTests.java | 10 +++--- .../c2/irTests/XorLNodeIdealizationTests.java | 10 +++--- 3 files changed, 44 insertions(+), 9 deletions(-) create mode 100644 test/hotspot/gtest/opto/test_xor_node.cpp diff --git a/test/hotspot/gtest/opto/test_xor_node.cpp b/test/hotspot/gtest/opto/test_xor_node.cpp new file mode 100644 index 0000000000000..9eb402b18828c --- /dev/null +++ b/test/hotspot/gtest/opto/test_xor_node.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018, 2025, 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. + * + */ + +#include "opto/addnode.hpp" +#include "unittest.hpp" +#include "runtime/interfaceSupport.inline.hpp" + +TEST_VM(opto, xor) { +// ThreadInVMfromNative ThreadInVMfromNative(JavaThread::current()); + +// const TypeLong *v=TypeLong::make(0); +} diff --git a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java index 580f9f7db80fb..0826ddb35af7c 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java @@ -23,6 +23,7 @@ package compiler.c2.irTests; import jdk.test.lib.Asserts; +import compiler.lib.generators.Generators; import compiler.lib.ir_framework.*; /* @@ -33,11 +34,11 @@ * @run driver compiler.c2.irTests.XorINodeIdealizationTests */ public class XorINodeIdealizationTests { - private static final int CONST_1 = RunInfo.getRandom().nextInt(); - private static final int CONST_2 = RunInfo.getRandom().nextInt(); + private static final int CONST_1 = Generators.G.ints().next(); + private static final int CONST_2 = Generators.G.ints().next(); private static final boolean CONST_BOOL_1 = RunInfo.getRandom().nextBoolean(); private static final boolean CONST_BOOL_2 = RunInfo.getRandom().nextBoolean(); - private static final int CONST_POW_2 = Math.abs(1 << RunInfo.getRandom().nextInt()); + private static final int CONST_POW_2 = Math.abs(1 << Generators.G.uniformInts().next()); public static void main(String[] args) { TestFramework.run(); @@ -269,8 +270,7 @@ public boolean testXorSelfBool(boolean x) { // clamp value to [1,CONST_POW_2] @ForceInline private static int forceMinMax(int value){ - // equivalent to Math.min(CONST_POW_2, Math.max(value, 1)) - return 1 + (value & (CONST_POW_2 - 1)); + return Math.min(CONST_POW_2, Math.max(value, 1)); } @Test diff --git a/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java index 6c866ea24186e..eb6accfe12f40 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java @@ -23,6 +23,7 @@ package compiler.c2.irTests; import jdk.test.lib.Asserts; +import compiler.lib.generators.Generators; import compiler.lib.ir_framework.*; /* @@ -34,9 +35,9 @@ */ public class XorLNodeIdealizationTests { - private static final long CONST_1 = RunInfo.getRandom().nextLong(); - private static final long CONST_2 = RunInfo.getRandom().nextLong(); - private static final long CONST_POW_2 = Math.abs(1L << RunInfo.getRandom().nextInt()); + private static final long CONST_1 = Generators.G.longs().next(); + private static final long CONST_2 = Generators.G.longs().next(); + private static final long CONST_POW_2 = Math.abs(1L << Generators.G.uniformLongs().next()); public static void main(String[] args) { TestFramework.run(); @@ -249,7 +250,8 @@ public long testXorSelf(long x) { // clamp value to [1,CONST_POW_2] @ForceInline private static long forceMinMax(long value) { - // equivalent to Math.min(CONST_POW_2, Math.max(value, 1)) + // Equivalent to Math.min(CONST_POW_2, Math.max(value, 1)). + // The bounds do not propagate to the type for longs with min/max return 1L + (value & (CONST_POW_2 - 1L)); } From fa56def3f314e550fb110961a427505dbf149e12 Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Mon, 27 Jan 2025 22:09:39 -0500 Subject: [PATCH 21/57] add extra info test assert messages --- .../jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java | 2 +- .../jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java index 0826ddb35af7c..ea3e130801faf 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java @@ -93,7 +93,7 @@ public void assertResult(int a, int b, int c, boolean d) { Asserts.assertEQ(CONST_BOOL_1 ^ CONST_BOOL_2 , testConstXorBool()); Asserts.assertEQ(false , testXorSelfBool(d)); Asserts.assertEQ(interpretedMaxPow2(a, b), testMaxPow2(a, b)); - Asserts.assertEQ(true, testMaxPow2Folded(a, b)); + Asserts.assertEQ(true, testMaxPow2Folded(a, b), String.format("assertEquals failed: CONST_POW_2=%d a=%d b=%d", CONST_POW_2,a,b)); } @Test diff --git a/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java index eb6accfe12f40..a4bb42b835277 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java @@ -89,7 +89,7 @@ public void assertResult(long a, long b, long c, long d) { Asserts.assertEQ(CONST_1 ^ CONST_2 , testConstXor()); Asserts.assertEQ(0L , testXorSelf(a)); Asserts.assertEQ(interpretedMaxPow2(a, b), testMaxPow2(a, b)); - Asserts.assertEQ(true, testMaxPow2Folded(a, b)); + Asserts.assertEQ(true, testMaxPow2Folded(a, b),String.format("assertEquals failed: CONST_POW_2=%d a=%d b=%d", CONST_POW_2,a,b)); } @Test From 61e9182141139625d000ca709c2e16665bdd1fc4 Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Mon, 27 Jan 2025 22:44:12 -0500 Subject: [PATCH 22/57] avoid MIN_VALUE in test --- .../compiler/c2/irTests/XorINodeIdealizationTests.java | 7 ++++--- .../compiler/c2/irTests/XorLNodeIdealizationTests.java | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java index ea3e130801faf..976bcb6801c1a 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java @@ -38,7 +38,7 @@ public class XorINodeIdealizationTests { private static final int CONST_2 = Generators.G.ints().next(); private static final boolean CONST_BOOL_1 = RunInfo.getRandom().nextBoolean(); private static final boolean CONST_BOOL_2 = RunInfo.getRandom().nextBoolean(); - private static final int CONST_POW_2 = Math.abs(1 << Generators.G.uniformInts().next()); + private static final int CONST_POW_2 = Math.abs(1 << Generators.G.uniformInts(0, 30).next()); public static void main(String[] args) { TestFramework.run(); @@ -92,8 +92,9 @@ public void assertResult(int a, int b, int c, boolean d) { Asserts.assertEQ(0 , testXorSelf(a)); Asserts.assertEQ(CONST_BOOL_1 ^ CONST_BOOL_2 , testConstXorBool()); Asserts.assertEQ(false , testXorSelfBool(d)); - Asserts.assertEQ(interpretedMaxPow2(a, b), testMaxPow2(a, b)); - Asserts.assertEQ(true, testMaxPow2Folded(a, b), String.format("assertEquals failed: CONST_POW_2=%d a=%d b=%d", CONST_POW_2,a,b)); + String msg = String.format("CONST_POW_2=%d a=%d b=%d", CONST_POW_2, a, b); + Asserts.assertEQ(interpretedMaxPow2(a, b), testMaxPow2(a, b), msg); + Asserts.assertEQ(true, testMaxPow2Folded(a, b), msg); } @Test diff --git a/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java index a4bb42b835277..c3b11930aa89e 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java @@ -37,7 +37,7 @@ public class XorLNodeIdealizationTests { private static final long CONST_1 = Generators.G.longs().next(); private static final long CONST_2 = Generators.G.longs().next(); - private static final long CONST_POW_2 = Math.abs(1L << Generators.G.uniformLongs().next()); + private static final long CONST_POW_2 = Math.abs(1L << Generators.G.uniformInts(0,62).next()); public static void main(String[] args) { TestFramework.run(); @@ -88,8 +88,9 @@ public void assertResult(long a, long b, long c, long d) { Asserts.assertEQ(-2023 - a , test17(a)); Asserts.assertEQ(CONST_1 ^ CONST_2 , testConstXor()); Asserts.assertEQ(0L , testXorSelf(a)); - Asserts.assertEQ(interpretedMaxPow2(a, b), testMaxPow2(a, b)); - Asserts.assertEQ(true, testMaxPow2Folded(a, b),String.format("assertEquals failed: CONST_POW_2=%d a=%d b=%d", CONST_POW_2,a,b)); + String msg = String.format("CONST_POW_2=%d a=%d b=%d", CONST_POW_2, a, b); + Asserts.assertEQ(interpretedMaxPow2(a, b), testMaxPow2(a, b), msg); + Asserts.assertEQ(true, testMaxPow2Folded(a, b), msg); } @Test From 03fcdb1e60fdfd46a28f81225feba63d00b59f12 Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Mon, 27 Jan 2025 22:58:34 -0500 Subject: [PATCH 23/57] avoid MIN_VALUE in test --- .../jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java | 3 ++- .../jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java index 976bcb6801c1a..290728a2747f5 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java @@ -38,7 +38,8 @@ public class XorINodeIdealizationTests { private static final int CONST_2 = Generators.G.ints().next(); private static final boolean CONST_BOOL_1 = RunInfo.getRandom().nextBoolean(); private static final boolean CONST_BOOL_2 = RunInfo.getRandom().nextBoolean(); - private static final int CONST_POW_2 = Math.abs(1 << Generators.G.uniformInts(0, 30).next()); + private static final int CONST_POW_2 = Generators.G.powerOfTwoInts(0) + .restricted(1, Integer.MAX_VALUE).next(); public static void main(String[] args) { TestFramework.run(); diff --git a/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java index c3b11930aa89e..0f7b3f27c277d 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java @@ -37,7 +37,8 @@ public class XorLNodeIdealizationTests { private static final long CONST_1 = Generators.G.longs().next(); private static final long CONST_2 = Generators.G.longs().next(); - private static final long CONST_POW_2 = Math.abs(1L << Generators.G.uniformInts(0,62).next()); + private static final long CONST_POW_2 = Generators.G.powerOfTwoLongs(0) + .restricted(1L, Long.MAX_VALUE).next(); public static void main(String[] args) { TestFramework.run(); From 7ef5c5c7108d06fe3fce50aa9ea0ddf5e15dab4f Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Thu, 30 Jan 2025 18:05:09 -0500 Subject: [PATCH 24/57] extract static methods for testing; add GTESTs; more extensive int ir tests; remove some ir tests for longs due to difficulty in applying constant clamping. --- src/hotspot/share/opto/addnode.cpp | 78 +++++---- src/hotspot/share/opto/addnode.hpp | 6 + .../c2/irTests/XorINodeIdealizationTests.java | 154 ++++++++++++------ .../c2/irTests/XorLNodeIdealizationTests.java | 54 +----- 4 files changed, 156 insertions(+), 136 deletions(-) diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index 63d44873d5251..81c795788f767 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -998,25 +998,29 @@ const Type *XorINode::add_ring( const Type *t0, const Type *t1 ) const { // At least one of the arguments is not constant - // Result of xor can only have bits sets where any of the // inputs have bits set. lo can always become 0. if (r0->_lo >= 0 && r1->_lo >= 0) { - // x ^ y cannot have any bit set that is higher than both the highest bits set in x and y - // x cannot have any bit set that is higher than the highest bit set in r0->_hi - // y cannot have any bit set that is higher than the highest bit set in r1->_hi - - // Note: cast to unsigned happens before +1 to avoid signed overflow, and - // round_up is safe because high bit is unset (0 <= lo <= hi) - juint max = round_up_power_of_2(juint(r0->_hi | r1->_hi) + 1) - 1; - + jint max = calc_xor_max(r0->_hi, r1->_hi); return TypeInt::make(0, max, MAX2(r0->_widen, r1->_widen)); } return TypeInt::INT; // Any integer, but still no symbols. } +jint XorINode::calc_xor_max(const jint hi_0, const jint hi_1) { + assert(hi_0 >= 0, "must be non-negative"); + assert(hi_1 >= 0, "must be non-negative"); + + // x ^ y cannot have any bit set that is higher than both the highest bits set in x and y + // x cannot have any bit set that is higher than the highest bit set in r0->_hi + // y cannot have any bit set that is higher than the highest bit set in r1->_hi + + // Note: cast to unsigned happens before +1 to avoid signed overflow, and + // round_up is safe because high bit is unset (0 <= lo <= hi) + return round_up_power_of_2(juint(hi_0 | hi_1) + 1) - 1; +} //============================================================================= //------------------------------add_ring--------------------------------------- const Type *XorLNode::add_ring( const Type *t0, const Type *t1 ) const { @@ -1034,37 +1038,43 @@ const Type *XorLNode::add_ring( const Type *t0, const Type *t1 ) const { // inputs have bits set. lo can always become 0. if (r0->_lo >= 0 && r1->_lo >= 0) { - // x ^ y cannot have any bit set that is higher than both the highest bits set in x and y - // x cannot have any bit set that is higher than the highest bit set in r0->_hi - // y cannot have any bit set that is higher than the highest bit set in r1->_hi - - // We want to find a value that has all 1 bits everywhere up to and including - // the highest bits set in r0->_hi as well as r1->_hi. For this,we can take the next - // power of 2 strictly greater than both hi values and subtract 1 from it. - - // Example 1: - // r0->_hi = 5 (0b0101) r1->_hi=1 (0b0001) - // (5|1)+1 = 0b0110 - // round_up_pow2 = 0b1000 - // -1 = 0b0111 = max - - // Example 2 - this demonstrates need for the +1: - // r0->_hi = 4 (0b0100) r1->_hi=4 (0b0100) - // (4|4)+1 = 0b0101 - // round_up_pow2 = 0b1000 - // -1 = 0b0111 = max - // Without the +1, round_up_pow2 would be 0b0100, resulting in 0b0011 as max - - // Note: cast to unsigned happens before +1 to avoid signed overflow, and - // round_up is safe because high bit is unset (0 <= lo <= hi) - julong max = round_up_power_of_2(julong(r0->_hi | r1->_hi) + 1) - 1; - + julong max = calc_xor_max(r0->_hi, r1->_hi); return TypeLong::make(0, max, MAX2(r0->_widen, r1->_widen)); } return TypeLong::LONG; // Any integer, but still no symbols. } +jlong XorLNode::calc_xor_max(const jlong hi_0, const jlong hi_1) { + assert(hi_0 >= 0, "must be non-negative"); + assert(hi_1 >= 0, "must be non-negative"); + + // x ^ y cannot have any bit set that is higher than both the highest bits set in x and y + // x cannot have any bit set that is higher than the highest bit set in r0->_hi + // y cannot have any bit set that is higher than the highest bit set in r1->_hi + + // We want to find a value that has all 1 bits everywhere up to and including + // the highest bits set in r0->_hi as well as r1->_hi. For this,we can take the next + // power of 2 strictly greater than both hi values and subtract 1 from it. + + // Example 1: + // r0->_hi = 5 (0b0101) r1->_hi=1 (0b0001) + // (5|1)+1 = 0b0110 + // round_up_pow2 = 0b1000 + // -1 = 0b0111 = max + + // Example 2 - this demonstrates need for the +1: + // r0->_hi = 4 (0b0100) r1->_hi=4 (0b0100) + // (4|4)+1 = 0b0101 + // round_up_pow2 = 0b1000 + // -1 = 0b0111 = max + // Without the +1, round_up_pow2 would be 0b0100, resulting in 0b0011 as max + + // Note: cast to unsigned happens before +1 to avoid signed overflow, and + // round_up is safe because high bit is unset (0 <= lo <= hi) + return round_up_power_of_2(julong(hi_0 | hi_1) + 1) - 1 ; +} + Node* XorLNode::Ideal(PhaseGVN* phase, bool can_reshape) { Node* in1 = in(1); Node* in2 = in(2); diff --git a/src/hotspot/share/opto/addnode.hpp b/src/hotspot/share/opto/addnode.hpp index c409fb8cea839..8fa7cbf9ce809 100644 --- a/src/hotspot/share/opto/addnode.hpp +++ b/src/hotspot/share/opto/addnode.hpp @@ -235,6 +235,9 @@ class XorINode : public AddNode { int min_opcode() const { return Op_MinI; } virtual const Type *Value(PhaseGVN *phase) const; virtual uint ideal_reg() const { return Op_RegI; } +private: + friend void test_xor_bounds(jlong, jlong, jlong, jlong); + static jint calc_xor_max( const jint, const jint ); }; //------------------------------XorINode--------------------------------------- @@ -251,6 +254,9 @@ class XorLNode : public AddNode { int min_opcode() const { return Op_MinL; } virtual const Type *Value(PhaseGVN *phase) const; virtual uint ideal_reg() const { return Op_RegL; } +private: + friend void test_xor_bounds(jlong, jlong, jlong, jlong); + static jlong calc_xor_max( const jlong, const jlong ); }; //------------------------------MaxNode---------------------------------------- diff --git a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java index 290728a2747f5..6e8563640c9df 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, 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 @@ -23,7 +23,7 @@ package compiler.c2.irTests; import jdk.test.lib.Asserts; -import compiler.lib.generators.Generators; +import compiler.lib.generators.*; import compiler.lib.ir_framework.*; /* @@ -34,45 +34,41 @@ * @run driver compiler.c2.irTests.XorINodeIdealizationTests */ public class XorINodeIdealizationTests { - private static final int CONST_1 = Generators.G.ints().next(); - private static final int CONST_2 = Generators.G.ints().next(); - private static final boolean CONST_BOOL_1 = RunInfo.getRandom().nextBoolean(); - private static final boolean CONST_BOOL_2 = RunInfo.getRandom().nextBoolean(); - private static final int CONST_POW_2 = Generators.G.powerOfTwoInts(0) - .restricted(1, Integer.MAX_VALUE).next(); + private static final RestrictableGenerator G = Generators.G.ints(); + private static final int CONST_1 = G.next(); + private static final int CONST_2 = G.next(); + + public static void main(String[] args) { TestFramework.run(); } @Run(test = {"test1", "test2", "test3", - "test4", "test5", "test6", - "test7", "test8", "test9", - "test10", "test11", "test12", - "test13", "test14", "test15", - "test16", "test17", - "testConstXor", "testXorSelf", - "testConstXorBool", "testXorSelfBool", - "testMaxPow2","testMaxPow2Folded" - + "test4", "test5", "test6", + "test7", "test8", "test9", + "test10", "test11", "test12", + "test13", "test14", "test15", + "test16", "test17", + "testConstXor", "testXorSelf" }) public void runMethod() { int a = RunInfo.getRandom().nextInt(); int b = RunInfo.getRandom().nextInt(); int c = RunInfo.getRandom().nextInt(); - boolean d = RunInfo.getRandom().nextBoolean(); + int d = RunInfo.getRandom().nextInt(); int min = Integer.MIN_VALUE; int max = Integer.MAX_VALUE; - assertResult(0, 0, 0, false); + assertResult(0, 0, 0, 0); assertResult(a, b, c, d); - assertResult(min, min, min, false); - assertResult(max, max, max, true); + assertResult(min, min, min, min); + assertResult(max, max, max, max); } @DontCompile - public void assertResult(int a, int b, int c, boolean d) { + public void assertResult(int a, int b, int c, int d) { Asserts.assertEQ(b - a , test1(a, b)); Asserts.assertEQ(a - b , test2(a, b)); Asserts.assertEQ(b - a , test3(a, b)); @@ -89,15 +85,12 @@ public void assertResult(int a, int b, int c, boolean d) { Asserts.assertEQ(~a , test14(a)); Asserts.assertEQ(~a , test15(a)); Asserts.assertEQ((~a + b) + (~a | c), test16(a, b, c)); - Asserts.assertEQ(CONST_1 ^ CONST_2 , testConstXor()); - Asserts.assertEQ(0 , testXorSelf(a)); - Asserts.assertEQ(CONST_BOOL_1 ^ CONST_BOOL_2 , testConstXorBool()); - Asserts.assertEQ(false , testXorSelfBool(d)); - String msg = String.format("CONST_POW_2=%d a=%d b=%d", CONST_POW_2, a, b); - Asserts.assertEQ(interpretedMaxPow2(a, b), testMaxPow2(a, b), msg); - Asserts.assertEQ(true, testMaxPow2Folded(a, b), msg); + Asserts.assertEQ(-2023 - a , test17(a)); + Asserts.assertEQ(CONST_1 ^ CONST_2, testConstXor()); + Asserts.assertEQ(0, testXorSelf(a)); } + @Test @IR(failOn = {IRNode.XOR, IRNode.ADD}) @IR(counts = {IRNode.SUB, "1"}) @@ -240,7 +233,7 @@ public int test17(int x) { @Test @IR(failOn = {IRNode.XOR}) @IR(counts = {IRNode.CON_I, "1"}) - // Checks (c ^c) => c (constant folded) + // Checks (c1 ^ c2) => c3 (constant folded) public int testConstXor() { return CONST_1 ^ CONST_2; } @@ -253,6 +246,23 @@ public int testXorSelf(int x) { return x ^ x; } + private static final boolean CONST_BOOL_1 = RunInfo.getRandom().nextBoolean(); + private static final boolean CONST_BOOL_2 = RunInfo.getRandom().nextBoolean(); + + @Run(test={ + "testConstXorBool", "testXorSelfBool" + }) + public void runBooleanTests() { + assertBooleanResult(true); + assertBooleanResult(false); + } + + @DontCompile + public void assertBooleanResult(boolean b){ + Asserts.assertEQ(CONST_BOOL_1 ^ CONST_BOOL_2, testConstXorBool()); + Asserts.assertEQ(false, testXorSelfBool(b)); + } + @Test @IR(failOn = {IRNode.XOR}) @IR(counts = {IRNode.CON_I, "1"}) @@ -269,40 +279,78 @@ public boolean testXorSelfBool(boolean x) { return x ^ x; } - // clamp value to [1,CONST_POW_2] - @ForceInline - private static int forceMinMax(int value){ - return Math.min(CONST_POW_2, Math.max(value, 1)); + private static final Range RANGE_1; + private static final Range RANGE_2; + private static final int XOR_MAX_OF_RANGES; + + static { + var r1 = RANGE_1 = Range.generate(G); + var r2 = RANGE_2 = Range.generate(G); + if (r1.lo() >= 0 && r2.lo() >= 0 && r1.hi() != 0 && r2.hi() != 0) { + XOR_MAX_OF_RANGES = Integer.highestOneBit(r1.hi() | r2.hi() * 2) - 1; + } else { + XOR_MAX_OF_RANGES = Integer.MAX_VALUE; + } } - @Test - @IR(counts = {IRNode.XOR, "1"}) // must not be constant-folded - // checks that add_ring computes correct max on exact powers of 2 - public boolean testMaxPow2(int x, int y) { - x = forceMinMax(x); - y = forceMinMax(y); - - long xor = x ^ y; - return xor < CONST_POW_2; + @Run(test = { + "testFoldableXor", "testXorConstRange" + }) + public void runRangeTests() { + var rand1 = G.restricted(RANGE_1.lo(), RANGE_1.hi()); + var rand2 = G.restricted(RANGE_2.lo(), RANGE_2.hi()); + + for (int i = 0; i < 100; i++) { + checkXor(rand1.next(), rand2.next()); + } + checkXor(RANGE_1.hi(), RANGE_2.hi()); + checkXor(RANGE_1.lo(), RANGE_2.lo()); } @DontCompile - public boolean interpretedMaxPow2(int x, int y) { - x = forceMinMax(x); - y = forceMinMax(y); + public void checkXor(int a, int b) { + Asserts.assertEQ(true, testFoldableXor(a, b)); + Asserts.assertEQ(RANGE_1.clamp(a) ^ RANGE_2.clamp(b), testXorConstRange(a, b)); + } - long xor = x ^ y; - return xor < CONST_POW_2; + @Test + public int testXorConstRange(int x, int y) { + x = RANGE_1.clamp(x); + y = RANGE_2.clamp(y); + + return x ^ y; } @Test @IR(failOn = {IRNode.XOR}) @IR(counts = {IRNode.CON_I, "1"}) - public boolean testMaxPow2Folded(int x, int y) { - x = forceMinMax(x); - y = forceMinMax(y); + public boolean testFoldableXor(int x, int y) { + x = RANGE_1.clamp(x); + y = RANGE_2.clamp(y); + var xor = x ^ y; + return xor <= XOR_MAX_OF_RANGES; + } - long xor = x ^ y; - return xor < (CONST_POW_2*2L); + record Range(int lo, int hi) { + Range { + if (lo > hi) { + throw new IllegalArgumentException("lo > hi"); + } + } + + int clamp(int v) { + return Math.min(hi, Math.max(v, lo)); + } + + static Range generate(Generator g) { + var a = g.next(); + var b = g.next(); + if (a > b) { + var tmp = a; + a = b; + b = tmp; + } + return new Range(a, b); + } } } diff --git a/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java index 0f7b3f27c277d..563797643525c 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, 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 @@ -23,7 +23,7 @@ package compiler.c2.irTests; import jdk.test.lib.Asserts; -import compiler.lib.generators.Generators; +import compiler.lib.generators.*; import compiler.lib.ir_framework.*; /* @@ -34,11 +34,9 @@ * @run driver compiler.c2.irTests.XorLNodeIdealizationTests */ public class XorLNodeIdealizationTests { - - private static final long CONST_1 = Generators.G.longs().next(); - private static final long CONST_2 = Generators.G.longs().next(); - private static final long CONST_POW_2 = Generators.G.powerOfTwoLongs(0) - .restricted(1L, Long.MAX_VALUE).next(); + private static final RestrictableGenerator G = Generators.G.longs(); + private static final long CONST_1 = G.next(); + private static final long CONST_2 = G.next(); public static void main(String[] args) { TestFramework.run(); @@ -51,7 +49,6 @@ public static void main(String[] args) { "test13", "test14", "test15", "test16", "test17", "testConstXor", "testXorSelf", - "testMaxPow2","testMaxPow2Folded" }) public void runMethod() { long min = Long.MIN_VALUE; @@ -89,9 +86,6 @@ public void assertResult(long a, long b, long c, long d) { Asserts.assertEQ(-2023 - a , test17(a)); Asserts.assertEQ(CONST_1 ^ CONST_2 , testConstXor()); Asserts.assertEQ(0L , testXorSelf(a)); - String msg = String.format("CONST_POW_2=%d a=%d b=%d", CONST_POW_2, a, b); - Asserts.assertEQ(interpretedMaxPow2(a, b), testMaxPow2(a, b), msg); - Asserts.assertEQ(true, testMaxPow2Folded(a, b), msg); } @Test @@ -248,42 +242,4 @@ public long testConstXor() { public long testXorSelf(long x) { return x ^ x; } - - // clamp value to [1,CONST_POW_2] - @ForceInline - private static long forceMinMax(long value) { - // Equivalent to Math.min(CONST_POW_2, Math.max(value, 1)). - // The bounds do not propagate to the type for longs with min/max - return 1L + (value & (CONST_POW_2 - 1L)); - } - - @Test - @IR(counts = {IRNode.XOR, "1"}) // must not be constant-folded - // checks that add_ring computes correct max on exact powers of 2 - public boolean testMaxPow2(long x, long y) { - x = forceMinMax(x); - y = forceMinMax(y); - long xor = x ^ y; - return xor < CONST_POW_2; - } - - @DontCompile - public boolean interpretedMaxPow2(long x, long y) { - x = forceMinMax(x); - y = forceMinMax(y); - - long xor = x ^ y; - return xor < CONST_POW_2; - } - - @Test - @IR(failOn = {IRNode.XOR}) - @IR(counts = {IRNode.CON_I, "1"}) - public boolean testMaxPow2Folded(long x, long y) { - x = forceMinMax(x); - y = forceMinMax(y); - - long xor = x ^ y; - return xor < (CONST_POW_2*2L); - } } From 9bc2afb4d0d11793a425d34d70719782954f4ee9 Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Thu, 30 Jan 2025 21:37:56 -0500 Subject: [PATCH 25/57] add missed test file --- test/hotspot/gtest/opto/test_xor_node.cpp | 70 +++++++++++++++++++++-- 1 file changed, 65 insertions(+), 5 deletions(-) diff --git a/test/hotspot/gtest/opto/test_xor_node.cpp b/test/hotspot/gtest/opto/test_xor_node.cpp index 9eb402b18828c..a28a540591b5e 100644 --- a/test/hotspot/gtest/opto/test_xor_node.cpp +++ b/test/hotspot/gtest/opto/test_xor_node.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 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 @@ -22,12 +22,72 @@ * */ +#include +#include + #include "opto/addnode.hpp" #include "unittest.hpp" -#include "runtime/interfaceSupport.inline.hpp" -TEST_VM(opto, xor) { -// ThreadInVMfromNative ThreadInVMfromNative(JavaThread::current()); +void test_xor_bounds(jlong hi_0, jlong hi_1, jlong val_0, jlong val_1) { + + // Skip out-of-bounds values for convenience + if(val_0> hi_0 || val_0 < 0 || val_1 > hi_1 || val_1< 0) { + return; + } + + EXPECT_LE(val_0 ^ val_1, XorLNode::calc_xor_max(hi_0, hi_1)); + + // check ints when in range + if(hi_0 <= INT_MAX && hi_1 <= INT_MAX) { + EXPECT_LE(val_0 ^ val_1, XorINode::calc_xor_max(hi_0, hi_1)); + } +} + +void test_exhaustive_values(jlong hi_0, jlong hi_1){ + + jlong fail_val_0, fail_val_1; + + bool hit_bound=false; + for(jlong val_0 = 0; val_0 <= hi_0; val_0++){ + for(jlong val_1 = val_0; val_1 <= hi_1; val_1++){ + test_xor_bounds(hi_0, hi_1, val_0, val_1); + } + } +} + +void test_sample_values(jlong hi_0, jlong hi_1){ + + jlong fail_val_0, fail_val_1; + + for(int i=0; i<=3; i++){ + for(int j=0; j<=3; j++){ + // Some bit combinations near the low and high ends of the range + test_xor_bounds(hi_0, hi_1, i, j); + test_xor_bounds(hi_0, hi_1, hi_0-i, hi_1-j); + } + } +} + +void test_exhaustive_values_with_bounds_in_range(jlong lo, jlong hi){ + for(jlong hi_0 = lo; hi_0 <= hi; hi_0++){ + for(jlong hi_1 = hi_0; hi_1 <=hi; hi_1++){ + test_exhaustive_values(hi_0, hi_1); + } + } +} + +void test_sample_values_with_bounds_in_range(jlong lo, jlong hi){ + for(jlong hi_0 = lo; hi_0 <= hi; hi_0++){ + for(jlong hi_1 = hi_0; hi_1 <=hi; hi_1++){ + test_sample_values(hi_0, hi_1); + } + } +} -// const TypeLong *v=TypeLong::make(0); +TEST_VM(opto, xor_max) { + test_exhaustive_values_with_bounds_in_range(0, 15); + test_sample_values_with_bounds_in_range(INT_MAX - 1, INT_MAX); + test_sample_values_with_bounds_in_range((1 << 30) - 1, 1 << 30); + test_sample_values_with_bounds_in_range(LONG_MAX - 1, LONG_MAX); + test_sample_values_with_bounds_in_range((1L << 62) - 1, 1L << 62); } From 710012b424df4839ee113010b28c883656456ec8 Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Thu, 30 Jan 2025 21:42:04 -0500 Subject: [PATCH 26/57] clean up comments --- src/hotspot/share/opto/addnode.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index 81c795788f767..f6f67057bf296 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -1006,7 +1006,7 @@ const Type *XorINode::add_ring( const Type *t0, const Type *t1 ) const { return TypeInt::make(0, max, MAX2(r0->_widen, r1->_widen)); } - return TypeInt::INT; // Any integer, but still no symbols. + return TypeInt::INT; } jint XorINode::calc_xor_max(const jint hi_0, const jint hi_1) { @@ -1042,7 +1042,7 @@ const Type *XorLNode::add_ring( const Type *t0, const Type *t1 ) const { return TypeLong::make(0, max, MAX2(r0->_widen, r1->_widen)); } - return TypeLong::LONG; // Any integer, but still no symbols. + return TypeLong::LONG; } jlong XorLNode::calc_xor_max(const jlong hi_0, const jlong hi_1) { From bdb1a82c374088181d21c9defa1b7261da53a7d6 Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Fri, 31 Jan 2025 00:04:27 -0500 Subject: [PATCH 27/57] template calc_xor_max --- src/hotspot/share/opto/addnode.cpp | 76 ++++++++++------------ src/hotspot/share/opto/addnode.hpp | 6 -- test/hotspot/gtest/opto/test_xor_node.cpp | 78 +++++++++++++---------- 3 files changed, 77 insertions(+), 83 deletions(-) diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index f6f67057bf296..0a5aea4b18efa 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -981,6 +981,37 @@ const Type* XorINode::Value(PhaseGVN* phase) const { return AddNode::Value(phase); } +template +S calc_xor_max(const S hi_0, const S hi_1) { + assert(hi_0 >= 0, "must be non-negative"); + assert(hi_1 >= 0, "must be non-negative"); + + // x ^ y cannot have any bit set that is higher than both the highest bits set in x and y + // x cannot have any bit set that is higher than the highest bit set in r0->_hi + // y cannot have any bit set that is higher than the highest bit set in r1->_hi + + // We want to find a value that has all 1 bits everywhere up to and including + // the highest bits set in r0->_hi as well as r1->_hi. For this,we can take the next + // power of 2 strictly greater than both hi values and subtract 1 from it. + + // Example 1: + // r0->_hi = 5 (0b0101) r1->_hi=1 (0b0001) + // (5|1)+1 = 0b0110 + // round_up_pow2 = 0b1000 + // -1 = 0b0111 = max + + // Example 2 - this demonstrates need for the +1: + // r0->_hi = 4 (0b0100) r1->_hi=4 (0b0100) + // (4|4)+1 = 0b0101 + // round_up_pow2 = 0b1000 + // -1 = 0b0111 = max + // Without the +1, round_up_pow2 would be 0b0100, resulting in 0b0011 as max + + // Note: cast to unsigned happens before +1 to avoid signed overflow, and + // round_up is safe because high bit is unset (0 <= lo <= hi) + + return round_up_power_of_2(U(hi_0 | hi_1) + 1) - 1 ; +} //------------------------------add_ring--------------------------------------- // Supplied function returns the sum of the inputs IN THE CURRENT RING. For @@ -1002,25 +1033,13 @@ const Type *XorINode::add_ring( const Type *t0, const Type *t1 ) const { // inputs have bits set. lo can always become 0. if (r0->_lo >= 0 && r1->_lo >= 0) { - jint max = calc_xor_max(r0->_hi, r1->_hi); + jint max = calc_xor_max(r0->_hi, r1->_hi); return TypeInt::make(0, max, MAX2(r0->_widen, r1->_widen)); } return TypeInt::INT; } -jint XorINode::calc_xor_max(const jint hi_0, const jint hi_1) { - assert(hi_0 >= 0, "must be non-negative"); - assert(hi_1 >= 0, "must be non-negative"); - - // x ^ y cannot have any bit set that is higher than both the highest bits set in x and y - // x cannot have any bit set that is higher than the highest bit set in r0->_hi - // y cannot have any bit set that is higher than the highest bit set in r1->_hi - - // Note: cast to unsigned happens before +1 to avoid signed overflow, and - // round_up is safe because high bit is unset (0 <= lo <= hi) - return round_up_power_of_2(juint(hi_0 | hi_1) + 1) - 1; -} //============================================================================= //------------------------------add_ring--------------------------------------- const Type *XorLNode::add_ring( const Type *t0, const Type *t1 ) const { @@ -1038,42 +1057,13 @@ const Type *XorLNode::add_ring( const Type *t0, const Type *t1 ) const { // inputs have bits set. lo can always become 0. if (r0->_lo >= 0 && r1->_lo >= 0) { - julong max = calc_xor_max(r0->_hi, r1->_hi); + julong max = calc_xor_max(r0->_hi, r1->_hi); return TypeLong::make(0, max, MAX2(r0->_widen, r1->_widen)); } return TypeLong::LONG; } -jlong XorLNode::calc_xor_max(const jlong hi_0, const jlong hi_1) { - assert(hi_0 >= 0, "must be non-negative"); - assert(hi_1 >= 0, "must be non-negative"); - - // x ^ y cannot have any bit set that is higher than both the highest bits set in x and y - // x cannot have any bit set that is higher than the highest bit set in r0->_hi - // y cannot have any bit set that is higher than the highest bit set in r1->_hi - - // We want to find a value that has all 1 bits everywhere up to and including - // the highest bits set in r0->_hi as well as r1->_hi. For this,we can take the next - // power of 2 strictly greater than both hi values and subtract 1 from it. - - // Example 1: - // r0->_hi = 5 (0b0101) r1->_hi=1 (0b0001) - // (5|1)+1 = 0b0110 - // round_up_pow2 = 0b1000 - // -1 = 0b0111 = max - - // Example 2 - this demonstrates need for the +1: - // r0->_hi = 4 (0b0100) r1->_hi=4 (0b0100) - // (4|4)+1 = 0b0101 - // round_up_pow2 = 0b1000 - // -1 = 0b0111 = max - // Without the +1, round_up_pow2 would be 0b0100, resulting in 0b0011 as max - - // Note: cast to unsigned happens before +1 to avoid signed overflow, and - // round_up is safe because high bit is unset (0 <= lo <= hi) - return round_up_power_of_2(julong(hi_0 | hi_1) + 1) - 1 ; -} Node* XorLNode::Ideal(PhaseGVN* phase, bool can_reshape) { Node* in1 = in(1); diff --git a/src/hotspot/share/opto/addnode.hpp b/src/hotspot/share/opto/addnode.hpp index 8fa7cbf9ce809..c409fb8cea839 100644 --- a/src/hotspot/share/opto/addnode.hpp +++ b/src/hotspot/share/opto/addnode.hpp @@ -235,9 +235,6 @@ class XorINode : public AddNode { int min_opcode() const { return Op_MinI; } virtual const Type *Value(PhaseGVN *phase) const; virtual uint ideal_reg() const { return Op_RegI; } -private: - friend void test_xor_bounds(jlong, jlong, jlong, jlong); - static jint calc_xor_max( const jint, const jint ); }; //------------------------------XorINode--------------------------------------- @@ -254,9 +251,6 @@ class XorLNode : public AddNode { int min_opcode() const { return Op_MinL; } virtual const Type *Value(PhaseGVN *phase) const; virtual uint ideal_reg() const { return Op_RegL; } -private: - friend void test_xor_bounds(jlong, jlong, jlong, jlong); - static jlong calc_xor_max( const jlong, const jlong ); }; //------------------------------MaxNode---------------------------------------- diff --git a/test/hotspot/gtest/opto/test_xor_node.cpp b/test/hotspot/gtest/opto/test_xor_node.cpp index a28a540591b5e..ea5e8b9419492 100644 --- a/test/hotspot/gtest/opto/test_xor_node.cpp +++ b/test/hotspot/gtest/opto/test_xor_node.cpp @@ -22,45 +22,46 @@ * */ -#include -#include - #include "opto/addnode.hpp" #include "unittest.hpp" -void test_xor_bounds(jlong hi_0, jlong hi_1, jlong val_0, jlong val_1) { +template S calc_xor_max(const S hi_0, const S hi_1); + +jint calc_max(const jint hi_0, const jint hi_1) { + return calc_xor_max(hi_0, hi_1); +} + +jlong calc_max(const jlong hi_0, const jlong hi_1) { + return calc_xor_max(hi_0, hi_1); +} + +template +void test_xor_bounds(S hi_0, S hi_1, S val_0, S val_1) { // Skip out-of-bounds values for convenience - if(val_0> hi_0 || val_0 < 0 || val_1 > hi_1 || val_1< 0) { + if(val_0> hi_0 || val_0 < S(0) || val_1 > hi_1 || val_1< S(0)) { return; } - EXPECT_LE(val_0 ^ val_1, XorLNode::calc_xor_max(hi_0, hi_1)); - - // check ints when in range - if(hi_0 <= INT_MAX && hi_1 <= INT_MAX) { - EXPECT_LE(val_0 ^ val_1, XorINode::calc_xor_max(hi_0, hi_1)); - } + S v = val_0 ^ val_1; + S max = calc_max(hi_0, hi_1); + EXPECT_LE(v, max); } -void test_exhaustive_values(jlong hi_0, jlong hi_1){ - - jlong fail_val_0, fail_val_1; - - bool hit_bound=false; - for(jlong val_0 = 0; val_0 <= hi_0; val_0++){ - for(jlong val_1 = val_0; val_1 <= hi_1; val_1++){ +template +void test_exhaustive_values(S hi_0, S hi_1){ + for(S val_0 = 0; val_0 <= hi_0; val_0++){ + for(S val_1 = val_0; val_1 <= hi_1; val_1++){ test_xor_bounds(hi_0, hi_1, val_0, val_1); } } } -void test_sample_values(jlong hi_0, jlong hi_1){ +template +void test_sample_values(S hi_0, S hi_1){ - jlong fail_val_0, fail_val_1; - - for(int i=0; i<=3; i++){ - for(int j=0; j<=3; j++){ + for(S i=0; i<=3; i++){ + for(S j=0; j<=3; j++){ // Some bit combinations near the low and high ends of the range test_xor_bounds(hi_0, hi_1, i, j); test_xor_bounds(hi_0, hi_1, hi_0-i, hi_1-j); @@ -68,26 +69,35 @@ void test_sample_values(jlong hi_0, jlong hi_1){ } } -void test_exhaustive_values_with_bounds_in_range(jlong lo, jlong hi){ - for(jlong hi_0 = lo; hi_0 <= hi; hi_0++){ - for(jlong hi_1 = hi_0; hi_1 <=hi; hi_1++){ +template +void test_exhaustive_values_with_bounds_in_range(S lo, S hi){ + for(S hi_0 = lo; hi_0 <= hi; hi_0++){ + for(S hi_1 = hi_0; hi_1 <=hi; hi_1++){ test_exhaustive_values(hi_0, hi_1); } } } -void test_sample_values_with_bounds_in_range(jlong lo, jlong hi){ - for(jlong hi_0 = lo; hi_0 <= hi; hi_0++){ - for(jlong hi_1 = hi_0; hi_1 <=hi; hi_1++){ +template +void test_sample_values_with_bounds_in_range(S lo, S hi){ + for(S hi_0 = lo; hi_0 <= hi; hi_0++){ + for(S hi_1 = hi_0; hi_1 <=hi; hi_1++){ test_sample_values(hi_0, hi_1); } } } TEST_VM(opto, xor_max) { - test_exhaustive_values_with_bounds_in_range(0, 15); - test_sample_values_with_bounds_in_range(INT_MAX - 1, INT_MAX); - test_sample_values_with_bounds_in_range((1 << 30) - 1, 1 << 30); - test_sample_values_with_bounds_in_range(LONG_MAX - 1, LONG_MAX); - test_sample_values_with_bounds_in_range((1L << 62) - 1, 1L << 62); + + auto maxjint = jint(std::numeric_limits::max()); + auto maxjlong = jint(std::numeric_limits::max()); + + test_exhaustive_values_with_bounds_in_range(0, 15); + test_exhaustive_values_with_bounds_in_range(0, 15); + + test_sample_values_with_bounds_in_range(maxjint-1, maxjint); + test_sample_values_with_bounds_in_range(maxjlong-1, maxjlong); + + test_sample_values_with_bounds_in_range((1 << 30) - 1, 1 << 30); + test_sample_values_with_bounds_in_range((1L << 62) - 1, 1L << 62); } From dbae63cda84c412f534c1163ee5c48622ffac885 Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Fri, 31 Jan 2025 00:30:43 -0500 Subject: [PATCH 28/57] dedup test code --- test/hotspot/gtest/opto/test_xor_node.cpp | 30 ++++++++++------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/test/hotspot/gtest/opto/test_xor_node.cpp b/test/hotspot/gtest/opto/test_xor_node.cpp index ea5e8b9419492..664a9efe9f9b5 100644 --- a/test/hotspot/gtest/opto/test_xor_node.cpp +++ b/test/hotspot/gtest/opto/test_xor_node.cpp @@ -22,6 +22,8 @@ * */ +#include + #include "opto/addnode.hpp" #include "unittest.hpp" @@ -70,34 +72,28 @@ void test_sample_values(S hi_0, S hi_1){ } template -void test_exhaustive_values_with_bounds_in_range(S lo, S hi){ +void test_in_ranges(S lo, S hi, std::function f){ for(S hi_0 = lo; hi_0 <= hi; hi_0++){ for(S hi_1 = hi_0; hi_1 <=hi; hi_1++){ - test_exhaustive_values(hi_0, hi_1); - } - } -} - -template -void test_sample_values_with_bounds_in_range(S lo, S hi){ - for(S hi_0 = lo; hi_0 <= hi; hi_0++){ - for(S hi_1 = hi_0; hi_1 <=hi; hi_1++){ - test_sample_values(hi_0, hi_1); + f(hi_0, hi_1); } } } TEST_VM(opto, xor_max) { + auto sample_values = [](auto a, auto b) { return test_sample_values(a, b); }; + auto exhaustive_values = [](auto a, auto b) { return test_exhaustive_values(a, b); }; auto maxjint = jint(std::numeric_limits::max()); auto maxjlong = jint(std::numeric_limits::max()); - test_exhaustive_values_with_bounds_in_range(0, 15); - test_exhaustive_values_with_bounds_in_range(0, 15); - test_sample_values_with_bounds_in_range(maxjint-1, maxjint); - test_sample_values_with_bounds_in_range(maxjlong-1, maxjlong); + test_in_ranges(0, 15, exhaustive_values); + test_in_ranges(0, 15, exhaustive_values); + + test_in_ranges(maxjint-1, maxjint, sample_values); + test_in_ranges(maxjlong-1, maxjlong, sample_values); - test_sample_values_with_bounds_in_range((1 << 30) - 1, 1 << 30); - test_sample_values_with_bounds_in_range((1L << 62) - 1, 1L << 62); + test_in_ranges((1 << 30) - 1, 1 << 30, sample_values); + test_in_ranges((1L << 62) - 1, 1L << 62, sample_values); } From d445cf4201a378e6603dc0d04fe211c8bc729bf1 Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Fri, 31 Jan 2025 00:40:44 -0500 Subject: [PATCH 29/57] fix int size --- test/hotspot/gtest/opto/test_xor_node.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/test/hotspot/gtest/opto/test_xor_node.cpp b/test/hotspot/gtest/opto/test_xor_node.cpp index 664a9efe9f9b5..8e492e61915f9 100644 --- a/test/hotspot/gtest/opto/test_xor_node.cpp +++ b/test/hotspot/gtest/opto/test_xor_node.cpp @@ -91,9 +91,12 @@ TEST_VM(opto, xor_max) { test_in_ranges(0, 15, exhaustive_values); test_in_ranges(0, 15, exhaustive_values); - test_in_ranges(maxjint-1, maxjint, sample_values); - test_in_ranges(maxjlong-1, maxjlong, sample_values); + test_in_ranges(maxjint - 1, maxjint, sample_values); + test_in_ranges(maxjlong - 1, maxjlong, sample_values); - test_in_ranges((1 << 30) - 1, 1 << 30, sample_values); - test_in_ranges((1L << 62) - 1, 1L << 62, sample_values); + auto top_pos_bit_int = jint(1) << 30; + auto top_pos_bit_long = jlong(1) << 62; + + test_in_ranges(top_pos_bit_int - 1, top_pos_bit_int, sample_values); + test_in_ranges(top_pos_bit_long - 1, top_pos_bit_long, sample_values); } From bf2fcfdc41fc3fa57d177dc468d8546bda142753 Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Fri, 31 Jan 2025 01:38:52 -0500 Subject: [PATCH 30/57] retry templated tests --- test/hotspot/gtest/opto/test_xor_node.cpp | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/test/hotspot/gtest/opto/test_xor_node.cpp b/test/hotspot/gtest/opto/test_xor_node.cpp index 8e492e61915f9..8ff300a0b761e 100644 --- a/test/hotspot/gtest/opto/test_xor_node.cpp +++ b/test/hotspot/gtest/opto/test_xor_node.cpp @@ -22,8 +22,6 @@ * */ -#include - #include "opto/addnode.hpp" #include "unittest.hpp" @@ -71,8 +69,8 @@ void test_sample_values(S hi_0, S hi_1){ } } -template -void test_in_ranges(S lo, S hi, std::function f){ +template +void test_in_ranges(S lo, S hi, F f){ for(S hi_0 = lo; hi_0 <= hi; hi_0++){ for(S hi_1 = hi_0; hi_1 <=hi; hi_1++){ f(hi_0, hi_1); @@ -81,22 +79,18 @@ void test_in_ranges(S lo, S hi, std::function f){ } TEST_VM(opto, xor_max) { - auto sample_values = [](auto a, auto b) { return test_sample_values(a, b); }; - auto exhaustive_values = [](auto a, auto b) { return test_exhaustive_values(a, b); }; - auto maxjint = jint(std::numeric_limits::max()); auto maxjlong = jint(std::numeric_limits::max()); + test_in_ranges(0, 15, test_exhaustive_values); + test_in_ranges(0, 15, test_exhaustive_values); - test_in_ranges(0, 15, exhaustive_values); - test_in_ranges(0, 15, exhaustive_values); - - test_in_ranges(maxjint - 1, maxjint, sample_values); - test_in_ranges(maxjlong - 1, maxjlong, sample_values); + test_in_ranges(maxjint - 1, maxjint, test_sample_values); + test_in_ranges(maxjlong - 1, maxjlong, test_sample_values); auto top_pos_bit_int = jint(1) << 30; auto top_pos_bit_long = jlong(1) << 62; - test_in_ranges(top_pos_bit_int - 1, top_pos_bit_int, sample_values); - test_in_ranges(top_pos_bit_long - 1, top_pos_bit_long, sample_values); + test_in_ranges(top_pos_bit_int - 1, top_pos_bit_long, test_sample_values); + test_in_ranges(top_pos_bit_long - 1, top_pos_bit_long, test_sample_values); } From 3e460f9fcacc058592b6f6ea94e4c1c6e1fab70b Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Fri, 31 Jan 2025 02:08:44 -0500 Subject: [PATCH 31/57] move template def to header --- src/hotspot/share/opto/addnode.cpp | 30 ------------------- src/hotspot/share/opto/addnode.hpp | 35 +++++++++++++++++++++++ test/hotspot/gtest/opto/test_xor_node.cpp | 2 -- 3 files changed, 35 insertions(+), 32 deletions(-) diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index 0a5aea4b18efa..dfcc884098d22 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -981,37 +981,7 @@ const Type* XorINode::Value(PhaseGVN* phase) const { return AddNode::Value(phase); } -template -S calc_xor_max(const S hi_0, const S hi_1) { - assert(hi_0 >= 0, "must be non-negative"); - assert(hi_1 >= 0, "must be non-negative"); - // x ^ y cannot have any bit set that is higher than both the highest bits set in x and y - // x cannot have any bit set that is higher than the highest bit set in r0->_hi - // y cannot have any bit set that is higher than the highest bit set in r1->_hi - - // We want to find a value that has all 1 bits everywhere up to and including - // the highest bits set in r0->_hi as well as r1->_hi. For this,we can take the next - // power of 2 strictly greater than both hi values and subtract 1 from it. - - // Example 1: - // r0->_hi = 5 (0b0101) r1->_hi=1 (0b0001) - // (5|1)+1 = 0b0110 - // round_up_pow2 = 0b1000 - // -1 = 0b0111 = max - - // Example 2 - this demonstrates need for the +1: - // r0->_hi = 4 (0b0100) r1->_hi=4 (0b0100) - // (4|4)+1 = 0b0101 - // round_up_pow2 = 0b1000 - // -1 = 0b0111 = max - // Without the +1, round_up_pow2 would be 0b0100, resulting in 0b0011 as max - - // Note: cast to unsigned happens before +1 to avoid signed overflow, and - // round_up is safe because high bit is unset (0 <= lo <= hi) - - return round_up_power_of_2(U(hi_0 | hi_1) + 1) - 1 ; -} //------------------------------add_ring--------------------------------------- // Supplied function returns the sum of the inputs IN THE CURRENT RING. For diff --git a/src/hotspot/share/opto/addnode.hpp b/src/hotspot/share/opto/addnode.hpp index c409fb8cea839..a5b4f6f15e162 100644 --- a/src/hotspot/share/opto/addnode.hpp +++ b/src/hotspot/share/opto/addnode.hpp @@ -253,6 +253,38 @@ class XorLNode : public AddNode { virtual uint ideal_reg() const { return Op_RegL; } }; +template +static S calc_xor_max(const S hi_0, const S hi_1) { + assert(hi_0 >= 0, "must be non-negative"); + assert(hi_1 >= 0, "must be non-negative"); + + // x ^ y cannot have any bit set that is higher than both the highest bits set in x and y + // x cannot have any bit set that is higher than the highest bit set in r0->_hi + // y cannot have any bit set that is higher than the highest bit set in r1->_hi + + // We want to find a value that has all 1 bits everywhere up to and including + // the highest bits set in r0->_hi as well as r1->_hi. For this,we can take the next + // power of 2 strictly greater than both hi values and subtract 1 from it. + + // Example 1: + // r0->_hi = 5 (0b0101) r1->_hi=1 (0b0001) + // (5|1)+1 = 0b0110 + // round_up_pow2 = 0b1000 + // -1 = 0b0111 = max + + // Example 2 - this demonstrates need for the +1: + // r0->_hi = 4 (0b0100) r1->_hi=4 (0b0100) + // (4|4)+1 = 0b0101 + // round_up_pow2 = 0b1000 + // -1 = 0b0111 = max + // Without the +1, round_up_pow2 would be 0b0100, resulting in 0b0011 as max + + // Note: cast to unsigned happens before +1 to avoid signed overflow, and + // round_up is safe because high bit is unset (0 <= lo <= hi) + + return round_up_power_of_2(U(hi_0 | hi_1) + 1) - 1 ; +} + //------------------------------MaxNode---------------------------------------- // Max (or min) of 2 values. Included with the ADD nodes because it inherits // all the behavior of addition on a ring. @@ -430,4 +462,7 @@ class MinDNode : public MaxNode { int min_opcode() const { return Op_MinD; } }; +template S calc_xor_max(const S hi_0, const S hi_1); + + #endif // SHARE_OPTO_ADDNODE_HPP diff --git a/test/hotspot/gtest/opto/test_xor_node.cpp b/test/hotspot/gtest/opto/test_xor_node.cpp index 8ff300a0b761e..9c3c0167ba2d5 100644 --- a/test/hotspot/gtest/opto/test_xor_node.cpp +++ b/test/hotspot/gtest/opto/test_xor_node.cpp @@ -25,8 +25,6 @@ #include "opto/addnode.hpp" #include "unittest.hpp" -template S calc_xor_max(const S hi_0, const S hi_1); - jint calc_max(const jint hi_0, const jint hi_1) { return calc_xor_max(hi_0, hi_1); } From d7bca8a3461da4eaa3e59fde9b59300ec74f43af Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Fri, 31 Jan 2025 18:49:30 -0500 Subject: [PATCH 32/57] move template def to cpp reorg gtest --- src/hotspot/share/opto/addnode.cpp | 38 +++++++++++ src/hotspot/share/opto/addnode.hpp | 41 ++---------- test/hotspot/gtest/opto/test_xor_node.cpp | 64 ++++++++++--------- .../c2/irTests/XorINodeIdealizationTests.java | 2 - 4 files changed, 78 insertions(+), 67 deletions(-) diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index dfcc884098d22..1738bd5c008f7 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -982,6 +982,37 @@ const Type* XorINode::Value(PhaseGVN* phase) const { } +template +static S calc_xor_max(const S hi_0, const S hi_1) { + assert(hi_0 >= 0, "must be non-negative"); + assert(hi_1 >= 0, "must be non-negative"); + + // x ^ y cannot have any bit set that is higher than both the highest bits set in x and y + // x cannot have any bit set that is higher than the highest bit set in r0->_hi + // y cannot have any bit set that is higher than the highest bit set in r1->_hi + + // We want to find a value that has all 1 bits everywhere up to and including + // the highest bits set in r0->_hi as well as r1->_hi. For this,we can take the next + // power of 2 strictly greater than both hi values and subtract 1 from it. + + // Example 1: + // r0->_hi = 5 (0b0101) r1->_hi=1 (0b0001) + // (5|1)+1 = 0b0110 + // round_up_pow2 = 0b1000 + // -1 = 0b0111 = max + + // Example 2 - this demonstrates need for the +1: + // r0->_hi = 4 (0b0100) r1->_hi=4 (0b0100) + // (4|4)+1 = 0b0101 + // round_up_pow2 = 0b1000 + // -1 = 0b0111 = max + // Without the +1, round_up_pow2 would be 0b0100, resulting in 0b0011 as max + + // Note: cast to unsigned happens before +1 to avoid signed overflow, and + // round_up is safe because high bit is unset (0 <= lo <= hi) + + return round_up_power_of_2(U(hi_0 | hi_1) + 1) - 1 ; +} //------------------------------add_ring--------------------------------------- // Supplied function returns the sum of the inputs IN THE CURRENT RING. For @@ -1010,6 +1041,10 @@ const Type *XorINode::add_ring( const Type *t0, const Type *t1 ) const { return TypeInt::INT; } +jint XorINode::calc_max(const jint hi_0, const jint hi_1) { + return calc_xor_max(hi_0, hi_1); +} + //============================================================================= //------------------------------add_ring--------------------------------------- const Type *XorLNode::add_ring( const Type *t0, const Type *t1 ) const { @@ -1034,6 +1069,9 @@ const Type *XorLNode::add_ring( const Type *t0, const Type *t1 ) const { return TypeLong::LONG; } +jlong XorLNode::calc_max(const jlong hi_0, const jlong hi_1) { + return calc_xor_max(hi_0, hi_1); +} Node* XorLNode::Ideal(PhaseGVN* phase, bool can_reshape) { Node* in1 = in(1); diff --git a/src/hotspot/share/opto/addnode.hpp b/src/hotspot/share/opto/addnode.hpp index a5b4f6f15e162..d72cdad797ed0 100644 --- a/src/hotspot/share/opto/addnode.hpp +++ b/src/hotspot/share/opto/addnode.hpp @@ -235,6 +235,9 @@ class XorINode : public AddNode { int min_opcode() const { return Op_MinI; } virtual const Type *Value(PhaseGVN *phase) const; virtual uint ideal_reg() const { return Op_RegI; } +private: + friend jint test_calc_max(const jint, const jint); + static jint calc_max(const jint hi_0, const jint hi_1); }; //------------------------------XorINode--------------------------------------- @@ -251,40 +254,11 @@ class XorLNode : public AddNode { int min_opcode() const { return Op_MinL; } virtual const Type *Value(PhaseGVN *phase) const; virtual uint ideal_reg() const { return Op_RegL; } +private: + friend jlong test_calc_max(const jlong, const jlong); + static jlong calc_max(const jlong hi_0, const jlong hi_1); }; -template -static S calc_xor_max(const S hi_0, const S hi_1) { - assert(hi_0 >= 0, "must be non-negative"); - assert(hi_1 >= 0, "must be non-negative"); - - // x ^ y cannot have any bit set that is higher than both the highest bits set in x and y - // x cannot have any bit set that is higher than the highest bit set in r0->_hi - // y cannot have any bit set that is higher than the highest bit set in r1->_hi - - // We want to find a value that has all 1 bits everywhere up to and including - // the highest bits set in r0->_hi as well as r1->_hi. For this,we can take the next - // power of 2 strictly greater than both hi values and subtract 1 from it. - - // Example 1: - // r0->_hi = 5 (0b0101) r1->_hi=1 (0b0001) - // (5|1)+1 = 0b0110 - // round_up_pow2 = 0b1000 - // -1 = 0b0111 = max - - // Example 2 - this demonstrates need for the +1: - // r0->_hi = 4 (0b0100) r1->_hi=4 (0b0100) - // (4|4)+1 = 0b0101 - // round_up_pow2 = 0b1000 - // -1 = 0b0111 = max - // Without the +1, round_up_pow2 would be 0b0100, resulting in 0b0011 as max - - // Note: cast to unsigned happens before +1 to avoid signed overflow, and - // round_up is safe because high bit is unset (0 <= lo <= hi) - - return round_up_power_of_2(U(hi_0 | hi_1) + 1) - 1 ; -} - //------------------------------MaxNode---------------------------------------- // Max (or min) of 2 values. Included with the ADD nodes because it inherits // all the behavior of addition on a ring. @@ -462,7 +436,4 @@ class MinDNode : public MaxNode { int min_opcode() const { return Op_MinD; } }; -template S calc_xor_max(const S hi_0, const S hi_1); - - #endif // SHARE_OPTO_ADDNODE_HPP diff --git a/test/hotspot/gtest/opto/test_xor_node.cpp b/test/hotspot/gtest/opto/test_xor_node.cpp index 9c3c0167ba2d5..f2875972f270f 100644 --- a/test/hotspot/gtest/opto/test_xor_node.cpp +++ b/test/hotspot/gtest/opto/test_xor_node.cpp @@ -25,70 +25,74 @@ #include "opto/addnode.hpp" #include "unittest.hpp" -jint calc_max(const jint hi_0, const jint hi_1) { - return calc_xor_max(hi_0, hi_1); +jint test_calc_max(const jint hi_0, const jint hi_1) { + return XorINode::calc_max(hi_0, hi_1); } -jlong calc_max(const jlong hi_0, const jlong hi_1) { - return calc_xor_max(hi_0, hi_1); +jlong test_calc_max(const jlong hi_0, const jlong hi_1) { + return XorLNode::calc_max(hi_0, hi_1); } template void test_xor_bounds(S hi_0, S hi_1, S val_0, S val_1) { // Skip out-of-bounds values for convenience - if(val_0> hi_0 || val_0 < S(0) || val_1 > hi_1 || val_1< S(0)) { + if(val_0 > hi_0 || val_0 < S(0) || val_1 > hi_1 || val_1 < S(0)) { return; } - S v = val_0 ^ val_1; - S max = calc_max(hi_0, hi_1); + S max = test_calc_max(hi_0, hi_1); EXPECT_LE(v, max); } -template -void test_exhaustive_values(S hi_0, S hi_1){ - for(S val_0 = 0; val_0 <= hi_0; val_0++){ - for(S val_1 = val_0; val_1 <= hi_1; val_1++){ - test_xor_bounds(hi_0, hi_1, val_0, val_1); - } - } -} - template void test_sample_values(S hi_0, S hi_1){ - for(S i=0; i<=3; i++){ - for(S j=0; j<=3; j++){ + for(S i = 0; i <= 3; i++){ + for(S j = 0; j <= 3; j++){ // Some bit combinations near the low and high ends of the range test_xor_bounds(hi_0, hi_1, i, j); - test_xor_bounds(hi_0, hi_1, hi_0-i, hi_1-j); + test_xor_bounds(hi_0, hi_1, hi_0 - i, hi_1 - j); } } } -template -void test_in_ranges(S lo, S hi, F f){ +template +void test_in_ranges(S lo, S hi){ for(S hi_0 = lo; hi_0 <= hi; hi_0++){ for(S hi_1 = hi_0; hi_1 <=hi; hi_1++){ - f(hi_0, hi_1); + test_sample_values(hi_0, hi_1); + } + } +} + +template +void test_exhaustive(S limit){ + for(S hi_0 = 0; hi_0 <= limit; hi_0++){ + for(S hi_1 = hi_0; hi_1 <= limit; hi_1++){ + for(S val_0 = 0; val_0 <= hi_0; val_0++){ + for(S val_1 = val_0; val_1 <= hi_1; val_1++){ + test_xor_bounds(hi_0, hi_1, val_0, val_1); + } + } } } } TEST_VM(opto, xor_max) { - auto maxjint = jint(std::numeric_limits::max()); - auto maxjlong = jint(std::numeric_limits::max()); - test_in_ranges(0, 15, test_exhaustive_values); - test_in_ranges(0, 15, test_exhaustive_values); + test_exhaustive(15); + test_exhaustive(15); + + auto max_jint = jint(std::numeric_limits::max()); + auto max_jlong = jint(std::numeric_limits::max()); - test_in_ranges(maxjint - 1, maxjint, test_sample_values); - test_in_ranges(maxjlong - 1, maxjlong, test_sample_values); + test_in_ranges(max_jint - 1, max_jint); + test_in_ranges(max_jlong - 1, max_jlong); auto top_pos_bit_int = jint(1) << 30; auto top_pos_bit_long = jlong(1) << 62; - test_in_ranges(top_pos_bit_int - 1, top_pos_bit_long, test_sample_values); - test_in_ranges(top_pos_bit_long - 1, top_pos_bit_long, test_sample_values); + test_in_ranges(top_pos_bit_int - 1, top_pos_bit_long); + test_in_ranges(top_pos_bit_long - 1, top_pos_bit_long); } diff --git a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java index 6e8563640c9df..26d40029077e0 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java @@ -38,8 +38,6 @@ public class XorINodeIdealizationTests { private static final int CONST_1 = G.next(); private static final int CONST_2 = G.next(); - - public static void main(String[] args) { TestFramework.run(); } From 112bcca8fa12a199e2c8f170072ba7c2a68a6bac Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Fri, 31 Jan 2025 19:45:14 -0500 Subject: [PATCH 33/57] try fewer tests --- test/hotspot/gtest/opto/test_xor_node.cpp | 31 +++++++++++++---------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/test/hotspot/gtest/opto/test_xor_node.cpp b/test/hotspot/gtest/opto/test_xor_node.cpp index f2875972f270f..bac8b52588794 100644 --- a/test/hotspot/gtest/opto/test_xor_node.cpp +++ b/test/hotspot/gtest/opto/test_xor_node.cpp @@ -24,6 +24,7 @@ #include "opto/addnode.hpp" #include "unittest.hpp" +#include jint test_calc_max(const jint hi_0, const jint hi_1) { return XorINode::calc_max(hi_0, hi_1); @@ -40,6 +41,9 @@ void test_xor_bounds(S hi_0, S hi_1, S val_0, S val_1) { if(val_0 > hi_0 || val_0 < S(0) || val_1 > hi_1 || val_1 < S(0)) { return; } + +// std::cout << std::hex << hi_0 << " " << hi_1 << " " << val_0 << " " << val_1 << std::endl; + S v = val_0 ^ val_1; S max = test_calc_max(hi_0, hi_1); EXPECT_LE(v, max); @@ -79,20 +83,19 @@ void test_exhaustive(S limit){ } } -TEST_VM(opto, xor_max) { - - test_exhaustive(15); - test_exhaustive(15); - - auto max_jint = jint(std::numeric_limits::max()); - auto max_jlong = jint(std::numeric_limits::max()); - - test_in_ranges(max_jint - 1, max_jint); - test_in_ranges(max_jlong - 1, max_jlong); +template +void exec_tests(){ + S max = jint(std::numeric_limits::max()); + S top_bit = max_power_of_2(); + S prev_bit = top_bit >> 1; - auto top_pos_bit_int = jint(1) << 30; - auto top_pos_bit_long = jlong(1) << 62; + test_exhaustive(15); +// test_in_ranges(max - 1, max); +// test_in_ranges(top_bit - 1, top_bit); +// test_in_ranges(prev_bit - 1, prev_bit); +} - test_in_ranges(top_pos_bit_int - 1, top_pos_bit_long); - test_in_ranges(top_pos_bit_long - 1, top_pos_bit_long); +TEST_VM(opto, xor_max) { + exec_tests(); + exec_tests(); } From 2c28f3e990ee106ea4c17ce779a5c3dc4a842fe6 Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Fri, 31 Jan 2025 20:29:23 -0500 Subject: [PATCH 34/57] re-add tests --- test/hotspot/gtest/opto/test_xor_node.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/hotspot/gtest/opto/test_xor_node.cpp b/test/hotspot/gtest/opto/test_xor_node.cpp index bac8b52588794..18a64cb3988e7 100644 --- a/test/hotspot/gtest/opto/test_xor_node.cpp +++ b/test/hotspot/gtest/opto/test_xor_node.cpp @@ -90,7 +90,7 @@ void exec_tests(){ S prev_bit = top_bit >> 1; test_exhaustive(15); -// test_in_ranges(max - 1, max); + test_in_ranges(max - 1, max); // test_in_ranges(top_bit - 1, top_bit); // test_in_ranges(prev_bit - 1, prev_bit); } From 9effc3d8c5d66265a0e01a9787bec0745f02c9fa Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Fri, 31 Jan 2025 21:00:23 -0500 Subject: [PATCH 35/57] add sanity asserts to tests --- test/hotspot/gtest/opto/test_xor_node.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/test/hotspot/gtest/opto/test_xor_node.cpp b/test/hotspot/gtest/opto/test_xor_node.cpp index 18a64cb3988e7..11c794322d142 100644 --- a/test/hotspot/gtest/opto/test_xor_node.cpp +++ b/test/hotspot/gtest/opto/test_xor_node.cpp @@ -24,7 +24,6 @@ #include "opto/addnode.hpp" #include "unittest.hpp" -#include jint test_calc_max(const jint hi_0, const jint hi_1) { return XorINode::calc_max(hi_0, hi_1); @@ -36,6 +35,8 @@ jlong test_calc_max(const jlong hi_0, const jlong hi_1) { template void test_xor_bounds(S hi_0, S hi_1, S val_0, S val_1) { + ASSERT_GE(hi_0, 0); + ASSERT_GE(hi_1, 0); // Skip out-of-bounds values for convenience if(val_0 > hi_0 || val_0 < S(0) || val_1 > hi_1 || val_1 < S(0)) { @@ -63,6 +64,9 @@ void test_sample_values(S hi_0, S hi_1){ template void test_in_ranges(S lo, S hi){ + ASSERT_GE(lo, 0); + ASSERT_LE(lo, hi); + for(S hi_0 = lo; hi_0 <= hi; hi_0++){ for(S hi_1 = hi_0; hi_1 <=hi; hi_1++){ test_sample_values(hi_0, hi_1); @@ -85,14 +89,13 @@ void test_exhaustive(S limit){ template void exec_tests(){ - S max = jint(std::numeric_limits::max()); S top_bit = max_power_of_2(); S prev_bit = top_bit >> 1; test_exhaustive(15); - test_in_ranges(max - 1, max); -// test_in_ranges(top_bit - 1, top_bit); -// test_in_ranges(prev_bit - 1, prev_bit); + + test_in_ranges(top_bit - 1, top_bit); + test_in_ranges(prev_bit - 1, prev_bit); } TEST_VM(opto, xor_max) { From ab804b88f091e7fc3c543317926c235a13750fee Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Sat, 1 Feb 2025 12:05:36 -0500 Subject: [PATCH 36/57] formatting --- test/hotspot/gtest/opto/test_xor_node.cpp | 27 ++++++++++------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/test/hotspot/gtest/opto/test_xor_node.cpp b/test/hotspot/gtest/opto/test_xor_node.cpp index 11c794322d142..0dac1309c54e1 100644 --- a/test/hotspot/gtest/opto/test_xor_node.cpp +++ b/test/hotspot/gtest/opto/test_xor_node.cpp @@ -39,22 +39,19 @@ void test_xor_bounds(S hi_0, S hi_1, S val_0, S val_1) { ASSERT_GE(hi_1, 0); // Skip out-of-bounds values for convenience - if(val_0 > hi_0 || val_0 < S(0) || val_1 > hi_1 || val_1 < S(0)) { + if (val_0 > hi_0 || val_0 < S(0) || val_1 > hi_1 || val_1 < S(0)) { return; } -// std::cout << std::hex << hi_0 << " " << hi_1 << " " << val_0 << " " << val_1 << std::endl; - S v = val_0 ^ val_1; S max = test_calc_max(hi_0, hi_1); EXPECT_LE(v, max); } template -void test_sample_values(S hi_0, S hi_1){ - - for(S i = 0; i <= 3; i++){ - for(S j = 0; j <= 3; j++){ +void test_sample_values(S hi_0, S hi_1) { + for (S i = 0; i <= 3; i++) { + for (S j = 0; j <= 3; j++) { // Some bit combinations near the low and high ends of the range test_xor_bounds(hi_0, hi_1, i, j); test_xor_bounds(hi_0, hi_1, hi_0 - i, hi_1 - j); @@ -67,19 +64,19 @@ void test_in_ranges(S lo, S hi){ ASSERT_GE(lo, 0); ASSERT_LE(lo, hi); - for(S hi_0 = lo; hi_0 <= hi; hi_0++){ - for(S hi_1 = hi_0; hi_1 <=hi; hi_1++){ + for (S hi_0 = lo; hi_0 <= hi; hi_0++) { + for (S hi_1 = hi_0; hi_1 <=hi; hi_1++) { test_sample_values(hi_0, hi_1); } } } template -void test_exhaustive(S limit){ - for(S hi_0 = 0; hi_0 <= limit; hi_0++){ - for(S hi_1 = hi_0; hi_1 <= limit; hi_1++){ - for(S val_0 = 0; val_0 <= hi_0; val_0++){ - for(S val_1 = val_0; val_1 <= hi_1; val_1++){ +void test_exhaustive(S limit) { + for (S hi_0 = 0; hi_0 <= limit; hi_0++) { + for (S hi_1 = hi_0; hi_1 <= limit; hi_1++) { + for (S val_0 = 0; val_0 <= hi_0; val_0++) { + for (S val_1 = val_0; val_1 <= hi_1; val_1++) { test_xor_bounds(hi_0, hi_1, val_0, val_1); } } @@ -88,7 +85,7 @@ void test_exhaustive(S limit){ } template -void exec_tests(){ +void exec_tests() { S top_bit = max_power_of_2(); S prev_bit = top_bit >> 1; From cf77949776f7a4601268c7291a5743c2eb164186 Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Sat, 1 Feb 2025 14:23:42 -0500 Subject: [PATCH 37/57] add IR tests for long, simplify tests for int --- .../c2/irTests/XorINodeIdealizationTests.java | 53 +++++++++++++++---- .../c2/irTests/XorLNodeIdealizationTests.java | 42 +++++++++++++-- 2 files changed, 82 insertions(+), 13 deletions(-) diff --git a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java index 26d40029077e0..1f0a24f1c29c3 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java @@ -43,12 +43,12 @@ public static void main(String[] args) { } @Run(test = {"test1", "test2", "test3", - "test4", "test5", "test6", - "test7", "test8", "test9", - "test10", "test11", "test12", - "test13", "test14", "test15", - "test16", "test17", - "testConstXor", "testXorSelf" + "test4", "test5", "test6", + "test7", "test8", "test9", + "test10", "test11", "test12", + "test13", "test14", "test15", + "test16", "test17", + "testConstXor", "testXorSelf" }) public void runMethod() { int a = RunInfo.getRandom().nextInt(); @@ -84,11 +84,10 @@ public void assertResult(int a, int b, int c, int d) { Asserts.assertEQ(~a , test15(a)); Asserts.assertEQ((~a + b) + (~a | c), test16(a, b, c)); Asserts.assertEQ(-2023 - a , test17(a)); - Asserts.assertEQ(CONST_1 ^ CONST_2, testConstXor()); - Asserts.assertEQ(0, testXorSelf(a)); + Asserts.assertEQ(CONST_1 ^ CONST_2 , testConstXor()); + Asserts.assertEQ(0 , testXorSelf(a)); } - @Test @IR(failOn = {IRNode.XOR, IRNode.ADD}) @IR(counts = {IRNode.SUB, "1"}) @@ -277,6 +276,41 @@ public boolean testXorSelfBool(boolean x) { return x ^ x; } + @Run(test = { + "testFoldableXor", "testXorConstRange" + }) + public void runRangeTests() { + int a = G.next(); + int b = G.next(); + checkXor(a, b); + + for (a = 0; a < 16; a++) { + for (b = a; b < 16; b++) { + checkXor(a, b); + } + } + } + + @DontCompile + public void checkXor(int a, int b) { + Asserts.assertEQ(true, testFoldableXor(a, b)); + Asserts.assertEQ((a & 0b1000) ^ (b & 0b1000), testXorConstRange(a, b)); + } + + @Test + public int testXorConstRange(int x, int y) { + return (x & 0b1000) ^ (y & 0b1000); + } + + @Test + @IR(failOn = {IRNode.XOR}) + @IR(counts = {IRNode.CON_I, "1"}) + public boolean testFoldableXor(int x, int y) { + var xor = (x & 0b111) ^ (y & 0b100); + return xor < 0b1000; + } + +/* private static final Range RANGE_1; private static final Range RANGE_2; private static final int XOR_MAX_OF_RANGES; @@ -351,4 +385,5 @@ static Range generate(Generator g) { return new Range(a, b); } } + */ } diff --git a/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java index 563797643525c..a044ae03b3a56 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java @@ -51,14 +51,14 @@ public static void main(String[] args) { "testConstXor", "testXorSelf", }) public void runMethod() { - long min = Long.MIN_VALUE; - long max = Long.MAX_VALUE; - long a = RunInfo.getRandom().nextLong(); long b = RunInfo.getRandom().nextLong(); long c = RunInfo.getRandom().nextLong(); long d = RunInfo.getRandom().nextLong(); + long min = Long.MIN_VALUE; + long max = Long.MAX_VALUE; + assertResult(0, 0, 0, 0); assertResult(a, b, c, d); assertResult(min, min, min, min); @@ -85,7 +85,7 @@ public void assertResult(long a, long b, long c, long d) { Asserts.assertEQ((~a + b) + (~a | c), test16(a, b, c)); Asserts.assertEQ(-2023 - a , test17(a)); Asserts.assertEQ(CONST_1 ^ CONST_2 , testConstXor()); - Asserts.assertEQ(0L , testXorSelf(a)); + Asserts.assertEQ(0L , testXorSelf(a)); } @Test @@ -242,4 +242,38 @@ public long testConstXor() { public long testXorSelf(long x) { return x ^ x; } + + @Run(test = { + "testFoldableXor", "testXorConstRange" + }) + public void runRangeTests() { + long a = G.next(); + long b = G.next(); + checkXor(a, b); + + for (a = 0; a < 16; a++) { + for (b = a; b < 16; b++) { + checkXor(a, b); + } + } + } + + @DontCompile + public void checkXor(long a, long b) { + Asserts.assertEQ(true, testFoldableXor(a, b)); + Asserts.assertEQ((a & 0b1000) ^ (b & 0b1000), testXorConstRange(a, b)); + } + + @Test + public long testXorConstRange(long x, long y) { + return (x & 0b1000) ^ (y & 0b1000); + } + + @Test + @IR(failOn = {IRNode.XOR}) + @IR(counts = {IRNode.CON_I, "1"}) // note boolean is a CON_I + public boolean testFoldableXor(long x, long y) { + var xor = (x & 0b111) ^ (y & 0b100); + return xor < 0b1000; + } } From 4a2912021103cefbe30fb3cc9e7d303b63ea454d Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Wed, 12 Feb 2025 14:44:43 -0500 Subject: [PATCH 38/57] formatting, remove commented tests --- src/hotspot/share/opto/addnode.cpp | 4 +- .../c2/irTests/XorINodeIdealizationTests.java | 77 ------------------- 2 files changed, 2 insertions(+), 79 deletions(-) diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index 1738bd5c008f7..d040a97875a3a 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -1025,7 +1025,7 @@ const Type *XorINode::add_ring( const Type *t0, const Type *t1 ) const { if (r0->is_con() && r1->is_con()) { // Constant fold: (c1 ^ c2) -> c3 - return TypeInt::make( r0->get_con() ^ r1->get_con() ); + return TypeInt::make(r0->get_con() ^ r1->get_con()); } // At least one of the arguments is not constant @@ -1053,7 +1053,7 @@ const Type *XorLNode::add_ring( const Type *t0, const Type *t1 ) const { if (r0->is_con() && r1->is_con()) { // Constant fold: (c1 ^ c2) -> c3 - return TypeLong::make( r0->get_con() ^ r1->get_con() ); + return TypeLong::make(r0->get_con() ^ r1->get_con()); } // At least one of the arguments is not constant diff --git a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java index 1f0a24f1c29c3..e7950287dae19 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java @@ -309,81 +309,4 @@ public boolean testFoldableXor(int x, int y) { var xor = (x & 0b111) ^ (y & 0b100); return xor < 0b1000; } - -/* - private static final Range RANGE_1; - private static final Range RANGE_2; - private static final int XOR_MAX_OF_RANGES; - - static { - var r1 = RANGE_1 = Range.generate(G); - var r2 = RANGE_2 = Range.generate(G); - if (r1.lo() >= 0 && r2.lo() >= 0 && r1.hi() != 0 && r2.hi() != 0) { - XOR_MAX_OF_RANGES = Integer.highestOneBit(r1.hi() | r2.hi() * 2) - 1; - } else { - XOR_MAX_OF_RANGES = Integer.MAX_VALUE; - } - } - - @Run(test = { - "testFoldableXor", "testXorConstRange" - }) - public void runRangeTests() { - var rand1 = G.restricted(RANGE_1.lo(), RANGE_1.hi()); - var rand2 = G.restricted(RANGE_2.lo(), RANGE_2.hi()); - - for (int i = 0; i < 100; i++) { - checkXor(rand1.next(), rand2.next()); - } - checkXor(RANGE_1.hi(), RANGE_2.hi()); - checkXor(RANGE_1.lo(), RANGE_2.lo()); - } - - @DontCompile - public void checkXor(int a, int b) { - Asserts.assertEQ(true, testFoldableXor(a, b)); - Asserts.assertEQ(RANGE_1.clamp(a) ^ RANGE_2.clamp(b), testXorConstRange(a, b)); - } - - @Test - public int testXorConstRange(int x, int y) { - x = RANGE_1.clamp(x); - y = RANGE_2.clamp(y); - - return x ^ y; - } - - @Test - @IR(failOn = {IRNode.XOR}) - @IR(counts = {IRNode.CON_I, "1"}) - public boolean testFoldableXor(int x, int y) { - x = RANGE_1.clamp(x); - y = RANGE_2.clamp(y); - var xor = x ^ y; - return xor <= XOR_MAX_OF_RANGES; - } - - record Range(int lo, int hi) { - Range { - if (lo > hi) { - throw new IllegalArgumentException("lo > hi"); - } - } - - int clamp(int v) { - return Math.min(hi, Math.max(v, lo)); - } - - static Range generate(Generator g) { - var a = g.next(); - var b = g.next(); - if (a > b) { - var tmp = a; - a = b; - b = tmp; - } - return new Range(a, b); - } - } - */ } From 0e9b7cbedbfae30223e8bbd54014db1c3c78049a Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Thu, 13 Feb 2025 14:58:01 -0500 Subject: [PATCH 39/57] address review comments --- src/hotspot/share/opto/addnode.cpp | 21 +++++++------------ .../c2/irTests/XorINodeIdealizationTests.java | 20 ++++++++++++++---- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index d040a97875a3a..493ca3ea692fa 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -981,9 +981,10 @@ const Type* XorINode::Value(PhaseGVN* phase) const { return AddNode::Value(phase); } - +// Given 2 non-negative values in the ranges [0, hi_0] and [0, hi_1], respectively. The bitwise +// xor of these values should also be non-negative. This method calculates an upper bound. template -static S calc_xor_max(const S hi_0, const S hi_1) { +static S calc_xor_upper_bound_of_non_neg(const S hi_0, const S hi_1) { assert(hi_0 >= 0, "must be non-negative"); assert(hi_1 >= 0, "must be non-negative"); @@ -992,7 +993,7 @@ static S calc_xor_max(const S hi_0, const S hi_1) { // y cannot have any bit set that is higher than the highest bit set in r1->_hi // We want to find a value that has all 1 bits everywhere up to and including - // the highest bits set in r0->_hi as well as r1->_hi. For this,we can take the next + // the highest bits set in r0->_hi as well as r1->_hi. For this, we can take the next // power of 2 strictly greater than both hi values and subtract 1 from it. // Example 1: @@ -1030,11 +1031,8 @@ const Type *XorINode::add_ring( const Type *t0, const Type *t1 ) const { // At least one of the arguments is not constant - // Result of xor can only have bits sets where any of the - // inputs have bits set. lo can always become 0. - if (r0->_lo >= 0 && r1->_lo >= 0) { - jint max = calc_xor_max(r0->_hi, r1->_hi); + jint max = calc_xor_upper_bound_of_non_neg(r0->_hi, r1->_hi); return TypeInt::make(0, max, MAX2(r0->_widen, r1->_widen)); } @@ -1042,7 +1040,7 @@ const Type *XorINode::add_ring( const Type *t0, const Type *t1 ) const { } jint XorINode::calc_max(const jint hi_0, const jint hi_1) { - return calc_xor_max(hi_0, hi_1); + return calc_xor_upper_bound_of_non_neg(hi_0, hi_1); } //============================================================================= @@ -1058,11 +1056,8 @@ const Type *XorLNode::add_ring( const Type *t0, const Type *t1 ) const { // At least one of the arguments is not constant - // Result of xor can only have bits sets where any of the - // inputs have bits set. lo can always become 0. - if (r0->_lo >= 0 && r1->_lo >= 0) { - julong max = calc_xor_max(r0->_hi, r1->_hi); + julong max = calc_xor_upper_bound_of_non_neg(r0->_hi, r1->_hi); return TypeLong::make(0, max, MAX2(r0->_widen, r1->_widen)); } @@ -1070,7 +1065,7 @@ const Type *XorLNode::add_ring( const Type *t0, const Type *t1 ) const { } jlong XorLNode::calc_max(const jlong hi_0, const jlong hi_1) { - return calc_xor_max(hi_0, hi_1); + return calc_xor_upper_bound_of_non_neg(hi_0, hi_1); } Node* XorLNode::Ideal(PhaseGVN* phase, bool can_reshape) { diff --git a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java index e7950287dae19..4fb3d830c3758 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java @@ -247,17 +247,21 @@ public int testXorSelf(int x) { private static final boolean CONST_BOOL_2 = RunInfo.getRandom().nextBoolean(); @Run(test={ - "testConstXorBool", "testXorSelfBool" + "testConstXorBool", "testXorSelfBool", "testXorIntAsBool" }) public void runBooleanTests() { - assertBooleanResult(true); - assertBooleanResult(false); + int c = G.next(); + int d = G.next(); + + assertBooleanResult(true, c, d); + assertBooleanResult(false, c, d); } @DontCompile - public void assertBooleanResult(boolean b){ + public void assertBooleanResult(boolean b, int x, int y) { Asserts.assertEQ(CONST_BOOL_1 ^ CONST_BOOL_2, testConstXorBool()); Asserts.assertEQ(false, testXorSelfBool(b)); + Asserts.assertEQ(true, testXorIntAsBool(x, y)); } @Test @@ -276,6 +280,14 @@ public boolean testXorSelfBool(boolean x) { return x ^ x; } + @Test + @IR(failOn = {IRNode.XOR}) + @IR(counts = {IRNode.CON_I, "1"}) + // Checks (x ^ y) => z <=1 when x and y are known to be in [0,1] (constant folded) + public boolean testXorIntAsBool(int xi, int yi) { + return ((xi & 0b1) ^ (yi & 0b1)) <= 1; + } + @Run(test = { "testFoldableXor", "testXorConstRange" }) From a522898937944b30bb4a7bea14c12bb74813c93d Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Thu, 13 Feb 2025 15:25:26 -0500 Subject: [PATCH 40/57] update test --- .../compiler/c2/irTests/XorINodeIdealizationTests.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java index 4fb3d830c3758..486a72a75d621 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java @@ -283,9 +283,11 @@ public boolean testXorSelfBool(boolean x) { @Test @IR(failOn = {IRNode.XOR}) @IR(counts = {IRNode.CON_I, "1"}) - // Checks (x ^ y) => z <=1 when x and y are known to be in [0,1] (constant folded) + // This test explicitly checks for constant folding over ints representing booleans. + // Checks (x ^ y) => z in [0, 1] when x and y are known to be in [0, 1] (constant folded) public boolean testXorIntAsBool(int xi, int yi) { - return ((xi & 0b1) ^ (yi & 0b1)) <= 1; + int xor = (xi & 1) ^ (yi & 1); + return 0 <= xor && xor <= 1; } @Run(test = { From 42b20b520aeb83a5925a6665bbd09fb2cfc7af09 Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Thu, 13 Feb 2025 16:13:40 -0500 Subject: [PATCH 41/57] fix variable names in comments --- src/hotspot/share/opto/addnode.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index 493ca3ea692fa..2b8be8129b887 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -989,21 +989,21 @@ static S calc_xor_upper_bound_of_non_neg(const S hi_0, const S hi_1) { assert(hi_1 >= 0, "must be non-negative"); // x ^ y cannot have any bit set that is higher than both the highest bits set in x and y - // x cannot have any bit set that is higher than the highest bit set in r0->_hi - // y cannot have any bit set that is higher than the highest bit set in r1->_hi + // x cannot have any bit set that is higher than the highest bit set in hi_0 + // y cannot have any bit set that is higher than the highest bit set in hi_1 // We want to find a value that has all 1 bits everywhere up to and including - // the highest bits set in r0->_hi as well as r1->_hi. For this, we can take the next + // the highest bits set in hi_0 as well as hi_1. For this, we can take the next // power of 2 strictly greater than both hi values and subtract 1 from it. // Example 1: - // r0->_hi = 5 (0b0101) r1->_hi=1 (0b0001) + // hi_0 = 5 (0b0101) hi_1=1 (0b0001) // (5|1)+1 = 0b0110 // round_up_pow2 = 0b1000 // -1 = 0b0111 = max // Example 2 - this demonstrates need for the +1: - // r0->_hi = 4 (0b0100) r1->_hi=4 (0b0100) + // hi_0 = 4 (0b0100) hi_1=4 (0b0100) // (4|4)+1 = 0b0101 // round_up_pow2 = 0b1000 // -1 = 0b0111 = max From e8fc6dab088c3a265d4c89dd0b3cf31ab1846058 Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Mon, 17 Feb 2025 21:37:42 -0500 Subject: [PATCH 42/57] Fix formatting Co-authored-by: Jasmine Karthikeyan <25208576+jaskarth@users.noreply.github.com> --- src/hotspot/share/opto/addnode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index 18ecf77546129..8b55b826f6011 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -1029,7 +1029,7 @@ static S calc_xor_upper_bound_of_non_neg(const S hi_0, const S hi_1) { // Note: cast to unsigned happens before +1 to avoid signed overflow, and // round_up is safe because high bit is unset (0 <= lo <= hi) - return round_up_power_of_2(U(hi_0 | hi_1) + 1) - 1 ; + return round_up_power_of_2(U(hi_0 | hi_1) + 1) - 1; } //------------------------------add_ring--------------------------------------- From 40b1f9c4eb3e5f9d2a7ea28bf2daeccb6ce8dc6d Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Thu, 20 Feb 2025 11:20:04 -0500 Subject: [PATCH 43/57] update tests --- .../c2/irTests/XorINodeIdealizationTests.java | 17 +++++++++++++---- .../c2/irTests/XorLNodeIdealizationTests.java | 17 +++++++++++++---- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java index 486a72a75d621..f0e8e96894d71 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java @@ -291,7 +291,7 @@ public boolean testXorIntAsBool(int xi, int yi) { } @Run(test = { - "testFoldableXor", "testXorConstRange" + "testFoldableXor", "testFoldableXorPow2", "testUnfoldableXorPow2" }) public void runRangeTests() { int a = G.next(); @@ -308,12 +308,21 @@ public void runRangeTests() { @DontCompile public void checkXor(int a, int b) { Asserts.assertEQ(true, testFoldableXor(a, b)); - Asserts.assertEQ((a & 0b1000) ^ (b & 0b1000), testXorConstRange(a, b)); + Asserts.assertEQ(((a & 0b1000) ^ (b & 0b1000)) < 0b1000, testUnfoldableXorPow2(a, b)); + Asserts.assertEQ(true, testFoldableXorPow2(a, b)); } @Test - public int testXorConstRange(int x, int y) { - return (x & 0b1000) ^ (y & 0b1000); + @IR(failOn = {IRNode.XOR}) + @IR(counts = {IRNode.CON_I, "1"}) + public boolean testFoldableXorPow2(int x, int y) { + return ((x & 0b1000) ^ (y & 0b1000)) < 0b10000; + } + + @Test + @IR(counts = {IRNode.XOR, "1"}) + public boolean testUnfoldableXorPow2(int x, int y) { + return ((x & 0b1000) ^ (y & 0b1000)) < 0b1000; } @Test diff --git a/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java index a044ae03b3a56..80e2bce840b96 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java @@ -244,7 +244,7 @@ public long testXorSelf(long x) { } @Run(test = { - "testFoldableXor", "testXorConstRange" + "testFoldableXor", "testFoldableXorPow2", "testUnfoldableXorPow2" }) public void runRangeTests() { long a = G.next(); @@ -261,12 +261,21 @@ public void runRangeTests() { @DontCompile public void checkXor(long a, long b) { Asserts.assertEQ(true, testFoldableXor(a, b)); - Asserts.assertEQ((a & 0b1000) ^ (b & 0b1000), testXorConstRange(a, b)); + Asserts.assertEQ(((a & 0b1000) ^ (b & 0b1000)) < 0b1000, testUnfoldableXorPow2(a, b)); + Asserts.assertEQ(true, testFoldableXorPow2(a, b)); } @Test - public long testXorConstRange(long x, long y) { - return (x & 0b1000) ^ (y & 0b1000); + @IR(failOn = {IRNode.XOR}) + @IR(counts = {IRNode.CON_I, "1"}) + public boolean testFoldableXorPow2(long x, long y) { + return ((x & 0b1000) ^ (y & 0b1000)) < 0b10000; + } + + @Test + @IR(counts = {IRNode.XOR, "1"}) + public boolean testUnfoldableXorPow2(long x, long y) { + return ((x & 0b1000) ^ (y & 0b1000)) < 0b1000; } @Test From bf8ba1b1c96324dae4eaf1f5201cb72965429351 Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Tue, 25 Feb 2025 10:18:45 -0500 Subject: [PATCH 44/57] add comments Co-authored-by: Emanuel Peter --- src/hotspot/share/opto/addnode.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index 8b55b826f6011..e46787700fd52 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -1074,6 +1074,7 @@ const Type *XorLNode::add_ring( const Type *t0, const Type *t1 ) const { // At least one of the arguments is not constant if (r0->_lo >= 0 && r1->_lo >= 0) { + // Combine [0, lo_1] ^ [0, hi_1] -> [0, max] julong max = calc_xor_upper_bound_of_non_neg(r0->_hi, r1->_hi); return TypeLong::make(0, max, MAX2(r0->_widen, r1->_widen)); } From 6939d420af2dca808f187ecb9d04a76a00bb4da3 Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Tue, 25 Feb 2025 11:57:50 -0500 Subject: [PATCH 45/57] a few more tests --- .../c2/irTests/XorINodeIdealizationTests.java | 25 +++++++++++++++++-- .../c2/irTests/XorLNodeIdealizationTests.java | 25 +++++++++++++++++-- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java index f0e8e96894d71..7e27531ff0874 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java @@ -26,6 +26,8 @@ import compiler.lib.generators.*; import compiler.lib.ir_framework.*; +import static java.lang.Integer.MAX_VALUE; + /* * @test * @bug 8281453 @@ -57,7 +59,7 @@ public void runMethod() { int d = RunInfo.getRandom().nextInt(); int min = Integer.MIN_VALUE; - int max = Integer.MAX_VALUE; + int max = MAX_VALUE; assertResult(0, 0, 0, 0); assertResult(a, b, c, d); @@ -291,7 +293,8 @@ public boolean testXorIntAsBool(int xi, int yi) { } @Run(test = { - "testFoldableXor", "testFoldableXorPow2", "testUnfoldableXorPow2" + "testFoldableXor", "testFoldableXorPow2", "testUnfoldableXorPow2", + "testFoldableXorDifferingLength", "testXorMax" }) public void runRangeTests() { int a = G.next(); @@ -301,6 +304,7 @@ public void runRangeTests() { for (a = 0; a < 16; a++) { for (b = a; b < 16; b++) { checkXor(a, b); + checkXor(MAX_VALUE, MAX_VALUE - b); } } } @@ -310,6 +314,8 @@ public void checkXor(int a, int b) { Asserts.assertEQ(true, testFoldableXor(a, b)); Asserts.assertEQ(((a & 0b1000) ^ (b & 0b1000)) < 0b1000, testUnfoldableXorPow2(a, b)); Asserts.assertEQ(true, testFoldableXorPow2(a, b)); + Asserts.assertEQ(true, testFoldableXorDifferingLength(a, b)); + Asserts.assertEQ((a & MAX_VALUE) ^ (b & 0b11), testXorMax(a, b)); } @Test @@ -332,4 +338,19 @@ public boolean testFoldableXor(int x, int y) { var xor = (x & 0b111) ^ (y & 0b100); return xor < 0b1000; } + + @Test + @IR(failOn = {IRNode.XOR}) + @IR(counts = {IRNode.CON_I, "1"}) + public boolean testFoldableXorDifferingLength(int x, int y) { + var xor = (x & 0b111) ^ (y & 0b11); + return xor < 0b1000; + } + + @Test + public int testXorMax(int x, int y) { + return (x & MAX_VALUE) ^ (y & 0b11); + // can't do the folding range check here since xor <= MAX_VALUE is + // constant with or without the xor + } } diff --git a/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java index 80e2bce840b96..3f8f2d8f093cd 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java @@ -26,6 +26,8 @@ import compiler.lib.generators.*; import compiler.lib.ir_framework.*; +import static java.lang.Long.MAX_VALUE; + /* * @test * @bug 8281453 @@ -57,7 +59,7 @@ public void runMethod() { long d = RunInfo.getRandom().nextLong(); long min = Long.MIN_VALUE; - long max = Long.MAX_VALUE; + long max = MAX_VALUE; assertResult(0, 0, 0, 0); assertResult(a, b, c, d); @@ -244,7 +246,8 @@ public long testXorSelf(long x) { } @Run(test = { - "testFoldableXor", "testFoldableXorPow2", "testUnfoldableXorPow2" + "testFoldableXor", "testFoldableXorPow2", "testUnfoldableXorPow2", + "testFoldableXorDifferingLength", "testXorMax" }) public void runRangeTests() { long a = G.next(); @@ -254,6 +257,7 @@ public void runRangeTests() { for (a = 0; a < 16; a++) { for (b = a; b < 16; b++) { checkXor(a, b); + checkXor(MAX_VALUE, MAX_VALUE - b); } } } @@ -263,6 +267,8 @@ public void checkXor(long a, long b) { Asserts.assertEQ(true, testFoldableXor(a, b)); Asserts.assertEQ(((a & 0b1000) ^ (b & 0b1000)) < 0b1000, testUnfoldableXorPow2(a, b)); Asserts.assertEQ(true, testFoldableXorPow2(a, b)); + Asserts.assertEQ(true, testFoldableXorDifferingLength(a, b)); + Asserts.assertEQ((a & MAX_VALUE) ^ (b & 0b11), testXorMax(a, b)); } @Test @@ -285,4 +291,19 @@ public boolean testFoldableXor(long x, long y) { var xor = (x & 0b111) ^ (y & 0b100); return xor < 0b1000; } + + @Test + @IR(failOn = {IRNode.XOR}) + @IR(counts = {IRNode.CON_I, "1"}) + public boolean testFoldableXorDifferingLength(long x, long y) { + var xor = (x & 0b111) ^ (y & 0b11); + return xor < 0b1000; + } + + @Test + public long testXorMax(long x, long y) { + return (x & MAX_VALUE) ^ (y & 0b11); + // can't do the folding range check here since xor <= MAX_VALUE is + // constant with or without the xor + } } From b1e79dcd1aec373ae6bf8e816a9eaaa40ea4a272 Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Tue, 25 Feb 2025 12:02:02 -0500 Subject: [PATCH 46/57] widen range of test values; add missing comment --- src/hotspot/share/opto/addnode.cpp | 1 + .../jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java | 4 ++-- .../jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index e46787700fd52..56a248f48b0c2 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -1049,6 +1049,7 @@ const Type *XorINode::add_ring( const Type *t0, const Type *t1 ) const { // At least one of the arguments is not constant if (r0->_lo >= 0 && r1->_lo >= 0) { + // Combine [0, lo_1] ^ [0, hi_1] -> [0, max] jint max = calc_xor_upper_bound_of_non_neg(r0->_hi, r1->_hi); return TypeInt::make(0, max, MAX2(r0->_widen, r1->_widen)); } diff --git a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java index 7e27531ff0874..2d75d449105d3 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java @@ -301,8 +301,8 @@ public void runRangeTests() { int b = G.next(); checkXor(a, b); - for (a = 0; a < 16; a++) { - for (b = a; b < 16; b++) { + for (a = 0; a < 32; a++) { + for (b = a; b < 32; b++) { checkXor(a, b); checkXor(MAX_VALUE, MAX_VALUE - b); } diff --git a/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java index 3f8f2d8f093cd..f5fb5ffa9d99b 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java @@ -254,8 +254,8 @@ public void runRangeTests() { long b = G.next(); checkXor(a, b); - for (a = 0; a < 16; a++) { - for (b = a; b < 16; b++) { + for (a = 0; a < 32; a++) { + for (b = a; b < 32; b++) { checkXor(a, b); checkXor(MAX_VALUE, MAX_VALUE - b); } From 4a8840c910014854dde264915e8c0d71a7b878ea Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Wed, 26 Feb 2025 12:15:37 -0500 Subject: [PATCH 47/57] consistency --- .../jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java | 3 ++- .../jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java index 2d75d449105d3..a30572ae0ea1e 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java @@ -27,6 +27,7 @@ import compiler.lib.ir_framework.*; import static java.lang.Integer.MAX_VALUE; +import static java.lang.Integer.MIN_VALUE; /* * @test @@ -58,7 +59,7 @@ public void runMethod() { int c = RunInfo.getRandom().nextInt(); int d = RunInfo.getRandom().nextInt(); - int min = Integer.MIN_VALUE; + int min = MIN_VALUE; int max = MAX_VALUE; assertResult(0, 0, 0, 0); diff --git a/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java index f5fb5ffa9d99b..191635c5194d0 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java @@ -27,6 +27,7 @@ import compiler.lib.ir_framework.*; import static java.lang.Long.MAX_VALUE; +import static java.lang.Long.MIN_VALUE; /* * @test @@ -58,7 +59,7 @@ public void runMethod() { long c = RunInfo.getRandom().nextLong(); long d = RunInfo.getRandom().nextLong(); - long min = Long.MIN_VALUE; + long min = MIN_VALUE; long max = MAX_VALUE; assertResult(0, 0, 0, 0); From 7658fc9a9e46a8fa8f1e4be69e53bb3dc4141f2b Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Wed, 26 Feb 2025 14:34:09 -0500 Subject: [PATCH 48/57] add test of random ranges --- .../c2/irTests/XorINodeIdealizationTests.java | 91 ++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java index a30572ae0ea1e..224fbcb63e47d 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java @@ -295,7 +295,8 @@ public boolean testXorIntAsBool(int xi, int yi) { @Run(test = { "testFoldableXor", "testFoldableXorPow2", "testUnfoldableXorPow2", - "testFoldableXorDifferingLength", "testXorMax" + "testFoldableXorDifferingLength", "testXorMax", + "testFoldableRange","testRandomLimits" }) public void runRangeTests() { int a = G.next(); @@ -317,6 +318,8 @@ public void checkXor(int a, int b) { Asserts.assertEQ(true, testFoldableXorPow2(a, b)); Asserts.assertEQ(true, testFoldableXorDifferingLength(a, b)); Asserts.assertEQ((a & MAX_VALUE) ^ (b & 0b11), testXorMax(a, b)); + Asserts.assertEQ(testRandomLimitsInterpreted(a, b), testRandomLimits(a, b)); + Asserts.assertEQ(true, testFoldableRange(a, b)); } @Test @@ -354,4 +357,90 @@ public int testXorMax(int x, int y) { // can't do the folding range check here since xor <= MAX_VALUE is // constant with or without the xor } + + private static final Range RANGE_1 = Range.generate(G.restricted(0, MAX_VALUE)); + private static final Range RANGE_2 = Range.generate(G.restricted(0, MAX_VALUE)); + private static final int UPPER_BOUND = Integer.max(0, Integer.highestOneBit(RANGE_1.hi() | RANGE_2.hi()) * 2 - 1); + + private static final int LIMIT_1 = G.next(); + private static final int LIMIT_2 = G.next(); + private static final int LIMIT_3 = G.next(); + private static final int LIMIT_4 = G.next(); + private static final int LIMIT_5 = G.next(); + private static final int LIMIT_6 = G.next(); + private static final int LIMIT_7 = G.next(); + private static final int LIMIT_8 = G.next(); + + + @Test + @IR(failOn = {IRNode.XOR}) + @IR(counts = {IRNode.CON_I, "1"}) + public boolean testFoldableRange(int x, int y) { + return (RANGE_1.clamp(x) ^ RANGE_2.clamp(y)) <= UPPER_BOUND; + } + + @Test + public int testRandomLimits(int x, int y) { + x = RANGE_1.clamp(x); + y = RANGE_2.clamp(y); + + int z = x ^ y; + // This should now have a new range, possibly some [0, max] + // Now let's test the range with some random if branches. + int sum = 0; + if (z < LIMIT_1) { sum += 1; } + if (z < LIMIT_2) { sum += 2; } + if (z < LIMIT_3) { sum += 4; } + if (z < LIMIT_4) { sum += 8; } + if (z < LIMIT_5) { sum += 16; } + if (z < LIMIT_6) { sum += 32; } + if (z < LIMIT_7) { sum += 64; } + if (z < LIMIT_8) { sum += 128; } + + return sum; + } + + @DontCompile + private int testRandomLimitsInterpreted(int x,int y) { + x = RANGE_1.clamp(x); + y = RANGE_2.clamp(y); + + int z = x ^ y; + // This should now have a new range, possibly some [0, max] + // Now let's test the range with some random if branches. + int sum = 0; + if (z < LIMIT_1) { sum += 1; } + if (z < LIMIT_2) { sum += 2; } + if (z < LIMIT_3) { sum += 4; } + if (z < LIMIT_4) { sum += 8; } + if (z < LIMIT_5) { sum += 16; } + if (z < LIMIT_6) { sum += 32; } + if (z < LIMIT_7) { sum += 64; } + if (z < LIMIT_8) { sum += 128; } + + return sum; + } + + record Range(int lo, int hi) { + Range { + if (lo > hi) { + throw new IllegalArgumentException("lo > hi"); + } + } + + int clamp(int v) { + return Math.min(hi, Math.max(v, lo)); + } + + static Range generate(Generator g) { + var a = g.next(); + var b = g.next(); + if (a > b) { + var tmp = a; + a = b; + b = tmp; + } + return new Range(a, b); + } + } } From f56744200df4390589cb74df0d09f7d6cbebf38b Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Wed, 26 Feb 2025 14:48:55 -0500 Subject: [PATCH 49/57] update bug numbers and summary --- .../jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java | 4 ++-- .../jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java index 224fbcb63e47d..3c1a4d179fef0 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java @@ -31,8 +31,8 @@ /* * @test - * @bug 8281453 - * @summary Convert ~x into -1-x when ~x is used in an arithmetic expression + * @bug 8281453 8347645 8261008 8267332 + * @summary Test correctness of optimizations of xor * @library /test/lib / * @run driver compiler.c2.irTests.XorINodeIdealizationTests */ diff --git a/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java index 191635c5194d0..8b57c1f7d25ff 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java @@ -31,8 +31,8 @@ /* * @test - * @bug 8281453 - * @summary Convert ~x into -1-x when ~x is used in an arithmetic expression + * @bug 8281453 8347645 8261008 8267332 + * @summary Test correctness of optimizations of xor * @library /test/lib / * @run driver compiler.c2.irTests.XorLNodeIdealizationTests */ From ec17fd243cae3e41f14cd87f36e5ce8b839cf491 Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Wed, 26 Feb 2025 16:48:35 -0500 Subject: [PATCH 50/57] invert comparison in tests --- .../c2/irTests/XorINodeIdealizationTests.java | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java index 3c1a4d179fef0..3b2ff8f6ba4d2 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java @@ -388,14 +388,14 @@ public int testRandomLimits(int x, int y) { // This should now have a new range, possibly some [0, max] // Now let's test the range with some random if branches. int sum = 0; - if (z < LIMIT_1) { sum += 1; } - if (z < LIMIT_2) { sum += 2; } - if (z < LIMIT_3) { sum += 4; } - if (z < LIMIT_4) { sum += 8; } - if (z < LIMIT_5) { sum += 16; } - if (z < LIMIT_6) { sum += 32; } - if (z < LIMIT_7) { sum += 64; } - if (z < LIMIT_8) { sum += 128; } + if (z > LIMIT_1) { sum += 1; } + if (z > LIMIT_2) { sum += 2; } + if (z > LIMIT_3) { sum += 4; } + if (z > LIMIT_4) { sum += 8; } + if (z > LIMIT_5) { sum += 16; } + if (z > LIMIT_6) { sum += 32; } + if (z > LIMIT_7) { sum += 64; } + if (z > LIMIT_8) { sum += 128; } return sum; } @@ -409,14 +409,14 @@ private int testRandomLimitsInterpreted(int x,int y) { // This should now have a new range, possibly some [0, max] // Now let's test the range with some random if branches. int sum = 0; - if (z < LIMIT_1) { sum += 1; } - if (z < LIMIT_2) { sum += 2; } - if (z < LIMIT_3) { sum += 4; } - if (z < LIMIT_4) { sum += 8; } - if (z < LIMIT_5) { sum += 16; } - if (z < LIMIT_6) { sum += 32; } - if (z < LIMIT_7) { sum += 64; } - if (z < LIMIT_8) { sum += 128; } + if (z > LIMIT_1) { sum += 1; } + if (z > LIMIT_2) { sum += 2; } + if (z > LIMIT_3) { sum += 4; } + if (z > LIMIT_4) { sum += 8; } + if (z > LIMIT_5) { sum += 16; } + if (z > LIMIT_6) { sum += 32; } + if (z > LIMIT_7) { sum += 64; } + if (z > LIMIT_8) { sum += 128; } return sum; } From 226fa3bf4c7fe4a28387536062ae5e2fb6d5e89d Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Mon, 24 Mar 2025 14:13:38 -0400 Subject: [PATCH 51/57] Add random range tests for Long --- .../c2/irTests/XorINodeIdealizationTests.java | 20 ++-- .../c2/irTests/XorLNodeIdealizationTests.java | 97 ++++++++++++++++++- 2 files changed, 103 insertions(+), 14 deletions(-) diff --git a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java index 3b2ff8f6ba4d2..e4fe40a4d35c2 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java @@ -362,14 +362,14 @@ public int testXorMax(int x, int y) { private static final Range RANGE_2 = Range.generate(G.restricted(0, MAX_VALUE)); private static final int UPPER_BOUND = Integer.max(0, Integer.highestOneBit(RANGE_1.hi() | RANGE_2.hi()) * 2 - 1); - private static final int LIMIT_1 = G.next(); - private static final int LIMIT_2 = G.next(); - private static final int LIMIT_3 = G.next(); - private static final int LIMIT_4 = G.next(); - private static final int LIMIT_5 = G.next(); - private static final int LIMIT_6 = G.next(); - private static final int LIMIT_7 = G.next(); - private static final int LIMIT_8 = G.next(); + private static final long LIMIT_1 = G.next(); + private static final long LIMIT_2 = G.next(); + private static final long LIMIT_3 = G.next(); + private static final long LIMIT_4 = G.next(); + private static final long LIMIT_5 = G.next(); + private static final long LIMIT_6 = G.next(); + private static final long LIMIT_7 = G.next(); + private static final long LIMIT_8 = G.next(); @Test @@ -401,11 +401,11 @@ public int testRandomLimits(int x, int y) { } @DontCompile - private int testRandomLimitsInterpreted(int x,int y) { + private int testRandomLimitsInterpreted(int x, int y) { x = RANGE_1.clamp(x); y = RANGE_2.clamp(y); - int z = x ^ y; + var z = x ^ y; // This should now have a new range, possibly some [0, max] // Now let's test the range with some random if branches. int sum = 0; diff --git a/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java index 8b57c1f7d25ff..ce328f7a37b00 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/XorLNodeIdealizationTests.java @@ -248,7 +248,8 @@ public long testXorSelf(long x) { @Run(test = { "testFoldableXor", "testFoldableXorPow2", "testUnfoldableXorPow2", - "testFoldableXorDifferingLength", "testXorMax" + "testFoldableXorDifferingLength", "testXorMax", + "testFoldableRange","testRandomLimits" }) public void runRangeTests() { long a = G.next(); @@ -270,11 +271,13 @@ public void checkXor(long a, long b) { Asserts.assertEQ(true, testFoldableXorPow2(a, b)); Asserts.assertEQ(true, testFoldableXorDifferingLength(a, b)); Asserts.assertEQ((a & MAX_VALUE) ^ (b & 0b11), testXorMax(a, b)); + Asserts.assertEQ(testRandomLimitsInterpreted(a, b), testRandomLimits(a, b)); + Asserts.assertEQ(true, testFoldableRange(a, b)); } @Test @IR(failOn = {IRNode.XOR}) - @IR(counts = {IRNode.CON_I, "1"}) + @IR(counts = {IRNode.CON_I, "1"}) // boolean is a CON_I public boolean testFoldableXorPow2(long x, long y) { return ((x & 0b1000) ^ (y & 0b1000)) < 0b10000; } @@ -287,7 +290,7 @@ public boolean testUnfoldableXorPow2(long x, long y) { @Test @IR(failOn = {IRNode.XOR}) - @IR(counts = {IRNode.CON_I, "1"}) // note boolean is a CON_I + @IR(counts = {IRNode.CON_I, "1"}) // boolean is a CON_I public boolean testFoldableXor(long x, long y) { var xor = (x & 0b111) ^ (y & 0b100); return xor < 0b1000; @@ -295,7 +298,7 @@ public boolean testFoldableXor(long x, long y) { @Test @IR(failOn = {IRNode.XOR}) - @IR(counts = {IRNode.CON_I, "1"}) + @IR(counts = {IRNode.CON_I, "1"}) // boolean is a CON_I public boolean testFoldableXorDifferingLength(long x, long y) { var xor = (x & 0b111) ^ (y & 0b11); return xor < 0b1000; @@ -307,4 +310,90 @@ public long testXorMax(long x, long y) { // can't do the folding range check here since xor <= MAX_VALUE is // constant with or without the xor } + + private static final Range RANGE_1 = Range.generate(G.restricted(0L, MAX_VALUE)); + private static final Range RANGE_2 = Range.generate(G.restricted(0L, MAX_VALUE)); + private static final long UPPER_BOUND = Math.max(0, Long.highestOneBit(RANGE_1.hi() | RANGE_2.hi()) * 2 - 1); + + private static final long LIMIT_1 = G.next(); + private static final long LIMIT_2 = G.next(); + private static final long LIMIT_3 = G.next(); + private static final long LIMIT_4 = G.next(); + private static final long LIMIT_5 = G.next(); + private static final long LIMIT_6 = G.next(); + private static final long LIMIT_7 = G.next(); + private static final long LIMIT_8 = G.next(); + + + @Test + @IR(failOn = {IRNode.XOR}) + @IR(counts = {IRNode.CON_I, "1"}) // Boolean is CON_I + public boolean testFoldableRange(long x, long y) { + return (RANGE_1.clamp(x) ^ RANGE_2.clamp(y)) <= UPPER_BOUND; + } + + @Test + public int testRandomLimits(long x, long y) { + x = RANGE_1.clamp(x); + y = RANGE_2.clamp(y); + + var z = x ^ y; + // This should now have a new range, possibly some [0, max] + // Now let's test the range with some random if branches. + int sum = 0; + if (z > LIMIT_1) { sum += 1; } + if (z > LIMIT_2) { sum += 2; } + if (z > LIMIT_3) { sum += 4; } + if (z > LIMIT_4) { sum += 8; } + if (z > LIMIT_5) { sum += 16; } + if (z > LIMIT_6) { sum += 32; } + if (z > LIMIT_7) { sum += 64; } + if (z > LIMIT_8) { sum += 128; } + + return sum; + } + + @DontCompile + private int testRandomLimitsInterpreted(long x, long y) { + x = RANGE_1.clamp(x); + y = RANGE_2.clamp(y); + + var z = x ^ y; + // This should now have a new range, possibly some [0, max] + // Now let's test the range with some random if branches. + int sum = 0; + if (z > LIMIT_1) { sum += 1; } + if (z > LIMIT_2) { sum += 2; } + if (z > LIMIT_3) { sum += 4; } + if (z > LIMIT_4) { sum += 8; } + if (z > LIMIT_5) { sum += 16; } + if (z > LIMIT_6) { sum += 32; } + if (z > LIMIT_7) { sum += 64; } + if (z > LIMIT_8) { sum += 128; } + + return sum; + } + + record Range(long lo, long hi) { + Range { + if (lo > hi) { + throw new IllegalArgumentException("lo > hi"); + } + } + + long clamp(long v) { + return Math.min(hi, Math.max(v, lo)); + } + + static Range generate(Generator g) { + var a = g.next(); + var b = g.next(); + if (a > b) { + var tmp = a; + a = b; + b = tmp; + } + return new Range(a, b); + } + } } From 8ca86fda8f1b068cb5853096bdfa6f2efabbc48b Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Mon, 24 Mar 2025 14:26:22 -0400 Subject: [PATCH 52/57] Undo accidental changes to Int tests --- .../c2/irTests/XorINodeIdealizationTests.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java index e4fe40a4d35c2..b0a1c4c9e3775 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java +++ b/test/hotspot/jtreg/compiler/c2/irTests/XorINodeIdealizationTests.java @@ -362,14 +362,14 @@ public int testXorMax(int x, int y) { private static final Range RANGE_2 = Range.generate(G.restricted(0, MAX_VALUE)); private static final int UPPER_BOUND = Integer.max(0, Integer.highestOneBit(RANGE_1.hi() | RANGE_2.hi()) * 2 - 1); - private static final long LIMIT_1 = G.next(); - private static final long LIMIT_2 = G.next(); - private static final long LIMIT_3 = G.next(); - private static final long LIMIT_4 = G.next(); - private static final long LIMIT_5 = G.next(); - private static final long LIMIT_6 = G.next(); - private static final long LIMIT_7 = G.next(); - private static final long LIMIT_8 = G.next(); + private static final int LIMIT_1 = G.next(); + private static final int LIMIT_2 = G.next(); + private static final int LIMIT_3 = G.next(); + private static final int LIMIT_4 = G.next(); + private static final int LIMIT_5 = G.next(); + private static final int LIMIT_6 = G.next(); + private static final int LIMIT_7 = G.next(); + private static final int LIMIT_8 = G.next(); @Test From 47d516a19d0d767e424fc6c6a556bd1d11808bc4 Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Sat, 29 Mar 2025 22:33:43 -0400 Subject: [PATCH 53/57] move code into a header file that can be shared with GTEST --- src/hotspot/share/opto/addnode.cpp | 35 +------------------- src/hotspot/share/opto/addnode.hpp | 2 -- src/hotspot/share/opto/addnodeXorUtil.hpp | 40 +++++++++++++++++++++++ test/hotspot/gtest/opto/test_xor_node.cpp | 7 ++-- 4 files changed, 45 insertions(+), 39 deletions(-) create mode 100644 src/hotspot/share/opto/addnodeXorUtil.hpp diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index 56a248f48b0c2..c2c41391fb817 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -33,6 +33,7 @@ #include "opto/phaseX.hpp" #include "opto/subnode.hpp" #include "runtime/stubRoutines.hpp" +#include "opto/addnodeXorUtil.hpp" // Portions of code courtesy of Clifford Click @@ -998,40 +999,6 @@ const Type* XorINode::Value(PhaseGVN* phase) const { return AddNode::Value(phase); } -// Given 2 non-negative values in the ranges [0, hi_0] and [0, hi_1], respectively. The bitwise -// xor of these values should also be non-negative. This method calculates an upper bound. -template -static S calc_xor_upper_bound_of_non_neg(const S hi_0, const S hi_1) { - assert(hi_0 >= 0, "must be non-negative"); - assert(hi_1 >= 0, "must be non-negative"); - - // x ^ y cannot have any bit set that is higher than both the highest bits set in x and y - // x cannot have any bit set that is higher than the highest bit set in hi_0 - // y cannot have any bit set that is higher than the highest bit set in hi_1 - - // We want to find a value that has all 1 bits everywhere up to and including - // the highest bits set in hi_0 as well as hi_1. For this, we can take the next - // power of 2 strictly greater than both hi values and subtract 1 from it. - - // Example 1: - // hi_0 = 5 (0b0101) hi_1=1 (0b0001) - // (5|1)+1 = 0b0110 - // round_up_pow2 = 0b1000 - // -1 = 0b0111 = max - - // Example 2 - this demonstrates need for the +1: - // hi_0 = 4 (0b0100) hi_1=4 (0b0100) - // (4|4)+1 = 0b0101 - // round_up_pow2 = 0b1000 - // -1 = 0b0111 = max - // Without the +1, round_up_pow2 would be 0b0100, resulting in 0b0011 as max - - // Note: cast to unsigned happens before +1 to avoid signed overflow, and - // round_up is safe because high bit is unset (0 <= lo <= hi) - - return round_up_power_of_2(U(hi_0 | hi_1) + 1) - 1; -} - //------------------------------add_ring--------------------------------------- // Supplied function returns the sum of the inputs IN THE CURRENT RING. For // the logical operations the ring's ADD is really a logical OR function. diff --git a/src/hotspot/share/opto/addnode.hpp b/src/hotspot/share/opto/addnode.hpp index ea52bbc237ffa..5bdb34223fec1 100644 --- a/src/hotspot/share/opto/addnode.hpp +++ b/src/hotspot/share/opto/addnode.hpp @@ -252,7 +252,6 @@ class XorINode : public AddNode { virtual const Type *Value(PhaseGVN *phase) const; virtual uint ideal_reg() const { return Op_RegI; } private: - friend jint test_calc_max(const jint, const jint); static jint calc_max(const jint hi_0, const jint hi_1); }; @@ -271,7 +270,6 @@ class XorLNode : public AddNode { virtual const Type *Value(PhaseGVN *phase) const; virtual uint ideal_reg() const { return Op_RegL; } private: - friend jlong test_calc_max(const jlong, const jlong); static jlong calc_max(const jlong hi_0, const jlong hi_1); }; diff --git a/src/hotspot/share/opto/addnodeXorUtil.hpp b/src/hotspot/share/opto/addnodeXorUtil.hpp new file mode 100644 index 0000000000000..19c40b8aba75e --- /dev/null +++ b/src/hotspot/share/opto/addnodeXorUtil.hpp @@ -0,0 +1,40 @@ +#ifndef SHARE_OPTO_ADDNODEXORUTIL_HPP +#define SHARE_OPTO_ADDNODEXORUTIL_HPP + +// Code separated into its own header to allow access from GTEST + +// Given 2 non-negative values in the ranges [0, hi_0] and [0, hi_1], respectively. The bitwise +// xor of these values should also be non-negative. This method calculates an upper bound. +template +static S calc_xor_upper_bound_of_non_neg(const S hi_0, const S hi_1) { + assert(hi_0 >= 0, "must be non-negative"); + assert(hi_1 >= 0, "must be non-negative"); + + // x ^ y cannot have any bit set that is higher than both the highest bits set in x and y + // x cannot have any bit set that is higher than the highest bit set in hi_0 + // y cannot have any bit set that is higher than the highest bit set in hi_1 + + // We want to find a value that has all 1 bits everywhere up to and including + // the highest bits set in hi_0 as well as hi_1. For this, we can take the next + // power of 2 strictly greater than both hi values and subtract 1 from it. + + // Example 1: + // hi_0 = 5 (0b0101) hi_1=1 (0b0001) + // (5|1)+1 = 0b0110 + // round_up_pow2 = 0b1000 + // -1 = 0b0111 = max + + // Example 2 - this demonstrates need for the +1: + // hi_0 = 4 (0b0100) hi_1=4 (0b0100) + // (4|4)+1 = 0b0101 + // round_up_pow2 = 0b1000 + // -1 = 0b0111 = max + // Without the +1, round_up_pow2 would be 0b0100, resulting in 0b0011 as max + + // Note: cast to unsigned happens before +1 to avoid signed overflow, and + // round_up is safe because high bit is unset (0 <= lo <= hi) + + return round_up_power_of_2(U(hi_0 | hi_1) + 1) - 1; +} + +#endif //SHARE_OPTO_ADDNODEXORUTIL_HPP diff --git a/test/hotspot/gtest/opto/test_xor_node.cpp b/test/hotspot/gtest/opto/test_xor_node.cpp index 0dac1309c54e1..be095e2dcba08 100644 --- a/test/hotspot/gtest/opto/test_xor_node.cpp +++ b/test/hotspot/gtest/opto/test_xor_node.cpp @@ -22,15 +22,16 @@ * */ -#include "opto/addnode.hpp" #include "unittest.hpp" +#include "opto/addnodeXorUtil.hpp" +#include "utilities/globalDefinitions.hpp" // For jint, juint jint test_calc_max(const jint hi_0, const jint hi_1) { - return XorINode::calc_max(hi_0, hi_1); + return calc_xor_upper_bound_of_non_neg(hi_0, hi_1); } jlong test_calc_max(const jlong hi_0, const jlong hi_1) { - return XorLNode::calc_max(hi_0, hi_1); + return calc_xor_upper_bound_of_non_neg(hi_0, hi_1); } template From 94a32dba5feab2a6ea340fa9717dd817d119cf7f Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Sat, 29 Mar 2025 23:10:17 -0400 Subject: [PATCH 54/57] add missing import --- src/hotspot/share/opto/addnodeXorUtil.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hotspot/share/opto/addnodeXorUtil.hpp b/src/hotspot/share/opto/addnodeXorUtil.hpp index 19c40b8aba75e..d903d3e210bce 100644 --- a/src/hotspot/share/opto/addnodeXorUtil.hpp +++ b/src/hotspot/share/opto/addnodeXorUtil.hpp @@ -1,6 +1,7 @@ #ifndef SHARE_OPTO_ADDNODEXORUTIL_HPP #define SHARE_OPTO_ADDNODEXORUTIL_HPP +#include "utilities/powerOfTwo.hpp" // Code separated into its own header to allow access from GTEST // Given 2 non-negative values in the ranges [0, hi_0] and [0, hi_1], respectively. The bitwise From 59875d548b5613e84cc26a755ff0397b85662398 Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Mon, 31 Mar 2025 22:24:34 -0400 Subject: [PATCH 55/57] address review comments --- src/hotspot/share/opto/addnode.cpp | 10 +++++----- .../opto/{addnodeXorUtil.hpp => utilities/xor.hpp} | 14 ++++++++++---- test/hotspot/gtest/opto/test_xor_node.cpp | 6 +++--- 3 files changed, 18 insertions(+), 12 deletions(-) rename src/hotspot/share/opto/{addnodeXorUtil.hpp => utilities/xor.hpp} (80%) diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index c2c41391fb817..284f9865ffed1 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -33,7 +33,7 @@ #include "opto/phaseX.hpp" #include "opto/subnode.hpp" #include "runtime/stubRoutines.hpp" -#include "opto/addnodeXorUtil.hpp" +#include "opto/utilities/xor.hpp" // Portions of code courtesy of Clifford Click @@ -1017,7 +1017,7 @@ const Type *XorINode::add_ring( const Type *t0, const Type *t1 ) const { if (r0->_lo >= 0 && r1->_lo >= 0) { // Combine [0, lo_1] ^ [0, hi_1] -> [0, max] - jint max = calc_xor_upper_bound_of_non_neg(r0->_hi, r1->_hi); + jint max = xor_upper_bound_for_ranges(r0->_hi, r1->_hi); return TypeInt::make(0, max, MAX2(r0->_widen, r1->_widen)); } @@ -1025,7 +1025,7 @@ const Type *XorINode::add_ring( const Type *t0, const Type *t1 ) const { } jint XorINode::calc_max(const jint hi_0, const jint hi_1) { - return calc_xor_upper_bound_of_non_neg(hi_0, hi_1); + return xor_upper_bound_for_ranges(hi_0, hi_1); } //============================================================================= @@ -1043,7 +1043,7 @@ const Type *XorLNode::add_ring( const Type *t0, const Type *t1 ) const { if (r0->_lo >= 0 && r1->_lo >= 0) { // Combine [0, lo_1] ^ [0, hi_1] -> [0, max] - julong max = calc_xor_upper_bound_of_non_neg(r0->_hi, r1->_hi); + julong max = xor_upper_bound_for_ranges(r0->_hi, r1->_hi); return TypeLong::make(0, max, MAX2(r0->_widen, r1->_widen)); } @@ -1051,7 +1051,7 @@ const Type *XorLNode::add_ring( const Type *t0, const Type *t1 ) const { } jlong XorLNode::calc_max(const jlong hi_0, const jlong hi_1) { - return calc_xor_upper_bound_of_non_neg(hi_0, hi_1); + return xor_upper_bound_for_ranges(hi_0, hi_1); } Node* XorLNode::Ideal(PhaseGVN* phase, bool can_reshape) { diff --git a/src/hotspot/share/opto/addnodeXorUtil.hpp b/src/hotspot/share/opto/utilities/xor.hpp similarity index 80% rename from src/hotspot/share/opto/addnodeXorUtil.hpp rename to src/hotspot/share/opto/utilities/xor.hpp index d903d3e210bce..20edaf0d01779 100644 --- a/src/hotspot/share/opto/addnodeXorUtil.hpp +++ b/src/hotspot/share/opto/utilities/xor.hpp @@ -1,13 +1,19 @@ -#ifndef SHARE_OPTO_ADDNODEXORUTIL_HPP -#define SHARE_OPTO_ADDNODEXORUTIL_HPP +#ifndef SHARE_OPTO_UTILITIES_XOR_HPP +#define SHARE_OPTO_UTILITIES_XOR_HPP #include "utilities/powerOfTwo.hpp" // Code separated into its own header to allow access from GTEST // Given 2 non-negative values in the ranges [0, hi_0] and [0, hi_1], respectively. The bitwise // xor of these values should also be non-negative. This method calculates an upper bound. + +// S and U type parameters correspond to the signed and unsigned +// variants of an integer to operate on. template -static S calc_xor_upper_bound_of_non_neg(const S hi_0, const S hi_1) { +static S xor_upper_bound_for_ranges(const S hi_0, const S hi_1) { + static_assert(S(-1) < S(0), "S must be signed"); + static_assert(U(-1) > U(0), "U must be unsigned"); + assert(hi_0 >= 0, "must be non-negative"); assert(hi_1 >= 0, "must be non-negative"); @@ -38,4 +44,4 @@ static S calc_xor_upper_bound_of_non_neg(const S hi_0, const S hi_1) { return round_up_power_of_2(U(hi_0 | hi_1) + 1) - 1; } -#endif //SHARE_OPTO_ADDNODEXORUTIL_HPP +#endif // SHARE_OPTO_UTILITIES_XOR_HPP diff --git a/test/hotspot/gtest/opto/test_xor_node.cpp b/test/hotspot/gtest/opto/test_xor_node.cpp index be095e2dcba08..ff21d9c4a9007 100644 --- a/test/hotspot/gtest/opto/test_xor_node.cpp +++ b/test/hotspot/gtest/opto/test_xor_node.cpp @@ -23,15 +23,15 @@ */ #include "unittest.hpp" -#include "opto/addnodeXorUtil.hpp" +#include "opto/utilities/xor.hpp" #include "utilities/globalDefinitions.hpp" // For jint, juint jint test_calc_max(const jint hi_0, const jint hi_1) { - return calc_xor_upper_bound_of_non_neg(hi_0, hi_1); + return xor_upper_bound_for_ranges(hi_0, hi_1); } jlong test_calc_max(const jlong hi_0, const jlong hi_1) { - return calc_xor_upper_bound_of_non_neg(hi_0, hi_1); + return xor_upper_bound_for_ranges(hi_0, hi_1); } template From 50d35dcd882e654566e834ab51e7b36fe91fdfec Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Mon, 31 Mar 2025 22:40:33 -0400 Subject: [PATCH 56/57] remove unused methods --- src/hotspot/share/opto/addnode.cpp | 8 -------- src/hotspot/share/opto/addnode.hpp | 4 ---- 2 files changed, 12 deletions(-) diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index 284f9865ffed1..15fa557f5b2a1 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -1024,10 +1024,6 @@ const Type *XorINode::add_ring( const Type *t0, const Type *t1 ) const { return TypeInt::INT; } -jint XorINode::calc_max(const jint hi_0, const jint hi_1) { - return xor_upper_bound_for_ranges(hi_0, hi_1); -} - //============================================================================= //------------------------------add_ring--------------------------------------- const Type *XorLNode::add_ring( const Type *t0, const Type *t1 ) const { @@ -1050,10 +1046,6 @@ const Type *XorLNode::add_ring( const Type *t0, const Type *t1 ) const { return TypeLong::LONG; } -jlong XorLNode::calc_max(const jlong hi_0, const jlong hi_1) { - return xor_upper_bound_for_ranges(hi_0, hi_1); -} - Node* XorLNode::Ideal(PhaseGVN* phase, bool can_reshape) { Node* in1 = in(1); Node* in2 = in(2); diff --git a/src/hotspot/share/opto/addnode.hpp b/src/hotspot/share/opto/addnode.hpp index 5bdb34223fec1..456a8d9f9a031 100644 --- a/src/hotspot/share/opto/addnode.hpp +++ b/src/hotspot/share/opto/addnode.hpp @@ -251,8 +251,6 @@ class XorINode : public AddNode { int min_opcode() const { return Op_MinI; } virtual const Type *Value(PhaseGVN *phase) const; virtual uint ideal_reg() const { return Op_RegI; } -private: - static jint calc_max(const jint hi_0, const jint hi_1); }; //------------------------------XorINode--------------------------------------- @@ -269,8 +267,6 @@ class XorLNode : public AddNode { int min_opcode() const { return Op_MinL; } virtual const Type *Value(PhaseGVN *phase) const; virtual uint ideal_reg() const { return Op_RegL; } -private: - static jlong calc_max(const jlong hi_0, const jlong hi_1); }; //------------------------------MaxNode---------------------------------------- From dda134fbdb1c3b9647c53ef36e5b4a952f9b9576 Mon Sep 17 00:00:00 2001 From: Johannes Graham Date: Tue, 1 Apr 2025 19:18:32 -0400 Subject: [PATCH 57/57] update comments --- src/hotspot/share/opto/addnode.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/hotspot/share/opto/addnode.cpp b/src/hotspot/share/opto/addnode.cpp index 15fa557f5b2a1..8112c5b86fc71 100644 --- a/src/hotspot/share/opto/addnode.cpp +++ b/src/hotspot/share/opto/addnode.cpp @@ -1009,16 +1009,16 @@ const Type *XorINode::add_ring( const Type *t0, const Type *t1 ) const { const TypeInt *r1 = t1->is_int(); if (r0->is_con() && r1->is_con()) { - // Constant fold: (c1 ^ c2) -> c3 + // compute constant result return TypeInt::make(r0->get_con() ^ r1->get_con()); } // At least one of the arguments is not constant if (r0->_lo >= 0 && r1->_lo >= 0) { - // Combine [0, lo_1] ^ [0, hi_1] -> [0, max] - jint max = xor_upper_bound_for_ranges(r0->_hi, r1->_hi); - return TypeInt::make(0, max, MAX2(r0->_widen, r1->_widen)); + // Combine [r0->_lo, r0->_hi] ^ [r0->_lo, r1->_hi] -> [0, upper_bound] + jint upper_bound = xor_upper_bound_for_ranges(r0->_hi, r1->_hi); + return TypeInt::make(0, upper_bound, MAX2(r0->_widen, r1->_widen)); } return TypeInt::INT; @@ -1031,16 +1031,16 @@ const Type *XorLNode::add_ring( const Type *t0, const Type *t1 ) const { const TypeLong *r1 = t1->is_long(); if (r0->is_con() && r1->is_con()) { - // Constant fold: (c1 ^ c2) -> c3 + // compute constant result return TypeLong::make(r0->get_con() ^ r1->get_con()); } // At least one of the arguments is not constant if (r0->_lo >= 0 && r1->_lo >= 0) { - // Combine [0, lo_1] ^ [0, hi_1] -> [0, max] - julong max = xor_upper_bound_for_ranges(r0->_hi, r1->_hi); - return TypeLong::make(0, max, MAX2(r0->_widen, r1->_widen)); + // Combine [r0->_lo, r0->_hi] ^ [r0->_lo, r1->_hi] -> [0, upper_bound] + julong upper_bound = xor_upper_bound_for_ranges(r0->_hi, r1->_hi); + return TypeLong::make(0, upper_bound, MAX2(r0->_widen, r1->_widen)); } return TypeLong::LONG;