8257985: count_trailing_zeros doesn't handle 64-bit values on 32-bit JVM

Reviewed-by: kbarrett
This commit is contained in:
Claes Redestad 2020-12-14 11:59:05 +00:00
parent 2ee795d9e4
commit e69ae07f08
2 changed files with 85 additions and 25 deletions
src/hotspot/share/utilities
test/hotspot/gtest/utilities

@ -25,14 +25,18 @@
#ifndef SHARE_UTILITIES_COUNT_TRAILING_ZEROS_HPP
#define SHARE_UTILITIES_COUNT_TRAILING_ZEROS_HPP
#include "metaprogramming/enableIf.hpp"
#include "utilities/debug.hpp"
#include "utilities/globalDefinitions.hpp"
// unsigned count_trailing_zeros(uintx x)
// unsigned count_trailing_zeros(T x)
// Return the number of trailing zeros in x, e.g. the zero-based index
// of the least significant set bit in x.
// Precondition: x != 0.
// We implement and support variants for 8, 16, 32 and 64 bit integral types.
// Dispatch on toolchain to select implementation.
/*****************************************************************************
@ -40,10 +44,12 @@
*****************************************************************************/
#if defined(TARGET_COMPILER_gcc)
inline unsigned count_trailing_zeros(uintx x) {
STATIC_ASSERT(sizeof(unsigned long) == sizeof(uintx));
assert(x != 0, "precondition");
return __builtin_ctzl(x);
inline unsigned count_trailing_zeros_32(uint32_t x) {
return __builtin_ctz(x);
}
inline unsigned count_trailing_zeros_64(uint64_t x) {
return __builtin_ctzll(x);
}
/*****************************************************************************
@ -53,19 +59,27 @@ inline unsigned count_trailing_zeros(uintx x) {
#include <intrin.h>
#pragma intrinsic(_BitScanForward)
#ifdef _LP64
#pragma intrinsic(_BitScanForward64)
#else
#pragma intrinsic(_BitScanForward)
#endif
inline unsigned count_trailing_zeros(uintx x) {
assert(x != 0, "precondition");
inline unsigned count_trailing_zeros_32(uint32_t x) {
unsigned long index;
_BitScanForward(&index, x);
return index;
}
inline unsigned count_trailing_zeros_64(uint64_t x) {
unsigned long index;
#ifdef _LP64
_BitScanForward64(&index, x);
#else
_BitScanForward(&index, x);
if (_BitScanForward(&index, static_cast<uint32_t>(x)) == 0) {
// no bit found? If so, try the upper dword. Otherwise index already contains the result
_BitScanForward(&index, static_cast<uint32_t>(x >> 32));
index += 32;
}
#endif
return index;
}
@ -77,13 +91,12 @@ inline unsigned count_trailing_zeros(uintx x) {
#include <builtins.h>
inline unsigned count_trailing_zeros(uintx x) {
assert(x != 0, "precondition");
#ifdef _LP64
return __cnttz8(x);
#else
inline unsigned count_trailing_zeros_32(uint32_t x) {
return __cnttz4(x);
#endif
}
inline unsigned count_trailing_zeros_64(uint64_t x) {
return __cnttz8(x);
}
/*****************************************************************************
@ -94,4 +107,15 @@ inline unsigned count_trailing_zeros(uintx x) {
#endif // Toolchain dispatch
template<typename T,
ENABLE_IF(std::is_integral<T>::value),
ENABLE_IF(sizeof(T) <= sizeof(uint64_t))>
inline unsigned count_trailing_zeros(T x) {
assert(x != 0, "precondition");
return (sizeof(x) <= sizeof(uint32_t)) ?
count_trailing_zeros_32(static_cast<uint32_t>(x)) :
count_trailing_zeros_64(x);
}
#endif // SHARE_UTILITIES_COUNT_TRAILING_ZEROS_HPP

@ -27,31 +27,67 @@
#include "utilities/globalDefinitions.hpp"
#include "unittest.hpp"
TEST(count_trailing_zeros, one_or_two_set_bits) {
template <typename T> static void test_one_or_two_set_bits() {
unsigned i = 0; // Position of a set bit.
for (uintx ix = 1; ix != 0; ix <<= 1, ++i) {
unsigned max = sizeof(T) * BitsPerByte;
for (T ix = T(1); i < max; ix <<= 1, ++i) {
unsigned j = 0; // Position of a set bit.
for (uintx jx = 1; jx != 0; jx <<= 1, ++j) {
uintx value = ix | jx;
for (T jx = T(1); j < max; jx <<= 1, ++j) {
T value = ix | jx;
EXPECT_EQ(MIN2(i, j), count_trailing_zeros(value))
<< "value = " << value;
}
}
}
TEST(count_trailing_zeros, high_zeros_low_ones) {
uintx value = ~(uintx)0;
TEST(count_trailing_zeros, one_or_two_set_bits) {
test_one_or_two_set_bits<int8_t>();
test_one_or_two_set_bits<int16_t>();
test_one_or_two_set_bits<int32_t>();
test_one_or_two_set_bits<int64_t>();
test_one_or_two_set_bits<uint8_t>();
test_one_or_two_set_bits<uint16_t>();
test_one_or_two_set_bits<uint32_t>();
test_one_or_two_set_bits<uint64_t>();
}
template <typename T> static void test_high_zeros_low_ones() {
T value = std::numeric_limits<T>::max();
for ( ; value != 0; value >>= 1) {
EXPECT_EQ(0u, count_trailing_zeros(value))
<< "value = " << value;
}
}
TEST(count_trailing_zeros, high_ones_low_zeros) {
TEST(count_trailing_zeros, high_zeros_low_ones) {
test_high_zeros_low_ones<int8_t>();
test_high_zeros_low_ones<int16_t>();
test_high_zeros_low_ones<int32_t>();
test_high_zeros_low_ones<int64_t>();
test_high_zeros_low_ones<uint8_t>();
test_high_zeros_low_ones<uint16_t>();
test_high_zeros_low_ones<uint32_t>();
test_high_zeros_low_ones<uint64_t>();
}
template <typename T> static void test_high_ones_low_zeros() {
unsigned i = 0; // Index of least significant set bit.
uintx value = ~(uintx)0;
for ( ; value != 0; value <<= 1, ++i) {
T value = ~T(0);
unsigned max = sizeof(T) * BitsPerByte;
for ( ; i < max; value <<= 1, ++i) {
EXPECT_EQ(i, count_trailing_zeros(value))
<< "value = " << value;
}
}
TEST(count_trailing_zeros, high_ones_low_zeros) {
test_high_ones_low_zeros<int8_t>();
test_high_ones_low_zeros<int16_t>();
test_high_ones_low_zeros<int32_t>();
test_high_ones_low_zeros<int64_t>();
test_high_ones_low_zeros<uint8_t>();
test_high_ones_low_zeros<uint16_t>();
test_high_ones_low_zeros<uint32_t>();
test_high_ones_low_zeros<uint64_t>();
}