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()
template <size_t N>
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>
using BitSet64 = BitSetT<N, uint64_t>;
#endif // defined(ANGLE_IS_64_BIT_CPU)
namespace priv
{
......
......@@ -15,14 +15,20 @@ using namespace angle;
namespace
{
template <typename T>
class BitSetTest : public testing::Test
{
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.any());
EXPECT_TRUE(mBits.none());
......@@ -96,8 +102,9 @@ TEST_F(BitSetTest, Basic)
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
// does not have an off-by-one error.
constexpr uint32_t kSelfValue = 0xF9E4;
......@@ -112,10 +119,10 @@ TEST_F(BitSetTest, BitwiseOperators)
constexpr uint32_t kSelfShiftedRight = kSelfMaskedValue >> kShift & kMask;
mBits |= kSelfValue;
BitSet<12> other(kOtherValue);
BitSet<12> anded(kSelfMaskedValue & kOtherMaskedValue);
BitSet<12> ored(kSelfMaskedValue | kOtherMaskedValue);
BitSet<12> xored(kSelfMaskedValue ^ kOtherMaskedValue);
typename TestFixture::BitSet other(kOtherValue);
typename TestFixture::BitSet anded(kSelfMaskedValue & kOtherMaskedValue);
typename TestFixture::BitSet ored(kSelfMaskedValue | kOtherMaskedValue);
typename TestFixture::BitSet xored(kSelfMaskedValue ^ kOtherMaskedValue);
EXPECT_EQ(mBits.bits(), kSelfMaskedValue);
EXPECT_EQ(other.bits(), kOtherMaskedValue);
......@@ -137,18 +144,18 @@ TEST_F(BitSetTest, BitwiseOperators)
mBits ^= other;
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, BitSet<12>(kSelfShiftedRight));
EXPECT_EQ(mBits << kShift, typename TestFixture::BitSet(kSelfShiftedLeft));
EXPECT_EQ(mBits >> kShift, typename TestFixture::BitSet(kSelfShiftedRight));
mBits <<= kShift;
EXPECT_EQ(mBits, BitSet<12>(kSelfShiftedLeft));
EXPECT_EQ(mBits, typename TestFixture::BitSet(kSelfShiftedLeft));
EXPECT_EQ(mBits.bits() & ~kMask, 0u);
mBits = BitSet<12>(kSelfValue);
mBits = typename TestFixture::BitSet(kSelfValue);
mBits >>= kShift;
EXPECT_EQ(mBits, BitSet<12>(kSelfShiftedRight));
EXPECT_EQ(mBits, typename TestFixture::BitSet(kSelfShiftedRight));
EXPECT_EQ(mBits.bits() & ~kMask, 0u);
mBits |= kSelfMaskedValue;
......@@ -157,15 +164,21 @@ TEST_F(BitSetTest, BitwiseOperators)
EXPECT_EQ(mBits.bits() & ~kMask, 0u);
}
template <typename T>
class BitSetIteratorTest : public testing::Test
{
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.
TEST_F(BitSetIteratorTest, Iterator)
TYPED_TEST(BitSetIteratorTest, Iterator)
{
TypeParam mStateBits = this->mStateBits;
std::set<size_t> originalValues;
originalValues.insert(2);
originalValues.insert(6);
......@@ -189,8 +202,9 @@ TEST_F(BitSetIteratorTest, 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,
// causing an unreachable code warning in MSVS
bool sawBit = false;
......@@ -203,9 +217,10 @@ TEST_F(BitSetIteratorTest, EmptySet)
}
// 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(2);
......@@ -219,7 +234,7 @@ TEST_F(BitSetIteratorTest, NonLValueBitset)
std::set<size_t> seenBits;
angle::BitSet<40> maskedBits = (mStateBits & otherBits);
typename TestFixture::BitSet maskedBits = (mStateBits & otherBits);
for (size_t bit : maskedBits)
{
EXPECT_EQ(0u, seenBits.count(bit));
......@@ -232,8 +247,9 @@ TEST_F(BitSetIteratorTest, NonLValueBitset)
}
// Test bit assignments.
TEST_F(BitSetIteratorTest, BitAssignment)
TYPED_TEST(BitSetIteratorTest, BitAssignment)
{
TypeParam mStateBits = this->mStateBits;
std::set<size_t> originalValues;
originalValues.insert(2);
originalValues.insert(6);
......@@ -252,8 +268,9 @@ TEST_F(BitSetIteratorTest, BitAssignment)
}
// 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};
mStateBits.set(1);
......@@ -276,8 +293,9 @@ TEST_F(BitSetIteratorTest, SetLaterBit)
}
// 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};
for (size_t index = 1; index <= 9; ++index)
......
......@@ -1052,17 +1052,28 @@ inline unsigned long ScanForward(uint32_t bits)
return firstBitIndex;
}
# if defined(ANGLE_IS_64_BIT_CPU)
inline unsigned long ScanForward(uint64_t bits)
{
ASSERT(bits != 0u);
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);
return firstBitIndex;
}
# endif // defined(ANGLE_IS_64_BIT_CPU)
#endif // defined(ANGLE_PLATFORM_WINDOWS)
#endif // defined(ANGLE_PLATFORM_WINDOWS)
#if defined(ANGLE_PLATFORM_POSIX)
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));
}
# if defined(ANGLE_IS_64_BIT_CPU)
inline unsigned long ScanForward(uint64_t bits)
{
ASSERT(bits != 0u);
# if defined(ANGLE_IS_64_BIT_CPU)
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_PLATFORM_POSIX)
}
#endif // defined(ANGLE_PLATFORM_POSIX)
inline unsigned long ScanForward(uint8_t bits)
{
......
......@@ -272,11 +272,13 @@ TEST(MathUtilTest, ScanForward)
EXPECT_EQ(16ul, gl::ScanForward(0x80010000u));
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(16ul, gl::ScanForward(static_cast<uint64_t>(0x80010000ull)));
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.
......
......@@ -48,11 +48,11 @@ void BitSetIteratorPerfTest<T>::step()
}
// These type names unfortunately don't get printed correctly in Gtest.
#if defined(ANGLE_IS_64_BIT_CPU)
using TestTypes = Types<angle::IterableBitSet<32>, angle::BitSet32<32>, angle::BitSet64<32>>;
#else
using TestTypes = Types<angle::IterableBitSet<32>, angle::BitSet32<32>>;
#endif // defined(ANGLE_IS_64_BIT_CPU)
using TestTypes = Types<angle::IterableBitSet<32>,
angle::BitSet32<32>,
angle::BitSet64<32>,
angle::IterableBitSet<64>,
angle::BitSet64<64>>;
TYPED_TEST_SUITE(BitSetIteratorPerfTest, TestTypes);
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