From 5912cccbf2caf73c0641decc5744a70379fdd825 Mon Sep 17 00:00:00 2001 From: Quan Anh Mai Date: Thu, 25 Jun 2026 08:35:13 +0000 Subject: [PATCH] 8379555: Test compiler/igvn/ExpressionFuzzer.java crashed with -Xcomp: Not monotonic Reviewed-by: shade, kvn Backport-of: bab561b0c5079979cf1c364bc46dcadb3b28b4b5 --- src/hotspot/share/opto/intrinsicnode.cpp | 241 +++++------------- src/hotspot/share/opto/rangeinference.hpp | 129 +++++++++- .../gtest/opto/test_rangeinference.cpp | 29 ++- .../ccp/TestCompressBitsMonotonicity.java | 60 +++++ 4 files changed, 262 insertions(+), 197 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/ccp/TestCompressBitsMonotonicity.java diff --git a/src/hotspot/share/opto/intrinsicnode.cpp b/src/hotspot/share/opto/intrinsicnode.cpp index 16ba829728b..887681233f1 100644 --- a/src/hotspot/share/opto/intrinsicnode.cpp +++ b/src/hotspot/share/opto/intrinsicnode.cpp @@ -22,14 +22,13 @@ * */ -#include "opto/addnode.hpp" #include "opto/intrinsicnode.hpp" #include "opto/memnode.hpp" #include "opto/mulnode.hpp" +#include "opto/opcodes.hpp" #include "opto/phaseX.hpp" -#include "utilities/count_leading_zeros.hpp" +#include "opto/rangeinference.hpp" #include "utilities/globalDefinitions.hpp" -#include "utilities/population_count.hpp" //============================================================================= // Do not match memory edge. @@ -231,171 +230,68 @@ Node* ExpandBitsNode::Identity(PhaseGVN* phase) { return compress_expand_identity(phase, this); } -static const Type* bitshuffle_value(const TypeInteger* src_type, const TypeInteger* mask_type, int opc, BasicType bt) { - +// Bit expansion is a reverse process of bit compression. It sequentially reads source bits +// starting from LSB and places them at bit positions in result value where corresponding mask bits +// are 1. Thus, bit expansion for non-negative mask value will always generate a +ve value, this is +// because sign bit of result will never be set to 1 as corresponding mask bit is always 0. +static const Type* expand_bits_value(const TypeInteger* mask_type, BasicType bt) { + assert(bt == T_INT || bt == T_LONG, "unexpected BasicType %s", type2name(bt)); jlong hi = bt == T_INT ? max_jint : max_jlong; jlong lo = bt == T_INT ? min_jint : min_jlong; - assert(bt == T_INT || bt == T_LONG, ""); - // Rule 1: Bit compression selects the source bits corresponding to true mask bits, - // packs them and places them contiguously at destination bit positions - // starting from least significant bit, remaining higher order bits are set - // to zero. - - // Rule 2: Bit expansion is a reverse process, which sequentially reads source bits - // starting from LSB and places them at bit positions in result value where - // corresponding mask bits are 1. Thus, bit expansion for non-negative mask - // value will always generate a +ve value, this is because sign bit of result - // will never be set to 1 as corresponding mask bit is always 0. - - // Case A) Constant mask if (mask_type->is_con()) { + // Case A) Constant mask jlong maskcon = mask_type->get_con_as_long(bt); - if (opc == Op_CompressBits) { - // Case A.1 bit compression:- - // For an outlier mask value of -1 upper bound of the result equals - // maximum integral value, for any other mask value its computed using - // following formula - // Result.Hi = 1 << popcount(mask_bits) - 1 + if (maskcon >= 0L) { + // Case A.2.1 constant mask >= 0 + // Result.Hi = mask, optimistically assuming all source bits + // read starting from least significant bit positions are 1. + // Result.Lo = 0, because at least one bit in mask is zero. + // e.g. + // src = 0xXXXXXXXX (non-constant source) + // mask = 0x7FFFFFFF (constant mask >= 0) + // result.hi = 0x7FFFFFFF + // result.lo = 0 + hi = maskcon; + lo = 0L; + } else { + // Case A.2.2) mask < 0 + // For constant mask strictly less than zero, the maximum result value will be + // the same as the mask value with its sign bit flipped, assuming all source bits + // except the MSB bit are set(one). // - // For mask values other than -1, lower bound of the result is estimated - // as zero, by assuming at least one mask bit is zero and corresponding source - // bit will be masked, hence result of bit compression will always be - // non-negative value. For outlier mask value of -1, assume all source bits - // apart from most significant bit were set to 0, thereby resulting in - // a minimum integral value. - // e.g. - // src = 0xXXXXXXXX (non-constant source) - // mask = 0xEFFFFFFF (constant mask) - // result.hi = 0x7FFFFFFF - // result.lo = 0 - if (maskcon != -1L) { - int bitcount = population_count(static_cast(bt == T_INT ? maskcon & 0xFFFFFFFFL : maskcon)); - hi = right_n_bits(bitcount); - lo = 0L; - } else { - // preserve originally assigned hi (MAX_INT/LONG) and lo (MIN_INT/LONG) values - // for unknown source bits. - assert(hi == (bt == T_INT ? max_jint : max_jlong), ""); - assert(lo == (bt == T_INT ? min_jint : min_jlong), ""); - } - } else { - // Case A.2 bit expansion:- - assert(opc == Op_ExpandBits, ""); - if (maskcon >= 0L) { - // Case A.2.1 constant mask >= 0 - // Result.Hi = mask, optimistically assuming all source bits - // read starting from least significant bit positions are 1. - // Result.Lo = 0, because at least one bit in mask is zero. - // e.g. - // src = 0xXXXXXXXX (non-constant source) - // mask = 0x7FFFFFFF (constant mask >= 0) - // result.hi = 0x7FFFFFFF - // result.lo = 0 - hi = maskcon; - lo = 0L; - } else { - // Case A.2.2) mask < 0 - // For constant mask strictly less than zero, the maximum result value will be - // the same as the mask value with its sign bit flipped, assuming all source bits - // except the MSB bit are set(one). - // - // To compute minimum result value we assume all but last read source bit as zero, - // this is because sign bit of result will always be set to 1 while other bit - // corresponding to set mask bit should be zero. - // e.g. - // src = 0xXXXXXXXX (non-constant source) - // mask = 0xEFFFFFFF (constant mask) - // result.hi = 0xEFFFFFFF ^ 0x80000000 = 0x6FFFFFFF - // result.lo = 0x80000000 - // - hi = maskcon ^ lo; - // lo still retains MIN_INT/LONG. - assert(lo == (bt == T_INT ? min_jint : min_jlong), ""); - } + // To compute minimum result value we assume all but last read source bit as zero, + // this is because sign bit of result will always be set to 1 while other bit + // corresponding to set mask bit should be zero. + // e.g. + // src = 0xXXXXXXXX (non-constant source) + // mask = 0xEFFFFFFF (constant mask) + // result.hi = 0xEFFFFFFF ^ 0x80000000 = 0x6FFFFFFF + // result.lo = 0x80000000 + // + hi = maskcon ^ lo; + // lo still retains MIN_INT/LONG. + assert(lo == (bt == T_INT ? min_jint : min_jlong), ""); } - } - - // Case B) Non-constant mask. - if (!mask_type->is_con()) { - if ( opc == Op_CompressBits) { - int result_bit_width; - int mask_bit_width = bt == T_INT ? 32 : 64; - if ((mask_type->lo_as_long() < 0L && mask_type->hi_as_long() >= -1L)) { - // Case B.1 The mask value range includes -1, hence we may use all bits, - // the result has the whole value range. - result_bit_width = mask_bit_width; - } else if (mask_type->hi_as_long() < -1L) { - // Case B.2 Mask value range is strictly less than -1, this indicates presence of at least - // one unset(zero) bit in mask value, thus as per Rule 1, bit compression will always - // result in a non-negative value. This guarantees that MSB bit of result value will - // always be set to zero. - result_bit_width = mask_bit_width - 1; - } else { - assert(mask_type->lo_as_long() >= 0, ""); - // Case B.3 Mask value range only includes non-negative values. Since all integral - // types honours an invariant that TypeInteger._lo <= TypeInteger._hi, thus computing - // leading zero bits of upper bound of mask value will allow us to ascertain - // optimistic upper bound of result i.e. all the bits other than leading zero bits - // can be assumed holding 1 value. - jlong clz = count_leading_zeros(mask_type->hi_as_long()); - // Here, result of clz is w.r.t to long argument, hence for integer argument - // we explicitly subtract 32 from the result. - clz = bt == T_INT ? clz - 32 : clz; - result_bit_width = mask_bit_width - clz; - } - // If the number of bits required to for the mask value range is less than the - // full bit width of the integral type, then the MSB bit is guaranteed to be zero, - // thus the compression result will never be a -ve value and we can safely set the - // lower bound of the bit compression to zero. - lo = result_bit_width == mask_bit_width ? lo : 0L; - - assert(hi == (bt == T_INT ? max_jint : max_jlong), ""); - assert(lo == (bt == T_INT ? min_jint : min_jlong) || lo == 0, ""); - - if (src_type->lo_as_long() >= 0) { - // Lemma 1: For strictly non-negative src, the result of the compression will never be - // greater than src. - // Proof: Since src is a non-negative value, its most significant bit is always 0. - // Thus even if the corresponding MSB of the mask is one, the result will be a +ve - // value. There are three possible cases - // a. All the mask bits corresponding to set source bits are unset(zero). - // b. All the mask bits corresponding to set source bits are set(one) - // c. Some mask bits corresponding to set source bits are set(one) while others are unset(zero) - // - // Case a. results into an allzero result, while Case b. gives us the upper bound which is equals source - // value, while for Case c. the result will lie within [0, src] - // - hi = src_type->hi_as_long(); - lo = 0L; - } - - if (result_bit_width < mask_bit_width) { - // Rule 3: - // We can further constrain the upper bound of bit compression if the number of bits - // which can be set(one) is less than the maximum number of bits of integral type. - hi = MIN2(right_n_bits(result_bit_width), hi); - } + } else { + // Case B) Non-constant mask. + jlong max_mask = mask_type->hi_as_long(); + jlong min_mask = mask_type->lo_as_long(); + // Since mask here a range and not a constant value, hence being + // conservative in determining the value range of result. + if (min_mask >= 0L) { + // Lemma 2: Based on the integral type invariant ie. TypeInteger.lo <= TypeInteger.hi, + // if the lower bound of non-constant mask is a non-negative value then result can never + // be greater than the mask. + // Proof: Since lower bound of the mask is a non-negative value, hence most significant + // bit of its entire value must be unset(zero). If all the lower order 'n' source bits + // where n corresponds to popcount of mask are set(ones) then upper bound of the result equals + // mask. In order to compute the lower bound, we pssimistically assume all the lower order 'n' + // source bits are unset(zero) there by resuling into a zero value. + hi = max_mask; + lo = 0; } else { - assert(opc == Op_ExpandBits, ""); - jlong max_mask = mask_type->hi_as_long(); - jlong min_mask = mask_type->lo_as_long(); - // Since mask here a range and not a constant value, hence being - // conservative in determining the value range of result. - if (min_mask >= 0L) { - // Lemma 2: Based on the integral type invariant ie. TypeInteger.lo <= TypeInteger.hi, - // if the lower bound of non-constant mask is a non-negative value then result can never - // be greater than the mask. - // Proof: Since lower bound of the mask is a non-negative value, hence most significant - // bit of its entire value must be unset(zero). If all the lower order 'n' source bits - // where n corresponds to popcount of mask are set(ones) then upper bound of the result equals - // mask. In order to compute the lower bound, we pssimistically assume all the lower order 'n' - // source bits are unset(zero) there by resuling into a zero value. - hi = max_mask; - lo = 0; - } else { - // preserve the lo and hi bounds estimated till now. - } + // preserve the lo and hi bounds estimated till now. } } @@ -423,25 +319,12 @@ const Type* CompressBitsNode::Value(PhaseGVN* phase) const { } BasicType bt = bottom_type()->basic_type(); - const TypeInteger* src_type = t1->is_integer(bt); - const TypeInteger* mask_type = t2->is_integer(bt); - int w = bt == T_INT ? 32 : 64; - - // Constant fold if both src and mask are constants. - if (src_type->is_con() && mask_type->is_con()) { - jlong src = src_type->get_con_as_long(bt); - jlong mask = mask_type->get_con_as_long(bt); - jlong res = compress_bits(src, mask, w); - return bt == T_INT ? static_cast(TypeInt::make(res)) : - static_cast(TypeLong::make(res)); + if (bt == T_INT) { + return RangeInference::infer_compress_bits(t1->is_int(), t2->is_int()); + } else { + assert(bt == T_LONG, "unexpected BasicType %s", type2name(bt)); + return RangeInference::infer_compress_bits(t1->is_long(), t2->is_long()); } - - // Result is zero if src is zero irrespective of mask value. - if (src_type == TypeInteger::zero(bt)) { - return TypeInteger::zero(bt); - } - - return bitshuffle_value(src_type, mask_type, Op_CompressBits, bt); } jlong ExpandBitsNode::expand_bits(jlong src, jlong mask, int bit_count) { @@ -482,5 +365,5 @@ const Type* ExpandBitsNode::Value(PhaseGVN* phase) const { return TypeInteger::zero(bt); } - return bitshuffle_value(src_type, mask_type, Op_ExpandBits, bt); + return expand_bits_value(mask_type, bt); } diff --git a/src/hotspot/share/opto/rangeinference.hpp b/src/hotspot/share/opto/rangeinference.hpp index 7c0f12f6ef7..e5e34051587 100644 --- a/src/hotspot/share/opto/rangeinference.hpp +++ b/src/hotspot/share/opto/rangeinference.hpp @@ -222,12 +222,16 @@ public: return TypeIntHelper::int_type_union(this, &o); } + bool contains(U u) const { + S s = S(u); + return s >= _lo && s <= _hi && u >= _ulo && u <= _uhi && _bits.is_satisfied_by(u); + } + // These allow TypeIntMirror to mimick the behaviors of TypeInt* and TypeLong*, so they can be // passed into RangeInference methods. These are only used in testing, so they are implemented in // the test file. static TypeIntMirror make(const TypeIntMirror& t, int widen); const TypeIntMirror* operator->() const; - bool contains(U u) const; bool contains(const TypeIntMirror& o) const; bool operator==(const TypeIntMirror& o) const; @@ -365,20 +369,23 @@ private: return CT::make(res, MAX2(t1->_widen, t2->_widen)); } + template + static TypeIntMirror, U> infer_and_impl(const TypeIntMirror, U>& st1, const TypeIntMirror, U>& st2) { + S lo = std::numeric_limits>::min(); + S hi = std::numeric_limits>::max(); + U ulo = std::numeric_limits>::min(); + // The unsigned value of the result of 'and' is always not greater than both of its inputs + // since there is no position at which the bit is 1 in the result and 0 in either input + U uhi = MIN2(st1._uhi, st2._uhi); + U zeros = st1._bits._zeros | st2._bits._zeros; + U ones = st1._bits._ones & st2._bits._ones; + return TypeIntMirror, U>::make(TypeIntPrototype, U>{{lo, hi}, {ulo, uhi}, {zeros, ones}}); + } + public: template static CTP infer_and(CTP t1, CTP t2) { - return infer_binary(t1, t2, [&](const TypeIntMirror, U>& st1, const TypeIntMirror, U>& st2) { - S lo = std::numeric_limits>::min(); - S hi = std::numeric_limits>::max(); - U ulo = std::numeric_limits>::min(); - // The unsigned value of the result of 'and' is always not greater than both of its inputs - // since there is no position at which the bit is 1 in the result and 0 in either input - U uhi = MIN2(st1._uhi, st2._uhi); - U zeros = st1._bits._zeros | st2._bits._zeros; - U ones = st1._bits._ones & st2._bits._ones; - return TypeIntMirror, U>::make(TypeIntPrototype, U>{{lo, hi}, {ulo, uhi}, {zeros, ones}}); - }); + return infer_binary(t1, t2, infer_and_impl); } template @@ -442,6 +449,104 @@ public: TypeIntPrototype, U> proto{{slo, shi}, {ulo, uhi}, known_bits}; return CT::make(proto, t1->_widen); } + + // Bit compression selects the source bits corresponding to true mask bits, packs them and places + // them contiguously at destination bit positions starting from least significant bit, remaining + // higher order bits are set to zero. + template + static CTP infer_compress_bits(CTP t1, CTP t2) { + return infer_binary(t1, t2, [](const TypeIntMirror, U>& st1, const TypeIntMirror, U>& st2) { + S lo = std::numeric_limits>::min(); + const S hi = std::numeric_limits>::max(); + const U ulo = U(0); + // Integer.compress(v, mask) == Integer.compress(v & mask, mask) + // Integer.compress(v, mask) u<= v + // So, Integer.compress(v, mask) u<= (v & mask) + const U uhi = infer_and_impl(st1, st2)._uhi; + // If the mask has at least 1 unset bit, then the result must have its highest bit unset, and + // since the only value with no unset bit is the maximum unsigned value, if st2 does not + // contain that value, the result must be non-negative + if (!st2.contains(std::numeric_limits>::max())) { + lo = S(0); + } + + U zeros = U(0); + U ones = U(0); + // Firstly, try to collect known bits by traversing from the lowest to the highest bits, we + // can collect bits up to the first position at which the corresponding bit in the second + // operand is unknown. + // For example, consider Integer.compress(v, mask), with: + // v = 0bxyztuv + // mask = 0b*1*110 + // we can walk the lowest 3 bits of the operands, and determine that the result must be + // 0b****tu + { + // The bit index in result that will be taken from the current bit in the first operand, + // can only be known if we have not encountered any unknown bit in the second operand + int res_bit_idx = 0; + for (int op_bit_idx = 0; op_bit_idx < HotSpotNumerics::type_width>(); op_bit_idx++) { + // If the bit is 0 in the second operand, the corresponding bit value in the first + // operand is irrelevant + U op_bit_mask = U(1) << op_bit_idx; + if ((st2._bits._zeros & op_bit_mask) != U(0)) { + continue; + } + + // No further analysis is possible + if ((st2._bits._ones & op_bit_mask) == U(0)) { + break; + } + + // The bit of the second operand at op_bit_idx must be 1 + U res_bit_mask = U(1) << res_bit_idx; + if ((st1._bits._zeros & op_bit_mask) != U(0)) { + zeros |= res_bit_mask; + } else if ((st1._bits._ones & op_bit_mask) != U(0)) { + ones |= res_bit_mask; + } + res_bit_idx++; + } + } + + // Secondly, try to infer the number of leading zeros by traversing from the highest to the + // lowest bits. Integer.compress(v, mask) == Integer.compress(v & mask, mask), so the number + // of leading zeros in the result is not less than the number of leading zeros in (v & mask). + // Furthermore, in the remaining bits, for each bit in the second operand that must be 0, an + // addition leading zero in result is guaranteed. + // For example, consider Integer.compress(v, mask), with: + // v = 0b*01*** + // mask = 0b0x1*0* + // v & mask = 0b001*0* + // So the result must have at least 2 leading zeros. Furthermore, we can see that it is + // irrelevant whether the bit x in mask is 0 or 1, because the bit in result corresponding to + // x must be 0, and the result must have no higher set bit in either case. As a result, we + // can assume mask = 0b001*0*. And since mask has at least 3 unset bits, the result must have + // at least 3 leading zeros. + { + // The bit index in result that is determined to be 0 + int res_bit_idx = HotSpotNumerics::type_width>() - 1; + // Whether we have encountered a bit that is not known 0 in either the first or the second + // operand + bool leading_zeros = true; + for (int op_bit_idx = HotSpotNumerics::type_width>() - 1; op_bit_idx >= 0; op_bit_idx--) { + U op_bit_mask = U(1) << op_bit_idx; + if ((st2._bits._zeros & op_bit_mask) != U(0)) { + zeros |= (U(1) << res_bit_idx); + res_bit_idx--; + } else if (leading_zeros) { + if ((st1._bits._zeros & op_bit_mask) != U(0)) { + zeros |= (U(1) << res_bit_idx); + res_bit_idx--; + } else { + leading_zeros = false; + } + } + } + } + + return TypeIntMirror, U>::make(TypeIntPrototype, U>{{lo, hi}, {ulo, uhi}, {zeros, ones}}); + }); + } }; #endif // SHARE_OPTO_RANGEINFERENCE_HPP diff --git a/test/hotspot/gtest/opto/test_rangeinference.cpp b/test/hotspot/gtest/opto/test_rangeinference.cpp index 641edaba4da..6f1dedf6923 100644 --- a/test/hotspot/gtest/opto/test_rangeinference.cpp +++ b/test/hotspot/gtest/opto/test_rangeinference.cpp @@ -22,6 +22,7 @@ * */ +#include "opto/intrinsicnode.hpp" #include "opto/rangeinference.hpp" #include "opto/type.hpp" #include "runtime/os.hpp" @@ -225,12 +226,6 @@ const TypeIntMirror* TypeIntMirror::operator->() const { return this; } -template -bool TypeIntMirror::contains(U u) const { - S s = S(u); - return s >= _lo && s <= _hi && u >= _ulo && u <= _uhi && _bits.is_satisfied_by(u); -} - template bool TypeIntMirror::contains(const TypeIntMirror& o) const { return TypeIntHelper::int_type_is_subset(*this, o); @@ -745,9 +740,31 @@ public: } }; +template +class OpCompressBits { +public: + U operator()(U v1, U v2) const { + constexpr int W = HotSpotNumerics::type_width(); + if constexpr (W == 64) { + return CompressBitsNode::compress_bits(v1, v2, W); + } else { + return U(uint(CompressBitsNode::compress_bits(uint(v1), uint(v2), W))); + } + } +}; + +template +class InferCompressBits { +public: + CTP operator()(CTP t1, CTP t2) const { + return RangeInference::infer_compress_bits(t1, t2); + } +}; + TEST(opto, range_inference) { test_binary(); test_binary(); test_binary(); + test_binary(); test_lshift(); } diff --git a/test/hotspot/jtreg/compiler/ccp/TestCompressBitsMonotonicity.java b/test/hotspot/jtreg/compiler/ccp/TestCompressBitsMonotonicity.java new file mode 100644 index 00000000000..732ba6f3502 --- /dev/null +++ b/test/hotspot/jtreg/compiler/ccp/TestCompressBitsMonotonicity.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package compiler.ccp; + +/* + * @test + * @bug 8379555 + * @summary Test that CompressBitsNode::Value does not violate monotonicity + * @run main/othervm -Xbatch -XX:CompileOnly=${test.main.class}::test* ${test.main.class} + */ +public class TestCompressBitsMonotonicity { + public static void main(String[] args) { + for (int i = 0; i < 10000; i++) { + testInt(0); + testLong(0); + } + } + + private static int testInt(int v) { + v &= 0b1101; + int mask = 0b1111; + int sum = 0; + for (int i = 1; i < 10; i *= 2) { + mask = Integer.compress(v, mask); + sum += mask; + } + return sum; + } + + private static long testLong(long v) { + v &= 0b1101; + long mask = 0b1111; + long sum = 0; + for (int i = 1; i < 10; i *= 2) { + mask = Long.compress(v, mask); + sum += mask; + } + return sum; + } +}