Skip to content

[ConstantRange] Expand makeAllowedICmpRegion to use samesign to give tighter range#174355

Open
Adar-Dagan wants to merge 8 commits intollvm:mainfrom
Adar-Dagan:MakeConstantRangeUseSameSign
Open

[ConstantRange] Expand makeAllowedICmpRegion to use samesign to give tighter range#174355
Adar-Dagan wants to merge 8 commits intollvm:mainfrom
Adar-Dagan:MakeConstantRangeUseSameSign

Conversation

@Adar-Dagan
Copy link
Contributor

@Adar-Dagan Adar-Dagan commented Jan 5, 2026

After the addition of samesign then instcombine correctly transforms unsigned greater than to use it if it can prove that value is greater than zero and then removes the assume that allowed it to prove that. For example:

define i8 @remove_for_samesign(i8 %x) {
  %gt = icmp sgt i8 %x, 10
  call void @llvm.assume(i1 %gt)
  %gt.zero = icmp sge i8 %x, 0
  call void @llvm.assume(i1 %gt.zero)
  ret i8 %x
}

Is optimized to:

define i8 @remove_for_samesign(i8 %x) {
  %gt= icmp samesign ugt i8 [[X:%.*]], 10
  call void @llvm.assume(i1 %gt)
  ret i8 %x
}

This leads to a wrong range being returned from computeConstantRange because it doesn't look at samesign when constructing a range from assumes, this patch fixes that.

@Adar-Dagan Adar-Dagan requested a review from nikic as a code owner January 5, 2026 04:49
@llvmbot llvmbot added the llvm:ir label Jan 5, 2026
@llvmbot
Copy link
Member

llvmbot commented Jan 5, 2026

@llvm/pr-subscribers-llvm-transforms
@llvm/pr-subscribers-llvm-analysis

@llvm/pr-subscribers-llvm-ir

Author: Adar Dagan (Adar-Dagan)

Changes

Full diff: https://github.com/llvm/llvm-project/pull/174355.diff

2 Files Affected:

  • (modified) llvm/include/llvm/IR/ConstantRange.h (+2-1)
  • (modified) llvm/lib/IR/ConstantRange.cpp (+6-1)
