Commit e6519445 by Alexey Knyazev Committed by Commit Bot

Implement ScanForward64 on 32-bit platforms

This unlocks opt-in usage of BitSet64 on 32-bit platforms. It is slightly faster than IterableBitSet when the amount of bits is greater than 32. Bug: angleproject:4473 Change-Id: I230784acc871e13b1f94c87503f6bb869dcd3a68 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2100969Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
parent 03de11ac
...@@ -494,11 +494,8 @@ std::size_t BitSetT<N, BitsT, ParamT>::Iterator::getNextBit() ...@@ -494,11 +494,8 @@ std::size_t BitSetT<N, BitsT, ParamT>::Iterator::getNextBit()
template <size_t N> template <size_t N>
using BitSet32 = BitSetT<N, uint32_t>; using BitSet32 = BitSetT<N, uint32_t>;
// ScanForward for 64-bits requires a 64-bit implementation.
#if defined(ANGLE_IS_64_BIT_CPU)
template <size_t N> template <size_t N>
using BitSet64 = BitSetT<N, uint64_t>; using BitSet64 = BitSetT<N, uint64_t>;
#endif // defined(ANGLE_IS_64_BIT_CPU)
namespace priv namespace priv
{ {
......
...@@ -15,14 +15,20 @@ using namespace angle; ...@@ -15,14 +15,20 @@ using namespace angle;
namespace namespace
{ {
template <typename T>
class BitSetTest : public testing::Test class BitSetTest : public testing::Test
{ {
protected: protected:
BitSet<12> mBits; T mBits;
typedef T BitSet;
}; };
TEST_F(BitSetTest, Basic) using BitSetTypes = ::testing::Types<BitSet<12>, BitSet32<12>, BitSet64<12>>;
TYPED_TEST_SUITE(BitSetTest, BitSetTypes);
TYPED_TEST(BitSetTest, Basic)
{ {
TypeParam mBits = this->mBits;
EXPECT_FALSE(mBits.all()); EXPECT_FALSE(mBits.all());
EXPECT_FALSE(mBits.any()); EXPECT_FALSE(mBits.any());
EXPECT_TRUE(mBits.none()); EXPECT_TRUE(mBits.none());
...@@ -96,8 +102,9 @@ TEST_F(BitSetTest, Basic) ...@@ -96,8 +102,9 @@ TEST_F(BitSetTest, Basic)
EXPECT_EQ(mBits.flip(13).bits() & ~kMask, 0u); EXPECT_EQ(mBits.flip(13).bits() & ~kMask, 0u);
} }
TEST_F(BitSetTest, BitwiseOperators) TYPED_TEST(BitSetTest, BitwiseOperators)
{ {
TypeParam mBits = this->mBits;
// Use a value that has a 1 in the 12th and 13th bits, to make sure masking to exactly 12 bits // Use a value that has a 1 in the 12th and 13th bits, to make sure masking to exactly 12 bits
// does not have an off-by-one error. // does not have an off-by-one error.
constexpr uint32_t kSelfValue = 0xF9E4; constexpr uint32_t kSelfValue = 0xF9E4;
...@@ -112,10 +119,10 @@ TEST_F(BitSetTest, BitwiseOperators) ...@@ -112,10 +119,10 @@ TEST_F(BitSetTest, BitwiseOperators)
constexpr uint32_t kSelfShiftedRight = kSelfMaskedValue >> kShift & kMask; constexpr uint32_t kSelfShiftedRight = kSelfMaskedValue >> kShift & kMask;
mBits |= kSelfValue; mBits |= kSelfValue;
BitSet<12> other(kOtherValue); typename TestFixture::BitSet other(kOtherValue);
BitSet<12> anded(kSelfMaskedValue & kOtherMaskedValue); typename TestFixture::BitSet anded(kSelfMaskedValue & kOtherMaskedValue);
BitSet<12> ored(kSelfMaskedValue | kOtherMaskedValue); typename TestFixture::BitSet ored(kSelfMaskedValue | kOtherMaskedValue);
BitSet<12> xored(kSelfMaskedValue ^ kOtherMaskedValue); typename TestFixture::BitSet xored(kSelfMaskedValue ^ kOtherMaskedValue);
EXPECT_EQ(mBits.bits(), kSelfMaskedValue); EXPECT_EQ(mBits.bits(), kSelfMaskedValue);
EXPECT_EQ(other.bits(), kOtherMaskedValue); EXPECT_EQ(other.bits(), kOtherMaskedValue);
...@@ -137,18 +144,18 @@ TEST_F(BitSetTest, BitwiseOperators) ...@@ -137,18 +144,18 @@ TEST_F(BitSetTest, BitwiseOperators)
mBits ^= other; mBits ^= other;
mBits ^= anded; mBits ^= anded;
EXPECT_EQ(mBits, BitSet<12>(kSelfValue)); EXPECT_EQ(mBits, typename TestFixture::BitSet(kSelfValue));
EXPECT_EQ(mBits << kShift, BitSet<12>(kSelfShiftedLeft)); EXPECT_EQ(mBits << kShift, typename TestFixture::BitSet(kSelfShiftedLeft));
EXPECT_EQ(mBits >> kShift, BitSet<12>(kSelfShiftedRight)); EXPECT_EQ(mBits >> kShift, typename TestFixture::BitSet(kSelfShiftedRight));
mBits <<= kShift; mBits <<= kShift;
EXPECT_EQ(mBits, BitSet<12>(kSelfShiftedLeft)); EXPECT_EQ(mBits, typename TestFixture::BitSet(kSelfShiftedLeft));
EXPECT_EQ(mBits.bits() & ~kMask, 0u); EXPECT_EQ(mBits.bits() & ~kMask, 0u);
mBits = BitSet<12>(kSelfValue); mBits = typename TestFixture::BitSet(kSelfValue);
mBits >>= kShift; mBits >>= kShift;
EXPECT_EQ(mBits, BitSet<12>(kSelfShiftedRight)); EXPECT_EQ(mBits, typename TestFixture::BitSet(kSelfShiftedRight));
EXPECT_EQ(mBits.bits() & ~kMask, 0u); EXPECT_EQ(mBits.bits() & ~kMask, 0u);
mBits |= kSelfMaskedValue; mBits |= kSelfMaskedValue;
...@@ -157,15 +164,21 @@ TEST_F(BitSetTest, BitwiseOperators) ...@@ -157,15 +164,21 @@ TEST_F(BitSetTest, BitwiseOperators)
EXPECT_EQ(mBits.bits() & ~kMask, 0u); EXPECT_EQ(mBits.bits() & ~kMask, 0u);
} }
template <typename T>
class BitSetIteratorTest : public testing::Test class BitSetIteratorTest : public testing::Test
{ {
protected: protected:
BitSet<40> mStateBits; T mStateBits;
typedef T BitSet;
}; };
using BitSetIteratorTypes = ::testing::Types<BitSet<40>, BitSet64<40>>;
TYPED_TEST_SUITE(BitSetIteratorTest, BitSetIteratorTypes);
// Simple iterator test. // Simple iterator test.
TEST_F(BitSetIteratorTest, Iterator) TYPED_TEST(BitSetIteratorTest, Iterator)
{ {
TypeParam mStateBits = this->mStateBits;
std::set<size_t> originalValues; std::set<size_t> originalValues;
originalValues.insert(2); originalValues.insert(2);
originalValues.insert(6); originalValues.insert(6);
...@@ -189,8 +202,9 @@ TEST_F(BitSetIteratorTest, Iterator) ...@@ -189,8 +202,9 @@ TEST_F(BitSetIteratorTest, Iterator)
} }
// Test an empty iterator. // Test an empty iterator.
TEST_F(BitSetIteratorTest, EmptySet) TYPED_TEST(BitSetIteratorTest, EmptySet)
{ {
TypeParam mStateBits = this->mStateBits;
// We don't use the FAIL gtest macro here since it returns immediately, // We don't use the FAIL gtest macro here since it returns immediately,
// causing an unreachable code warning in MSVS // causing an unreachable code warning in MSVS
bool sawBit = false; bool sawBit = false;
...@@ -203,9 +217,10 @@ TEST_F(BitSetIteratorTest, EmptySet) ...@@ -203,9 +217,10 @@ TEST_F(BitSetIteratorTest, EmptySet)
} }
// Test iterating a result of combining two bitsets. // Test iterating a result of combining two bitsets.
TEST_F(BitSetIteratorTest, NonLValueBitset) TYPED_TEST(BitSetIteratorTest, NonLValueBitset)
{ {
BitSet<40> otherBits; TypeParam mStateBits = this->mStateBits;
typename TestFixture::BitSet otherBits;
mStateBits.set(1); mStateBits.set(1);
mStateBits.set(2); mStateBits.set(2);
...@@ -219,7 +234,7 @@ TEST_F(BitSetIteratorTest, NonLValueBitset) ...@@ -219,7 +234,7 @@ TEST_F(BitSetIteratorTest, NonLValueBitset)
std::set<size_t> seenBits; std::set<size_t> seenBits;
angle::BitSet<40> maskedBits = (mStateBits & otherBits); typename TestFixture::BitSet maskedBits = (mStateBits & otherBits);
for (size_t bit : maskedBits) for (size_t bit : maskedBits)
{ {
EXPECT_EQ(0u, seenBits.count(bit)); EXPECT_EQ(0u, seenBits.count(bit));
...@@ -232,8 +247,9 @@ TEST_F(BitSetIteratorTest, NonLValueBitset) ...@@ -232,8 +247,9 @@ TEST_F(BitSetIteratorTest, NonLValueBitset)
} }
// Test bit assignments. // Test bit assignments.
TEST_F(BitSetIteratorTest, BitAssignment) TYPED_TEST(BitSetIteratorTest, BitAssignment)
{ {
TypeParam mStateBits = this->mStateBits;
std::set<size_t> originalValues; std::set<size_t> originalValues;
originalValues.insert(2); originalValues.insert(2);
originalValues.insert(6); originalValues.insert(6);
...@@ -252,8 +268,9 @@ TEST_F(BitSetIteratorTest, BitAssignment) ...@@ -252,8 +268,9 @@ TEST_F(BitSetIteratorTest, BitAssignment)
} }
// Tests adding bits to the iterator during iteration. // Tests adding bits to the iterator during iteration.
TEST_F(BitSetIteratorTest, SetLaterBit) TYPED_TEST(BitSetIteratorTest, SetLaterBit)
{ {
TypeParam mStateBits = this->mStateBits;
std::set<size_t> expectedValues = {1, 3, 5, 7, 9}; std::set<size_t> expectedValues = {1, 3, 5, 7, 9};
mStateBits.set(1); mStateBits.set(1);
...@@ -276,8 +293,9 @@ TEST_F(BitSetIteratorTest, SetLaterBit) ...@@ -276,8 +293,9 @@ TEST_F(BitSetIteratorTest, SetLaterBit)
} }
// Tests removing bits from the iterator during iteration. // Tests removing bits from the iterator during iteration.
TEST_F(BitSetIteratorTest, ResetLaterBit) TYPED_TEST(BitSetIteratorTest, ResetLaterBit)
{ {
TypeParam mStateBits = this->mStateBits;
std::set<size_t> expectedValues = {1, 3, 5, 7, 9}; std::set<size_t> expectedValues = {1, 3, 5, 7, 9};
for (size_t index = 1; index <= 9; ++index) for (size_t index = 1; index <= 9; ++index)
......
...@@ -1052,17 +1052,28 @@ inline unsigned long ScanForward(uint32_t bits) ...@@ -1052,17 +1052,28 @@ inline unsigned long ScanForward(uint32_t bits)
return firstBitIndex; return firstBitIndex;
} }
# if defined(ANGLE_IS_64_BIT_CPU)
inline unsigned long ScanForward(uint64_t bits) inline unsigned long ScanForward(uint64_t bits)
{ {
ASSERT(bits != 0u); ASSERT(bits != 0u);
unsigned long firstBitIndex = 0ul; unsigned long firstBitIndex = 0ul;
unsigned char ret = _BitScanForward64(&firstBitIndex, bits); # if defined(ANGLE_IS_64_BIT_CPU)
unsigned char ret = _BitScanForward64(&firstBitIndex, bits);
# else
unsigned char ret;
if (static_cast<uint32_t>(bits) == 0)
{
ret = _BitScanForward(&firstBitIndex, static_cast<uint32_t>(bits >> 32));
firstBitIndex += 32ul;
}
else
{
ret = _BitScanForward(&firstBitIndex, static_cast<uint32_t>(bits));
}
# endif // defined(ANGLE_IS_64_BIT_CPU)
ASSERT(ret != 0u); ASSERT(ret != 0u);
return firstBitIndex; return firstBitIndex;
} }
# endif // defined(ANGLE_IS_64_BIT_CPU) #endif // defined(ANGLE_PLATFORM_WINDOWS)
#endif // defined(ANGLE_PLATFORM_WINDOWS)
#if defined(ANGLE_PLATFORM_POSIX) #if defined(ANGLE_PLATFORM_POSIX)
inline unsigned long ScanForward(uint32_t bits) inline unsigned long ScanForward(uint32_t bits)
...@@ -1071,14 +1082,18 @@ inline unsigned long ScanForward(uint32_t bits) ...@@ -1071,14 +1082,18 @@ inline unsigned long ScanForward(uint32_t bits)
return static_cast<unsigned long>(__builtin_ctz(bits)); return static_cast<unsigned long>(__builtin_ctz(bits));
} }
# if defined(ANGLE_IS_64_BIT_CPU)
inline unsigned long ScanForward(uint64_t bits) inline unsigned long ScanForward(uint64_t bits)
{ {
ASSERT(bits != 0u); ASSERT(bits != 0u);
# if defined(ANGLE_IS_64_BIT_CPU)
return static_cast<unsigned long>(__builtin_ctzll(bits)); return static_cast<unsigned long>(__builtin_ctzll(bits));
} # else
return static_cast<unsigned long>(static_cast<uint32_t>(bits) == 0
? __builtin_ctz(static_cast<uint32_t>(bits >> 32)) + 32
: __builtin_ctz(static_cast<uint32_t>(bits)));
# endif // defined(ANGLE_IS_64_BIT_CPU) # endif // defined(ANGLE_IS_64_BIT_CPU)
#endif // defined(ANGLE_PLATFORM_POSIX) }
#endif // defined(ANGLE_PLATFORM_POSIX)
inline unsigned long ScanForward(uint8_t bits) inline unsigned long ScanForward(uint8_t bits)
{ {
......
...@@ -272,11 +272,13 @@ TEST(MathUtilTest, ScanForward) ...@@ -272,11 +272,13 @@ TEST(MathUtilTest, ScanForward)
EXPECT_EQ(16ul, gl::ScanForward(0x80010000u)); EXPECT_EQ(16ul, gl::ScanForward(0x80010000u));
EXPECT_EQ(31ul, gl::ScanForward(0x80000000u)); EXPECT_EQ(31ul, gl::ScanForward(0x80000000u));
#if defined(ANGLE_IS_64_BIT_CPU)
EXPECT_EQ(0ul, gl::ScanForward(static_cast<uint64_t>(1ull))); EXPECT_EQ(0ul, gl::ScanForward(static_cast<uint64_t>(1ull)));
EXPECT_EQ(16ul, gl::ScanForward(static_cast<uint64_t>(0x80010000ull))); EXPECT_EQ(16ul, gl::ScanForward(static_cast<uint64_t>(0x80010000ull)));
EXPECT_EQ(31ul, gl::ScanForward(static_cast<uint64_t>(0x80000000ull))); EXPECT_EQ(31ul, gl::ScanForward(static_cast<uint64_t>(0x80000000ull)));
#endif // defined(ANGLE_IS_64_BIT_CPU)
EXPECT_EQ(32ul, gl::ScanForward(static_cast<uint64_t>(0x100000000ull)));
EXPECT_EQ(48ul, gl::ScanForward(static_cast<uint64_t>(0x8001000000000000ull)));
EXPECT_EQ(63ul, gl::ScanForward(static_cast<uint64_t>(0x8000000000000000ull)));
} }
// Test ScanReverse, which scans for the most significant 1 bit from a non-zero integer. // Test ScanReverse, which scans for the most significant 1 bit from a non-zero integer.
......
...@@ -48,11 +48,11 @@ void BitSetIteratorPerfTest<T>::step() ...@@ -48,11 +48,11 @@ void BitSetIteratorPerfTest<T>::step()
} }
// These type names unfortunately don't get printed correctly in Gtest. // These type names unfortunately don't get printed correctly in Gtest.
#if defined(ANGLE_IS_64_BIT_CPU) using TestTypes = Types<angle::IterableBitSet<32>,
using TestTypes = Types<angle::IterableBitSet<32>, angle::BitSet32<32>, angle::BitSet64<32>>; angle::BitSet32<32>,
#else angle::BitSet64<32>,
using TestTypes = Types<angle::IterableBitSet<32>, angle::BitSet32<32>>; angle::IterableBitSet<64>,
#endif // defined(ANGLE_IS_64_BIT_CPU) angle::BitSet64<64>>;
TYPED_TEST_SUITE(BitSetIteratorPerfTest, TestTypes); TYPED_TEST_SUITE(BitSetIteratorPerfTest, TestTypes);
TYPED_TEST(BitSetIteratorPerfTest, Run) TYPED_TEST(BitSetIteratorPerfTest, Run)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment