[llvm] [ValueTracking] Extend computeConstantRange for add/sub, sext/zext/trunc (PR #181110)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Feb 12 01:59:25 PST 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-analysis
Author: Guy David (guy-david)
<details>
<summary>Changes</summary>
Recursively compute operand ranges for add/sub and propagate ranges through sext/zext/trunc.
For add/sub, the computed range is intersected with any existing range from setLimitsForBinOp, and NSW/NUW flags are used via addWithNoWrap/subWithNoWrap to tighten bounds.
The motivation is to enable further folding of reduce.add expressions in comparisons, where the result range can be bounded by the input element ranges.
Compile-time impact on llvm-test-suite is <0.1% mean (5 rounds).
---
Full diff: https://github.com/llvm/llvm-project/pull/181110.diff
3 Files Affected:
- (modified) llvm/lib/Analysis/ValueTracking.cpp (+28)
- (modified) llvm/test/Analysis/BasicAA/range.ll (+66)
- (modified) llvm/unittests/Analysis/ValueTrackingTest.cpp (+92)
``````````diff
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 8761b7bcb51a2..acbb50b8cae53 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -10242,6 +10242,34 @@ ConstantRange llvm::computeConstantRange(const Value *V, bool ForSigned,
// TODO: Return ConstantRange.
setLimitsForBinOp(*BO, Lower, Upper, IIQ, ForSigned);
CR = ConstantRange::getNonEmpty(Lower, Upper);
+ if (BO->getOpcode() == Instruction::Add ||
+ BO->getOpcode() == Instruction::Sub) {
+ ConstantRange LHS = computeConstantRange(
+ BO->getOperand(0), ForSigned, UseInstrInfo, AC, CtxI, DT, Depth + 1);
+ ConstantRange RHS = computeConstantRange(
+ BO->getOperand(1), ForSigned, UseInstrInfo, AC, CtxI, DT, Depth + 1);
+ unsigned NoWrapKind = 0;
+ if (IIQ.hasNoUnsignedWrap(BO))
+ NoWrapKind |= OverflowingBinaryOperator::NoUnsignedWrap;
+ if (IIQ.hasNoSignedWrap(BO))
+ NoWrapKind |= OverflowingBinaryOperator::NoSignedWrap;
+ ConstantRange OpCR = BO->getOpcode() == Instruction::Add
+ ? LHS.addWithNoWrap(RHS, NoWrapKind)
+ : LHS.subWithNoWrap(RHS, NoWrapKind);
+ CR = CR.intersectWith(OpCR);
+ }
+ } else if (auto *SExt = dyn_cast<SExtInst>(V)) {
+ CR = computeConstantRange(SExt->getOperand(0), ForSigned, UseInstrInfo, AC,
+ CtxI, DT, Depth + 1)
+ .signExtend(BitWidth);
+ } else if (auto *ZExt = dyn_cast<ZExtInst>(V)) {
+ CR = computeConstantRange(ZExt->getOperand(0), ForSigned, UseInstrInfo, AC,
+ CtxI, DT, Depth + 1)
+ .zeroExtend(BitWidth);
+ } else if (auto *Trunc = dyn_cast<TruncInst>(V)) {
+ CR = computeConstantRange(Trunc->getOperand(0), ForSigned, UseInstrInfo, AC,
+ CtxI, DT, Depth + 1)
+ .truncate(BitWidth);
} else if (auto *II = dyn_cast<IntrinsicInst>(V))
CR = getRangeForIntrinsic(*II, UseInstrInfo);
else if (auto *SI = dyn_cast<SelectInst>(V)) {
diff --git a/llvm/test/Analysis/BasicAA/range.ll b/llvm/test/Analysis/BasicAA/range.ll
index e5dfb60c8b878..a41fd63ee52f6 100644
--- a/llvm/test/Analysis/BasicAA/range.ll
+++ b/llvm/test/Analysis/BasicAA/range.ll
@@ -271,6 +271,72 @@ entry:
ret i32 %load_
}
+; CHECK-LABEL: Function: zext_propagate_range
+; CHECK: NoAlias: i32* %gep, i32* %gep128
+define void @zext_propagate_range(ptr %p, i8 %idx) {
+ %narrow = and i8 %idx, 127
+ %wide = zext i8 %narrow to i64
+ %gep = getelementptr i32, ptr %p, i64 %wide
+ %gep128 = getelementptr i32, ptr %p, i64 128
+ load i32, ptr %gep
+ load i32, ptr %gep128
+ ret void
+}
+
+; CHECK-LABEL: Function: sext_propagate_range
+; CHECK: NoAlias: i32* %gep, i32* %gep128
+define void @sext_propagate_range(ptr %p, i8 %idx) {
+ %clamped = and i8 %idx, 100
+ %wide = sext i8 %clamped to i64
+ %gep = getelementptr i32, ptr %p, i64 %wide
+ %gep128 = getelementptr i32, ptr %p, i64 128
+ load i32, ptr %gep
+ load i32, ptr %gep128
+ ret void
+}
+
+; CHECK-LABEL: Function: zext_add_range
+; CHECK: NoAlias: i32* %gep, i32* %gep512
+define void @zext_add_range(ptr %p, i8 %x, i8 %y) {
+ %ext.x = zext i8 %x to i64
+ %ext.y = zext i8 %y to i64
+ %sum = add i64 %ext.x, %ext.y
+ %gep = getelementptr i32, ptr %p, i64 %sum
+ %gep512 = getelementptr i32, ptr %p, i64 512
+ load i32, ptr %gep
+ load i32, ptr %gep512
+ ret void
+}
+
+; CHECK-LABEL: Function: zext_sub_range
+; CHECK: NoAlias: i32* %gep, i32* %gep256
+; CHECK: NoAlias: i32* %gep, i32* %gepneg256
+define void @zext_sub_range(ptr %p, i8 %x, i8 %y) {
+ %ext.x = zext i8 %x to i64
+ %ext.y = zext i8 %y to i64
+ %diff = sub i64 %ext.x, %ext.y
+ %gep = getelementptr i32, ptr %p, i64 %diff
+ %gep256 = getelementptr i32, ptr %p, i64 256
+ %gepneg256 = getelementptr i32, ptr %p, i64 -256
+ load i32, ptr %gep
+ load i32, ptr %gep256
+ load i32, ptr %gepneg256
+ ret void
+}
+
+; CHECK-LABEL: Function: trunc_propagate_range
+; CHECK: NoAlias: i32* %gep, i32* %gep64
+define void @trunc_propagate_range(ptr %p, i64 %idx) {
+ %clamped = and i64 %idx, 63
+ %narrow = trunc i64 %clamped to i8
+ %wide = zext i8 %narrow to i64
+ %gep = getelementptr i32, ptr %p, i64 %wide
+ %gep64 = getelementptr i32, ptr %p, i64 64
+ load i32, ptr %gep
+ load i32, ptr %gep64
+ ret void
+}
+
declare void @llvm.assume(i1)
!0 = !{ i32 0, i32 2 }
diff --git a/llvm/unittests/Analysis/ValueTrackingTest.cpp b/llvm/unittests/Analysis/ValueTrackingTest.cpp
index 6229d408de2a8..2ee45dccc6595 100644
--- a/llvm/unittests/Analysis/ValueTrackingTest.cpp
+++ b/llvm/unittests/Analysis/ValueTrackingTest.cpp
@@ -3394,6 +3394,98 @@ TEST_F(ValueTrackingTest, ComputeConstantRange) {
// If we don't know the value of x.2, we don't know the value of x.1.
EXPECT_TRUE(CR1.isFullSet());
}
+ {
+ auto M = parseModule(R"(
+ define void @test(i8 %x) {
+ %sext = sext i8 %x to i32
+ %zext = zext i8 %x to i32
+ ret void
+ })");
+ Function *F = M->getFunction("test");
+ AssumptionCache AC(*F);
+ Instruction *SExt = &findInstructionByName(F, "sext");
+ Instruction *ZExt = &findInstructionByName(F, "zext");
+ ConstantRange SExtCR = computeConstantRange(SExt, true, true, &AC, SExt);
+ EXPECT_EQ(SExtCR.getSignedMin().getSExtValue(), -128);
+ EXPECT_EQ(SExtCR.getSignedMax().getSExtValue(), 127);
+ ConstantRange ZExtCR = computeConstantRange(ZExt, false, true, &AC, ZExt);
+ EXPECT_EQ(ZExtCR.getUnsignedMin().getZExtValue(), 0u);
+ EXPECT_EQ(ZExtCR.getUnsignedMax().getZExtValue(), 255u);
+ }
+ {
+ auto M = parseModule(R"(
+ define i32 @test(i8 %x) {
+ %ext = sext i8 %x to i32
+ %add = add nsw i32 %ext, 10
+ ret i32 %add
+ })");
+ Function *F = M->getFunction("test");
+ AssumptionCache AC(*F);
+ Instruction *Add = &findInstructionByName(F, "add");
+ ConstantRange CR = computeConstantRange(Add, true, true, &AC, Add);
+ EXPECT_EQ(CR.getSignedMin().getSExtValue(), -118);
+ EXPECT_EQ(CR.getSignedMax().getSExtValue(), 137);
+ }
+ {
+ auto M = parseModule(R"(
+ define i32 @test(i8 %x, i8 %y) {
+ %ext.x = zext i8 %x to i32
+ %ext.y = zext i8 %y to i32
+ %sub = sub i32 %ext.x, %ext.y
+ ret i32 %sub
+ })");
+ Function *F = M->getFunction("test");
+ AssumptionCache AC(*F);
+ Instruction *Sub = &findInstructionByName(F, "sub");
+ ConstantRange CR = computeConstantRange(Sub, true, true, &AC, Sub);
+ EXPECT_EQ(CR.getSignedMin().getSExtValue(), -255);
+ EXPECT_EQ(CR.getSignedMax().getSExtValue(), 255);
+ }
+ {
+ // trunc
+ auto M = parseModule(R"(
+ define void @test(i32 %x) {
+ %narrow = trunc i32 %x to i8
+ ret void
+ })");
+ Function *F = M->getFunction("test");
+ AssumptionCache AC(*F);
+ Instruction *Trunc = &findInstructionByName(F, "narrow");
+ ConstantRange CR = computeConstantRange(Trunc, false, true, &AC, Trunc);
+ EXPECT_TRUE(CR.isFullSet());
+ EXPECT_EQ(CR.getBitWidth(), 8u);
+ }
+ {
+ // trunc with restricted input range
+ auto M = parseModule(R"(
+ define i8 @test(i32 %x) {
+ %clamped = and i32 %x, 127
+ %narrow = trunc i32 %clamped to i8
+ ret i8 %narrow
+ })");
+ Function *F = M->getFunction("test");
+ AssumptionCache AC(*F);
+ Instruction *Trunc = &findInstructionByName(F, "narrow");
+ ConstantRange CR = computeConstantRange(Trunc, false, true, &AC, Trunc);
+ EXPECT_EQ(CR.getUnsignedMin().getZExtValue(), 0u);
+ EXPECT_EQ(CR.getUnsignedMax().getZExtValue(), 127u);
+ }
+ {
+ // Chained adds from i1
+ auto M = parseModule(R"(
+ define i32 @test(i1 %x) {
+ %ext = sext i1 %x to i32
+ %add1 = add nsw i32 %ext, %ext
+ %add2 = add nsw i32 %add1, %ext
+ ret i32 %add2
+ })");
+ Function *F = M->getFunction("test");
+ AssumptionCache AC(*F);
+ Instruction *Add2 = &findInstructionByName(F, "add2");
+ ConstantRange CR = computeConstantRange(Add2, true, true, &AC, Add2);
+ EXPECT_EQ(CR.getSignedMin().getSExtValue(), -3);
+ EXPECT_EQ(CR.getSignedMax().getSExtValue(), 0);
+ }
}
struct FindAllocaForValueTestParams {
``````````
</details>
https://github.com/llvm/llvm-project/pull/181110
More information about the llvm-commits
mailing list