Skip to content

Commit 6f1d3b9

Browse files
authored
[ConstantRange] Handle Intrinsic::cttz (#67917)
This patch adds support for `Intrinsic::cttz` in ConstantRange. It calculates the range in O(1) with the LCP-based method. Migrated from https://reviews.llvm.org/D153505.
1 parent 333124c commit 6f1d3b9

File tree

4 files changed

+94
-2
lines changed

4 files changed

+94
-2
lines changed

llvm/include/llvm/IR/ConstantRange.h

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

533+
/// Calculate cttz range. If \p ZeroIsPoison is set, the range is computed
534+
/// ignoring a possible zero value contained in the input range.
535+
ConstantRange cttz(bool ZeroIsPoison = false) const;
536+
533537
/// Calculate ctpop range.
534538
ConstantRange ctpop() const;
535539

llvm/lib/IR/ConstantRange.cpp

Lines changed: 75 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::cttz:
952953
case Intrinsic::ctpop:
953954
return true;
954955
default:
@@ -987,6 +988,12 @@ ConstantRange ConstantRange::intrinsic(Intrinsic::ID IntrinsicID,
987988
assert(ZeroIsPoison->getBitWidth() == 1 && "Must be boolean");
988989
return Ops[0].ctlz(ZeroIsPoison->getBoolValue());
989990
}
991+
case Intrinsic::cttz: {
992+
const APInt *ZeroIsPoison = Ops[1].getSingleElement();
993+
assert(ZeroIsPoison && "Must be known (immarg)");
994+
assert(ZeroIsPoison->getBitWidth() == 1 && "Must be boolean");
995+
return Ops[0].cttz(ZeroIsPoison->getBoolValue());
996+
}
990997
case Intrinsic::ctpop:
991998
return Ops[0].ctpop();
992999
default:
@@ -1739,6 +1746,74 @@ ConstantRange ConstantRange::ctlz(bool ZeroIsPoison) const {
17391746
APInt(getBitWidth(), getUnsignedMin().countl_zero() + 1));
17401747
}
17411748

