Skip to content

[APInt] Added APInt::clearBits() method #137098

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
May 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
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
22 changes: 22 additions & 0 deletions llvm/include/llvm/ADT/APInt.h
Original file line number Diff line number Diff line change
Expand Up @@ -1412,6 +1412,25 @@ 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.
void clearBits(unsigned LoBit, unsigned HiBit) {
assert(HiBit <= BitWidth && "HiBit out of range");
assert(LoBit <= HiBit && "LoBit greater than HiBit");
if (LoBit == HiBit)
return;
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 @@ -2052,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);

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

Expand Down
4 changes: 2 additions & 2 deletions llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3546,7 +3546,7 @@ KnownBits SelectionDAG::computeKnownBits(SDValue Op, const APInt &DemandedElts,
unsigned NumSubElts = Sub.getValueType().getVectorNumElements();
APInt DemandedSubElts = DemandedElts.extractBits(NumSubElts, Idx);
APInt DemandedSrcElts = DemandedElts;
DemandedSrcElts.insertBits(APInt::getZero(NumSubElts), Idx);
DemandedSrcElts.clearBits(Idx, Idx + NumSubElts);

Known.One.setAllBits();
Known.Zero.setAllBits();
Expand Down Expand Up @@ -5230,7 +5230,7 @@ unsigned SelectionDAG::ComputeNumSignBits(SDValue Op, const APInt &DemandedElts,
unsigned NumSubElts = Sub.getValueType().getVectorNumElements();
APInt DemandedSubElts = DemandedElts.extractBits(NumSubElts, Idx);
APInt DemandedSrcElts = DemandedElts;
DemandedSrcElts.insertBits(APInt::getZero(NumSubElts), Idx);
DemandedSrcElts.clearBits(Idx, Idx + NumSubElts);

Tmp = std::numeric_limits<unsigned>::max();
if (!!DemandedSubElts) {
Expand Down
4 changes: 2 additions & 2 deletions llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1290,7 +1290,7 @@ bool TargetLowering::SimplifyDemandedBits(
unsigned NumSubElts = Sub.getValueType().getVectorNumElements();
APInt DemandedSubElts = DemandedElts.extractBits(NumSubElts, Idx);
APInt DemandedSrcElts = DemandedElts;
DemandedSrcElts.insertBits(APInt::getZero(NumSubElts), Idx);
DemandedSrcElts.clearBits(Idx, Idx + NumSubElts);

KnownBits KnownSub, KnownSrc;
if (SimplifyDemandedBits(Sub, DemandedBits, DemandedSubElts, KnownSub, TLO,
Expand Down Expand Up @@ -3357,7 +3357,7 @@ bool TargetLowering::SimplifyDemandedVectorElts(
unsigned NumSubElts = Sub.getValueType().getVectorNumElements();
APInt DemandedSubElts = DemandedElts.extractBits(NumSubElts, Idx);
APInt DemandedSrcElts = DemandedElts;
DemandedSrcElts.insertBits(APInt::getZero(NumSubElts), Idx);
DemandedSrcElts.clearBits(Idx, Idx + NumSubElts);

APInt SubUndef, SubZero;
if (SimplifyDemandedVectorElts(Sub, DemandedSubElts, SubUndef, SubZero, TLO,
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;
Copy link
Collaborator

Choose a reason for hiding this comment

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

@liamsemeria should this be LoMask |= HiMask? Noticed when triaging #141098

Copy link
Collaborator

Choose a reason for hiding this comment

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

Additional unit test (clear bits in same word) that fails without the fix:

  APInt i299 = APInt::getAllOnes(299);
  i299.clearBits(240, 250);
  EXPECT_EQ(240u, i299.countr_one());
  EXPECT_EQ(0u, i299.countr_zero());
  EXPECT_EQ(299u, i299.getActiveBits());
  EXPECT_EQ(0u, i299.countl_zero());
  EXPECT_EQ(49u, i299.countl_one());
  EXPECT_EQ(289u, i299.popcount());

Copy link
Collaborator

@topperc topperc May 22, 2025

Choose a reason for hiding this comment

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

I agree it should be LoMask |= HiMask. I've confirmed it fixes the failure on qemu.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'll clean up my tests and put up a PR

Copy link
Collaborator

Choose a reason for hiding this comment

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

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
64 changes: 64 additions & 0 deletions llvm/unittests/ADT/APIntTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2520,6 +2520,70 @@ 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(APInt(32, "8FFFFFF9", 16), i32);

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 i311 = APInt::getAllOnes(311);
i311.clearBits(33, 99);
EXPECT_EQ(33u, i311.countr_one());
EXPECT_EQ(0u, i311.countr_zero());
EXPECT_EQ(311u, i311.getActiveBits());
EXPECT_EQ(0u, i311.countl_zero());
EXPECT_EQ(212u, i311.countl_one());
EXPECT_EQ(245u, i311.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