Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions llvm/include/llvm/ADT/APInt.h
Original file line number Diff line number Diff line change
Expand Up @@ -1413,6 +1413,26 @@ class [[nodiscard]] APInt {
U.pVal[whichWord(BitPosition)] &= Mask;
}

/// Clear the bits from loBit (inclusive) to hiBit (exclusive) to 0.
/// This function handles case when \p loBit <= \p hiBit.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix argument names in this comment.

void clearBits(unsigned loBit, unsigned hiBit) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable and parameter names should start with a capital letter. Same for mask below.

assert(hiBit <= BitWidth && "hiBit out of range");
assert(loBit <= BitWidth && "loBit out of range");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't need this assert. It's implied by the other two.

assert(loBit <= hiBit && "loBit greater than hiBit");
if (loBit == hiBit)
return;
if (loBit < APINT_BITS_PER_WORD && hiBit <= APINT_BITS_PER_WORD) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simplify this to:

Suggested change
if (loBit < APINT_BITS_PER_WORD && hiBit <= APINT_BITS_PER_WORD) {
if (hiBit <= APINT_BITS_PER_WORD) {

uint64_t mask = WORDTYPE_MAX >> (APINT_BITS_PER_WORD - (hiBit - loBit));
mask = ~(mask << loBit);
if (isSingleWord())
U.VAL &= mask;
else
U.pVal[0] &= mask;
} else {
clearBitsSlowCase(loBit, hiBit);
}
}

/// Set bottom loBits bits to 0.
void clearLowBits(unsigned loBits) {
assert(loBits <= BitWidth && "More bits than bitwidth");
Expand Down Expand Up @@ -2051,6 +2071,9 @@ class [[nodiscard]] APInt {
/// out-of-line slow case for setBits.
void setBitsSlowCase(unsigned loBit, unsigned hiBit);

/// out-of-line slow case for clearBits.
void clearBitsSlowCase(unsigned loBit, unsigned hiBit);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LoBit + HiBit


/// out-of-line slow case for flipAllBits.
void flipAllBitsSlowCase();

Expand Down
27 changes: 27 additions & 0 deletions llvm/lib/Support/APInt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,33 @@ void APInt::setBitsSlowCase(unsigned loBit, unsigned hiBit) {
U.pVal[word] = WORDTYPE_MAX;
}

void APInt::clearBitsSlowCase(unsigned loBit, unsigned hiBit) {
unsigned loWord = whichWord(loBit);
unsigned hiWord = whichWord(hiBit);

// Create an initial mask for the low word with ones below loBit.
uint64_t loMask = ~(WORDTYPE_MAX << whichBit(loBit));

// If hiBit is not aligned, we need a high mask.
unsigned hiShiftAmt = whichBit(hiBit);
if (hiShiftAmt != 0) {
// Create a high mask with ones above hiBit.
uint64_t hiMask = ~(WORDTYPE_MAX >> (APINT_BITS_PER_WORD - hiShiftAmt));
// If loWord and hiWord are equal, then we combine the masks. Otherwise,
// set the bits in hiWord.
if (hiWord == loWord)
loMask &= hiMask;
else
U.pVal[hiWord] &= hiMask;
}
// Apply the mask to the low word.
U.pVal[loWord] &= loMask;

// Fill any words between loWord and hiWord with all zeros.
for (unsigned word = loWord + 1; word < hiWord; ++word)
U.pVal[word] = 0;
}

// Complement a bignum in-place.
static void tcComplement(APInt::WordType *dst, unsigned parts) {
for (unsigned i = 0; i < parts; i++)
Expand Down
56 changes: 56 additions & 0 deletions llvm/unittests/ADT/APIntTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2520,6 +2520,62 @@ TEST(APIntTest, setAllBits) {
EXPECT_EQ(128u, i128.popcount());
}

TEST(APIntTest, clearBits) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tip: tests might be shorter and clearer with the string‑based APInt ctor:

APInt hi = APInt::getAllOnes(64);
hi.clearBits(0, 32);
EXPECT_EQ(APInt(64, "FFFFFFFF00000000", 16), hi);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thats very helpful thanks for the tip!

APInt i32 = APInt::getAllOnes(32);
i32.clearBits(1, 3);
EXPECT_EQ(1u, i32.countr_one());
EXPECT_EQ(0u, i32.countr_zero());
EXPECT_EQ(32u, i32.getActiveBits());
EXPECT_EQ(0u, i32.countl_zero());
EXPECT_EQ(29u, i32.countl_one());
EXPECT_EQ(30u, i32.popcount());

i32.clearBits(15, 15);
EXPECT_EQ(1u, i32.countr_one());
EXPECT_EQ(0u, i32.countr_zero());
EXPECT_EQ(32u, i32.getActiveBits());
EXPECT_EQ(0u, i32.countl_zero());
EXPECT_EQ(29u, i32.countl_one());
EXPECT_EQ(30u, i32.popcount());

i32.clearBits(28, 31);
EXPECT_EQ(1u, i32.countr_one());
EXPECT_EQ(0u, i32.countr_zero());
EXPECT_EQ(32u, i32.getActiveBits());
EXPECT_EQ(0u, i32.countl_zero());
EXPECT_EQ(1u, i32.countl_one());
EXPECT_EQ(27u, i32.popcount());
EXPECT_EQ(static_cast<uint64_t>((1 << 31) | ((~0u >> 4) & (~0u << 3)) | 1),
i32.getZExtValue());

APInt i256 = APInt::getAllOnes(256);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please can you add an additional case of > 64 bits that isn't a multiple of 64 bits (e.g. APInt::getAllOnes(311))? This will test clearBitsSlowCase more thoroughly

Copy link
Contributor Author

@liamsemeria liamsemeria Apr 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I'll add that test case.

i256.clearBits(10, 250);
EXPECT_EQ(10u, i256.countr_one());
EXPECT_EQ(0u, i256.countr_zero());
EXPECT_EQ(256u, i256.getActiveBits());
EXPECT_EQ(0u, i256.countl_zero());
EXPECT_EQ(6u, i256.countl_one());
EXPECT_EQ(16u, i256.popcount());

APInt i64hi32 = APInt::getAllOnes(64);
i64hi32.clearBits(0, 32);
EXPECT_EQ(32u, i64hi32.countl_one());
EXPECT_EQ(0u, i64hi32.countl_zero());
EXPECT_EQ(64u, i64hi32.getActiveBits());
EXPECT_EQ(32u, i64hi32.countr_zero());
EXPECT_EQ(0u, i64hi32.countr_one());
EXPECT_EQ(32u, i64hi32.popcount());

i64hi32 = APInt::getAllOnes(64);
i64hi32.clearBits(32, 64);
EXPECT_EQ(32u, i64hi32.countr_one());
EXPECT_EQ(0u, i64hi32.countr_zero());
EXPECT_EQ(32u, i64hi32.getActiveBits());
EXPECT_EQ(32u, i64hi32.countl_zero());
EXPECT_EQ(0u, i64hi32.countl_one());
EXPECT_EQ(32u, i64hi32.popcount());
}

TEST(APIntTest, getLoBits) {
APInt i32(32, 0xfa);
i32.setHighBits(1);
Expand Down
Loading