From 0faa2099b060d5bcfd6ca71ff7146f6092fcbe07 Mon Sep 17 00:00:00 2001 From: MaxXSoft Date: Mon, 23 Jun 2025 10:05:35 +0800 Subject: [PATCH 01/19] Make the type of count leading/trailing zero nodes more precise --- src/hotspot/share/opto/countbitsnode.cpp | 8 +- .../c2/irTests/TestCountBitsRange.java | 103 ++++++++++++++++++ .../compiler/lib/ir_framework/IRNode.java | 20 ++++ 3 files changed, 127 insertions(+), 4 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/c2/irTests/TestCountBitsRange.java diff --git a/src/hotspot/share/opto/countbitsnode.cpp b/src/hotspot/share/opto/countbitsnode.cpp index 4023678b51c68..470865557bf64 100644 --- a/src/hotspot/share/opto/countbitsnode.cpp +++ b/src/hotspot/share/opto/countbitsnode.cpp @@ -46,7 +46,7 @@ const Type* CountLeadingZerosINode::Value(PhaseGVN* phase) const { n -= x >> 31; return TypeInt::make(n); } - return TypeInt::INT; + return TypeInt::make(0, sizeof(jint) * BitsPerByte, Type::WidenMax); } //------------------------------Value------------------------------------------ @@ -69,7 +69,7 @@ const Type* CountLeadingZerosLNode::Value(PhaseGVN* phase) const { n -= x >> 31; return TypeInt::make(n); } - return TypeInt::INT; + return TypeInt::make(0, sizeof(jlong) * BitsPerByte, Type::WidenMax); } //------------------------------Value------------------------------------------ @@ -91,7 +91,7 @@ const Type* CountTrailingZerosINode::Value(PhaseGVN* phase) const { y = i << 1; if (y != 0) { n = n - 1; } return TypeInt::make(n); } - return TypeInt::INT; + return TypeInt::make(0, sizeof(jint) * BitsPerByte, Type::WidenMax); } //------------------------------Value------------------------------------------ @@ -114,5 +114,5 @@ const Type* CountTrailingZerosLNode::Value(PhaseGVN* phase) const { y = x << 1; if (y != 0) { n = n - 1; } return TypeInt::make(n); } - return TypeInt::INT; + return TypeInt::make(0, sizeof(jlong) * BitsPerByte, Type::WidenMax); } diff --git a/test/hotspot/jtreg/compiler/c2/irTests/TestCountBitsRange.java b/test/hotspot/jtreg/compiler/c2/irTests/TestCountBitsRange.java new file mode 100644 index 0000000000000..84eb88b00b364 --- /dev/null +++ b/test/hotspot/jtreg/compiler/c2/irTests/TestCountBitsRange.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2025 Alibaba Group Holding Limited. 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 + * @bug 8360192 + * @summary Tests that count bits nodes are handled correctly. + * @library /test/lib / + * @requires vm.compiler2.enabled + * @run driver compiler.c2.irTests.TestCountBitsRange + */ +public class TestCountBitsRange { + public static void main(String[] args) { + TestFramework.run(); + } + + static int i = RunInfo.getRandom().nextInt(); + static long l = RunInfo.getRandom().nextLong(); + + @Test + @IR(failOn = IRNode.COUNT_LEADING_ZEROS_I) + public boolean clzCompareInt() { + return Integer.numberOfLeadingZeros(i) < 0 || Integer.numberOfLeadingZeros(i) > 32; + } + + @Test + @IR(counts = {IRNode.COUNT_LEADING_ZEROS_I, "1", + IRNode.RSHIFT_I, "1", + IRNode.URSHIFT_I, "0", + IRNode.ADD_I, "0"}) + public int clzDiv8Int() { + return Integer.numberOfLeadingZeros(i) / 8; + } + + @Test + @IR(failOn = IRNode.COUNT_LEADING_ZEROS_L) + public boolean clzCompareLong() { + return Long.numberOfLeadingZeros(l) < 0 || Long.numberOfLeadingZeros(l) > 64; + } + + @Test + @IR(counts = {IRNode.COUNT_LEADING_ZEROS_L, "1", + IRNode.RSHIFT_I, "1", + IRNode.URSHIFT_I, "0", + IRNode.ADD_I, "0"}) + public int clzDiv8Long() { + return Long.numberOfLeadingZeros(l) / 8; + } + + @Test + @IR(failOn = IRNode.COUNT_TRAILING_ZEROS_I) + public boolean ctzCompareInt() { + return Integer.numberOfTrailingZeros(i) < 0 || Integer.numberOfTrailingZeros(i) > 32; + } + + @Test + @IR(counts = {IRNode.COUNT_TRAILING_ZEROS_I, "1", + IRNode.RSHIFT_I, "1", + IRNode.URSHIFT_I, "0", + IRNode.ADD_I, "0"}) + public int ctzDiv8Int() { + return Integer.numberOfTrailingZeros(i) / 8; + } + + @Test + @IR(failOn = IRNode.COUNT_TRAILING_ZEROS_L) + public boolean ctzCompareLong() { + return Long.numberOfTrailingZeros(l) < 0 || Long.numberOfTrailingZeros(l) > 64; + } + + @Test + @IR(counts = {IRNode.COUNT_TRAILING_ZEROS_L, "1", + IRNode.RSHIFT_I, "1", + IRNode.URSHIFT_I, "0", + IRNode.ADD_I, "0"}) + public int ctzDiv8Long() { + return Long.numberOfTrailingZeros(l) / 8; + } +} diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java index e7582015e861e..28345c8e8ec2e 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java @@ -1607,6 +1607,16 @@ public class IRNode { vectorNode(POPCOUNT_VL, "PopCountVL", TYPE_LONG); } + public static final String COUNT_TRAILING_ZEROS_I = PREFIX + "COUNT_TRAILING_ZEROS_I" + POSTFIX; + static { + beforeMatchingNameRegex(COUNT_TRAILING_ZEROS_I, "CountTrailingZerosI"); + } + + public static final String COUNT_TRAILING_ZEROS_L = PREFIX + "COUNT_TRAILING_ZEROS_L" + POSTFIX; + static { + beforeMatchingNameRegex(COUNT_TRAILING_ZEROS_L, "CountTrailingZerosL"); + } + public static final String COUNT_TRAILING_ZEROS_VL = VECTOR_PREFIX + "COUNT_TRAILING_ZEROS_VL" + POSTFIX; static { vectorNode(COUNT_TRAILING_ZEROS_VL, "CountTrailingZerosV", TYPE_LONG); @@ -1617,6 +1627,16 @@ public class IRNode { vectorNode(COUNT_TRAILING_ZEROS_VI, "CountTrailingZerosV", TYPE_INT); } + public static final String COUNT_LEADING_ZEROS_I = PREFIX + "COUNT_LEADING_ZEROS_I" + POSTFIX; + static { + beforeMatchingNameRegex(COUNT_LEADING_ZEROS_I, "CountLeadingZerosI"); + } + + public static final String COUNT_LEADING_ZEROS_L = PREFIX + "COUNT_LEADING_ZEROS_L" + POSTFIX; + static { + beforeMatchingNameRegex(COUNT_LEADING_ZEROS_L, "CountLeadingZerosL"); + } + public static final String COUNT_LEADING_ZEROS_VL = VECTOR_PREFIX + "COUNT_LEADING_ZEROS_VL" + POSTFIX; static { vectorNode(COUNT_LEADING_ZEROS_VL, "CountLeadingZerosV", TYPE_LONG); From 0d0bb579d3c86af7a4f26c0ef69c0894d475d48a Mon Sep 17 00:00:00 2001 From: MaxXSoft Date: Mon, 23 Jun 2025 13:58:46 +0800 Subject: [PATCH 02/19] Use `BitsPerX` constant instead of `sizeof` --- src/hotspot/share/opto/countbitsnode.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/hotspot/share/opto/countbitsnode.cpp b/src/hotspot/share/opto/countbitsnode.cpp index 470865557bf64..901ba055118a1 100644 --- a/src/hotspot/share/opto/countbitsnode.cpp +++ b/src/hotspot/share/opto/countbitsnode.cpp @@ -46,7 +46,7 @@ const Type* CountLeadingZerosINode::Value(PhaseGVN* phase) const { n -= x >> 31; return TypeInt::make(n); } - return TypeInt::make(0, sizeof(jint) * BitsPerByte, Type::WidenMax); + return TypeInt::make(0, BitsPerInt, Type::WidenMax); } //------------------------------Value------------------------------------------ @@ -69,7 +69,7 @@ const Type* CountLeadingZerosLNode::Value(PhaseGVN* phase) const { n -= x >> 31; return TypeInt::make(n); } - return TypeInt::make(0, sizeof(jlong) * BitsPerByte, Type::WidenMax); + return TypeInt::make(0, BitsPerLong, Type::WidenMax); } //------------------------------Value------------------------------------------ @@ -91,7 +91,7 @@ const Type* CountTrailingZerosINode::Value(PhaseGVN* phase) const { y = i << 1; if (y != 0) { n = n - 1; } return TypeInt::make(n); } - return TypeInt::make(0, sizeof(jint) * BitsPerByte, Type::WidenMax); + return TypeInt::make(0, BitsPerInt, Type::WidenMax); } //------------------------------Value------------------------------------------ @@ -114,5 +114,5 @@ const Type* CountTrailingZerosLNode::Value(PhaseGVN* phase) const { y = x << 1; if (y != 0) { n = n - 1; } return TypeInt::make(n); } - return TypeInt::make(0, sizeof(jlong) * BitsPerByte, Type::WidenMax); + return TypeInt::make(0, BitsPerLong, Type::WidenMax); } From 1cb931b16f46254ea49154ad3f14c0b07f77ff56 Mon Sep 17 00:00:00 2001 From: MaxXSoft Date: Mon, 23 Jun 2025 15:29:09 +0800 Subject: [PATCH 03/19] Narrow type bound --- src/hotspot/share/opto/countbitsnode.cpp | 108 +++++++++++------------ 1 file changed, 50 insertions(+), 58 deletions(-) diff --git a/src/hotspot/share/opto/countbitsnode.cpp b/src/hotspot/share/opto/countbitsnode.cpp index 901ba055118a1..8554d543316f1 100644 --- a/src/hotspot/share/opto/countbitsnode.cpp +++ b/src/hotspot/share/opto/countbitsnode.cpp @@ -26,27 +26,39 @@ #include "opto/opcodes.hpp" #include "opto/phaseX.hpp" #include "opto/type.hpp" +#include "utilities/count_leading_zeros.hpp" +#include "utilities/count_trailing_zeros.hpp" + +static int count_leading_zeros_int(jint i) { + return i == 0 ? BitsPerInt : count_leading_zeros(i); +} + +static int count_leading_zeros_long(jlong l) { + return l == 0 ? BitsPerLong : count_leading_zeros(l); +} + +static int count_trailing_zeros_int(jint i) { + return i == 0 ? BitsPerInt : count_trailing_zeros(i); +} + +static int count_trailing_zeros_long(jlong l) { + return l == 0 ? BitsPerLong : count_trailing_zeros(l); +} //------------------------------Value------------------------------------------ const Type* CountLeadingZerosINode::Value(PhaseGVN* phase) const { const Type* t = phase->type(in(1)); if (t == Type::TOP) return Type::TOP; const TypeInt* ti = t->isa_int(); - if (ti && ti->is_con()) { - jint i = ti->get_con(); - // HD, Figure 5-6 - if (i == 0) - return TypeInt::make(BitsPerInt); - int n = 1; - unsigned int x = i; - if (x >> 16 == 0) { n += 16; x <<= 16; } - if (x >> 24 == 0) { n += 8; x <<= 8; } - if (x >> 28 == 0) { n += 4; x <<= 4; } - if (x >> 30 == 0) { n += 2; x <<= 2; } - n -= x >> 31; - return TypeInt::make(n); + if (ti) { + if (ti->is_con()) { + return TypeInt::make(count_leading_zeros_int(ti->get_con())); + } + return TypeInt::make(count_leading_zeros_int(~ti->_bits._zeros), + count_leading_zeros_int(ti->_bits._ones), + ti->_widen); } - return TypeInt::make(0, BitsPerInt, Type::WidenMax); + return TypeInt::INT; } //------------------------------Value------------------------------------------ @@ -54,22 +66,15 @@ const Type* CountLeadingZerosLNode::Value(PhaseGVN* phase) const { const Type* t = phase->type(in(1)); if (t == Type::TOP) return Type::TOP; const TypeLong* tl = t->isa_long(); - if (tl && tl->is_con()) { - jlong l = tl->get_con(); - // HD, Figure 5-6 - if (l == 0) - return TypeInt::make(BitsPerLong); - int n = 1; - unsigned int x = (((julong) l) >> 32); - if (x == 0) { n += 32; x = (int) l; } - if (x >> 16 == 0) { n += 16; x <<= 16; } - if (x >> 24 == 0) { n += 8; x <<= 8; } - if (x >> 28 == 0) { n += 4; x <<= 4; } - if (x >> 30 == 0) { n += 2; x <<= 2; } - n -= x >> 31; - return TypeInt::make(n); + if (tl) { + if (tl->is_con()) { + return TypeInt::make(count_leading_zeros_long(tl->get_con())); + } + return TypeInt::make(count_leading_zeros_long(~tl->_bits._zeros), + count_leading_zeros_long(tl->_bits._ones), + tl->_widen); } - return TypeInt::make(0, BitsPerLong, Type::WidenMax); + return TypeInt::INT; } //------------------------------Value------------------------------------------ @@ -77,21 +82,15 @@ const Type* CountTrailingZerosINode::Value(PhaseGVN* phase) const { const Type* t = phase->type(in(1)); if (t == Type::TOP) return Type::TOP; const TypeInt* ti = t->isa_int(); - if (ti && ti->is_con()) { - jint i = ti->get_con(); - // HD, Figure 5-14 - int y; - if (i == 0) - return TypeInt::make(BitsPerInt); - int n = 31; - y = i << 16; if (y != 0) { n = n - 16; i = y; } - y = i << 8; if (y != 0) { n = n - 8; i = y; } - y = i << 4; if (y != 0) { n = n - 4; i = y; } - y = i << 2; if (y != 0) { n = n - 2; i = y; } - y = i << 1; if (y != 0) { n = n - 1; } - return TypeInt::make(n); + if (ti) { + if (ti->is_con()) { + return TypeInt::make(count_trailing_zeros_int(ti->get_con())); + } + return TypeInt::make(count_trailing_zeros_int(~ti->_bits._zeros), + count_trailing_zeros_int(ti->_bits._ones), + ti->_widen); } - return TypeInt::make(0, BitsPerInt, Type::WidenMax); + return TypeInt::INT; } //------------------------------Value------------------------------------------ @@ -99,20 +98,13 @@ const Type* CountTrailingZerosLNode::Value(PhaseGVN* phase) const { const Type* t = phase->type(in(1)); if (t == Type::TOP) return Type::TOP; const TypeLong* tl = t->isa_long(); - if (tl && tl->is_con()) { - jlong l = tl->get_con(); - // HD, Figure 5-14 - int x, y; - if (l == 0) - return TypeInt::make(BitsPerLong); - int n = 63; - y = (int) l; if (y != 0) { n = n - 32; x = y; } else x = (((julong) l) >> 32); - y = x << 16; if (y != 0) { n = n - 16; x = y; } - y = x << 8; if (y != 0) { n = n - 8; x = y; } - y = x << 4; if (y != 0) { n = n - 4; x = y; } - y = x << 2; if (y != 0) { n = n - 2; x = y; } - y = x << 1; if (y != 0) { n = n - 1; } - return TypeInt::make(n); + if (tl) { + if (tl->is_con()) { + return TypeInt::make(count_trailing_zeros_long(tl->get_con())); + } + return TypeInt::make(count_trailing_zeros_long(~tl->_bits._zeros), + count_trailing_zeros_long(tl->_bits._ones), + tl->_widen); } - return TypeInt::make(0, BitsPerLong, Type::WidenMax); + return TypeInt::INT; } From 1ce8ebbb0331b276c9be1942f85e9d7cc0bf7c4d Mon Sep 17 00:00:00 2001 From: MaxXSoft Date: Tue, 24 Jun 2025 15:36:19 +0800 Subject: [PATCH 04/19] Fix null checks --- src/hotspot/share/opto/countbitsnode.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/hotspot/share/opto/countbitsnode.cpp b/src/hotspot/share/opto/countbitsnode.cpp index 8554d543316f1..2aae934cd8354 100644 --- a/src/hotspot/share/opto/countbitsnode.cpp +++ b/src/hotspot/share/opto/countbitsnode.cpp @@ -50,7 +50,7 @@ const Type* CountLeadingZerosINode::Value(PhaseGVN* phase) const { const Type* t = phase->type(in(1)); if (t == Type::TOP) return Type::TOP; const TypeInt* ti = t->isa_int(); - if (ti) { + if (ti != nullptr) { if (ti->is_con()) { return TypeInt::make(count_leading_zeros_int(ti->get_con())); } @@ -66,7 +66,7 @@ const Type* CountLeadingZerosLNode::Value(PhaseGVN* phase) const { const Type* t = phase->type(in(1)); if (t == Type::TOP) return Type::TOP; const TypeLong* tl = t->isa_long(); - if (tl) { + if (tl != nullptr) { if (tl->is_con()) { return TypeInt::make(count_leading_zeros_long(tl->get_con())); } @@ -82,7 +82,7 @@ const Type* CountTrailingZerosINode::Value(PhaseGVN* phase) const { const Type* t = phase->type(in(1)); if (t == Type::TOP) return Type::TOP; const TypeInt* ti = t->isa_int(); - if (ti) { + if (ti != nullptr) { if (ti->is_con()) { return TypeInt::make(count_trailing_zeros_int(ti->get_con())); } @@ -98,7 +98,7 @@ const Type* CountTrailingZerosLNode::Value(PhaseGVN* phase) const { const Type* t = phase->type(in(1)); if (t == Type::TOP) return Type::TOP; const TypeLong* tl = t->isa_long(); - if (tl) { + if (tl != nullptr) { if (tl->is_con()) { return TypeInt::make(count_trailing_zeros_long(tl->get_con())); } From c965311b164a0234cb1b585e63949e124ccda3b0 Mon Sep 17 00:00:00 2001 From: MaxXSoft Date: Tue, 24 Jun 2025 15:41:47 +0800 Subject: [PATCH 05/19] Move `TestCountBitsRange` to `compiler.c2.gvn` --- .../compiler/c2/{irTests => gvn}/TestCountBitsRange.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename test/hotspot/jtreg/compiler/c2/{irTests => gvn}/TestCountBitsRange.java (97%) diff --git a/test/hotspot/jtreg/compiler/c2/irTests/TestCountBitsRange.java b/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java similarity index 97% rename from test/hotspot/jtreg/compiler/c2/irTests/TestCountBitsRange.java rename to test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java index 84eb88b00b364..86c9d9cbb942f 100644 --- a/test/hotspot/jtreg/compiler/c2/irTests/TestCountBitsRange.java +++ b/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java @@ -21,7 +21,7 @@ * questions. */ -package compiler.c2.irTests; +package compiler.c2.gvn; import compiler.lib.ir_framework.*; @@ -31,7 +31,7 @@ * @summary Tests that count bits nodes are handled correctly. * @library /test/lib / * @requires vm.compiler2.enabled - * @run driver compiler.c2.irTests.TestCountBitsRange + * @run driver compiler.c2.gvn.TestCountBitsRange */ public class TestCountBitsRange { public static void main(String[] args) { From db2d822aebaa0c0356a0bb182e280ee3b8a26be2 Mon Sep 17 00:00:00 2001 From: MaxXSoft Date: Wed, 6 Aug 2025 10:24:52 +0800 Subject: [PATCH 06/19] Replace `isa_*` with `is_*` and add checks for `Type::BOTTOM` --- src/hotspot/share/opto/countbitsnode.cpp | 104 ++++++++++++++--------- 1 file changed, 64 insertions(+), 40 deletions(-) diff --git a/src/hotspot/share/opto/countbitsnode.cpp b/src/hotspot/share/opto/countbitsnode.cpp index 2aae934cd8354..3a8c57bb00365 100644 --- a/src/hotspot/share/opto/countbitsnode.cpp +++ b/src/hotspot/share/opto/countbitsnode.cpp @@ -47,64 +47,88 @@ static int count_trailing_zeros_long(jlong l) { //------------------------------Value------------------------------------------ const Type* CountLeadingZerosINode::Value(PhaseGVN* phase) const { + // If the input is TOP, the result is also TOP. const Type* t = phase->type(in(1)); - if (t == Type::TOP) return Type::TOP; - const TypeInt* ti = t->isa_int(); - if (ti != nullptr) { - if (ti->is_con()) { - return TypeInt::make(count_leading_zeros_int(ti->get_con())); - } - return TypeInt::make(count_leading_zeros_int(~ti->_bits._zeros), - count_leading_zeros_int(ti->_bits._ones), - ti->_widen); + if (t == Type::TOP) { + return Type::TOP; } - return TypeInt::INT; + + // If the input is BOTTOM, the result is the local BOTTOM. + if (t == Type::BOTTOM) { + return bottom_type(); + } + + const TypeInt* ti = t->is_int(); + if (ti->is_con()) { + return TypeInt::make(count_leading_zeros_int(ti->get_con())); + } + return TypeInt::make(count_leading_zeros_int(~ti->_bits._zeros), + count_leading_zeros_int(ti->_bits._ones), + ti->_widen); } //------------------------------Value------------------------------------------ const Type* CountLeadingZerosLNode::Value(PhaseGVN* phase) const { + // If the input is TOP, the result is also TOP. const Type* t = phase->type(in(1)); - if (t == Type::TOP) return Type::TOP; - const TypeLong* tl = t->isa_long(); - if (tl != nullptr) { - if (tl->is_con()) { - return TypeInt::make(count_leading_zeros_long(tl->get_con())); - } - return TypeInt::make(count_leading_zeros_long(~tl->_bits._zeros), - count_leading_zeros_long(tl->_bits._ones), - tl->_widen); + if (t == Type::TOP) { + return Type::TOP; + } + + // If the input is BOTTOM, the result is the local BOTTOM. + if (t == Type::BOTTOM) { + return bottom_type(); + } + + const TypeLong* tl = t->is_long(); + if (tl->is_con()) { + return TypeInt::make(count_leading_zeros_long(tl->get_con())); } - return TypeInt::INT; + return TypeInt::make(count_leading_zeros_long(~tl->_bits._zeros), + count_leading_zeros_long(tl->_bits._ones), + tl->_widen); } //------------------------------Value------------------------------------------ const Type* CountTrailingZerosINode::Value(PhaseGVN* phase) const { + // If the input is TOP, the result is also TOP. const Type* t = phase->type(in(1)); - if (t == Type::TOP) return Type::TOP; - const TypeInt* ti = t->isa_int(); - if (ti != nullptr) { - if (ti->is_con()) { - return TypeInt::make(count_trailing_zeros_int(ti->get_con())); - } - return TypeInt::make(count_trailing_zeros_int(~ti->_bits._zeros), - count_trailing_zeros_int(ti->_bits._ones), - ti->_widen); + if (t == Type::TOP) { + return Type::TOP; } - return TypeInt::INT; + + // If the input is BOTTOM, the result is the local BOTTOM. + if (t == Type::BOTTOM) { + return bottom_type(); + } + + const TypeInt* ti = t->is_int(); + if (ti->is_con()) { + return TypeInt::make(count_trailing_zeros_int(ti->get_con())); + } + return TypeInt::make(count_trailing_zeros_int(~ti->_bits._zeros), + count_trailing_zeros_int(ti->_bits._ones), + ti->_widen); } //------------------------------Value------------------------------------------ const Type* CountTrailingZerosLNode::Value(PhaseGVN* phase) const { + // If the input is TOP, the result is also TOP. const Type* t = phase->type(in(1)); - if (t == Type::TOP) return Type::TOP; - const TypeLong* tl = t->isa_long(); - if (tl != nullptr) { - if (tl->is_con()) { - return TypeInt::make(count_trailing_zeros_long(tl->get_con())); - } - return TypeInt::make(count_trailing_zeros_long(~tl->_bits._zeros), - count_trailing_zeros_long(tl->_bits._ones), - tl->_widen); + if (t == Type::TOP) { + return Type::TOP; + } + + // If the input is BOTTOM, the result is the local BOTTOM. + if (t == Type::BOTTOM) { + return bottom_type(); + } + + const TypeLong* tl = t->is_long(); + if (tl->is_con()) { + return TypeInt::make(count_trailing_zeros_long(tl->get_con())); } - return TypeInt::INT; + return TypeInt::make(count_trailing_zeros_long(~tl->_bits._zeros), + count_trailing_zeros_long(tl->_bits._ones), + tl->_widen); } From da805b035ec9880ad2016900ae08a1bf0d1d6484 Mon Sep 17 00:00:00 2001 From: MaxXSoft Date: Wed, 6 Aug 2025 11:03:16 +0800 Subject: [PATCH 07/19] Add checks for results of all test methods --- .../compiler/c2/gvn/TestCountBitsRange.java | 50 +++++++++++++++---- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java b/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java index 86c9d9cbb942f..fc2df179da640 100644 --- a/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java +++ b/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java @@ -24,6 +24,7 @@ package compiler.c2.gvn; import compiler.lib.ir_framework.*; +import jdk.test.lib.Asserts; /* * @test @@ -38,12 +39,41 @@ public static void main(String[] args) { TestFramework.run(); } - static int i = RunInfo.getRandom().nextInt(); - static long l = RunInfo.getRandom().nextLong(); + @Run(test = { + "clzCompareInt", "clzDiv8Int", + "clzCompareLong", "clzDiv8Long", + "ctzCompareInt", "ctzDiv8Int", + "ctzCompareLong", "ctzDiv8Long", + }) + public void runTest() { + int i = RunInfo.getRandom().nextInt(); + long l = RunInfo.getRandom().nextLong(); + assertResult(i, l); + } + + @DontCompile + public void assertResult(int i, long l) { + Asserts.assertEQ(Integer.numberOfLeadingZeros(i) < 0 || Integer.numberOfLeadingZeros(i) > 32, + clzCompareInt(i)); + Asserts.assertEQ(Integer.numberOfLeadingZeros(i) / 8, + clzDiv8Int(i)); + Asserts.assertEQ(Long.numberOfLeadingZeros(l) < 0 || Long.numberOfLeadingZeros(l) > 64, + clzCompareLong(l)); + Asserts.assertEQ(Long.numberOfLeadingZeros(l) / 8, + clzDiv8Long(l)); + Asserts.assertEQ(Integer.numberOfTrailingZeros(i) < 0 || Integer.numberOfTrailingZeros(i) > 32, + ctzCompareInt(i)); + Asserts.assertEQ(Integer.numberOfTrailingZeros(i) / 8, + ctzDiv8Int(i)); + Asserts.assertEQ(Long.numberOfTrailingZeros(l) < 0 || Long.numberOfTrailingZeros(l) > 64, + ctzCompareLong(l)); + Asserts.assertEQ(Long.numberOfTrailingZeros(l) / 8, + ctzDiv8Long(l)); + } @Test @IR(failOn = IRNode.COUNT_LEADING_ZEROS_I) - public boolean clzCompareInt() { + public boolean clzCompareInt(int i) { return Integer.numberOfLeadingZeros(i) < 0 || Integer.numberOfLeadingZeros(i) > 32; } @@ -52,13 +82,13 @@ public boolean clzCompareInt() { IRNode.RSHIFT_I, "1", IRNode.URSHIFT_I, "0", IRNode.ADD_I, "0"}) - public int clzDiv8Int() { + public int clzDiv8Int(int i) { return Integer.numberOfLeadingZeros(i) / 8; } @Test @IR(failOn = IRNode.COUNT_LEADING_ZEROS_L) - public boolean clzCompareLong() { + public boolean clzCompareLong(long l) { return Long.numberOfLeadingZeros(l) < 0 || Long.numberOfLeadingZeros(l) > 64; } @@ -67,13 +97,13 @@ public boolean clzCompareLong() { IRNode.RSHIFT_I, "1", IRNode.URSHIFT_I, "0", IRNode.ADD_I, "0"}) - public int clzDiv8Long() { + public int clzDiv8Long(long l) { return Long.numberOfLeadingZeros(l) / 8; } @Test @IR(failOn = IRNode.COUNT_TRAILING_ZEROS_I) - public boolean ctzCompareInt() { + public boolean ctzCompareInt(int i) { return Integer.numberOfTrailingZeros(i) < 0 || Integer.numberOfTrailingZeros(i) > 32; } @@ -82,13 +112,13 @@ public boolean ctzCompareInt() { IRNode.RSHIFT_I, "1", IRNode.URSHIFT_I, "0", IRNode.ADD_I, "0"}) - public int ctzDiv8Int() { + public int ctzDiv8Int(int i) { return Integer.numberOfTrailingZeros(i) / 8; } @Test @IR(failOn = IRNode.COUNT_TRAILING_ZEROS_L) - public boolean ctzCompareLong() { + public boolean ctzCompareLong(long l) { return Long.numberOfTrailingZeros(l) < 0 || Long.numberOfTrailingZeros(l) > 64; } @@ -97,7 +127,7 @@ public boolean ctzCompareLong() { IRNode.RSHIFT_I, "1", IRNode.URSHIFT_I, "0", IRNode.ADD_I, "0"}) - public int ctzDiv8Long() { + public int ctzDiv8Long(long l) { return Long.numberOfTrailingZeros(l) / 8; } } From ce5f86956b0f512d5278d022f47cf041496b2bad Mon Sep 17 00:00:00 2001 From: MaxXSoft Date: Thu, 7 Aug 2025 10:09:26 +0800 Subject: [PATCH 08/19] Remove unnecessary code --- src/hotspot/share/opto/countbitsnode.cpp | 36 ------------------- .../compiler/c2/gvn/TestCountBitsRange.java | 32 +++++++++++++++++ 2 files changed, 32 insertions(+), 36 deletions(-) diff --git a/src/hotspot/share/opto/countbitsnode.cpp b/src/hotspot/share/opto/countbitsnode.cpp index 3a8c57bb00365..a6bbdc25f4c75 100644 --- a/src/hotspot/share/opto/countbitsnode.cpp +++ b/src/hotspot/share/opto/countbitsnode.cpp @@ -47,21 +47,12 @@ static int count_trailing_zeros_long(jlong l) { //------------------------------Value------------------------------------------ const Type* CountLeadingZerosINode::Value(PhaseGVN* phase) const { - // If the input is TOP, the result is also TOP. const Type* t = phase->type(in(1)); if (t == Type::TOP) { return Type::TOP; } - // If the input is BOTTOM, the result is the local BOTTOM. - if (t == Type::BOTTOM) { - return bottom_type(); - } - const TypeInt* ti = t->is_int(); - if (ti->is_con()) { - return TypeInt::make(count_leading_zeros_int(ti->get_con())); - } return TypeInt::make(count_leading_zeros_int(~ti->_bits._zeros), count_leading_zeros_int(ti->_bits._ones), ti->_widen); @@ -69,21 +60,12 @@ const Type* CountLeadingZerosINode::Value(PhaseGVN* phase) const { //------------------------------Value------------------------------------------ const Type* CountLeadingZerosLNode::Value(PhaseGVN* phase) const { - // If the input is TOP, the result is also TOP. const Type* t = phase->type(in(1)); if (t == Type::TOP) { return Type::TOP; } - // If the input is BOTTOM, the result is the local BOTTOM. - if (t == Type::BOTTOM) { - return bottom_type(); - } - const TypeLong* tl = t->is_long(); - if (tl->is_con()) { - return TypeInt::make(count_leading_zeros_long(tl->get_con())); - } return TypeInt::make(count_leading_zeros_long(~tl->_bits._zeros), count_leading_zeros_long(tl->_bits._ones), tl->_widen); @@ -91,21 +73,12 @@ const Type* CountLeadingZerosLNode::Value(PhaseGVN* phase) const { //------------------------------Value------------------------------------------ const Type* CountTrailingZerosINode::Value(PhaseGVN* phase) const { - // If the input is TOP, the result is also TOP. const Type* t = phase->type(in(1)); if (t == Type::TOP) { return Type::TOP; } - // If the input is BOTTOM, the result is the local BOTTOM. - if (t == Type::BOTTOM) { - return bottom_type(); - } - const TypeInt* ti = t->is_int(); - if (ti->is_con()) { - return TypeInt::make(count_trailing_zeros_int(ti->get_con())); - } return TypeInt::make(count_trailing_zeros_int(~ti->_bits._zeros), count_trailing_zeros_int(ti->_bits._ones), ti->_widen); @@ -113,21 +86,12 @@ const Type* CountTrailingZerosINode::Value(PhaseGVN* phase) const { //------------------------------Value------------------------------------------ const Type* CountTrailingZerosLNode::Value(PhaseGVN* phase) const { - // If the input is TOP, the result is also TOP. const Type* t = phase->type(in(1)); if (t == Type::TOP) { return Type::TOP; } - // If the input is BOTTOM, the result is the local BOTTOM. - if (t == Type::BOTTOM) { - return bottom_type(); - } - const TypeLong* tl = t->is_long(); - if (tl->is_con()) { - return TypeInt::make(count_trailing_zeros_long(tl->get_con())); - } return TypeInt::make(count_trailing_zeros_long(~tl->_bits._zeros), count_trailing_zeros_long(tl->_bits._ones), tl->_widen); diff --git a/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java b/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java index fc2df179da640..6b7e2b5a33576 100644 --- a/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java +++ b/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java @@ -53,24 +53,38 @@ public void runTest() { @DontCompile public void assertResult(int i, long l) { + Asserts.assertEQ(Integer.numberOfLeadingZeros(42), + clzConstInt()); Asserts.assertEQ(Integer.numberOfLeadingZeros(i) < 0 || Integer.numberOfLeadingZeros(i) > 32, clzCompareInt(i)); Asserts.assertEQ(Integer.numberOfLeadingZeros(i) / 8, clzDiv8Int(i)); + Asserts.assertEQ(Long.numberOfLeadingZeros(42), + clzConstLong()); Asserts.assertEQ(Long.numberOfLeadingZeros(l) < 0 || Long.numberOfLeadingZeros(l) > 64, clzCompareLong(l)); Asserts.assertEQ(Long.numberOfLeadingZeros(l) / 8, clzDiv8Long(l)); + Asserts.assertEQ(Integer.numberOfTrailingZeros(42), + ctzConstInt()); Asserts.assertEQ(Integer.numberOfTrailingZeros(i) < 0 || Integer.numberOfTrailingZeros(i) > 32, ctzCompareInt(i)); Asserts.assertEQ(Integer.numberOfTrailingZeros(i) / 8, ctzDiv8Int(i)); + Asserts.assertEQ(Long.numberOfTrailingZeros(42), + ctzConstLong()); Asserts.assertEQ(Long.numberOfTrailingZeros(l) < 0 || Long.numberOfTrailingZeros(l) > 64, ctzCompareLong(l)); Asserts.assertEQ(Long.numberOfTrailingZeros(l) / 8, ctzDiv8Long(l)); } + @Test + @IR(failOn = IRNode.COUNT_LEADING_ZEROS_I) + public int clzConstInt() { + return Integer.numberOfLeadingZeros(42); + } + @Test @IR(failOn = IRNode.COUNT_LEADING_ZEROS_I) public boolean clzCompareInt(int i) { @@ -86,6 +100,12 @@ public int clzDiv8Int(int i) { return Integer.numberOfLeadingZeros(i) / 8; } + @Test + @IR(failOn = IRNode.COUNT_LEADING_ZEROS_L) + public int clzConstLong() { + return Long.numberOfLeadingZeros(42); + } + @Test @IR(failOn = IRNode.COUNT_LEADING_ZEROS_L) public boolean clzCompareLong(long l) { @@ -101,6 +121,12 @@ public int clzDiv8Long(long l) { return Long.numberOfLeadingZeros(l) / 8; } + @Test + @IR(failOn = IRNode.COUNT_TRAILING_ZEROS_I) + public int ctzConstInt() { + return Integer.numberOfTrailingZeros(42); + } + @Test @IR(failOn = IRNode.COUNT_TRAILING_ZEROS_I) public boolean ctzCompareInt(int i) { @@ -116,6 +142,12 @@ public int ctzDiv8Int(int i) { return Integer.numberOfTrailingZeros(i) / 8; } + @Test + @IR(failOn = IRNode.COUNT_TRAILING_ZEROS_L) + public int ctzConstLong() { + return Long.numberOfTrailingZeros(42); + } + @Test @IR(failOn = IRNode.COUNT_TRAILING_ZEROS_L) public boolean ctzCompareLong(long l) { From 68d99387bab8fbb8d480b8c06da3a3d6d7c063fe Mon Sep 17 00:00:00 2001 From: MaxXSoft Date: Fri, 8 Aug 2025 15:19:22 +0800 Subject: [PATCH 09/19] Add missing test method declarations --- .../hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java b/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java index 6b7e2b5a33576..0d3dffd97f52d 100644 --- a/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java +++ b/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java @@ -40,10 +40,10 @@ public static void main(String[] args) { } @Run(test = { - "clzCompareInt", "clzDiv8Int", - "clzCompareLong", "clzDiv8Long", - "ctzCompareInt", "ctzDiv8Int", - "ctzCompareLong", "ctzDiv8Long", + "clzConstInt", "clzCompareInt", "clzDiv8Int", + "clzConstLong", "clzCompareLong", "clzDiv8Long", + "ctzConstInt", "ctzCompareInt", "ctzDiv8Int", + "ctzConstLong", "ctzCompareLong", "ctzDiv8Long", }) public void runTest() { int i = RunInfo.getRandom().nextInt(); From b4b9b6433de6555d88c763224b0c0a031ca1717c Mon Sep 17 00:00:00 2001 From: MaxXSoft Date: Fri, 8 Aug 2025 16:10:16 +0800 Subject: [PATCH 10/19] Add microbench --- .../bench/vm/compiler/CountLeadingZeros.java | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 test/micro/org/openjdk/bench/vm/compiler/CountLeadingZeros.java diff --git a/test/micro/org/openjdk/bench/vm/compiler/CountLeadingZeros.java b/test/micro/org/openjdk/bench/vm/compiler/CountLeadingZeros.java new file mode 100644 index 0000000000000..dc06e3ae76411 --- /dev/null +++ b/test/micro/org/openjdk/bench/vm/compiler/CountLeadingZeros.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2025 Alibaba Group Holding Limited. 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 org.openjdk.bench.vm.compiler; + +import org.openjdk.jmh.annotations.*; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.ThreadLocalRandom; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Fork(value = 3) +@Warmup(iterations = 10, time = 1) +@Measurement(iterations = 5, time = 1) +@State(Scope.Thread) +public class CountLeadingZeros { + private long[] longArray = new long[1000]; + + @Setup + public void setup() { + for (int i = 0; i < longArray.length; i++) { + longArray[i] = ThreadLocalRandom.current().nextLong(); + } + } + + @Benchmark + public int benchNumberOfNibbles() { + int sum = 0; + for (long l : longArray) { + sum += numberOfNibbles((int) l); + } + return sum; + } + + public static int numberOfNibbles(int i) { + int mag = Integer.SIZE - Integer.numberOfLeadingZeros(i); + return Math.max((mag + 3) / 4, 1); + } + + @Benchmark + public int benchClzLongConstrained() { + int sum = 0; + for (long l : longArray) { + sum += clzLongConstrained(l); + } + return sum; + } + + public static int clzLongConstrained(long param) { + long constrainedParam = Math.min(175, Math.max(param, 160)); + return Long.numberOfLeadingZeros(constrainedParam); + } +} From f1c0b45ad86f597b8967941f7f75275ec96cef59 Mon Sep 17 00:00:00 2001 From: MaxXSoft Date: Tue, 19 Aug 2025 09:28:13 +0800 Subject: [PATCH 11/19] Remove redundant `@require` in IR test --- test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java | 1 - 1 file changed, 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java b/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java index 0d3dffd97f52d..48420bc4c330c 100644 --- a/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java +++ b/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java @@ -31,7 +31,6 @@ * @bug 8360192 * @summary Tests that count bits nodes are handled correctly. * @library /test/lib / - * @requires vm.compiler2.enabled * @run driver compiler.c2.gvn.TestCountBitsRange */ public class TestCountBitsRange { From 5cfe39b69b82e6433ef10593b9d62f9ce90216e0 Mon Sep 17 00:00:00 2001 From: MaxXSoft Date: Mon, 8 Sep 2025 16:27:29 +0800 Subject: [PATCH 12/19] Add proof of correstness comments --- src/hotspot/share/opto/countbitsnode.cpp | 36 ++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/hotspot/share/opto/countbitsnode.cpp b/src/hotspot/share/opto/countbitsnode.cpp index a6bbdc25f4c75..16984ab02bad6 100644 --- a/src/hotspot/share/opto/countbitsnode.cpp +++ b/src/hotspot/share/opto/countbitsnode.cpp @@ -52,6 +52,22 @@ const Type* CountLeadingZerosINode::Value(PhaseGVN* phase) const { return Type::TOP; } + // To minimize `count_leading_zeros(x)`, we should make the highest 1 bit in x + // as far to the left as possible. A bit in x can be 1 iff this bit is not + // forced to be 0, i.e. the corresponding bit in `x._bits._zeros` is 0. Thus: + // min(clz(x)) = number of bits to the left of the highest 0 bit in x._bits._zeros + // = count_leading_ones(x._bits._zeros) = clz(~x._bits._zeros) + // + // To maximize `count_leading_zeros(x)`, we should make the leading zeros as + // many as possible. A bit in x can be 0 iff this bit is not forced to be 1, + // i.e. the corresponding bit in `x._bits._ones` is 0. Thus: + // max(clz(x)) = clz(x._bits._ones) + // + // Therefore, the range of `count_leading_zeros(x)` is: + // [clz(~x._bits._zeros), clz(x._bits._ones)] + // + // A more detailed proof using Z3 can be found at: + // https://github.com/openjdk/jdk/pull/25928#discussion_r2256750507 const TypeInt* ti = t->is_int(); return TypeInt::make(count_leading_zeros_int(~ti->_bits._zeros), count_leading_zeros_int(ti->_bits._ones), @@ -65,6 +81,8 @@ const Type* CountLeadingZerosLNode::Value(PhaseGVN* phase) const { return Type::TOP; } + // The proof of correctness is same as the above comments + // in `CountLeadingZerosINode::Value`. const TypeLong* tl = t->is_long(); return TypeInt::make(count_leading_zeros_long(~tl->_bits._zeros), count_leading_zeros_long(tl->_bits._ones), @@ -78,6 +96,22 @@ const Type* CountTrailingZerosINode::Value(PhaseGVN* phase) const { return Type::TOP; } + // To minimize `count_trailing_zeros(x)`, we should make the lowest 1 bit in x + // as far to the right as possible. A bit in x can be 1 iff this bit is not + // forced to be 0, i.e. the corresponding bit in `x._bits._zeros` is 0. Thus: + // min(ctz(x)) = number of bits to the right of the lowest 0 bit in x._bits._zeros + // = count_trailing_ones(x._bits._zeros) = ctz(~x._bits._zeros) + // + // To maximize `count_trailing_zeros(x)`, we should make the trailing zeros as + // many as possible. A bit in x can be 0 iff this bit is not forced to be 1, + // i.e. the corresponding bit in `x._bits._ones` is 0. Thus: + // max(ctz(x)) = ctz(x._bits._ones) + // + // Therefore, the range of `count_trailing_zeros(x)` is: + // [ctz(~x._bits._zeros), ctz(x._bits._ones)] + // + // A more detailed proof using Z3 can be found at: + // https://github.com/openjdk/jdk/pull/25928#discussion_r2256750507 const TypeInt* ti = t->is_int(); return TypeInt::make(count_trailing_zeros_int(~ti->_bits._zeros), count_trailing_zeros_int(ti->_bits._ones), @@ -91,6 +125,8 @@ const Type* CountTrailingZerosLNode::Value(PhaseGVN* phase) const { return Type::TOP; } + // The proof of correctness is same as the above comments + // in `CountTrailingZerosINode::Value`. const TypeLong* tl = t->is_long(); return TypeInt::make(count_trailing_zeros_long(~tl->_bits._zeros), count_trailing_zeros_long(tl->_bits._ones), From d09d4cb0c7e0d4d569e589909776d1134f29caab Mon Sep 17 00:00:00 2001 From: MaxXSoft Date: Mon, 8 Sep 2025 17:29:05 +0800 Subject: [PATCH 13/19] Add more constant folding tests for CLZ/CTZ --- .../compiler/c2/gvn/TestCountBitsRange.java | 335 +++++++++++++++--- 1 file changed, 279 insertions(+), 56 deletions(-) diff --git a/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java b/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java index 48420bc4c330c..af00325b22ca7 100644 --- a/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java +++ b/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java @@ -39,55 +39,109 @@ public static void main(String[] args) { } @Run(test = { - "clzConstInt", "clzCompareInt", "clzDiv8Int", - "clzConstLong", "clzCompareLong", "clzDiv8Long", - "ctzConstInt", "ctzCompareInt", "ctzDiv8Int", - "ctzConstLong", "ctzCompareLong", "ctzDiv8Long", + "clzConstInts", "clzCompareInt", "clzDiv8Int", + "clzConstLongs", "clzCompareLong", "clzDiv8Long", + "ctzConstInts", "ctzCompareInt", "ctzDiv8Int", + "ctzConstLongs", "ctzCompareLong", "ctzDiv8Long", }) public void runTest() { - int i = RunInfo.getRandom().nextInt(); - long l = RunInfo.getRandom().nextLong(); - assertResult(i, l); + int randInt = RunInfo.getRandom().nextInt(); + long randLong = RunInfo.getRandom().nextLong(); + assertResult(randInt, randLong); } @DontCompile - public void assertResult(int i, long l) { - Asserts.assertEQ(Integer.numberOfLeadingZeros(42), - clzConstInt()); - Asserts.assertEQ(Integer.numberOfLeadingZeros(i) < 0 || Integer.numberOfLeadingZeros(i) > 32, - clzCompareInt(i)); - Asserts.assertEQ(Integer.numberOfLeadingZeros(i) / 8, - clzDiv8Int(i)); - Asserts.assertEQ(Long.numberOfLeadingZeros(42), - clzConstLong()); - Asserts.assertEQ(Long.numberOfLeadingZeros(l) < 0 || Long.numberOfLeadingZeros(l) > 64, - clzCompareLong(l)); - Asserts.assertEQ(Long.numberOfLeadingZeros(l) / 8, - clzDiv8Long(l)); - Asserts.assertEQ(Integer.numberOfTrailingZeros(42), - ctzConstInt()); - Asserts.assertEQ(Integer.numberOfTrailingZeros(i) < 0 || Integer.numberOfTrailingZeros(i) > 32, - ctzCompareInt(i)); - Asserts.assertEQ(Integer.numberOfTrailingZeros(i) / 8, - ctzDiv8Int(i)); - Asserts.assertEQ(Long.numberOfTrailingZeros(42), - ctzConstLong()); - Asserts.assertEQ(Long.numberOfTrailingZeros(l) < 0 || Long.numberOfTrailingZeros(l) > 64, - ctzCompareLong(l)); - Asserts.assertEQ(Long.numberOfTrailingZeros(l) / 8, - ctzDiv8Long(l)); + public void assertResult(int randInt, long randLong) { + int[] results = clzConstInts(); + Asserts.assertEQ(Integer.numberOfLeadingZeros(0), results[0]); + for (int i = 0; i < 32; ++i) { + Asserts.assertEQ(Integer.numberOfLeadingZeros(1 << i), results[i + 1]); + } + Asserts.assertEQ(Integer.numberOfLeadingZeros(randInt) < 0 + || Integer.numberOfLeadingZeros(randInt) > 32, + clzCompareInt(randInt)); + Asserts.assertEQ(Integer.numberOfLeadingZeros(randInt) / 8, + clzDiv8Int(randInt)); + + results = clzConstLongs(); + Asserts.assertEQ(Long.numberOfLeadingZeros(0), results[0]); + for (int i = 0; i < 64; ++i) { + Asserts.assertEQ(Long.numberOfLeadingZeros(1l << i), results[i + 1]); + } + Asserts.assertEQ(Long.numberOfLeadingZeros(randLong) < 0 + || Long.numberOfLeadingZeros(randLong) > 64, + clzCompareLong(randLong)); + Asserts.assertEQ(Long.numberOfLeadingZeros(randLong) / 8, + clzDiv8Long(randLong)); + + results = ctzConstInts(); + Asserts.assertEQ(Integer.numberOfTrailingZeros(0), results[0]); + for (int i = 0; i < 32; ++i) { + Asserts.assertEQ(Integer.numberOfTrailingZeros(1 << i), results[i + 1]); + } + Asserts.assertEQ(Integer.numberOfTrailingZeros(randInt) < 0 + || Integer.numberOfTrailingZeros(randInt) > 32, + ctzCompareInt(randInt)); + Asserts.assertEQ(Integer.numberOfTrailingZeros(randInt) / 8, + ctzDiv8Int(randInt)); + + results = ctzConstLongs(); + Asserts.assertEQ(Long.numberOfTrailingZeros(0), results[0]); + for (int i = 0; i < 64; ++i) { + Asserts.assertEQ(Long.numberOfTrailingZeros(1l << i), results[i + 1]); + } + Asserts.assertEQ(Long.numberOfTrailingZeros(randLong) < 0 + || Long.numberOfTrailingZeros(randLong) > 64, + ctzCompareLong(randLong)); + Asserts.assertEQ(Long.numberOfTrailingZeros(randLong) / 8, + ctzDiv8Long(randLong)); } @Test @IR(failOn = IRNode.COUNT_LEADING_ZEROS_I) - public int clzConstInt() { - return Integer.numberOfLeadingZeros(42); + public int[] clzConstInts() { + return new int[] { + Integer.numberOfLeadingZeros(0), + Integer.numberOfLeadingZeros(1 << 0), + Integer.numberOfLeadingZeros(1 << 1), + Integer.numberOfLeadingZeros(1 << 2), + Integer.numberOfLeadingZeros(1 << 3), + Integer.numberOfLeadingZeros(1 << 4), + Integer.numberOfLeadingZeros(1 << 5), + Integer.numberOfLeadingZeros(1 << 6), + Integer.numberOfLeadingZeros(1 << 7), + Integer.numberOfLeadingZeros(1 << 8), + Integer.numberOfLeadingZeros(1 << 9), + Integer.numberOfLeadingZeros(1 << 10), + Integer.numberOfLeadingZeros(1 << 11), + Integer.numberOfLeadingZeros(1 << 12), + Integer.numberOfLeadingZeros(1 << 13), + Integer.numberOfLeadingZeros(1 << 14), + Integer.numberOfLeadingZeros(1 << 15), + Integer.numberOfLeadingZeros(1 << 16), + Integer.numberOfLeadingZeros(1 << 17), + Integer.numberOfLeadingZeros(1 << 18), + Integer.numberOfLeadingZeros(1 << 19), + Integer.numberOfLeadingZeros(1 << 20), + Integer.numberOfLeadingZeros(1 << 21), + Integer.numberOfLeadingZeros(1 << 22), + Integer.numberOfLeadingZeros(1 << 23), + Integer.numberOfLeadingZeros(1 << 24), + Integer.numberOfLeadingZeros(1 << 25), + Integer.numberOfLeadingZeros(1 << 26), + Integer.numberOfLeadingZeros(1 << 27), + Integer.numberOfLeadingZeros(1 << 28), + Integer.numberOfLeadingZeros(1 << 29), + Integer.numberOfLeadingZeros(1 << 30), + Integer.numberOfLeadingZeros(1 << 31), + }; } @Test @IR(failOn = IRNode.COUNT_LEADING_ZEROS_I) - public boolean clzCompareInt(int i) { - return Integer.numberOfLeadingZeros(i) < 0 || Integer.numberOfLeadingZeros(i) > 32; + public boolean clzCompareInt(int randInt) { + return Integer.numberOfLeadingZeros(randInt) < 0 + || Integer.numberOfLeadingZeros(randInt) > 32; } @Test @@ -95,20 +149,87 @@ public boolean clzCompareInt(int i) { IRNode.RSHIFT_I, "1", IRNode.URSHIFT_I, "0", IRNode.ADD_I, "0"}) - public int clzDiv8Int(int i) { - return Integer.numberOfLeadingZeros(i) / 8; + public int clzDiv8Int(int randInt) { + return Integer.numberOfLeadingZeros(randInt) / 8; } @Test @IR(failOn = IRNode.COUNT_LEADING_ZEROS_L) - public int clzConstLong() { - return Long.numberOfLeadingZeros(42); + public int[] clzConstLongs() { + return new int[] { + Long.numberOfLeadingZeros(0), + Long.numberOfLeadingZeros(1l << 0), + Long.numberOfLeadingZeros(1l << 1), + Long.numberOfLeadingZeros(1l << 2), + Long.numberOfLeadingZeros(1l << 3), + Long.numberOfLeadingZeros(1l << 4), + Long.numberOfLeadingZeros(1l << 5), + Long.numberOfLeadingZeros(1l << 6), + Long.numberOfLeadingZeros(1l << 7), + Long.numberOfLeadingZeros(1l << 8), + Long.numberOfLeadingZeros(1l << 9), + Long.numberOfLeadingZeros(1l << 10), + Long.numberOfLeadingZeros(1l << 11), + Long.numberOfLeadingZeros(1l << 12), + Long.numberOfLeadingZeros(1l << 13), + Long.numberOfLeadingZeros(1l << 14), + Long.numberOfLeadingZeros(1l << 15), + Long.numberOfLeadingZeros(1l << 16), + Long.numberOfLeadingZeros(1l << 17), + Long.numberOfLeadingZeros(1l << 18), + Long.numberOfLeadingZeros(1l << 19), + Long.numberOfLeadingZeros(1l << 20), + Long.numberOfLeadingZeros(1l << 21), + Long.numberOfLeadingZeros(1l << 22), + Long.numberOfLeadingZeros(1l << 23), + Long.numberOfLeadingZeros(1l << 24), + Long.numberOfLeadingZeros(1l << 25), + Long.numberOfLeadingZeros(1l << 26), + Long.numberOfLeadingZeros(1l << 27), + Long.numberOfLeadingZeros(1l << 28), + Long.numberOfLeadingZeros(1l << 29), + Long.numberOfLeadingZeros(1l << 30), + Long.numberOfLeadingZeros(1l << 31), + Long.numberOfLeadingZeros(1l << 32), + Long.numberOfLeadingZeros(1l << 33), + Long.numberOfLeadingZeros(1l << 34), + Long.numberOfLeadingZeros(1l << 35), + Long.numberOfLeadingZeros(1l << 36), + Long.numberOfLeadingZeros(1l << 37), + Long.numberOfLeadingZeros(1l << 38), + Long.numberOfLeadingZeros(1l << 39), + Long.numberOfLeadingZeros(1l << 40), + Long.numberOfLeadingZeros(1l << 41), + Long.numberOfLeadingZeros(1l << 42), + Long.numberOfLeadingZeros(1l << 43), + Long.numberOfLeadingZeros(1l << 44), + Long.numberOfLeadingZeros(1l << 45), + Long.numberOfLeadingZeros(1l << 46), + Long.numberOfLeadingZeros(1l << 47), + Long.numberOfLeadingZeros(1l << 48), + Long.numberOfLeadingZeros(1l << 49), + Long.numberOfLeadingZeros(1l << 50), + Long.numberOfLeadingZeros(1l << 51), + Long.numberOfLeadingZeros(1l << 52), + Long.numberOfLeadingZeros(1l << 53), + Long.numberOfLeadingZeros(1l << 54), + Long.numberOfLeadingZeros(1l << 55), + Long.numberOfLeadingZeros(1l << 56), + Long.numberOfLeadingZeros(1l << 57), + Long.numberOfLeadingZeros(1l << 58), + Long.numberOfLeadingZeros(1l << 59), + Long.numberOfLeadingZeros(1l << 60), + Long.numberOfLeadingZeros(1l << 61), + Long.numberOfLeadingZeros(1l << 62), + Long.numberOfLeadingZeros(1l << 63), + }; } @Test @IR(failOn = IRNode.COUNT_LEADING_ZEROS_L) - public boolean clzCompareLong(long l) { - return Long.numberOfLeadingZeros(l) < 0 || Long.numberOfLeadingZeros(l) > 64; + public boolean clzCompareLong(long randLong) { + return Long.numberOfLeadingZeros(randLong) < 0 + || Long.numberOfLeadingZeros(randLong) > 64; } @Test @@ -116,20 +237,55 @@ public boolean clzCompareLong(long l) { IRNode.RSHIFT_I, "1", IRNode.URSHIFT_I, "0", IRNode.ADD_I, "0"}) - public int clzDiv8Long(long l) { - return Long.numberOfLeadingZeros(l) / 8; + public int clzDiv8Long(long randLong) { + return Long.numberOfLeadingZeros(randLong) / 8; } @Test @IR(failOn = IRNode.COUNT_TRAILING_ZEROS_I) - public int ctzConstInt() { - return Integer.numberOfTrailingZeros(42); + public int[] ctzConstInts() { + return new int[] { + Integer.numberOfTrailingZeros(0), + Integer.numberOfTrailingZeros(1 << 0), + Integer.numberOfTrailingZeros(1 << 1), + Integer.numberOfTrailingZeros(1 << 2), + Integer.numberOfTrailingZeros(1 << 3), + Integer.numberOfTrailingZeros(1 << 4), + Integer.numberOfTrailingZeros(1 << 5), + Integer.numberOfTrailingZeros(1 << 6), + Integer.numberOfTrailingZeros(1 << 7), + Integer.numberOfTrailingZeros(1 << 8), + Integer.numberOfTrailingZeros(1 << 9), + Integer.numberOfTrailingZeros(1 << 10), + Integer.numberOfTrailingZeros(1 << 11), + Integer.numberOfTrailingZeros(1 << 12), + Integer.numberOfTrailingZeros(1 << 13), + Integer.numberOfTrailingZeros(1 << 14), + Integer.numberOfTrailingZeros(1 << 15), + Integer.numberOfTrailingZeros(1 << 16), + Integer.numberOfTrailingZeros(1 << 17), + Integer.numberOfTrailingZeros(1 << 18), + Integer.numberOfTrailingZeros(1 << 19), + Integer.numberOfTrailingZeros(1 << 20), + Integer.numberOfTrailingZeros(1 << 21), + Integer.numberOfTrailingZeros(1 << 22), + Integer.numberOfTrailingZeros(1 << 23), + Integer.numberOfTrailingZeros(1 << 24), + Integer.numberOfTrailingZeros(1 << 25), + Integer.numberOfTrailingZeros(1 << 26), + Integer.numberOfTrailingZeros(1 << 27), + Integer.numberOfTrailingZeros(1 << 28), + Integer.numberOfTrailingZeros(1 << 29), + Integer.numberOfTrailingZeros(1 << 30), + Integer.numberOfTrailingZeros(1 << 31), + }; } @Test @IR(failOn = IRNode.COUNT_TRAILING_ZEROS_I) - public boolean ctzCompareInt(int i) { - return Integer.numberOfTrailingZeros(i) < 0 || Integer.numberOfTrailingZeros(i) > 32; + public boolean ctzCompareInt(int randInt) { + return Integer.numberOfTrailingZeros(randInt) < 0 + || Integer.numberOfTrailingZeros(randInt) > 32; } @Test @@ -137,20 +293,87 @@ public boolean ctzCompareInt(int i) { IRNode.RSHIFT_I, "1", IRNode.URSHIFT_I, "0", IRNode.ADD_I, "0"}) - public int ctzDiv8Int(int i) { - return Integer.numberOfTrailingZeros(i) / 8; + public int ctzDiv8Int(int randInt) { + return Integer.numberOfTrailingZeros(randInt) / 8; } @Test @IR(failOn = IRNode.COUNT_TRAILING_ZEROS_L) - public int ctzConstLong() { - return Long.numberOfTrailingZeros(42); + public int[] ctzConstLongs() { + return new int[] { + Long.numberOfTrailingZeros(0), + Long.numberOfTrailingZeros(1l << 0), + Long.numberOfTrailingZeros(1l << 1), + Long.numberOfTrailingZeros(1l << 2), + Long.numberOfTrailingZeros(1l << 3), + Long.numberOfTrailingZeros(1l << 4), + Long.numberOfTrailingZeros(1l << 5), + Long.numberOfTrailingZeros(1l << 6), + Long.numberOfTrailingZeros(1l << 7), + Long.numberOfTrailingZeros(1l << 8), + Long.numberOfTrailingZeros(1l << 9), + Long.numberOfTrailingZeros(1l << 10), + Long.numberOfTrailingZeros(1l << 11), + Long.numberOfTrailingZeros(1l << 12), + Long.numberOfTrailingZeros(1l << 13), + Long.numberOfTrailingZeros(1l << 14), + Long.numberOfTrailingZeros(1l << 15), + Long.numberOfTrailingZeros(1l << 16), + Long.numberOfTrailingZeros(1l << 17), + Long.numberOfTrailingZeros(1l << 18), + Long.numberOfTrailingZeros(1l << 19), + Long.numberOfTrailingZeros(1l << 20), + Long.numberOfTrailingZeros(1l << 21), + Long.numberOfTrailingZeros(1l << 22), + Long.numberOfTrailingZeros(1l << 23), + Long.numberOfTrailingZeros(1l << 24), + Long.numberOfTrailingZeros(1l << 25), + Long.numberOfTrailingZeros(1l << 26), + Long.numberOfTrailingZeros(1l << 27), + Long.numberOfTrailingZeros(1l << 28), + Long.numberOfTrailingZeros(1l << 29), + Long.numberOfTrailingZeros(1l << 30), + Long.numberOfTrailingZeros(1l << 31), + Long.numberOfTrailingZeros(1l << 32), + Long.numberOfTrailingZeros(1l << 33), + Long.numberOfTrailingZeros(1l << 34), + Long.numberOfTrailingZeros(1l << 35), + Long.numberOfTrailingZeros(1l << 36), + Long.numberOfTrailingZeros(1l << 37), + Long.numberOfTrailingZeros(1l << 38), + Long.numberOfTrailingZeros(1l << 39), + Long.numberOfTrailingZeros(1l << 40), + Long.numberOfTrailingZeros(1l << 41), + Long.numberOfTrailingZeros(1l << 42), + Long.numberOfTrailingZeros(1l << 43), + Long.numberOfTrailingZeros(1l << 44), + Long.numberOfTrailingZeros(1l << 45), + Long.numberOfTrailingZeros(1l << 46), + Long.numberOfTrailingZeros(1l << 47), + Long.numberOfTrailingZeros(1l << 48), + Long.numberOfTrailingZeros(1l << 49), + Long.numberOfTrailingZeros(1l << 50), + Long.numberOfTrailingZeros(1l << 51), + Long.numberOfTrailingZeros(1l << 52), + Long.numberOfTrailingZeros(1l << 53), + Long.numberOfTrailingZeros(1l << 54), + Long.numberOfTrailingZeros(1l << 55), + Long.numberOfTrailingZeros(1l << 56), + Long.numberOfTrailingZeros(1l << 57), + Long.numberOfTrailingZeros(1l << 58), + Long.numberOfTrailingZeros(1l << 59), + Long.numberOfTrailingZeros(1l << 60), + Long.numberOfTrailingZeros(1l << 61), + Long.numberOfTrailingZeros(1l << 62), + Long.numberOfTrailingZeros(1l << 63), + }; } @Test @IR(failOn = IRNode.COUNT_TRAILING_ZEROS_L) - public boolean ctzCompareLong(long l) { - return Long.numberOfTrailingZeros(l) < 0 || Long.numberOfTrailingZeros(l) > 64; + public boolean ctzCompareLong(long randLong) { + return Long.numberOfTrailingZeros(randLong) < 0 + || Long.numberOfTrailingZeros(randLong) > 64; } @Test @@ -158,7 +381,7 @@ public boolean ctzCompareLong(long l) { IRNode.RSHIFT_I, "1", IRNode.URSHIFT_I, "0", IRNode.ADD_I, "0"}) - public int ctzDiv8Long(long l) { - return Long.numberOfTrailingZeros(l) / 8; + public int ctzDiv8Long(long randLong) { + return Long.numberOfTrailingZeros(randLong) / 8; } } From 42c079f1661c41cb3356a3b879e0640bbbc72b40 Mon Sep 17 00:00:00 2001 From: MaxXSoft Date: Tue, 9 Sep 2025 16:37:53 +0800 Subject: [PATCH 14/19] Add more comments to IR test --- .../compiler/c2/gvn/TestCountBitsRange.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java b/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java index af00325b22ca7..0abd3e499bdb9 100644 --- a/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java +++ b/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java @@ -97,6 +97,8 @@ public void assertResult(int randInt, long randLong) { ctzDiv8Long(randLong)); } + // Test CLZ with constant integer inputs. + // All CLZs in this test are expected to be optimized away. @Test @IR(failOn = IRNode.COUNT_LEADING_ZEROS_I) public int[] clzConstInts() { @@ -137,6 +139,9 @@ public int[] clzConstInts() { }; } + // Test the range of CLZ with random integer input. + // The result of CLZ should be in range [0, 32], so CLZs in this test are + // expected to be optimized away, and the test should always return false. @Test @IR(failOn = IRNode.COUNT_LEADING_ZEROS_I) public boolean clzCompareInt(int randInt) { @@ -144,6 +149,9 @@ public boolean clzCompareInt(int randInt) { || Integer.numberOfLeadingZeros(randInt) > 32; } + // Test the combination of CLZ and division by 8. + // The result of CLZ should be positive, so the division by 8 should be + // optimized to a simple right shift without rounding. @Test @IR(counts = {IRNode.COUNT_LEADING_ZEROS_I, "1", IRNode.RSHIFT_I, "1", @@ -153,6 +161,8 @@ public int clzDiv8Int(int randInt) { return Integer.numberOfLeadingZeros(randInt) / 8; } + // Test CLZ with constant long inputs. + // All CLZs in this test are expected to be optimized away. @Test @IR(failOn = IRNode.COUNT_LEADING_ZEROS_L) public int[] clzConstLongs() { @@ -225,6 +235,9 @@ public int[] clzConstLongs() { }; } + // Test the range of CLZ with random long input. + // The result of CLZ should be in range [0, 64], so CLZs in this test are + // expected to be optimized away, and the test should always return false. @Test @IR(failOn = IRNode.COUNT_LEADING_ZEROS_L) public boolean clzCompareLong(long randLong) { @@ -232,6 +245,9 @@ public boolean clzCompareLong(long randLong) { || Long.numberOfLeadingZeros(randLong) > 64; } + // Test the combination of CLZ and division by 8. + // The result of CLZ should be positive, so the division by 8 should be + // optimized to a simple right shift without rounding. @Test @IR(counts = {IRNode.COUNT_LEADING_ZEROS_L, "1", IRNode.RSHIFT_I, "1", @@ -241,6 +257,8 @@ public int clzDiv8Long(long randLong) { return Long.numberOfLeadingZeros(randLong) / 8; } + // Test CTZ with constant integer inputs. + // All CTZs in this test are expected to be optimized away. @Test @IR(failOn = IRNode.COUNT_TRAILING_ZEROS_I) public int[] ctzConstInts() { @@ -281,6 +299,9 @@ public int[] ctzConstInts() { }; } + // Test the range of CTZ with random integer input. + // The result of CTZ should be in range [0, 32], so CTZs in this test are + // expected to be optimized away, and the test should always return false. @Test @IR(failOn = IRNode.COUNT_TRAILING_ZEROS_I) public boolean ctzCompareInt(int randInt) { @@ -288,6 +309,9 @@ public boolean ctzCompareInt(int randInt) { || Integer.numberOfTrailingZeros(randInt) > 32; } + // Test the combination of CTZ and division by 8. + // The result of CTZ should be positive, so the division by 8 should be + // optimized to a simple right shift without rounding. @Test @IR(counts = {IRNode.COUNT_TRAILING_ZEROS_I, "1", IRNode.RSHIFT_I, "1", @@ -297,6 +321,8 @@ public int ctzDiv8Int(int randInt) { return Integer.numberOfTrailingZeros(randInt) / 8; } + // Test CTZ with constant long inputs. + // All CTZs in this test are expected to be optimized away. @Test @IR(failOn = IRNode.COUNT_TRAILING_ZEROS_L) public int[] ctzConstLongs() { @@ -369,6 +395,9 @@ public int[] ctzConstLongs() { }; } + // Test the range of CTZ with random long input. + // The result of CTZ should be in range [0, 64], so CTZs in this test are + // expected to be optimized away, and the test should always return false. @Test @IR(failOn = IRNode.COUNT_TRAILING_ZEROS_L) public boolean ctzCompareLong(long randLong) { @@ -376,6 +405,9 @@ public boolean ctzCompareLong(long randLong) { || Long.numberOfTrailingZeros(randLong) > 64; } + // Test the combination of CTZ and division by 8. + // The result of CTZ should be positive, so the division by 8 should be + // optimized to a simple right shift without rounding. @Test @IR(counts = {IRNode.COUNT_TRAILING_ZEROS_L, "1", IRNode.RSHIFT_I, "1", From 79394a25e6772be0fc33cf205bb940c926e4e350 Mon Sep 17 00:00:00 2001 From: MaxXSoft Date: Wed, 10 Sep 2025 14:52:33 +0800 Subject: [PATCH 15/19] Add random range tests --- .../compiler/c2/gvn/TestCountBitsRange.java | 158 +++++++++++++++--- 1 file changed, 132 insertions(+), 26 deletions(-) diff --git a/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java b/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java index 0abd3e499bdb9..7d14e5dac9be8 100644 --- a/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java +++ b/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java @@ -23,7 +23,11 @@ package compiler.c2.gvn; +import compiler.lib.generators.Generator; +import compiler.lib.generators.Generators; +import compiler.lib.generators.RestrictableGenerator; import compiler.lib.ir_framework.*; +import java.util.function.Function; import jdk.test.lib.Asserts; /* @@ -34,67 +38,81 @@ * @run driver compiler.c2.gvn.TestCountBitsRange */ public class TestCountBitsRange { + private static final Generator INTS = Generators.G.ints(); + private static final Generator LONGS = Generators.G.longs(); + + private static final int[] LIMITS_32 = new int[8]; + private static final int[] LIMITS_64 = new int[8]; + + private static final Range RANGE_INT = Range.generate(INTS); + private static final Range RANGE_LONG = Range.generate(LONGS); + + static { + var INTS_32 = Generators.G.ints().restricted(0, 32); + var INTS_64 = Generators.G.ints().restricted(0, 64); + for (int i = 0; i < 8; ++i) { + LIMITS_32[i] = INTS_32.next(); + LIMITS_64[i] = INTS_64.next(); + } + } + public static void main(String[] args) { TestFramework.run(); } @Run(test = { - "clzConstInts", "clzCompareInt", "clzDiv8Int", - "clzConstLongs", "clzCompareLong", "clzDiv8Long", - "ctzConstInts", "ctzCompareInt", "ctzDiv8Int", - "ctzConstLongs", "ctzCompareLong", "ctzDiv8Long", + "clzConstInts", "clzCompareInt", "clzDiv8Int", "clzRandLimitInt", + "clzConstLongs", "clzCompareLong", "clzDiv8Long", "clzRandLimitLong", + "ctzConstInts", "ctzCompareInt", "ctzDiv8Int", "ctzRandLimitInt", + "ctzConstLongs", "ctzCompareLong", "ctzDiv8Long", "ctzRandLimitLong", }) public void runTest() { - int randInt = RunInfo.getRandom().nextInt(); - long randLong = RunInfo.getRandom().nextLong(); + int randInt = INTS.next(); + long randLong = LONGS.next(); assertResult(randInt, randLong); } @DontCompile public void assertResult(int randInt, long randLong) { - int[] results = clzConstInts(); - Asserts.assertEQ(Integer.numberOfLeadingZeros(0), results[0]); - for (int i = 0; i < 32; ++i) { - Asserts.assertEQ(Integer.numberOfLeadingZeros(1 << i), results[i + 1]); - } + checkConstResults(clzConstInts(), x -> Integer.numberOfLeadingZeros(x.intValue())); Asserts.assertEQ(Integer.numberOfLeadingZeros(randInt) < 0 || Integer.numberOfLeadingZeros(randInt) > 32, clzCompareInt(randInt)); Asserts.assertEQ(Integer.numberOfLeadingZeros(randInt) / 8, clzDiv8Int(randInt)); + Asserts.assertEQ(clzRandLimitInterpretedInt(randInt), clzRandLimitInt(randInt)); - results = clzConstLongs(); - Asserts.assertEQ(Long.numberOfLeadingZeros(0), results[0]); - for (int i = 0; i < 64; ++i) { - Asserts.assertEQ(Long.numberOfLeadingZeros(1l << i), results[i + 1]); - } + checkConstResults(clzConstLongs(), x -> Long.numberOfLeadingZeros(x.longValue())); Asserts.assertEQ(Long.numberOfLeadingZeros(randLong) < 0 || Long.numberOfLeadingZeros(randLong) > 64, clzCompareLong(randLong)); Asserts.assertEQ(Long.numberOfLeadingZeros(randLong) / 8, clzDiv8Long(randLong)); + Asserts.assertEQ(clzRandLimitInterpretedLong(randLong), clzRandLimitLong(randLong)); - results = ctzConstInts(); - Asserts.assertEQ(Integer.numberOfTrailingZeros(0), results[0]); - for (int i = 0; i < 32; ++i) { - Asserts.assertEQ(Integer.numberOfTrailingZeros(1 << i), results[i + 1]); - } + checkConstResults(ctzConstInts(), x -> Integer.numberOfTrailingZeros(x.intValue())); Asserts.assertEQ(Integer.numberOfTrailingZeros(randInt) < 0 || Integer.numberOfTrailingZeros(randInt) > 32, ctzCompareInt(randInt)); Asserts.assertEQ(Integer.numberOfTrailingZeros(randInt) / 8, ctzDiv8Int(randInt)); + Asserts.assertEQ(ctzRandLimitInterpretedInt(randInt), ctzRandLimitInt(randInt)); - results = ctzConstLongs(); - Asserts.assertEQ(Long.numberOfTrailingZeros(0), results[0]); - for (int i = 0; i < 64; ++i) { - Asserts.assertEQ(Long.numberOfTrailingZeros(1l << i), results[i + 1]); - } + checkConstResults(ctzConstLongs(), x -> Long.numberOfTrailingZeros(x.longValue())); Asserts.assertEQ(Long.numberOfTrailingZeros(randLong) < 0 || Long.numberOfTrailingZeros(randLong) > 64, ctzCompareLong(randLong)); Asserts.assertEQ(Long.numberOfTrailingZeros(randLong) / 8, ctzDiv8Long(randLong)); + Asserts.assertEQ(ctzRandLimitInterpretedLong(randLong), ctzRandLimitLong(randLong)); + } + + @DontCompile + public void checkConstResults(int[] results, Function op) { + Asserts.assertEQ(op.apply(Long.valueOf(0)), results[0]); + for (int i = 0; i < results.length - 1; ++i) { + Asserts.assertEQ(op.apply(Long.valueOf(1l << i)), results[i + 1]); + } } // Test CLZ with constant integer inputs. @@ -161,6 +179,21 @@ public int clzDiv8Int(int randInt) { return Integer.numberOfLeadingZeros(randInt) / 8; } + // Test the output range of CLZ with random input range. + @Test + public int clzRandLimitInt(int randInt) { + randInt = RANGE_INT.clamp(randInt); + int result = Integer.numberOfLeadingZeros(randInt); + return getResultChecksum(result, LIMITS_32); + } + + @DontCompile + public int clzRandLimitInterpretedInt(int randInt) { + randInt = RANGE_INT.clamp(randInt); + int result = Integer.numberOfLeadingZeros(randInt); + return getResultChecksum(result, LIMITS_32); + } + // Test CLZ with constant long inputs. // All CLZs in this test are expected to be optimized away. @Test @@ -257,6 +290,21 @@ public int clzDiv8Long(long randLong) { return Long.numberOfLeadingZeros(randLong) / 8; } + // Test the output range of CLZ with random input range. + @Test + public int clzRandLimitLong(long randLong) { + randLong = RANGE_LONG.clamp(randLong); + int result = Long.numberOfLeadingZeros(randLong); + return getResultChecksum(result, LIMITS_64); + } + + @DontCompile + public int clzRandLimitInterpretedLong(long randLong) { + randLong = RANGE_LONG.clamp(randLong); + int result = Long.numberOfLeadingZeros(randLong); + return getResultChecksum(result, LIMITS_64); + } + // Test CTZ with constant integer inputs. // All CTZs in this test are expected to be optimized away. @Test @@ -321,6 +369,21 @@ public int ctzDiv8Int(int randInt) { return Integer.numberOfTrailingZeros(randInt) / 8; } + // Test the output range of CTZ with random input range. + @Test + public int ctzRandLimitInt(int randInt) { + randInt = RANGE_INT.clamp(randInt); + int result = Integer.numberOfTrailingZeros(randInt); + return getResultChecksum(result, LIMITS_32); + } + + @DontCompile + public int ctzRandLimitInterpretedInt(int randInt) { + randInt = RANGE_INT.clamp(randInt); + int result = Integer.numberOfTrailingZeros(randInt); + return getResultChecksum(result, LIMITS_32); + } + // Test CTZ with constant long inputs. // All CTZs in this test are expected to be optimized away. @Test @@ -416,4 +479,47 @@ public boolean ctzCompareLong(long randLong) { public int ctzDiv8Long(long randLong) { return Long.numberOfTrailingZeros(randLong) / 8; } + + // Test the output range of CTZ with random input range. + @Test + public int ctzRandLimitLong(long randLong) { + randLong = RANGE_LONG.clamp(randLong); + int result = Long.numberOfLeadingZeros(randLong); + return getResultChecksum(result, LIMITS_64); + } + + @DontCompile + public int ctzRandLimitInterpretedLong(long randLong) { + randLong = RANGE_LONG.clamp(randLong); + int result = Long.numberOfLeadingZeros(randLong); + return getResultChecksum(result, LIMITS_64); + } + + record Range>(T lo, T hi) { + Range { + if (lo.compareTo(hi) > 0) { + throw new IllegalArgumentException("lo > hi"); + } + } + + @ForceInline + T clamp(T v) { + return v.compareTo(lo) < 0 ? lo : + v.compareTo(hi) > 0 ? hi : v; + } + + static > Range generate(Generator g) { + T a = g.next(), b = g.next(); + return a.compareTo(b) < 0 ? new Range(a, b) : new Range(b, a); + } + } + + int getResultChecksum(int result, int[] LIMITS) { + int sum = 0; + for (int i = 0; i < LIMITS.length; i += 2) { + if (result < LIMITS[i]) sum += 1 << i; + if (result > LIMITS[i + 1]) sum += 1 << (i + 1); + } + return sum; + } } From f5d1e53d5c38a83b6a8976ef6247b9cfd219ee9e Mon Sep 17 00:00:00 2001 From: MaxXSoft Date: Wed, 10 Sep 2025 14:59:12 +0800 Subject: [PATCH 16/19] Remove redundant import --- test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java | 1 - 1 file changed, 1 deletion(-) diff --git a/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java b/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java index 7d14e5dac9be8..80a86bfedfd0a 100644 --- a/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java +++ b/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java @@ -25,7 +25,6 @@ import compiler.lib.generators.Generator; import compiler.lib.generators.Generators; -import compiler.lib.generators.RestrictableGenerator; import compiler.lib.ir_framework.*; import java.util.function.Function; import jdk.test.lib.Asserts; From 04f27268727a6776b7844c3fce9b3dea3015fe43 Mon Sep 17 00:00:00 2001 From: MaxXSoft Date: Tue, 14 Oct 2025 11:33:53 +0800 Subject: [PATCH 17/19] Fix constant fold --- .../compiler/c2/gvn/TestCountBitsRange.java | 129 +++++++++++++----- 1 file changed, 97 insertions(+), 32 deletions(-) diff --git a/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java b/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java index 80a86bfedfd0a..484872cd577d2 100644 --- a/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java +++ b/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java @@ -40,19 +40,48 @@ public class TestCountBitsRange { private static final Generator INTS = Generators.G.ints(); private static final Generator LONGS = Generators.G.longs(); - private static final int[] LIMITS_32 = new int[8]; - private static final int[] LIMITS_64 = new int[8]; - - private static final Range RANGE_INT = Range.generate(INTS); - private static final Range RANGE_LONG = Range.generate(LONGS); + private static final int LIMITS_32_0; + private static final int LIMITS_32_1; + private static final int LIMITS_32_2; + private static final int LIMITS_32_3; + private static final int LIMITS_32_4; + private static final int LIMITS_32_5; + private static final int LIMITS_32_6; + private static final int LIMITS_32_7; + + private static final int LIMITS_64_0; + private static final int LIMITS_64_1; + private static final int LIMITS_64_2; + private static final int LIMITS_64_3; + private static final int LIMITS_64_4; + private static final int LIMITS_64_5; + private static final int LIMITS_64_6; + private static final int LIMITS_64_7; + + private static final IntRange RANGE_INT = IntRange.generate(INTS); + private static final LongRange RANGE_LONG = LongRange.generate(LONGS); static { var INTS_32 = Generators.G.ints().restricted(0, 32); var INTS_64 = Generators.G.ints().restricted(0, 64); - for (int i = 0; i < 8; ++i) { - LIMITS_32[i] = INTS_32.next(); - LIMITS_64[i] = INTS_64.next(); - } + + LIMITS_32_0 = INTS_32.next(); + LIMITS_32_1 = INTS_32.next(); + LIMITS_32_2 = INTS_32.next(); + LIMITS_32_3 = INTS_32.next(); + LIMITS_32_4 = INTS_32.next(); + LIMITS_32_5 = INTS_32.next(); + LIMITS_32_6 = INTS_32.next(); + LIMITS_32_7 = INTS_32.next(); + + LIMITS_64_0 = INTS_64.next(); + LIMITS_64_1 = INTS_64.next(); + LIMITS_64_2 = INTS_64.next(); + LIMITS_64_3 = INTS_64.next(); + LIMITS_64_4 = INTS_64.next(); + LIMITS_64_5 = INTS_64.next(); + LIMITS_64_6 = INTS_64.next(); + LIMITS_64_7 = INTS_64.next(); } public static void main(String[] args) { @@ -180,17 +209,17 @@ public int clzDiv8Int(int randInt) { // Test the output range of CLZ with random input range. @Test - public int clzRandLimitInt(int randInt) { + public int clzRandLimitInt(int randInt) { randInt = RANGE_INT.clamp(randInt); int result = Integer.numberOfLeadingZeros(randInt); - return getResultChecksum(result, LIMITS_32); + return getResultChecksum32(result); } @DontCompile public int clzRandLimitInterpretedInt(int randInt) { randInt = RANGE_INT.clamp(randInt); int result = Integer.numberOfLeadingZeros(randInt); - return getResultChecksum(result, LIMITS_32); + return getResultChecksum32(result); } // Test CLZ with constant long inputs. @@ -294,14 +323,14 @@ public int clzDiv8Long(long randLong) { public int clzRandLimitLong(long randLong) { randLong = RANGE_LONG.clamp(randLong); int result = Long.numberOfLeadingZeros(randLong); - return getResultChecksum(result, LIMITS_64); + return getResultChecksum64(result); } @DontCompile public int clzRandLimitInterpretedLong(long randLong) { randLong = RANGE_LONG.clamp(randLong); int result = Long.numberOfLeadingZeros(randLong); - return getResultChecksum(result, LIMITS_64); + return getResultChecksum64(result); } // Test CTZ with constant integer inputs. @@ -373,14 +402,14 @@ public int ctzDiv8Int(int randInt) { public int ctzRandLimitInt(int randInt) { randInt = RANGE_INT.clamp(randInt); int result = Integer.numberOfTrailingZeros(randInt); - return getResultChecksum(result, LIMITS_32); + return getResultChecksum32(result); } @DontCompile public int ctzRandLimitInterpretedInt(int randInt) { randInt = RANGE_INT.clamp(randInt); int result = Integer.numberOfTrailingZeros(randInt); - return getResultChecksum(result, LIMITS_32); + return getResultChecksum32(result); } // Test CTZ with constant long inputs. @@ -484,41 +513,77 @@ public int ctzDiv8Long(long randLong) { public int ctzRandLimitLong(long randLong) { randLong = RANGE_LONG.clamp(randLong); int result = Long.numberOfLeadingZeros(randLong); - return getResultChecksum(result, LIMITS_64); + return getResultChecksum64(result); } @DontCompile public int ctzRandLimitInterpretedLong(long randLong) { randLong = RANGE_LONG.clamp(randLong); int result = Long.numberOfLeadingZeros(randLong); - return getResultChecksum(result, LIMITS_64); + return getResultChecksum64(result); } - record Range>(T lo, T hi) { - Range { - if (lo.compareTo(hi) > 0) { + record IntRange(int lo, int hi) { + IntRange { + if (lo > hi) { throw new IllegalArgumentException("lo > hi"); } } @ForceInline - T clamp(T v) { - return v.compareTo(lo) < 0 ? lo : - v.compareTo(hi) > 0 ? hi : v; + int clamp(int v) { + return v < lo ? lo : v > hi ? hi : v; } - static > Range generate(Generator g) { - T a = g.next(), b = g.next(); - return a.compareTo(b) < 0 ? new Range(a, b) : new Range(b, a); + static IntRange generate(Generator g) { + int a = g.next(), b = g.next(); + return a < b ? new IntRange(a, b) : new IntRange(b, a); } } - int getResultChecksum(int result, int[] LIMITS) { - int sum = 0; - for (int i = 0; i < LIMITS.length; i += 2) { - if (result < LIMITS[i]) sum += 1 << i; - if (result > LIMITS[i + 1]) sum += 1 << (i + 1); + record LongRange(long lo, long hi) { + LongRange { + if (lo > hi) { + throw new IllegalArgumentException("lo > hi"); + } + } + + @ForceInline + long clamp(long v) { + return v < lo ? lo : v > hi ? hi : v; } + + static LongRange generate(Generator g) { + long a = g.next(), b = g.next(); + return a < b ? new LongRange(a, b) : new LongRange(b, a); + } + } + + @ForceInline + int getResultChecksum32(int result) { + int sum = 0; + if (result < LIMITS_32_0) sum += 1; + if (result < LIMITS_32_1) sum += 2; + if (result < LIMITS_32_2) sum += 4; + if (result < LIMITS_32_3) sum += 8; + if (result > LIMITS_32_4) sum += 16; + if (result > LIMITS_32_5) sum += 32; + if (result > LIMITS_32_6) sum += 64; + if (result > LIMITS_32_7) sum += 128; + return sum; + } + + @ForceInline + int getResultChecksum64(int result) { + int sum = 0; + if (result < LIMITS_64_0) sum += 1; + if (result < LIMITS_64_1) sum += 2; + if (result < LIMITS_64_2) sum += 4; + if (result < LIMITS_64_3) sum += 8; + if (result > LIMITS_64_4) sum += 16; + if (result > LIMITS_64_5) sum += 32; + if (result > LIMITS_64_6) sum += 64; + if (result > LIMITS_64_7) sum += 128; return sum; } } From 9bb3f7d7be71525b9dbd4ef898b4b88d2fea9ef8 Mon Sep 17 00:00:00 2001 From: MaxXSoft Date: Wed, 15 Oct 2025 16:53:11 +0800 Subject: [PATCH 18/19] Fix include order --- src/hotspot/share/opto/countbitsnode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/share/opto/countbitsnode.cpp b/src/hotspot/share/opto/countbitsnode.cpp index 527d5d6c5e09c..1601b33ea2b6c 100644 --- a/src/hotspot/share/opto/countbitsnode.cpp +++ b/src/hotspot/share/opto/countbitsnode.cpp @@ -26,9 +26,9 @@ #include "opto/opcodes.hpp" #include "opto/phaseX.hpp" #include "opto/type.hpp" -#include "utilities/population_count.hpp" #include "utilities/count_leading_zeros.hpp" #include "utilities/count_trailing_zeros.hpp" +#include "utilities/population_count.hpp" static int count_leading_zeros_int(jint i) { return i == 0 ? BitsPerInt : count_leading_zeros(i); From 092d968d2fb54aaa59f9a28b907be5e0ddf3606c Mon Sep 17 00:00:00 2001 From: MaxXSoft Date: Wed, 29 Oct 2025 17:33:59 +0800 Subject: [PATCH 19/19] Make code more compact --- .../compiler/c2/gvn/TestCountBitsRange.java | 61 +++++++------------ 1 file changed, 21 insertions(+), 40 deletions(-) diff --git a/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java b/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java index 484872cd577d2..00aa466e82262 100644 --- a/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java +++ b/test/hotspot/jtreg/compiler/c2/gvn/TestCountBitsRange.java @@ -25,6 +25,7 @@ import compiler.lib.generators.Generator; import compiler.lib.generators.Generators; +import compiler.lib.generators.RestrictableGenerator; import compiler.lib.ir_framework.*; import java.util.function.Function; import jdk.test.lib.Asserts; @@ -40,50 +41,30 @@ public class TestCountBitsRange { private static final Generator INTS = Generators.G.ints(); private static final Generator LONGS = Generators.G.longs(); - private static final int LIMITS_32_0; - private static final int LIMITS_32_1; - private static final int LIMITS_32_2; - private static final int LIMITS_32_3; - private static final int LIMITS_32_4; - private static final int LIMITS_32_5; - private static final int LIMITS_32_6; - private static final int LIMITS_32_7; - - private static final int LIMITS_64_0; - private static final int LIMITS_64_1; - private static final int LIMITS_64_2; - private static final int LIMITS_64_3; - private static final int LIMITS_64_4; - private static final int LIMITS_64_5; - private static final int LIMITS_64_6; - private static final int LIMITS_64_7; + private static final RestrictableGenerator INTS_32 = Generators.G.ints().restricted(0, 32); + private static final RestrictableGenerator INTS_64 = Generators.G.ints().restricted(0, 64); + + private static final int LIMITS_32_0 = INTS_32.next(); + private static final int LIMITS_32_1 = INTS_32.next(); + private static final int LIMITS_32_2 = INTS_32.next(); + private static final int LIMITS_32_3 = INTS_32.next(); + private static final int LIMITS_32_4 = INTS_32.next(); + private static final int LIMITS_32_5 = INTS_32.next(); + private static final int LIMITS_32_6 = INTS_32.next(); + private static final int LIMITS_32_7 = INTS_32.next(); + + private static final int LIMITS_64_0 = INTS_64.next(); + private static final int LIMITS_64_1 = INTS_64.next(); + private static final int LIMITS_64_2 = INTS_64.next(); + private static final int LIMITS_64_3 = INTS_64.next(); + private static final int LIMITS_64_4 = INTS_64.next(); + private static final int LIMITS_64_5 = INTS_64.next(); + private static final int LIMITS_64_6 = INTS_64.next(); + private static final int LIMITS_64_7 = INTS_64.next(); private static final IntRange RANGE_INT = IntRange.generate(INTS); private static final LongRange RANGE_LONG = LongRange.generate(LONGS); - static { - var INTS_32 = Generators.G.ints().restricted(0, 32); - var INTS_64 = Generators.G.ints().restricted(0, 64); - - LIMITS_32_0 = INTS_32.next(); - LIMITS_32_1 = INTS_32.next(); - LIMITS_32_2 = INTS_32.next(); - LIMITS_32_3 = INTS_32.next(); - LIMITS_32_4 = INTS_32.next(); - LIMITS_32_5 = INTS_32.next(); - LIMITS_32_6 = INTS_32.next(); - LIMITS_32_7 = INTS_32.next(); - - LIMITS_64_0 = INTS_64.next(); - LIMITS_64_1 = INTS_64.next(); - LIMITS_64_2 = INTS_64.next(); - LIMITS_64_3 = INTS_64.next(); - LIMITS_64_4 = INTS_64.next(); - LIMITS_64_5 = INTS_64.next(); - LIMITS_64_6 = INTS_64.next(); - LIMITS_64_7 = INTS_64.next(); - } - public static void main(String[] args) { TestFramework.run(); }