Skip to content

Commit

Permalink
[ConstantRange] Handle Intrinsic::ctpop (#68310)
Browse files Browse the repository at this point in the history
This patch adds support for `Intrinsic::ctpop` in ConstantRange. It
calculates the range in O(1) with the LCP-based method.

Migrated from https://reviews.llvm.org/D153505.
  • Loading branch information
dtcxzyw committed Nov 6, 2023
1 parent a07dbf1 commit b6deea1
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 2 deletions.
3 changes: 3 additions & 0 deletions llvm/include/llvm/IR/ConstantRange.h
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,9 @@ class [[nodiscard]] ConstantRange {
/// ignoring a possible zero value contained in the input range.
ConstantRange ctlz(bool ZeroIsPoison = false) const;

/// Calculate ctpop range.
ConstantRange ctpop() const;

/// Represents whether an operation on the given constant range is known to
/// always or never overflow.
enum class OverflowResult {
Expand Down
49 changes: 49 additions & 0 deletions llvm/lib/IR/ConstantRange.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -949,6 +949,7 @@ bool ConstantRange::isIntrinsicSupported(Intrinsic::ID IntrinsicID) {
case Intrinsic::smax:
case Intrinsic::abs:
case Intrinsic::ctlz:
case Intrinsic::ctpop:
return true;
default:
return false;
Expand Down Expand Up @@ -986,6 +987,8 @@ ConstantRange ConstantRange::intrinsic(Intrinsic::ID IntrinsicID,
assert(ZeroIsPoison->getBitWidth() == 1 && "Must be boolean");
return Ops[0].ctlz(ZeroIsPoison->getBoolValue());
}
case Intrinsic::ctpop:
return Ops[0].ctpop();
default:
assert(!isIntrinsicSupported(IntrinsicID) && "Shouldn't be supported");
llvm_unreachable("Unsupported intrinsic");
Expand Down Expand Up @@ -1736,6 +1739,52 @@ ConstantRange ConstantRange::ctlz(bool ZeroIsPoison) const {
APInt(getBitWidth(), getUnsignedMin().countl_zero() + 1));
}

static ConstantRange getUnsignedPopCountRange(const APInt &Lower,
const APInt &Upper) {
assert(!ConstantRange(Lower, Upper).isWrappedSet() &&
"Unexpected wrapped set.");
assert(Lower != Upper && "Unexpected empty set.");
unsigned BitWidth = Lower.getBitWidth();
if (Lower + 1 == Upper)
return ConstantRange(APInt(BitWidth, Lower.popcount()));

APInt Max = Upper - 1;
// Calculate longest common prefix.
unsigned LCPLength = (Lower ^ Max).countl_zero();
unsigned LCPPopCount = Lower.getHiBits(LCPLength).popcount();
// If Lower is {LCP, 000...}, the minimum is the popcount of LCP.
// Otherwise, the minimum is the popcount of LCP + 1.
unsigned MinBits =
LCPPopCount + (Lower.countr_zero() < BitWidth - LCPLength ? 1 : 0);
// If Max is {LCP, 111...}, the maximum is the popcount of LCP + (BitWidth -
// length of LCP).
// Otherwise, the minimum is the popcount of LCP + (BitWidth -
// length of LCP - 1).
unsigned MaxBits = LCPPopCount + (BitWidth - LCPLength) -
(Max.countr_one() < BitWidth - LCPLength ? 1 : 0);
return ConstantRange(APInt(BitWidth, MinBits), APInt(BitWidth, MaxBits + 1));
}

ConstantRange ConstantRange::ctpop() const {
if (isEmptySet())
return getEmpty();

unsigned BitWidth = getBitWidth();
APInt Zero = APInt::getZero(BitWidth);
if (isFullSet())
return getNonEmpty(Zero, APInt(BitWidth, BitWidth + 1));
if (!isWrappedSet())
return getUnsignedPopCountRange(Lower, Upper);
// The range is wrapped. We decompose it into two ranges, [0, Upper) and
// [Lower, 0).
// Handle [Lower, 0) == [Lower, Max]
ConstantRange CR1 = ConstantRange(APInt(BitWidth, Lower.countl_one()),
APInt(BitWidth, BitWidth + 1));
// Handle [0, Upper)
ConstantRange CR2 = getUnsignedPopCountRange(Zero, Upper);
return CR1.unionWith(CR2);
}

ConstantRange::OverflowResult ConstantRange::unsignedAddMayOverflow(
const ConstantRange &Other) const {
if (isEmptySet() || Other.isEmptySet())
Expand Down
3 changes: 1 addition & 2 deletions llvm/test/Transforms/CorrelatedValuePropagation/range.ll
Original file line number Diff line number Diff line change
Expand Up @@ -1085,8 +1085,7 @@ define i1 @ctpop_fold(i16 %x) {
; CHECK-NEXT: br i1 [[CMP]], label [[IF:%.*]], label [[ELSE:%.*]]
; CHECK: if:
; CHECK-NEXT: [[CTPOP:%.*]] = call i16 @llvm.ctpop.i16(i16 [[X]])
; CHECK-NEXT: [[RES:%.*]] = icmp ule i16 [[CTPOP]], 8
; CHECK-NEXT: ret i1 [[RES]]
; CHECK-NEXT: ret i1 true
; CHECK: else:
; CHECK-NEXT: ret i1 true
;
Expand Down
6 changes: 6 additions & 0 deletions llvm/unittests/IR/ConstantRangeTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2438,6 +2438,12 @@ TEST_F(ConstantRangeTest, Ctlz) {
});
}

TEST_F(ConstantRangeTest, Ctpop) {
TestUnaryOpExhaustive(
[](const ConstantRange &CR) { return CR.ctpop(); },
[](const APInt &N) { return APInt(N.getBitWidth(), N.popcount()); });
}

TEST_F(ConstantRangeTest, castOps) {
ConstantRange A(APInt(16, 66), APInt(16, 128));
ConstantRange FpToI8 = A.castOp(Instruction::FPToSI, 8);
Expand Down

0 comments on commit b6deea1

Please sign in to comment.