diff --git a/llvm/include/llvm/IR/ConstantRange.h b/llvm/include/llvm/IR/ConstantRange.h
index f4d4f1f555fa4..47b9b60b64b2e 100644
--- a/llvm/include/llvm/IR/ConstantRange.h
+++ b/llvm/include/llvm/IR/ConstantRange.h
@@ -41,6 +41,7 @@ namespace llvm {
 
 class MDNode;
 class raw_ostream;
+class CmpPredicate;
 struct KnownBits;
 
 /// This class represents a range of values.
@@ -106,7 +107,7 @@ class [[nodiscard]] ConstantRange {
   ///
   /// Example: Pred = ult and Other = i8 [2, 5) returns Result = [0, 4)
   LLVM_ABI static ConstantRange
-  makeAllowedICmpRegion(CmpInst::Predicate Pred, const ConstantRange &Other);
+  makeAllowedICmpRegion(CmpPredicate Pred, const ConstantRange &Other);
 
   /// Produce the largest range such that all values in the returned range
   /// satisfy the given predicate with all values contained within Other.
diff --git a/llvm/lib/IR/ConstantRange.cpp b/llvm/lib/IR/ConstantRange.cpp
index 9beaee60d0bc1..87d180f19470e 100644
--- a/llvm/lib/IR/ConstantRange.cpp
+++ b/llvm/lib/IR/ConstantRange.cpp
@@ -23,6 +23,7 @@
 #include "llvm/IR/ConstantRange.h"
 #include "llvm/ADT/APInt.h"
 #include "llvm/Config/llvm-config.h"
+#include "llvm/IR/CmpPredicate.h"
 #include "llvm/IR/Constants.h"
 #include "llvm/IR/InstrTypes.h"
 #include "llvm/IR/Instruction.h"
@@ -106,7 +107,7 @@ std::pair<ConstantRange, ConstantRange> ConstantRange::splitPosNeg() const {
   return {intersectWith(PosFilter), intersectWith(NegFilter)};
 }
 
-ConstantRange ConstantRange::makeAllowedICmpRegion(CmpInst::Predicate Pred,
+ConstantRange ConstantRange::makeAllowedICmpRegion(CmpPredicate Pred,
                                                    const ConstantRange &CR) {
   if (CR.isEmptySet())
     return CR;
@@ -141,6 +142,8 @@ ConstantRange ConstantRange::makeAllowedICmpRegion(CmpInst::Predicate Pred,
     APInt UMin(CR.getUnsignedMin());
     if (UMin.isMaxValue())
       return getEmpty(W);
+    if (Pred.hasSameSign() && CR.isAllNonNegative())
+      return ConstantRange(std::move(UMin) + 1, APInt::getSignedMinValue(W));
     return ConstantRange(std::move(UMin) + 1, APInt::getZero(W));
   }
   case CmpInst::ICMP_SGT: {
@@ -150,6 +153,8 @@ ConstantRange ConstantRange::makeAllowedICmpRegion(CmpInst::Predicate Pred,
     return ConstantRange(std::move(SMin) + 1, APInt::getSignedMinValue(W));
   }
   case CmpInst::ICMP_UGE:
+    if (Pred.hasSameSign() && CR.isAllNonNegative())
+      return getNonEmpty(CR.getUnsignedMin(), APInt::getSignedMinValue(W));
     return getNonEmpty(CR.getUnsignedMin(), APInt::getZero(W));
   case CmpInst::ICMP_SGE:
     return getNonEmpty(CR.getSignedMin(), APInt::getSignedMinValue(W));

@Adar-Dagan
Copy link
Contributor Author

@nikic Hey, I see you were added as a reviewer. Could you review this? or point me to who I should get a review from?

@Adar-Dagan Adar-Dagan force-pushed the MakeConstantRangeUseSameSign branch from 1599826 to 4154699 Compare January 12, 2026 09:46
@llvmbot llvmbot added the llvm:analysis Includes value tracking, cost tables and constant folding label Jan 12, 2026
@Adar-Dagan
Copy link
Contributor Author

@nikic ping?

Copy link
Contributor

@nikic nikic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing PR description, motivation and tests?

@llvmbot llvmbot added llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:transforms labels Jan 15, 2026
@Adar-Dagan Adar-Dagan force-pushed the MakeConstantRangeUseSameSign branch 2 times, most recently from 0f57fca to 74e72d5 Compare January 15, 2026 05:55
@github-actions
Copy link

github-actions bot commented Jan 15, 2026

🪟 Windows x64 Test Results

  • 131364 tests passed
  • 2950 tests skipped

✅ The build succeeded and all tests passed.

Adar Dagan added 2 commits January 15, 2026 10:24
@Adar-Dagan Adar-Dagan force-pushed the MakeConstantRangeUseSameSign branch from 74e72d5 to 94734c3 Compare January 15, 2026 08:24
@dtcxzyw dtcxzyw changed the title Expand makeAllowedICmpRegion to use samesign to give tighter range [ConstantRange] Expand makeAllowedICmpRegion to use samesign to give tighter range Jan 15, 2026
ret i1 %q
}

define i8 @remove_for_samesign(i8 %x) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test doesn't change with your patch: https://godbolt.org/z/84YPE5Kq4

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I meant to show here that instcombine does an optimization that leads to loss of information in computeConstantRange without this patch. I'll remove it and add to the description instead

/// Example: Pred = ult and Other = i8 [2, 5) returns Result = [0, 4)
LLVM_ABI static ConstantRange
makeAllowedICmpRegion(CmpInst::Predicate Pred, const ConstantRange &Other);
makeAllowedICmpRegion(CmpPredicate Pred, const ConstantRange &Other);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd expect this to simplify isImpliedCondCommonOperandWithCR:

if (auto Res = CRImpliesPred(ConstantRange::makeAllowedICmpRegion(LPred, LCR),
RPred))
return Res;
if (LPred.hasSameSign() ^ RPred.hasSameSign()) {
LPred = LPred.hasSameSign() ? ICmpInst::getFlippedSignednessPredicate(LPred)
: LPred.dropSameSign();
RPred = RPred.hasSameSign() ? ICmpInst::getFlippedSignednessPredicate(RPred)
: RPred.dropSameSign();
return CRImpliesPred(ConstantRange::makeAllowedICmpRegion(LPred, LCR),
RPred);
}
return std::nullopt;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately no, we need to do the same to ConstantRange::icmp to simplify this

EXPECT_EQ(10, CR2.getUpper());
}

{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also add an exhaustive test in ConstantRangeTest.cpp to make sure the result is optimal. I also doubt the correctness of Boolean constant ranges.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch, there is an edge case for booleans

Added tests to check my changes

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For exhaustive tests, I mean you can use EnumerateInterestingConstantRanges to enumerate all possible ranges from i1 to i4. Then you use TestRange to check for correctness and optimality. If the optimality is infeasible, please make sure it is always smaller than the result without samesign.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I printed out the elements I create for each test to validate the correct elements are created, attached file with that print
res.txt

@dtcxzyw dtcxzyw requested a review from artagnon January 15, 2026 17:31
@Adar-Dagan
Copy link
Contributor Author

@dtcxzyw I answered your comments, any followups or something else? or could you approve?

Copy link
Contributor

@artagnon artagnon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this is looking pretty good.

@github-actions
Copy link

github-actions bot commented Feb 5, 2026

🐧 Linux x64 Test Results

  • 191087 tests passed
  • 4968 tests skipped

✅ The build succeeded and all tests passed.

Copy link
Member

@dtcxzyw dtcxzyw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. I guess there is still much room for further simplifying the implementation. But I don't have any ideas for now. Please wait for additional approval from other reviewers.

@Adar-Dagan Adar-Dagan force-pushed the MakeConstantRangeUseSameSign branch from e94c036 to 004eb60 Compare February 8, 2026 04:13
Copy link
Contributor

@artagnon artagnon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, there's quite a bit of subtle complexity here. I wonder why this has such a small impact on llvm-opt-benchmark?

@dtcxzyw
Copy link
Member

dtcxzyw commented Feb 9, 2026

I wonder why this has such a small impact on llvm-opt-benchmark?

I guess it will become more useful after we let makeExactICmpRegion to leverage samesign as well.

@Adar-Dagan
Copy link
Contributor Author

@artagnon Could you take another look?

Copy link
Contributor

@artagnon artagnon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This LGTM. Pending sign-off from @nikic.

@Adar-Dagan
Copy link
Contributor Author

@nikic ping

Copy link
Contributor

@antoniofrighetto antoniofrighetto left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess there is still much room for further simplifying the implementation.

Second this concern, implementation looks quite bespoke (and sort of getting harder to follow). Just wondering, don't intend to block this, would it be possible to leverage samesign semantics, thus splitting the input range in positive and negative ones, reuse existing SGT/SLT logic, and union the twos? Or would that still lead to a suboptimal range?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May be worth elaborating on the edge cases?

Comment on lines 131 to 132
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a comment explaining that can never be satisfied for i1?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I imagine there could be other users in the codebase that may start benefiting from using the whole CmpPredicate, so may be worth postponing this change in a follow-up?

@Adar-Dagan
Copy link
Contributor Author

I guess there is still much room for further simplifying the implementation.

Second this concern, implementation looks quite bespoke (and sort of getting harder to follow). Just wondering, don't intend to block this, would it be possible to leverage samesign semantics, thus splitting the input range in positive and negative ones, reuse existing SGT/SLT logic, and union the twos? Or would that still lead to a suboptimal range?

reusing the signed logic and using intersect works

@dtcxzyw
Copy link
Member

dtcxzyw commented Feb 27, 2026

I guess there is still much room for further simplifying the implementation.

Second this concern, implementation looks quite bespoke (and sort of getting harder to follow). Just wondering, don't intend to block this, would it be possible to leverage samesign semantics, thus splitting the input range in positive and negative ones, reuse existing SGT/SLT logic, and union the twos? Or would that still lead to a suboptimal range?

reusing the signed logic and using intersect works

Looks like this introduces significant compile-time overhead (+0.00349118% -> +0.13571089%) :(
Before: dtcxzyw/llvm-opt-benchmark#3514
After: dtcxzyw/llvm-opt-benchmark#3512

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

llvm:analysis Includes value tracking, cost tables and constant folding llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:ir llvm:transforms

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants