Skip to content

Commit b6deea1

Browse files
authored
[ConstantRange] Handle Intrinsic::ctpop (llvm#68310)
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.
1 parent a07dbf1 commit b6deea1

File tree

4 files changed

+59
-2
lines changed

4 files changed

+59
-2
lines changed

llvm/include/llvm/IR/ConstantRange.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,9 @@ class [[nodiscard]] ConstantRange {
530530
/// ignoring a possible zero value contained in the input range.
531531
ConstantRange ctlz(bool ZeroIsPoison = false) const;
532532

533+
/// Calculate ctpop range.
534+
ConstantRange ctpop() const;
535+
533536
/// Represents whether an operation on the given constant range is known to
534537
/// always or never overflow.
535538
enum class OverflowResult {

llvm/lib/IR/ConstantRange.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -949,6 +949,7 @@ bool ConstantRange::isIntrinsicSupported(Intrinsic::ID IntrinsicID) {
949949
case Intrinsic::smax:
950950
case Intrinsic::abs:
951951
case Intrinsic::ctlz:
952+
case Intrinsic::ctpop:
952953
return true;
953954
default:
954955
return false;
@@ -986,6 +987,8 @@ ConstantRange ConstantRange::intrinsic(Intrinsic::ID IntrinsicID,
986987
assert(ZeroIsPoison->getBitWidth() == 1 && "Must be boolean");
987988
return Ops[0].ctlz(ZeroIsPoison->getBoolValue());
988989
}
990+
case Intrinsic::ctpop:
991+
return Ops[0].ctpop();
989992
default:
990993
assert(!isIntrinsicSupported(IntrinsicID) && "Shouldn't be supported");
991994
llvm_unreachable("Unsupported intrinsic");
@@ -1736,6 +1739,52 @@ ConstantRange ConstantRange::ctlz(bool ZeroIsPoison) const {
17361739
APInt(getBitWidth(), getUnsignedMin().countl_zero() + 1));
17371740
}
17381741

1742+
static ConstantRange getUnsignedPopCountRange(const APInt &Lower,
1743+
const APInt &Upper) {
1744+
assert(!ConstantRange(Lower, Upper).isWrappedSet() &&
1745+
"Unexpected wrapped set.");
1746+
assert(Lower != Upper && "Unexpected empty set.");
1747+
unsigned BitWidth = Lower.getBitWidth();
1748+
if (Lower + 1 == Upper)
1749+
return ConstantRange(APInt(BitWidth, Lower.popcount()));
1750+
1751+
APInt Max = Upper - 1;
1752+
// Calculate longest common prefix.
1753+
unsigned LCPLength = (Lower ^ Max).countl_zero();
1754+
unsigned LCPPopCount = Lower.getHiBits(LCPLength).popcount();
1755+
// If Lower is {LCP, 000...}, the minimum is the popcount of LCP.
1756+
// Otherwise, the minimum is the popcount of LCP + 1.
1757+
unsigned MinBits =
1758+
LCPPopCount + (Lower.countr_zero() < BitWidth - LCPLength ? 1 : 0);
1759+
// If Max is {LCP, 111...}, the maximum is the popcount of LCP + (BitWidth -
1760+
// length of LCP).
1761+
// Otherwise, the minimum is the popcount of LCP + (BitWidth -
1762+
// length of LCP - 1).
1763+
unsigned MaxBits = LCPPopCount + (BitWidth - LCPLength) -
1764+
(Max.countr_one() < BitWidth - LCPLength ? 1 : 0);
1765+
return ConstantRange(APInt(BitWidth, MinBits), APInt(BitWidth, MaxBits + 1));
1766+
}
1767+
1768+
ConstantRange ConstantRange::ctpop() const {
1769+
if (isEmptySet())
1770+
return getEmpty();
1771+
1772+
unsigned BitWidth = getBitWidth();
1773+
APInt Zero = APInt::getZero(BitWidth);
1774+
if (isFullSet())
1775+
return getNonEmpty(Zero, APInt(BitWidth, BitWidth + 1));
1776+
if (!isWrappedSet())
1777+
return getUnsignedPopCountRange(Lower, Upper);
1778+
// The range is wrapped. We decompose it into two ranges, [0, Upper) and
1779+
// [Lower, 0).
1780+
// Handle [Lower, 0) == [Lower, Max]
1781+
ConstantRange CR1 = ConstantRange(APInt(BitWidth, Lower.countl_one()),
1782+
APInt(BitWidth, BitWidth + 1));
1783+
// Handle [0, Upper)
1784+
ConstantRange CR2 = getUnsignedPopCountRange(Zero, Upper);
1785+
return CR1.unionWith(CR2);
1786+
}
1787+
17391788
ConstantRange::OverflowResult ConstantRange::unsignedAddMayOverflow(
17401789
const ConstantRange &Other) const {
17411790
if (isEmptySet() || Other.isEmptySet())

llvm/test/Transforms/CorrelatedValuePropagation/range.ll

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,8 +1085,7 @@ define i1 @ctpop_fold(i16 %x) {
10851085
; CHECK-NEXT: br i1 [[CMP]], label [[IF:%.*]], label [[ELSE:%.*]]
10861086
; CHECK: if:
10871087
; CHECK-NEXT: [[CTPOP:%.*]] = call i16 @llvm.ctpop.i16(i16 [[X]])
1088-
; CHECK-NEXT: [[RES:%.*]] = icmp ule i16 [[CTPOP]], 8
1089-
; CHECK-NEXT: ret i1 [[RES]]
1088+
; CHECK-NEXT: ret i1 true
10901089
; CHECK: else:
10911090
; CHECK-NEXT: ret i1 true
10921091
;

llvm/unittests/IR/ConstantRangeTest.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2438,6 +2438,12 @@ TEST_F(ConstantRangeTest, Ctlz) {
24382438
});
24392439
}
24402440

2441+
TEST_F(ConstantRangeTest, Ctpop) {
2442+
TestUnaryOpExhaustive(
2443+
[](const ConstantRange &CR) { return CR.ctpop(); },
2444+
[](const APInt &N) { return APInt(N.getBitWidth(), N.popcount()); });
2445+
}
2446+
24412447
TEST_F(ConstantRangeTest, castOps) {
24422448
ConstantRange A(APInt(16, 66), APInt(16, 128));
24432449
ConstantRange FpToI8 = A.castOp(Instruction::FPToSI, 8);

0 commit comments

Comments
 (0)