1749+
static ConstantRange getUnsignedCountTrailingZerosRange(const APInt &Lower,
1750+
const APInt &Upper) {
1751+
assert(!ConstantRange(Lower, Upper).isWrappedSet() &&
1752+
"Unexpected wrapped set.");
1753+
assert(Lower != Upper && "Unexpected empty set.");
1754+
unsigned BitWidth = Lower.getBitWidth();
1755+
if (Lower + 1 == Upper)
1756+
return ConstantRange(APInt(BitWidth, Lower.countr_zero()));
1757+
if (Lower.isZero())
1758+
return ConstantRange(APInt::getZero(BitWidth),
1759+
APInt(BitWidth, BitWidth + 1));
1760+
1761+
// Calculate longest common prefix.
1762+
unsigned LCPLength = (Lower ^ (Upper - 1)).countl_zero();
1763+
// If Lower is {LCP, 000...}, the maximum is Lower.countr_zero().
1764+
// Otherwise, the maximum is BitWidth - LCPLength - 1 ({LCP, 100...}).
1765+
return ConstantRange(
1766+
APInt::getZero(BitWidth),
1767+
APInt(BitWidth,
1768+
std::max(BitWidth - LCPLength - 1, Lower.countr_zero()) + 1));
1769+
}
1770+
1771+
ConstantRange ConstantRange::cttz(bool ZeroIsPoison) const {
1772+
if (isEmptySet())
1773+
return getEmpty();
1774+
1775+
unsigned BitWidth = getBitWidth();
1776+
APInt Zero = APInt::getZero(BitWidth);
1777+
if (ZeroIsPoison && contains(Zero)) {
1778+
// ZeroIsPoison is set, and zero is contained. We discern three cases, in
1779+
// which a zero can appear:
1780+
// 1) Lower is zero, handling cases of kind [0, 1), [0, 2), etc.
1781+
// 2) Upper is zero, wrapped set, handling cases of kind [3, 0], etc.
1782+
// 3) Zero contained in a wrapped set, e.g., [3, 2), [3, 1), etc.
1783+
1784+
if (Lower.isZero()) {
1785+
if (Upper == 1) {
1786+
// We have in input interval of kind [0, 1). In this case we cannot
1787+
// really help but return empty-set.
1788+
return getEmpty();
1789+
}
1790+
1791+
// Compute the resulting range by excluding zero from Lower.
1792+
return getUnsignedCountTrailingZerosRange(APInt(BitWidth, 1), Upper);
1793+
} else if (Upper == 1) {
1794+
// Compute the resulting range by excluding zero from Upper.
1795+
return getUnsignedCountTrailingZerosRange(Lower, Zero);
1796+
} else {
1797+
ConstantRange CR1 = getUnsignedCountTrailingZerosRange(Lower, Zero);
1798+
ConstantRange CR2 =
1799+
getUnsignedCountTrailingZerosRange(APInt(BitWidth, 1), Upper);
1800+
return CR1.unionWith(CR2);
1801+
}
1802+
}
1803+
1804+
if (isFullSet())
1805+
return getNonEmpty(Zero, APInt(BitWidth, BitWidth + 1));
1806+
if (!isWrappedSet())
1807+
return getUnsignedCountTrailingZerosRange(Lower, Upper);
1808+
// The range is wrapped. We decompose it into two ranges, [0, Upper) and
1809+
// [Lower, 0).
1810+
// Handle [Lower, 0)
1811+
ConstantRange CR1 = getUnsignedCountTrailingZerosRange(Lower, Zero);
1812+
// Handle [0, Upper)
1813+
ConstantRange CR2 = getUnsignedCountTrailingZerosRange(Zero, Upper);
1814+
return CR1.unionWith(CR2);
1815+
}
1816+
17421817
static ConstantRange getUnsignedPopCountRange(const APInt &Lower,
17431818
const APInt &Upper) {
17441819
assert(!ConstantRange(Lower, Upper).isWrappedSet() &&

llvm/test/Transforms/CorrelatedValuePropagation/range.ll

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,8 +1016,7 @@ define i1 @cttz_fold(i16 %x) {
10161016
; CHECK-NEXT: br i1 [[CMP]], label [[IF:%.*]], label [[ELSE:%.*]]
10171017
; CHECK: if:
10181018
; CHECK-NEXT: [[CTTZ:%.*]] = call i16 @llvm.cttz.i16(i16 [[X]], i1 true)
1019-
; CHECK-NEXT: [[RES:%.*]] = icmp uge i16 [[CTTZ]], 8
1020-
; CHECK-NEXT: ret i1 [[RES]]
1019+
; CHECK-NEXT: ret i1 false
10211020
; CHECK: else:
10221021
; CHECK-NEXT: ret i1 false
10231022
;

llvm/unittests/IR/ConstantRangeTest.cpp

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

2441+
TEST_F(ConstantRangeTest, Cttz) {
2442+
TestUnaryOpExhaustive(
2443+
[](const ConstantRange &CR) { return CR.cttz(); },
2444+
[](const APInt &N) { return APInt(N.getBitWidth(), N.countr_zero()); });
2445+
2446+
TestUnaryOpExhaustive(
2447+
[](const ConstantRange &CR) { return CR.cttz(/*ZeroIsPoison=*/true); },
2448+
[](const APInt &N) -> std::optional<APInt> {
2449+
if (N.isZero())
2450+
return std::nullopt;
2451+
return APInt(N.getBitWidth(), N.countr_zero());
2452+
});
2453+
}
2454+
24412455
TEST_F(ConstantRangeTest, Ctpop) {
24422456
TestUnaryOpExhaustive(
24432457
[](const ConstantRange &CR) { return CR.ctpop(); },

0 commit comments

Comments
 (0)