[llvm] [SCCP] Add support for trunc nuw range. (PR #152990)
Andreas Jonson via llvm-commits
llvm-commits at lists.llvm.org
Mon Aug 11 08:52:26 PDT 2025
https://github.com/andjo403 updated https://github.com/llvm/llvm-project/pull/152990
>From 409ef47f12b6b7c984c3471dcdf9d77548873b48 Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Sun, 10 Aug 2025 17:47:47 +0200
Subject: [PATCH 1/5] [ConstantRange] add nuw support to truncate (NFC)
---
llvm/include/llvm/IR/ConstantRange.h | 5 +--
llvm/lib/IR/ConstantRange.cpp | 41 ++++++++++++++++++-------
llvm/unittests/IR/ConstantRangeTest.cpp | 39 +++++++++++++++++++++++
3 files changed, 72 insertions(+), 13 deletions(-)
diff --git a/llvm/include/llvm/IR/ConstantRange.h b/llvm/include/llvm/IR/ConstantRange.h
index 9a6a9db65688a..4b2fda364fdf4 100644
--- a/llvm/include/llvm/IR/ConstantRange.h
+++ b/llvm/include/llvm/IR/ConstantRange.h
@@ -380,8 +380,9 @@ class [[nodiscard]] ConstantRange {
/// Return a new range in the specified integer type, which must be
/// strictly smaller than the current type. The returned range will
/// correspond to the possible range of values if the source range had been
- /// truncated to the specified type.
- LLVM_ABI ConstantRange truncate(uint32_t BitWidth) const;
+ /// truncated to the specified type with wrap type \p NoWrapKind.
+ LLVM_ABI ConstantRange truncate(uint32_t BitWidth,
+ unsigned NoWrapKind = 0) const;
/// Make this range have the bit width given by \p BitWidth. The
/// value is zero extended, truncated, or left alone to make it that width.
diff --git a/llvm/lib/IR/ConstantRange.cpp b/llvm/lib/IR/ConstantRange.cpp
index 2fcdbcc6a3db2..b1ea4bbd5ae6b 100644
--- a/llvm/lib/IR/ConstantRange.cpp
+++ b/llvm/lib/IR/ConstantRange.cpp
@@ -872,7 +872,8 @@ ConstantRange ConstantRange::signExtend(uint32_t DstTySize) const {
return ConstantRange(Lower.sext(DstTySize), Upper.sext(DstTySize));
}
-ConstantRange ConstantRange::truncate(uint32_t DstTySize) const {
+ConstantRange ConstantRange::truncate(uint32_t DstTySize,
+ unsigned NoWrapKind) const {
assert(getBitWidth() > DstTySize && "Not a value truncation");
if (isEmptySet())
return getEmpty(DstTySize);
@@ -886,22 +887,36 @@ ConstantRange ConstantRange::truncate(uint32_t DstTySize) const {
// We use the non-wrapped set code to analyze the [Lower, MaxValue) part, and
// then we do the union with [MaxValue, Upper)
if (isUpperWrapped()) {
- // If Upper is greater than or equal to MaxValue(DstTy), it covers the whole
- // truncated range.
- if (Upper.getActiveBits() > DstTySize || Upper.countr_one() == DstTySize)
+ // If Upper is greater than MaxValue(DstTy), it covers the whole truncated
+ // range.
+ if (Upper.getActiveBits() > DstTySize)
return getFull(DstTySize);
- Union = ConstantRange(APInt::getMaxValue(DstTySize),Upper.trunc(DstTySize));
- UpperDiv.setAllBits();
-
- // Union covers the MaxValue case, so return if the remaining range is just
- // MaxValue(DstTy).
- if (LowerDiv == UpperDiv)
- return Union;
+ // For nuw the two parts is: [0, Upper) \/ [Lower, MaxValue(DstTy) + 1]
+ if (NoWrapKind & TruncInst::NoUnsignedWrap) {
+ Union = ConstantRange(APInt::getZero(DstTySize), Upper.trunc(DstTySize));
+ UpperDiv = APInt::getOneBitSet(getBitWidth(), DstTySize);
+ } else {
+ // If Upper is equal to MaxValue(DstTy), it covers the whole truncated
+ // range.
+ if (Upper.countr_one() == DstTySize)
+ return getFull(DstTySize);
+ Union =
+ ConstantRange(APInt::getMaxValue(DstTySize), Upper.trunc(DstTySize));
+ UpperDiv.setAllBits();
+ // Union covers the MaxValue case, so return if the remaining range is
+ // just MaxValue(DstTy).
+ if (LowerDiv == UpperDiv)
+ return Union;
+ }
}
// Chop off the most significant bits that are past the destination bitwidth.
if (LowerDiv.getActiveBits() > DstTySize) {
+ // For trunc nuw if LowerDiv is greater than MaxValue(DstTy), the range is
+ // outside the whole truncated range.
+ if (NoWrapKind & TruncInst::NoUnsignedWrap)
+ return Union;
// Mask to just the signficant bits and subtract from LowerDiv/UpperDiv.
APInt Adjust = LowerDiv & APInt::getBitsSetFrom(getBitWidth(), DstTySize);
LowerDiv -= Adjust;
@@ -913,6 +928,10 @@ ConstantRange ConstantRange::truncate(uint32_t DstTySize) const {
return ConstantRange(LowerDiv.trunc(DstTySize),
UpperDiv.trunc(DstTySize)).unionWith(Union);
+ if (!LowerDiv.isZero() && NoWrapKind & TruncInst::NoUnsignedWrap)
+ return ConstantRange(LowerDiv.trunc(DstTySize), APInt::getZero(DstTySize))
+ .unionWith(Union);
+
// The truncated value wraps around. Check if we can do better than fullset.
if (UpperDivWidth == DstTySize + 1) {
// Clear the MSB so that UpperDiv wraps around.
diff --git a/llvm/unittests/IR/ConstantRangeTest.cpp b/llvm/unittests/IR/ConstantRangeTest.cpp
index bcb5d498c8cb9..6bb6903bae703 100644
--- a/llvm/unittests/IR/ConstantRangeTest.cpp
+++ b/llvm/unittests/IR/ConstantRangeTest.cpp
@@ -451,6 +451,45 @@ TEST_F(ConstantRangeTest, Trunc) {
EXPECT_EQ(SevenOne.truncate(2), ConstantRange(APInt(2, 3), APInt(2, 1)));
}
+TEST_F(ConstantRangeTest, TruncNuw) {
+ auto Range = [](unsigned NumBits, unsigned Lower, unsigned Upper) {
+ return ConstantRange(APInt(NumBits, Lower), APInt(NumBits, Upper));
+ };
+ // trunc([0, 4), 3->2) = full
+ EXPECT_TRUE(
+ Range(3, 0, 4).truncate(2, TruncInst::NoUnsignedWrap).isFullSet());
+ // trunc([0, 3), 3->2) = [0, 3)
+ EXPECT_EQ(Range(3, 0, 3).truncate(2, TruncInst::NoUnsignedWrap),
+ Range(2, 0, 3));
+ // trunc([1, 3), 3->2) = [1, 3)
+ EXPECT_EQ(Range(3, 1, 3).truncate(2, TruncInst::NoUnsignedWrap),
+ Range(2, 1, 3));
+ // trunc([1, 5), 3->2) = [1, 0)
+ EXPECT_EQ(Range(3, 1, 5).truncate(2, TruncInst::NoUnsignedWrap),
+ Range(2, 1, 0));
+ // trunc([4, 7), 3->2) = empty
+ EXPECT_TRUE(
+ Range(3, 4, 7).truncate(2, TruncInst::NoUnsignedWrap).isEmptySet());
+ // trunc([4, 0), 3->2) = empty
+ EXPECT_TRUE(
+ Range(3, 4, 0).truncate(2, TruncInst::NoUnsignedWrap).isEmptySet());
+ // trunc([4, 1), 3->2) = [0, 1)
+ EXPECT_EQ(Range(3, 4, 1).truncate(2, TruncInst::NoUnsignedWrap),
+ Range(2, 0, 1));
+ // trunc([3, 1), 3->2) = [3, 1)
+ EXPECT_EQ(Range(3, 3, 1).truncate(2, TruncInst::NoUnsignedWrap),
+ Range(2, 3, 1));
+ // trunc([3, 0), 3->2) = [3, 0)
+ EXPECT_EQ(Range(3, 3, 0).truncate(2, TruncInst::NoUnsignedWrap),
+ Range(2, 3, 0));
+ // trunc([1, 0), 2->1) = [1, 0)
+ EXPECT_EQ(Range(2, 1, 0).truncate(1, TruncInst::NoUnsignedWrap),
+ Range(1, 1, 0));
+ // trunc([2, 1), 2->1) = [0, 1)
+ EXPECT_EQ(Range(2, 2, 1).truncate(1, TruncInst::NoUnsignedWrap),
+ Range(1, 0, 1));
+}
+
TEST_F(ConstantRangeTest, ZExt) {
ConstantRange ZFull = Full.zeroExtend(20);
ConstantRange ZEmpty = Empty.zeroExtend(20);
>From d64cf91ccda1a866797338650981105d0c860b2f Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Mon, 11 Aug 2025 12:31:33 +0200
Subject: [PATCH 2/5] [SCCP] Add test for trunc nuw range (NFC).
---
.../test/Transforms/SCCP/conditions-ranges.ll | 42 +++++++++++++++++++
1 file changed, 42 insertions(+)
diff --git a/llvm/test/Transforms/SCCP/conditions-ranges.ll b/llvm/test/Transforms/SCCP/conditions-ranges.ll
index 7a64778b60789..6d88a2a1639b3 100644
--- a/llvm/test/Transforms/SCCP/conditions-ranges.ll
+++ b/llvm/test/Transforms/SCCP/conditions-ranges.ll
@@ -1436,6 +1436,48 @@ if.end:
ret i32 0
}
+define void @trunc_nuw_i1_dominating_icmp_ne_0(i8 %x) {
+; CHECK-LABEL: @trunc_nuw_i1_dominating_icmp_ne_0(
+; CHECK-NEXT: [[ICMP:%.*]] = icmp ne i8 [[X:%.*]], 0
+; CHECK-NEXT: br i1 [[ICMP]], label [[BB1:%.*]], label [[BB2:%.*]]
+; CHECK: bb1:
+; CHECK-NEXT: [[C1:%.*]] = trunc nuw i8 [[X]] to i1
+; CHECK-NEXT: call void @use(i1 [[C1]])
+; CHECK-NEXT: ret void
+; CHECK: bb2:
+; CHECK-NEXT: ret void
+;
+ %icmp = icmp ne i8 %x, 0
+ br i1 %icmp, label %bb1, label %bb2
+bb1:
+ %c1 = trunc nuw i8 %x to i1
+ call void @use(i1 %c1)
+ ret void
+bb2:
+ ret void
+}
+
+define void @neg_trunc_i1_dominating_icmp_ne_0(i8 %x) {
+; CHECK-LABEL: @neg_trunc_i1_dominating_icmp_ne_0(
+; CHECK-NEXT: [[ICMP:%.*]] = icmp ne i8 [[X:%.*]], 0
+; CHECK-NEXT: br i1 [[ICMP]], label [[BB1:%.*]], label [[BB2:%.*]]
+; CHECK: bb1:
+; CHECK-NEXT: [[C1:%.*]] = trunc i8 [[X]] to i1
+; CHECK-NEXT: call void @use(i1 [[C1]])
+; CHECK-NEXT: ret void
+; CHECK: bb2:
+; CHECK-NEXT: ret void
+;
+ %icmp = icmp ne i8 %x, 0
+ br i1 %icmp, label %bb1, label %bb2
+bb1:
+ %c1 = trunc i8 %x to i1
+ call void @use(i1 %c1)
+ ret void
+bb2:
+ ret void
+}
+
define i1 @ptr_icmp_data_layout() {
; CHECK-LABEL: @ptr_icmp_data_layout(
; CHECK-NEXT: ret i1 false
>From 553552011f419539f1c710d4a89c589f88aaa1c8 Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Mon, 11 Aug 2025 12:32:53 +0200
Subject: [PATCH 3/5] [SCCP] Add support for trunc nuw range.
---
llvm/lib/Transforms/Utils/SCCPSolver.cpp | 8 ++++++--
llvm/test/Transforms/SCCP/conditions-ranges.ll | 3 +--
2 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/SCCPSolver.cpp b/llvm/lib/Transforms/Utils/SCCPSolver.cpp
index acb9911e8a522..84485176ad4ff 100644
--- a/llvm/lib/Transforms/Utils/SCCPSolver.cpp
+++ b/llvm/lib/Transforms/Utils/SCCPSolver.cpp
@@ -1442,8 +1442,12 @@ void SCCPInstVisitor::visitCastInst(CastInst &I) {
OpSt.asConstantRange(I.getSrcTy(), /*UndefAllowed=*/false);
Type *DestTy = I.getDestTy();
- ConstantRange Res =
- OpRange.castOp(I.getOpcode(), DestTy->getScalarSizeInBits());
+ ConstantRange Res = ConstantRange::getEmpty(DestTy->getScalarSizeInBits());
+ if (auto *Trunc = dyn_cast<TruncInst>(&I))
+ Res = OpRange.truncate(DestTy->getScalarSizeInBits(),
+ Trunc->getNoWrapKind());
+ else
+ Res = OpRange.castOp(I.getOpcode(), DestTy->getScalarSizeInBits());
mergeInValue(LV, &I, ValueLatticeElement::getRange(Res));
} else
markOverdefined(&I);
diff --git a/llvm/test/Transforms/SCCP/conditions-ranges.ll b/llvm/test/Transforms/SCCP/conditions-ranges.ll
index 6d88a2a1639b3..a3cf23bd3ae9a 100644
--- a/llvm/test/Transforms/SCCP/conditions-ranges.ll
+++ b/llvm/test/Transforms/SCCP/conditions-ranges.ll
@@ -1441,8 +1441,7 @@ define void @trunc_nuw_i1_dominating_icmp_ne_0(i8 %x) {
; CHECK-NEXT: [[ICMP:%.*]] = icmp ne i8 [[X:%.*]], 0
; CHECK-NEXT: br i1 [[ICMP]], label [[BB1:%.*]], label [[BB2:%.*]]
; CHECK: bb1:
-; CHECK-NEXT: [[C1:%.*]] = trunc nuw i8 [[X]] to i1
-; CHECK-NEXT: call void @use(i1 [[C1]])
+; CHECK-NEXT: call void @use(i1 true)
; CHECK-NEXT: ret void
; CHECK: bb2:
; CHECK-NEXT: ret void
>From 7851363a171bd01a2531b6acf7a2abc1c3174048 Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Mon, 11 Aug 2025 15:08:31 +0200
Subject: [PATCH 4/5] fixup! [ConstantRange] add nuw support to truncate (NFC)
---
llvm/unittests/IR/ConstantRangeTest.cpp | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/llvm/unittests/IR/ConstantRangeTest.cpp b/llvm/unittests/IR/ConstantRangeTest.cpp
index 6bb6903bae703..0baacafd74839 100644
--- a/llvm/unittests/IR/ConstantRangeTest.cpp
+++ b/llvm/unittests/IR/ConstantRangeTest.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "llvm/IR/ConstantRange.h"
+#include "llvm/ADT/APInt.h"
#include "llvm/ADT/BitVector.h"
#include "llvm/ADT/Sequence.h"
#include "llvm/ADT/SmallBitVector.h"
@@ -490,6 +491,30 @@ TEST_F(ConstantRangeTest, TruncNuw) {
Range(1, 0, 1));
}
+TEST_F(ConstantRangeTest, TruncNuwExhaustive) {
+ unsigned NumBits = 3;
+ EnumerateConstantRanges(4, [&](const ConstantRange &CR) {
+ ConstantRange Trunc = CR.truncate(NumBits, TruncInst::NoUnsignedWrap);
+ DenseSet<APInt> TruncValueSet;
+ ForeachNumInConstantRange(Trunc,
+ [&](const APInt &N) { TruncValueSet.insert(N); });
+ DenseSet<APInt> ValueSet;
+ ForeachNumInConstantRange(CR, [&](const APInt &N) {
+ if (N.isIntN(NumBits)) {
+ ValueSet.insert(N.trunc(NumBits));
+ }
+ });
+ EXPECT_EQ(ValueSet, TruncValueSet);
+ });
+ EnumerateConstantRanges(NumBits, [&](const ConstantRange &CR) {
+ ConstantRange Trunc = CR.truncate(1, TruncInst::NoUnsignedWrap);
+ EXPECT_EQ(CR.contains(APInt::getZero(NumBits)),
+ Trunc.contains(APInt::getZero(1)));
+ EXPECT_EQ(CR.contains(APInt::getOneBitSet(NumBits, 0)),
+ Trunc.contains(APInt::getAllOnes(1)));
+ });
+}
+
TEST_F(ConstantRangeTest, ZExt) {
ConstantRange ZFull = Full.zeroExtend(20);
ConstantRange ZEmpty = Empty.zeroExtend(20);
>From 0f8fb96391f0e281db89ae29c58c32bc71863907 Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Mon, 11 Aug 2025 17:52:10 +0200
Subject: [PATCH 5/5] fixup! fixup! [ConstantRange] add nuw support to truncate
(NFC)
---
llvm/lib/IR/ConstantRange.cpp | 2 +-
llvm/unittests/IR/ConstantRangeTest.cpp | 20 ++++++++------------
2 files changed, 9 insertions(+), 13 deletions(-)
diff --git a/llvm/lib/IR/ConstantRange.cpp b/llvm/lib/IR/ConstantRange.cpp
index b1ea4bbd5ae6b..a01fce3f4037e 100644
--- a/llvm/lib/IR/ConstantRange.cpp
+++ b/llvm/lib/IR/ConstantRange.cpp
@@ -892,7 +892,7 @@ ConstantRange ConstantRange::truncate(uint32_t DstTySize,
if (Upper.getActiveBits() > DstTySize)
return getFull(DstTySize);
- // For nuw the two parts is: [0, Upper) \/ [Lower, MaxValue(DstTy) + 1]
+ // For nuw the two parts are: [0, Upper) \/ [Lower, MaxValue(DstTy) + 1]
if (NoWrapKind & TruncInst::NoUnsignedWrap) {
Union = ConstantRange(APInt::getZero(DstTySize), Upper.trunc(DstTySize));
UpperDiv = APInt::getOneBitSet(getBitWidth(), DstTySize);
diff --git a/llvm/unittests/IR/ConstantRangeTest.cpp b/llvm/unittests/IR/ConstantRangeTest.cpp
index 0baacafd74839..90399ac6c68d7 100644
--- a/llvm/unittests/IR/ConstantRangeTest.cpp
+++ b/llvm/unittests/IR/ConstantRangeTest.cpp
@@ -492,25 +492,21 @@ TEST_F(ConstantRangeTest, TruncNuw) {
}
TEST_F(ConstantRangeTest, TruncNuwExhaustive) {
- unsigned NumBits = 3;
EnumerateConstantRanges(4, [&](const ConstantRange &CR) {
+ unsigned NumBits = 3;
ConstantRange Trunc = CR.truncate(NumBits, TruncInst::NoUnsignedWrap);
- DenseSet<APInt> TruncValueSet;
- ForeachNumInConstantRange(Trunc,
- [&](const APInt &N) { TruncValueSet.insert(N); });
- DenseSet<APInt> ValueSet;
+ SmallBitVector Elems(1 << NumBits);
ForeachNumInConstantRange(CR, [&](const APInt &N) {
- if (N.isIntN(NumBits)) {
- ValueSet.insert(N.trunc(NumBits));
- }
+ if (N.isIntN(NumBits))
+ Elems.set(N.getZExtValue());
});
- EXPECT_EQ(ValueSet, TruncValueSet);
+ TestRange(Trunc, Elems, PreferSmallest, {CR});
});
- EnumerateConstantRanges(NumBits, [&](const ConstantRange &CR) {
+ EnumerateConstantRanges(3, [&](const ConstantRange &CR) {
ConstantRange Trunc = CR.truncate(1, TruncInst::NoUnsignedWrap);
- EXPECT_EQ(CR.contains(APInt::getZero(NumBits)),
+ EXPECT_EQ(CR.contains(APInt::getZero(3)),
Trunc.contains(APInt::getZero(1)));
- EXPECT_EQ(CR.contains(APInt::getOneBitSet(NumBits, 0)),
+ EXPECT_EQ(CR.contains(APInt::getOneBitSet(3, 0)),
Trunc.contains(APInt::getAllOnes(1)));
});
}
More information about the llvm-commits
mailing list