From c83be331c3b64b8c7ba9e28d7dab0ee3b3e9d7f0 Mon Sep 17 00:00:00 2001 From: Jatin Bhateja Date: Wed, 3 Sep 2025 16:03:30 +0000 Subject: [PATCH 01/13] 8365205: C2: Optimize popcount value computation using knownbits --- src/hotspot/share/opto/countbitsnode.cpp | 52 ++++++++++++ src/hotspot/share/opto/countbitsnode.hpp | 2 + .../java/lang/PopCountValueTransform.java | 83 +++++++++++++++++++ 3 files changed, 137 insertions(+) create mode 100644 test/micro/org/openjdk/bench/java/lang/PopCountValueTransform.java diff --git a/src/hotspot/share/opto/countbitsnode.cpp b/src/hotspot/share/opto/countbitsnode.cpp index 4023678b51c68..1c87c5dd91bd2 100644 --- a/src/hotspot/share/opto/countbitsnode.cpp +++ b/src/hotspot/share/opto/countbitsnode.cpp @@ -26,6 +26,7 @@ #include "opto/opcodes.hpp" #include "opto/phaseX.hpp" #include "opto/type.hpp" +#include "utilities/population_count.hpp" //------------------------------Value------------------------------------------ const Type* CountLeadingZerosINode::Value(PhaseGVN* phase) const { @@ -116,3 +117,54 @@ const Type* CountTrailingZerosLNode::Value(PhaseGVN* phase) const { } return TypeInt::INT; } + +/* +Lemma 1: For a given known bits information, _lo and _hi bounds of corresponding value + range are computed using following formulas :- + - _hi = ~ZEROS + - _lo = ONES +Proof:- + - KnownBits.ZEROS and KnownBits.ONES are inferred out of common prefix of value range + delimiting bounds. + + - Thus, ~ZEROS not only include set bits in the common prefix but optimistically assumes + that all other bits not included in common prefix are also set, thereby implicitly covering + the actual set bits at runtime. + + - Consider following illustration which performs round trip translation + of a value range via knowbits information e.g. + A) Initial value range bounds to infer knownbits. + _lo = 0b11000100 + _hi = 0b11000110 + _common_prefix = 0b11000100 + _common_prefix_mask = 0b11111100 + _known_bits.ones = _lo & _common_prefix_mask = 0b11000100 + _known_bits.zeros = ~_lo & _common_prefix_mask = 0b00111000 + + B) Now transform computed knownbits back to value range. + _new_lo = _known_bits.ones = 0b11000100 + _new_hi = ~known_bits.zeros = 0b11000111 + + - We now know that ~KnownBits.ZEROS >= UB >= LB >= KnownBits.ONES + - Therefore, popcount(~ZEROS) is guaranteed to be greater than popcount(ONES). + - Also, popcount(~ZEROS) >= Res.UB >= Res.LB >= popcount(ONES) +*/ + +const Type* PopCountINode::Value(PhaseGVN* phase) const { + const Type* t = phase->type(in(1)); + if (t == Type::TOP) { + return Type::TOP; + } + KnownBits bits = t->isa_int()->_bits; + return TypeInt::make(population_count(bits._ones), population_count(~bits._zeros), Type::WidenMax); + +} + +const Type* PopCountLNode::Value(PhaseGVN* phase) const { + const Type* t = phase->type(in(1)); + if (t == Type::TOP) { + return Type::TOP; + } + KnownBits bits = t->isa_long()->_bits; + return TypeInt::make(population_count(bits._ones), population_count(~bits._zeros), Type::WidenMax); +} diff --git a/src/hotspot/share/opto/countbitsnode.hpp b/src/hotspot/share/opto/countbitsnode.hpp index 410d51298829b..35465b1835be6 100644 --- a/src/hotspot/share/opto/countbitsnode.hpp +++ b/src/hotspot/share/opto/countbitsnode.hpp @@ -80,6 +80,7 @@ class PopCountINode : public CountBitsNode { public: PopCountINode(Node* in1) : CountBitsNode(in1) {} virtual int Opcode() const; + virtual const Type* Value(PhaseGVN* phase) const; }; //---------- PopCountLNode ----------------------------------------------------- @@ -88,6 +89,7 @@ class PopCountLNode : public CountBitsNode { public: PopCountLNode(Node* in1) : CountBitsNode(in1) {} virtual int Opcode() const; + virtual const Type* Value(PhaseGVN* phase) const; }; diff --git a/test/micro/org/openjdk/bench/java/lang/PopCountValueTransform.java b/test/micro/org/openjdk/bench/java/lang/PopCountValueTransform.java new file mode 100644 index 0000000000000..cb6f6bac95677 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/PopCountValueTransform.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 org.openjdk.bench.java.lang; + +import org.openjdk.jmh.annotations.*; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.SECONDS) +@State(Scope.Thread) + +public class PopCountValueTransform { + + public int lower_bound = 0; + + public int upper_bound = 10000; + + @Benchmark + public int StockKernelInt() { + int res = 0; + for (int i = lower_bound; i < upper_bound; i++) { + int constrained_i = Integer.min(1179, Integer.max(1169, i)); + res += constrained_i; + } + return res; + } + + @Benchmark + public int LogicFoldingKerenlInt() { + int res = 0; + for (int i = lower_bound; i < upper_bound; i++) { + int constrained_i = Integer.min(1179, Integer.max(1169, i)); + if (Integer.bitCount(constrained_i) > 20 || Integer.bitCount(constrained_i) < 3) { + throw new AssertionError("Uncommon trap"); + } + res += constrained_i; + } + return res; + } + + @Benchmark + public long StockKernelLong() { + long res = 0; + for (int i = lower_bound; i < upper_bound; i++) { + long constrained_i = Long.min(1179, Long.max(1169, i)); + res += constrained_i; + } + return res; + } + + @Benchmark + public long LogicFoldingKerenLong() { + long res = 0; + for (int i = lower_bound; i < upper_bound; i++) { + long constrained_i = Long.min(1179L, Long.max(1169L, i)); + if (Long.bitCount(constrained_i) > 20L || Long.bitCount(constrained_i) < 3L) { + throw new AssertionError("Uncommon trap"); + } + res += constrained_i; + } + return res; + } +} From a68fbc08c31886001787df77a841851de0585fc9 Mon Sep 17 00:00:00 2001 From: Jatin Bhateja Date: Fri, 5 Sep 2025 22:37:41 +0530 Subject: [PATCH 02/13] New IR test addition and review resolutions --- src/hotspot/share/opto/countbitsnode.cpp | 11 +- .../TestPopCountValueTransforms.java | 150 ++++++++++++++++++ .../compiler/lib/ir_framework/IRNode.java | 5 + .../java/lang/PopCountValueTransform.java | 15 +- 4 files changed, 166 insertions(+), 15 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/intrinsics/TestPopCountValueTransforms.java diff --git a/src/hotspot/share/opto/countbitsnode.cpp b/src/hotspot/share/opto/countbitsnode.cpp index 1c87c5dd91bd2..fd3c4e5f22c5b 100644 --- a/src/hotspot/share/opto/countbitsnode.cpp +++ b/src/hotspot/share/opto/countbitsnode.cpp @@ -127,9 +127,8 @@ Proof:- - KnownBits.ZEROS and KnownBits.ONES are inferred out of common prefix of value range delimiting bounds. - - Thus, ~ZEROS not only include set bits in the common prefix but optimistically assumes - that all other bits not included in common prefix are also set, thereby implicitly covering - the actual set bits at runtime. + - Thus, ~KnownBits.ZEROS not only include set bits in the common prefix but optimistically assumes + that all other bits not included in common prefix are also set. - Consider following illustration which performs round trip translation of a value range via knowbits information e.g. @@ -141,13 +140,13 @@ Proof:- _known_bits.ones = _lo & _common_prefix_mask = 0b11000100 _known_bits.zeros = ~_lo & _common_prefix_mask = 0b00111000 - B) Now transform computed knownbits back to value range. + B) Now, transform computed knownbits back to value range. _new_lo = _known_bits.ones = 0b11000100 _new_hi = ~known_bits.zeros = 0b11000111 - We now know that ~KnownBits.ZEROS >= UB >= LB >= KnownBits.ONES - - Therefore, popcount(~ZEROS) is guaranteed to be greater than popcount(ONES). - - Also, popcount(~ZEROS) >= Res.UB >= Res.LB >= popcount(ONES) + - Therefore, popcount(ONES) and popcount(~ZEROS) can safely be assumed as the upper and lower + bounds of the result value range. */ const Type* PopCountINode::Value(PhaseGVN* phase) const { diff --git a/test/hotspot/jtreg/compiler/intrinsics/TestPopCountValueTransforms.java b/test/hotspot/jtreg/compiler/intrinsics/TestPopCountValueTransforms.java new file mode 100644 index 0000000000000..2f709a027a352 --- /dev/null +++ b/test/hotspot/jtreg/compiler/intrinsics/TestPopCountValueTransforms.java @@ -0,0 +1,150 @@ +/* + * 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. + */ + +/** + * @test + * @bug 8365205 + * @summary C2: Optimize popcount value computation using knownbits + * @library /test/lib / + * @run driver compiler.intrinsics.TestPopCountValueTransforms + */ +package compiler.intrinsics; + +import compiler.lib.ir_framework.*; +import compiler.lib.generators.*; +import compiler.lib.verify.*; +import static compiler.lib.generators.Generators.*; +import jdk.test.lib.Utils; + +public class TestPopCountValueTransforms { + int [] inI1; + int [] inI2; + long [] inL1; + long [] inL2; + + @Test + @IR(counts = {IRNode.POPCOUNT_L, " 0 "}) + public long testPopCountElisionLong1(long num) { + num = Math.clamp(num, 0xF000F000L, 0xF000F0FFL); + // PopCount ValueRange = {lo:8, hi:16} + if (Long.bitCount(num) < 8 || Long.bitCount(num) > 16) { + return 0; + } + return 1; + } + + @Run(test = {"testPopCountElisionLong1"}, mode = RunMode.STANDALONE) + public void runPopCountElisionLong1() { + long res = 1; + for (int i = 0; i < inL1.length; i++) { + res &= testPopCountElisionLong1(inL1[i]); + } + Verify.checkEQ(res, 1L); + } + + @Test + @IR(counts = {IRNode.POPCOUNT_L, " >0 "}) + public long testPopCountElisionLong2(long num) { + num = Math.clamp(num, 0x3L, 0xFFFFL); + // PopCount ValueRange = {lo:0, hi:16} + if (Long.bitCount(num) >= 0 && Long.bitCount(num) <= 11) { + return 0; + } + return 1; + } + + @Run(test = {"testPopCountElisionLong2"}, mode = RunMode.STANDALONE) + public void runPopCountElisionLong2() { + long res = 0; + for (int i = 0; i < inL2.length; i++) { + res |= testPopCountElisionLong2(inL2[i]); + } + Verify.checkEQ(res, 0L); + } + + @Test + @IR(counts = {IRNode.POPCOUNT_I, " 0 "}) + public int testPopCountElisionInt1(int num) { + // PopCount ValueRange = {lo:11, hi:15} + num = Math.clamp(num, 0xFE00F000, 0xFE00F00F); + if (Integer.bitCount(num) < 11 || Integer.bitCount(num) > 15) { + return 0; + } + return 1; + } + + @Run(test = {"testPopCountElisionInt1"}, mode = RunMode.STANDALONE) + public void runPopCountElisionInt1() { + int res = 1; + for (int i = 0; i < inI1.length; i++) { + res &= testPopCountElisionInt1(inI1[i]); + } + Verify.checkEQ(res, 1); + } + + @Test + @IR(counts = {IRNode.POPCOUNT_I, " >0 "}) + public int testPopCountElisionInt2(int num) { + // PopCount ValueRange = {lo:0, hi:8} + num = Math.clamp(num, 0x3, 0xFF); + if (Integer.bitCount(num) >= 0 && Integer.bitCount(num) <= 5) { + return 0; + } + return 1; + } + + @Run(test = {"testPopCountElisionInt2"}, mode = RunMode.STANDALONE) + public void runPopCountElisionInt2() { + int res = 0; + for (int i = 0; i < inI2.length; i++) { + res |= testPopCountElisionInt2(inI2[i]); + } + Verify.checkEQ(res, 0); + } + + static final int SIZE = 4096; + + public TestPopCountValueTransforms() { + inL1 = new long[SIZE]; + G.fill(G.longs(), inL1); + + inL2 = new long[SIZE]; + Generator genL = G.uniformLongs(0x3L, 0xFFCL); + for (int i = 0; i < SIZE; i++) { + inL2[i] = genL.next(); + } + + inI1 = new int[SIZE]; + G.fill(G.ints(), inI1); + + inI2 = new int[SIZE]; + Generator genI = G.uniformInts(0x3, 0x1F); + for (int i = 0; i < SIZE; i++) { + inI2[i] = genI.next(); + } + } + + public static void main(String[] args) { + TestFramework.runWithFlags("-XX:-TieredCompilation", "-XX:CompileThresholdScaling=0.2"); + } +} diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java index 16c6d99a64f33..16c9bd0a55436 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java @@ -1610,6 +1610,11 @@ public class IRNode { beforeMatchingNameRegex(PHI, "Phi"); } + public static final String POPCOUNT_I = PREFIX + "POPCOUNT_I" + POSTFIX; + static { + beforeMatchingNameRegex(POPCOUNT_I, "PopCountI"); + } + public static final String POPCOUNT_L = PREFIX + "POPCOUNT_L" + POSTFIX; static { beforeMatchingNameRegex(POPCOUNT_L, "PopCountL"); diff --git a/test/micro/org/openjdk/bench/java/lang/PopCountValueTransform.java b/test/micro/org/openjdk/bench/java/lang/PopCountValueTransform.java index cb6f6bac95677..5325d279ac55a 100644 --- a/test/micro/org/openjdk/bench/java/lang/PopCountValueTransform.java +++ b/test/micro/org/openjdk/bench/java/lang/PopCountValueTransform.java @@ -28,18 +28,15 @@ @BenchmarkMode(Mode.Throughput) @OutputTimeUnit(TimeUnit.SECONDS) @State(Scope.Thread) - public class PopCountValueTransform { - public int lower_bound = 0; - public int upper_bound = 10000; @Benchmark public int StockKernelInt() { int res = 0; for (int i = lower_bound; i < upper_bound; i++) { - int constrained_i = Integer.min(1179, Integer.max(1169, i)); + int constrained_i = i & 0xFFFF; res += constrained_i; } return res; @@ -49,8 +46,8 @@ public int StockKernelInt() { public int LogicFoldingKerenlInt() { int res = 0; for (int i = lower_bound; i < upper_bound; i++) { - int constrained_i = Integer.min(1179, Integer.max(1169, i)); - if (Integer.bitCount(constrained_i) > 20 || Integer.bitCount(constrained_i) < 3) { + int constrained_i = i & 0xFFFF; + if (Integer.bitCount(constrained_i) > 16) { throw new AssertionError("Uncommon trap"); } res += constrained_i; @@ -62,7 +59,7 @@ public int LogicFoldingKerenlInt() { public long StockKernelLong() { long res = 0; for (int i = lower_bound; i < upper_bound; i++) { - long constrained_i = Long.min(1179, Long.max(1169, i)); + long constrained_i = i & 0xFFFFFFL; res += constrained_i; } return res; @@ -72,8 +69,8 @@ public long StockKernelLong() { public long LogicFoldingKerenLong() { long res = 0; for (int i = lower_bound; i < upper_bound; i++) { - long constrained_i = Long.min(1179L, Long.max(1169L, i)); - if (Long.bitCount(constrained_i) > 20L || Long.bitCount(constrained_i) < 3L) { + long constrained_i = i & 0xFFFFFFL; + if (Long.bitCount(constrained_i) > 24) { throw new AssertionError("Uncommon trap"); } res += constrained_i; From 52ae6bc89cfa335bb03276575261835fca70ec0b Mon Sep 17 00:00:00 2001 From: Jatin Bhateja Date: Fri, 5 Sep 2025 22:43:10 +0530 Subject: [PATCH 03/13] Update countbitsnode.cpp --- src/hotspot/share/opto/countbitsnode.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/hotspot/share/opto/countbitsnode.cpp b/src/hotspot/share/opto/countbitsnode.cpp index fd3c4e5f22c5b..4b6fbf1fa7a2a 100644 --- a/src/hotspot/share/opto/countbitsnode.cpp +++ b/src/hotspot/share/opto/countbitsnode.cpp @@ -119,19 +119,19 @@ const Type* CountTrailingZerosLNode::Value(PhaseGVN* phase) const { } /* -Lemma 1: For a given known bits information, _lo and _hi bounds of corresponding value - range are computed using following formulas :- +Lemma 1: For a given known bits information, _lo and _hi bounds of the corresponding value + range is computed using the following formulas:- - _hi = ~ZEROS - _lo = ONES Proof:- - - KnownBits.ZEROS and KnownBits.ONES are inferred out of common prefix of value range + - KnownBits.ZEROS and KnownBits.ONES are inferred out of the common prefix of the value range delimiting bounds. - - Thus, ~KnownBits.ZEROS not only include set bits in the common prefix but optimistically assumes - that all other bits not included in common prefix are also set. + - Thus, ~KnownBits.ZEROS not only includes set bits in the common prefix but also optimistically assumes + that all other bits not included in the common prefix are also set. - - Consider following illustration which performs round trip translation - of a value range via knowbits information e.g. + - Consider the following illustration, which performs round-trip translation + of a value range via knowbits information, e.g. A) Initial value range bounds to infer knownbits. _lo = 0b11000100 _hi = 0b11000110 @@ -140,7 +140,7 @@ Proof:- _known_bits.ones = _lo & _common_prefix_mask = 0b11000100 _known_bits.zeros = ~_lo & _common_prefix_mask = 0b00111000 - B) Now, transform computed knownbits back to value range. + B) Now, transform the computed knownbits back to the value range. _new_lo = _known_bits.ones = 0b11000100 _new_hi = ~known_bits.zeros = 0b11000111 @@ -148,7 +148,6 @@ Proof:- - Therefore, popcount(ONES) and popcount(~ZEROS) can safely be assumed as the upper and lower bounds of the result value range. */ - const Type* PopCountINode::Value(PhaseGVN* phase) const { const Type* t = phase->type(in(1)); if (t == Type::TOP) { From 36ecb5d186e711b40bf5de9cc6e7a1dd99f6850b Mon Sep 17 00:00:00 2001 From: Jatin Bhateja Date: Wed, 10 Sep 2025 17:52:07 +0530 Subject: [PATCH 04/13] Update src/hotspot/share/opto/countbitsnode.cpp Co-authored-by: Emanuel Peter --- src/hotspot/share/opto/countbitsnode.cpp | 50 +++++++++--------------- 1 file changed, 19 insertions(+), 31 deletions(-) diff --git a/src/hotspot/share/opto/countbitsnode.cpp b/src/hotspot/share/opto/countbitsnode.cpp index 4b6fbf1fa7a2a..d37acdad73e62 100644 --- a/src/hotspot/share/opto/countbitsnode.cpp +++ b/src/hotspot/share/opto/countbitsnode.cpp @@ -117,37 +117,25 @@ const Type* CountTrailingZerosLNode::Value(PhaseGVN* phase) const { } return TypeInt::INT; } - -/* -Lemma 1: For a given known bits information, _lo and _hi bounds of the corresponding value - range is computed using the following formulas:- - - _hi = ~ZEROS - - _lo = ONES -Proof:- - - KnownBits.ZEROS and KnownBits.ONES are inferred out of the common prefix of the value range - delimiting bounds. - - - Thus, ~KnownBits.ZEROS not only includes set bits in the common prefix but also optimistically assumes - that all other bits not included in the common prefix are also set. - - - Consider the following illustration, which performs round-trip translation - of a value range via knowbits information, e.g. - A) Initial value range bounds to infer knownbits. - _lo = 0b11000100 - _hi = 0b11000110 - _common_prefix = 0b11000100 - _common_prefix_mask = 0b11111100 - _known_bits.ones = _lo & _common_prefix_mask = 0b11000100 - _known_bits.zeros = ~_lo & _common_prefix_mask = 0b00111000 - - B) Now, transform the computed knownbits back to the value range. - _new_lo = _known_bits.ones = 0b11000100 - _new_hi = ~known_bits.zeros = 0b11000111 - - - We now know that ~KnownBits.ZEROS >= UB >= LB >= KnownBits.ONES - - Therefore, popcount(ONES) and popcount(~ZEROS) can safely be assumed as the upper and lower - bounds of the result value range. -*/ +// We use the KnownBits information from the integer types to derive how many one bits +// we have at least and at most. +// From the definition of KnownBits, we know: +// zeros: Indicates which bits must be 0: ones[i] =1 -> t[i]=0 +// ones: Indicates which bits must be 1: zeros[i]=1 -> t[i]=1 +// +// From this, we derive: +// numer_of_zeros_in_t >= pop_count(zeros) +// -> number_of_ones_in_t <= bits_per_type - pop_count(zeros) = pop_count(~zeros) +// number_of_ones_in_t >= pop_count(ones) +// +// By definition: +// pop_count(t) = number_of_ones_in_t +// +// It follows: +// pop_count(ones) <= pop_count(t) <= pop_count(~zeros) +// +// Note: signed _lo and _hi, as well as unsigned _ulo and _uhi bounds of the integer types +// are already reflected in the KnownBits information, see TypeInt / TypeLong definitions. const Type* PopCountINode::Value(PhaseGVN* phase) const { const Type* t = phase->type(in(1)); if (t == Type::TOP) { From f1095b581c4e515179d7f496ff42e260df503185 Mon Sep 17 00:00:00 2001 From: Jatin Bhateja Date: Wed, 10 Sep 2025 19:49:41 +0530 Subject: [PATCH 05/13] review resoultions --- .../TestPopCountValueTransforms.java | 16 ++++++++------- .../java/lang/PopCountValueTransform.java | 20 ------------------- 2 files changed, 9 insertions(+), 27 deletions(-) diff --git a/test/hotspot/jtreg/compiler/intrinsics/TestPopCountValueTransforms.java b/test/hotspot/jtreg/compiler/intrinsics/TestPopCountValueTransforms.java index 2f709a027a352..6bd965b431cda 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/TestPopCountValueTransforms.java +++ b/test/hotspot/jtreg/compiler/intrinsics/TestPopCountValueTransforms.java @@ -53,7 +53,8 @@ public long testPopCountElisionLong1(long num) { return 1; } - @Run(test = {"testPopCountElisionLong1"}, mode = RunMode.STANDALONE) + @Warmup(10000) + @Run(test = {"testPopCountElisionLong1"}) public void runPopCountElisionLong1() { long res = 1; for (int i = 0; i < inL1.length; i++) { @@ -63,7 +64,7 @@ public void runPopCountElisionLong1() { } @Test - @IR(counts = {IRNode.POPCOUNT_L, " >0 "}) + @IR(counts = {IRNode.POPCOUNT_L, " 1 "}) public long testPopCountElisionLong2(long num) { num = Math.clamp(num, 0x3L, 0xFFFFL); // PopCount ValueRange = {lo:0, hi:16} @@ -73,7 +74,8 @@ public long testPopCountElisionLong2(long num) { return 1; } - @Run(test = {"testPopCountElisionLong2"}, mode = RunMode.STANDALONE) + @Warmup(10000) + @Run(test = {"testPopCountElisionLong2"}) public void runPopCountElisionLong2() { long res = 0; for (int i = 0; i < inL2.length; i++) { @@ -93,7 +95,7 @@ public int testPopCountElisionInt1(int num) { return 1; } - @Run(test = {"testPopCountElisionInt1"}, mode = RunMode.STANDALONE) + @Run(test = {"testPopCountElisionInt1"}) public void runPopCountElisionInt1() { int res = 1; for (int i = 0; i < inI1.length; i++) { @@ -103,7 +105,7 @@ public void runPopCountElisionInt1() { } @Test - @IR(counts = {IRNode.POPCOUNT_I, " >0 "}) + @IR(counts = {IRNode.POPCOUNT_I, " 1 "}) public int testPopCountElisionInt2(int num) { // PopCount ValueRange = {lo:0, hi:8} num = Math.clamp(num, 0x3, 0xFF); @@ -113,7 +115,7 @@ public int testPopCountElisionInt2(int num) { return 1; } - @Run(test = {"testPopCountElisionInt2"}, mode = RunMode.STANDALONE) + @Run(test = {"testPopCountElisionInt2"}) public void runPopCountElisionInt2() { int res = 0; for (int i = 0; i < inI2.length; i++) { @@ -145,6 +147,6 @@ public TestPopCountValueTransforms() { } public static void main(String[] args) { - TestFramework.runWithFlags("-XX:-TieredCompilation", "-XX:CompileThresholdScaling=0.2"); + TestFramework.run(); } } diff --git a/test/micro/org/openjdk/bench/java/lang/PopCountValueTransform.java b/test/micro/org/openjdk/bench/java/lang/PopCountValueTransform.java index 5325d279ac55a..c896c7504a653 100644 --- a/test/micro/org/openjdk/bench/java/lang/PopCountValueTransform.java +++ b/test/micro/org/openjdk/bench/java/lang/PopCountValueTransform.java @@ -32,16 +32,6 @@ public class PopCountValueTransform { public int lower_bound = 0; public int upper_bound = 10000; - @Benchmark - public int StockKernelInt() { - int res = 0; - for (int i = lower_bound; i < upper_bound; i++) { - int constrained_i = i & 0xFFFF; - res += constrained_i; - } - return res; - } - @Benchmark public int LogicFoldingKerenlInt() { int res = 0; @@ -55,16 +45,6 @@ public int LogicFoldingKerenlInt() { return res; } - @Benchmark - public long StockKernelLong() { - long res = 0; - for (int i = lower_bound; i < upper_bound; i++) { - long constrained_i = i & 0xFFFFFFL; - res += constrained_i; - } - return res; - } - @Benchmark public long LogicFoldingKerenLong() { long res = 0; From 9e3957deebbfc12ede8109b1ea6b5a0e90cb1db4 Mon Sep 17 00:00:00 2001 From: Jatin Bhateja Date: Wed, 10 Sep 2025 19:55:27 +0530 Subject: [PATCH 06/13] Update TestPopCountValueTransforms.java --- .../jtreg/compiler/intrinsics/TestPopCountValueTransforms.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/intrinsics/TestPopCountValueTransforms.java b/test/hotspot/jtreg/compiler/intrinsics/TestPopCountValueTransforms.java index 6bd965b431cda..a98a2f41220e5 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/TestPopCountValueTransforms.java +++ b/test/hotspot/jtreg/compiler/intrinsics/TestPopCountValueTransforms.java @@ -53,7 +53,6 @@ public long testPopCountElisionLong1(long num) { return 1; } - @Warmup(10000) @Run(test = {"testPopCountElisionLong1"}) public void runPopCountElisionLong1() { long res = 1; @@ -74,7 +73,6 @@ public long testPopCountElisionLong2(long num) { return 1; } - @Warmup(10000) @Run(test = {"testPopCountElisionLong2"}) public void runPopCountElisionLong2() { long res = 0; From a7f9b79c7d9941b48913536ebfb2f09d64383763 Mon Sep 17 00:00:00 2001 From: Jatin Bhateja Date: Thu, 11 Sep 2025 17:42:08 +0530 Subject: [PATCH 07/13] Adding random bound test point --- .../TestPopCountValueTransforms.java | 61 ++++++++++++++++++- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/intrinsics/TestPopCountValueTransforms.java b/test/hotspot/jtreg/compiler/intrinsics/TestPopCountValueTransforms.java index a98a2f41220e5..df30998d314bc 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/TestPopCountValueTransforms.java +++ b/test/hotspot/jtreg/compiler/intrinsics/TestPopCountValueTransforms.java @@ -34,7 +34,6 @@ import compiler.lib.generators.*; import compiler.lib.verify.*; import static compiler.lib.generators.Generators.*; -import jdk.test.lib.Utils; public class TestPopCountValueTransforms { int [] inI1; @@ -42,6 +41,20 @@ public class TestPopCountValueTransforms { long [] inL1; long [] inL2; + static final int SIZE = 4096; + + static int rand_numI = G.uniformInts(0, Integer.MAX_VALUE).next(); + static final int rand_bndI1 = G.uniformInts(0xF, Integer.MAX_VALUE).next(); + static final int rand_bndI2 = G.uniformInts(0xFF, Integer.MAX_VALUE).next(); + static final int rand_popcI1 = G.uniformInts(0, 2).next(); + static final int rand_popcI2 = G.uniformInts(10, 20).next(); + + static long rand_numL = G.uniformLongs(0, Long.MAX_VALUE).next(); + static final long rand_bndL1 = G.uniformLongs(0xFL, Long.MAX_VALUE).next(); + static final long rand_bndL2 = G.uniformLongs(0xFFL, Long.MAX_VALUE).next(); + static final long rand_popcL1 = G.uniformLongs(0, 3).next(); + static final long rand_popcL2 = G.uniformLongs(20, 40).next(); + @Test @IR(counts = {IRNode.POPCOUNT_L, " 0 "}) public long testPopCountElisionLong1(long num) { @@ -122,7 +135,51 @@ public void runPopCountElisionInt2() { Verify.checkEQ(res, 0); } - static final int SIZE = 4096; + @Test + public int testPopCountRandomInt() { + int num = Math.clamp(rand_numI, Math.min(rand_bndI1, rand_bndI2), Math.max(rand_bndI1, rand_bndI2)); + if (Integer.bitCount(num) >= rand_popcI1 && Integer.bitCount(num) < rand_popcI2) { + return 1; + } else { + return -1; + } + } + + @Check(test = "testPopCountRandomInt") + public void checkPopCountRandomInt(int res) { + if (res == 1) { + int exp = 0; + int num = Math.clamp(rand_numI, Math.min(rand_bndI1, rand_bndI2), Math.max(rand_bndI1, rand_bndI2)); + while(num != 0) { + num &= (num - 1); + exp++; + } + Verify.checkEQ(exp >= rand_popcI1 && exp < rand_popcI2, true); + } + } + + @Test + public long testPopCountRandomLong() { + long num = Math.clamp(rand_numL, Math.min(rand_bndL1, rand_bndL2), Math.max(rand_bndL1, rand_bndL2)); + if (Long.bitCount(num) >= rand_popcL1 && Long.bitCount(num) < rand_popcL2) { + return 1L; + } else { + return -1L; + } + } + + @Check(test = "testPopCountRandomLong") + public void checkPopCountRandomLong(long res) { + if (res == 1) { + int exp = 0; + long num = Math.clamp(rand_numL, Math.min(rand_bndL1, rand_bndL2), Math.max(rand_bndL1, rand_bndL2)); + while(num != 0) { + num &= (num - 1L); + exp++; + } + Verify.checkEQ(exp >= rand_popcL1 && exp < rand_popcL2, true); + } + } public TestPopCountValueTransforms() { inL1 = new long[SIZE]; From 278f1dc847422b1c9374f312a3a9f2e122cacde9 Mon Sep 17 00:00:00 2001 From: Jatin Bhateja Date: Mon, 15 Sep 2025 08:01:01 +0000 Subject: [PATCH 08/13] Extending the random ranges --- .../TestPopCountValueTransforms.java | 50 +++++++++---------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/test/hotspot/jtreg/compiler/intrinsics/TestPopCountValueTransforms.java b/test/hotspot/jtreg/compiler/intrinsics/TestPopCountValueTransforms.java index df30998d314bc..4cb3cbf51a7b5 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/TestPopCountValueTransforms.java +++ b/test/hotspot/jtreg/compiler/intrinsics/TestPopCountValueTransforms.java @@ -43,17 +43,17 @@ public class TestPopCountValueTransforms { static final int SIZE = 4096; - static int rand_numI = G.uniformInts(0, Integer.MAX_VALUE).next(); - static final int rand_bndI1 = G.uniformInts(0xF, Integer.MAX_VALUE).next(); - static final int rand_bndI2 = G.uniformInts(0xFF, Integer.MAX_VALUE).next(); - static final int rand_popcI1 = G.uniformInts(0, 2).next(); - static final int rand_popcI2 = G.uniformInts(10, 20).next(); - - static long rand_numL = G.uniformLongs(0, Long.MAX_VALUE).next(); - static final long rand_bndL1 = G.uniformLongs(0xFL, Long.MAX_VALUE).next(); - static final long rand_bndL2 = G.uniformLongs(0xFFL, Long.MAX_VALUE).next(); - static final long rand_popcL1 = G.uniformLongs(0, 3).next(); - static final long rand_popcL2 = G.uniformLongs(20, 40).next(); + static int rand_numI = G.uniformInts(Integer.MIN_VALUE, Integer.MAX_VALUE).next(); + static final int rand_bndI1 = G.uniformInts(-0xFF, 0xFF).next(); + static final int rand_bndI2 = G.uniformInts(-0xFFFFF, 0xFFFFF).next(); + static final int rand_popcI1 = G.uniformInts(0, 4).next(); + static final int rand_popcI2 = G.uniformInts(0, 32).next(); + + static long rand_numL = G.uniformLongs(Long.MIN_VALUE, Long.MAX_VALUE).next(); + static final long rand_bndL1 = G.uniformLongs(-0xFFL, 0xFFL).next(); + static final long rand_bndL2 = G.uniformLongs(-0xFFFFFFL, 0xFFFFFF).next(); + static final long rand_popcL1 = G.uniformLongs(0, 4).next(); + static final long rand_popcL2 = G.uniformLongs(0, 32).next(); @Test @IR(counts = {IRNode.POPCOUNT_L, " 0 "}) @@ -147,15 +147,13 @@ public int testPopCountRandomInt() { @Check(test = "testPopCountRandomInt") public void checkPopCountRandomInt(int res) { - if (res == 1) { - int exp = 0; - int num = Math.clamp(rand_numI, Math.min(rand_bndI1, rand_bndI2), Math.max(rand_bndI1, rand_bndI2)); - while(num != 0) { - num &= (num - 1); - exp++; - } - Verify.checkEQ(exp >= rand_popcI1 && exp < rand_popcI2, true); + int exp = 0; + int num = Math.clamp(rand_numI, Math.min(rand_bndI1, rand_bndI2), Math.max(rand_bndI1, rand_bndI2)); + while(num != 0) { + num &= (num - 1); + exp++; } + Verify.checkEQ(exp >= rand_popcI1 && exp < rand_popcI2 ? 1 : -1, res); } @Test @@ -170,15 +168,13 @@ public long testPopCountRandomLong() { @Check(test = "testPopCountRandomLong") public void checkPopCountRandomLong(long res) { - if (res == 1) { - int exp = 0; - long num = Math.clamp(rand_numL, Math.min(rand_bndL1, rand_bndL2), Math.max(rand_bndL1, rand_bndL2)); - while(num != 0) { - num &= (num - 1L); - exp++; - } - Verify.checkEQ(exp >= rand_popcL1 && exp < rand_popcL2, true); + int exp = 0; + long num = Math.clamp(rand_numL, Math.min(rand_bndL1, rand_bndL2), Math.max(rand_bndL1, rand_bndL2)); + while(num != 0) { + num &= (num - 1L); + exp++; } + Verify.checkEQ(exp >= rand_popcL1 && exp < rand_popcL2 ? 1L : -1L, res); } public TestPopCountValueTransforms() { From 367622bf32063037c46525e24ff0710791b5ce18 Mon Sep 17 00:00:00 2001 From: Jatin Bhateja Date: Fri, 19 Sep 2025 08:13:40 +0000 Subject: [PATCH 09/13] Review comments resolutions --- .../TestPopCountValueTransforms.java | 47 ++++++++++++------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/test/hotspot/jtreg/compiler/intrinsics/TestPopCountValueTransforms.java b/test/hotspot/jtreg/compiler/intrinsics/TestPopCountValueTransforms.java index 4cb3cbf51a7b5..618521ab5aeef 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/TestPopCountValueTransforms.java +++ b/test/hotspot/jtreg/compiler/intrinsics/TestPopCountValueTransforms.java @@ -43,16 +43,14 @@ public class TestPopCountValueTransforms { static final int SIZE = 4096; - static int rand_numI = G.uniformInts(Integer.MIN_VALUE, Integer.MAX_VALUE).next(); - static final int rand_bndI1 = G.uniformInts(-0xFF, 0xFF).next(); - static final int rand_bndI2 = G.uniformInts(-0xFFFFF, 0xFFFFF).next(); - static final int rand_popcI1 = G.uniformInts(0, 4).next(); + static final int rand_bndI1 = G.ints().next(); + static final int rand_bndI2 = G.ints().next(); + static final int rand_popcI1 = G.uniformInts(0, 32).next(); static final int rand_popcI2 = G.uniformInts(0, 32).next(); - static long rand_numL = G.uniformLongs(Long.MIN_VALUE, Long.MAX_VALUE).next(); - static final long rand_bndL1 = G.uniformLongs(-0xFFL, 0xFFL).next(); - static final long rand_bndL2 = G.uniformLongs(-0xFFFFFFL, 0xFFFFFF).next(); - static final long rand_popcL1 = G.uniformLongs(0, 4).next(); + static final long rand_bndL1 = G.longs().next(); + static final long rand_bndL2 = G.longs().next(); + static final long rand_popcL1 = G.uniformLongs(0, 32).next(); static final long rand_popcL2 = G.uniformLongs(0, 32).next(); @Test @@ -136,17 +134,18 @@ public void runPopCountElisionInt2() { } @Test - public int testPopCountRandomInt() { + public void testPopCountRandomInt(int rand_numI) { + int res = 0; int num = Math.clamp(rand_numI, Math.min(rand_bndI1, rand_bndI2), Math.max(rand_bndI1, rand_bndI2)); if (Integer.bitCount(num) >= rand_popcI1 && Integer.bitCount(num) < rand_popcI2) { - return 1; + res = 1; } else { - return -1; + res = -1; } + checkPopCountRandomInt(rand_numI, res); } - @Check(test = "testPopCountRandomInt") - public void checkPopCountRandomInt(int res) { + public void checkPopCountRandomInt(int rand_numI, int res) { int exp = 0; int num = Math.clamp(rand_numI, Math.min(rand_bndI1, rand_bndI2), Math.max(rand_bndI1, rand_bndI2)); while(num != 0) { @@ -156,18 +155,25 @@ public void checkPopCountRandomInt(int res) { Verify.checkEQ(exp >= rand_popcI1 && exp < rand_popcI2 ? 1 : -1, res); } + @Run(test="testPopCountRandomInt") + public void runPopCountRandomInt() { + testPopCountRandomInt(G.ints().next()); + } + + @Test - public long testPopCountRandomLong() { + public void testPopCountRandomLong(long rand_numL) { + long res = 0; long num = Math.clamp(rand_numL, Math.min(rand_bndL1, rand_bndL2), Math.max(rand_bndL1, rand_bndL2)); if (Long.bitCount(num) >= rand_popcL1 && Long.bitCount(num) < rand_popcL2) { - return 1L; + res = 1L; } else { - return -1L; + res = -1L; } + checkPopCountRandomLong(rand_numL, res); } - @Check(test = "testPopCountRandomLong") - public void checkPopCountRandomLong(long res) { + public void checkPopCountRandomLong(long rand_numL, long res) { int exp = 0; long num = Math.clamp(rand_numL, Math.min(rand_bndL1, rand_bndL2), Math.max(rand_bndL1, rand_bndL2)); while(num != 0) { @@ -177,6 +183,11 @@ public void checkPopCountRandomLong(long res) { Verify.checkEQ(exp >= rand_popcL1 && exp < rand_popcL2 ? 1L : -1L, res); } + @Run(test="testPopCountRandomLong") + public void runPopCountRandomLong() { + testPopCountRandomLong(G.longs().next()); + } + public TestPopCountValueTransforms() { inL1 = new long[SIZE]; G.fill(G.longs(), inL1); From 92cf2fad2e4ebf18f79f2c6a977a650ccefd49c8 Mon Sep 17 00:00:00 2001 From: Jatin Bhateja Date: Fri, 19 Sep 2025 15:16:23 +0530 Subject: [PATCH 10/13] Update TestPopCountValueTransforms.java --- .../compiler/intrinsics/TestPopCountValueTransforms.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/hotspot/jtreg/compiler/intrinsics/TestPopCountValueTransforms.java b/test/hotspot/jtreg/compiler/intrinsics/TestPopCountValueTransforms.java index 618521ab5aeef..f587e15585e10 100644 --- a/test/hotspot/jtreg/compiler/intrinsics/TestPopCountValueTransforms.java +++ b/test/hotspot/jtreg/compiler/intrinsics/TestPopCountValueTransforms.java @@ -50,8 +50,8 @@ public class TestPopCountValueTransforms { static final long rand_bndL1 = G.longs().next(); static final long rand_bndL2 = G.longs().next(); - static final long rand_popcL1 = G.uniformLongs(0, 32).next(); - static final long rand_popcL2 = G.uniformLongs(0, 32).next(); + static final long rand_popcL1 = G.uniformLongs(0, 64).next(); + static final long rand_popcL2 = G.uniformLongs(0, 64).next(); @Test @IR(counts = {IRNode.POPCOUNT_L, " 0 "}) From e206ccc31ccd5a0c4b71032f89af08719f95be16 Mon Sep 17 00:00:00 2001 From: Jatin Bhateja Date: Sat, 20 Sep 2025 02:11:47 +0530 Subject: [PATCH 11/13] Update countbitsnode.cpp --- src/hotspot/share/opto/countbitsnode.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/opto/countbitsnode.cpp b/src/hotspot/share/opto/countbitsnode.cpp index d37acdad73e62..985a5513f3122 100644 --- a/src/hotspot/share/opto/countbitsnode.cpp +++ b/src/hotspot/share/opto/countbitsnode.cpp @@ -120,8 +120,8 @@ const Type* CountTrailingZerosLNode::Value(PhaseGVN* phase) const { // We use the KnownBits information from the integer types to derive how many one bits // we have at least and at most. // From the definition of KnownBits, we know: -// zeros: Indicates which bits must be 0: ones[i] =1 -> t[i]=0 -// ones: Indicates which bits must be 1: zeros[i]=1 -> t[i]=1 +// zeros: Indicates which bits must be 0: zeros[i]=1 -> t[i]=0 +// ones: Indicates which bits must be 1: ones[i]=1 -> t[i]=1 // // From this, we derive: // numer_of_zeros_in_t >= pop_count(zeros) From 85b10e886aa1062de5451cdb892e701468ae30bd Mon Sep 17 00:00:00 2001 From: Jatin Bhateja Date: Sat, 4 Oct 2025 05:56:16 +0000 Subject: [PATCH 12/13] Review comments resolution --- src/hotspot/share/opto/countbitsnode.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/hotspot/share/opto/countbitsnode.cpp b/src/hotspot/share/opto/countbitsnode.cpp index 985a5513f3122..cfd21e7ffc181 100644 --- a/src/hotspot/share/opto/countbitsnode.cpp +++ b/src/hotspot/share/opto/countbitsnode.cpp @@ -141,8 +141,9 @@ const Type* PopCountINode::Value(PhaseGVN* phase) const { if (t == Type::TOP) { return Type::TOP; } - KnownBits bits = t->isa_int()->_bits; - return TypeInt::make(population_count(bits._ones), population_count(~bits._zeros), Type::WidenMax); + const TypeInt* tint = t->isa_int(); + KnownBits bits = tint->_bits; + return TypeInt::make(population_count(bits._ones), population_count(~bits._zeros), tint->_widen); } @@ -151,6 +152,7 @@ const Type* PopCountLNode::Value(PhaseGVN* phase) const { if (t == Type::TOP) { return Type::TOP; } - KnownBits bits = t->isa_long()->_bits; - return TypeInt::make(population_count(bits._ones), population_count(~bits._zeros), Type::WidenMax); + const TypeLong* tlong = t->isa_long(); + KnownBits bits = tlong->_bits; + return TypeInt::make(population_count(bits._ones), population_count(~bits._zeros), tlong->_widen); } From 49cdf2963376859ff459b9338e9b535513ca2326 Mon Sep 17 00:00:00 2001 From: Jatin Bhateja Date: Thu, 9 Oct 2025 06:21:20 +0000 Subject: [PATCH 13/13] Review comments resolutions --- src/hotspot/share/opto/countbitsnode.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hotspot/share/opto/countbitsnode.cpp b/src/hotspot/share/opto/countbitsnode.cpp index cfd21e7ffc181..aac874e94b1fa 100644 --- a/src/hotspot/share/opto/countbitsnode.cpp +++ b/src/hotspot/share/opto/countbitsnode.cpp @@ -141,7 +141,7 @@ const Type* PopCountINode::Value(PhaseGVN* phase) const { if (t == Type::TOP) { return Type::TOP; } - const TypeInt* tint = t->isa_int(); + const TypeInt* tint = t->is_int(); KnownBits bits = tint->_bits; return TypeInt::make(population_count(bits._ones), population_count(~bits._zeros), tint->_widen); @@ -152,7 +152,7 @@ const Type* PopCountLNode::Value(PhaseGVN* phase) const { if (t == Type::TOP) { return Type::TOP; } - const TypeLong* tlong = t->isa_long(); + const TypeLong* tlong = t->is_long(); KnownBits bits = tlong->_bits; return TypeInt::make(population_count(bits._ones), population_count(~bits._zeros), tlong->_widen); }