[llvm] [InstSimplify] Fold bounds on non-underflowing subtraction (PR #218085)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 21 18:51:48 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-analysis
Author: SomeFlyingThing
<details>
<summary>Changes</summary>
If X - Y cannot unsigned-underflow, X - Y is at most X. Use known bounds on X to fold unsigned comparisons of the subtraction, including bounds established by dominating assumptions.
Fixes #<!-- -->91619.
LLM assisted pr
---
Full diff: https://github.com/llvm/llvm-project/pull/218085.diff
2 Files Affected:
- (modified) llvm/lib/Analysis/InstructionSimplify.cpp (+18)
- (added) llvm/test/Transforms/InstSimplify/icmp-subtraction-bounds.ll (+162)
``````````diff
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index c76f4d2e9f327..b057c38c8008e 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -3343,6 +3343,24 @@ static Value *simplifyICmpWithBinOpOnLHS(CmpPredicate Pred, BinaryOperator *LBO,
return getTrue(ITy);
}
+ // If X - Y does not unsigned underflow, then X - Y <=u X. Use a known
+ // upper bound for X to simplify the comparison.
+ if (ICmpInst::isUnsigned(Pred) &&
+ LBO->getOpcode() == Instruction::Sub && MaxRecurse) {
+ Value *X = LBO->getOperand(0), *Y = LBO->getOperand(1);
+ if (Q.IIQ.hasNoUnsignedWrap(LBO) ||
+ isICmpTrue(ICmpInst::ICMP_ULE, Y, X, Q, MaxRecurse - 1)) {
+ CmpPredicate BoundPred = Pred;
+ bool Result = true;
+ if (Pred == ICmpInst::ICMP_UGT || Pred == ICmpInst::ICMP_UGE) {
+ BoundPred = ICmpInst::getInversePredicate(Pred);
+ Result = false;
+ }
+ if (isICmpTrue(BoundPred, X, RHS, Q, MaxRecurse - 1))
+ return ConstantInt::getBool(ITy, Result);
+ }
+ }
+
// (sub C, X) == X, C is odd --> false
// (sub C, X) != X, C is odd --> true
if (match(LBO, m_Sub(m_APIntAllowPoison(C), m_Specific(RHS))) &&
diff --git a/llvm/test/Transforms/InstSimplify/icmp-subtraction-bounds.ll b/llvm/test/Transforms/InstSimplify/icmp-subtraction-bounds.ll
new file mode 100644
index 0000000000000..b0fad3ef11370
--- /dev/null
+++ b/llvm/test/Transforms/InstSimplify/icmp-subtraction-bounds.ll
@@ -0,0 +1,162 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes=instsimplify -S | FileCheck %s
+
+declare void @llvm.assume(i1 noundef)
+
+define i1 @issue_91619(i64 %a, i64 %b) {
+; CHECK-LABEL: define i1 @issue_91619(
+; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]]) {
+; CHECK-NEXT: [[A_LE_B:%.*]] = icmp ule i64 [[A]], [[B]]
+; CHECK-NEXT: call void @llvm.assume(i1 [[A_LE_B]])
+; CHECK-NEXT: [[B_LE_MAX:%.*]] = icmp ule i64 [[B]], 9223372036854775807
+; CHECK-NEXT: call void @llvm.assume(i1 [[B_LE_MAX]])
+; CHECK-NEXT: ret i1 true
+;
+ %a.le.b = icmp ule i64 %a, %b
+ call void @llvm.assume(i1 %a.le.b)
+ %b.le.max = icmp ule i64 %b, 9223372036854775807
+ call void @llvm.assume(i1 %b.le.max)
+ %sub = sub i64 %b, %a
+ %cmp = icmp ule i64 %sub, 9223372036854775807
+ ret i1 %cmp
+}
+
+define i1 @sub_nuw_ule(i8 %x, i8 %y, i8 %limit) {
+; CHECK-LABEL: define i1 @sub_nuw_ule(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[LIMIT:%.*]]) {
+; CHECK-NEXT: [[X_LE_LIMIT:%.*]] = icmp ule i8 [[X]], [[LIMIT]]
+; CHECK-NEXT: call void @llvm.assume(i1 [[X_LE_LIMIT]])
+; CHECK-NEXT: ret i1 true
+;
+ %sub = sub nuw i8 %x, %y
+ %x.le.limit = icmp ule i8 %x, %limit
+ call void @llvm.assume(i1 %x.le.limit)
+ %cmp = icmp ule i8 %sub, %limit
+ ret i1 %cmp
+}
+
+define i1 @sub_nuw_ugt(i8 %x, i8 %y, i8 %limit) {
+; CHECK-LABEL: define i1 @sub_nuw_ugt(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[LIMIT:%.*]]) {
+; CHECK-NEXT: [[X_LE_LIMIT:%.*]] = icmp ule i8 [[X]], [[LIMIT]]
+; CHECK-NEXT: call void @llvm.assume(i1 [[X_LE_LIMIT]])
+; CHECK-NEXT: ret i1 false
+;
+ %sub = sub nuw i8 %x, %y
+ %x.le.limit = icmp ule i8 %x, %limit
+ call void @llvm.assume(i1 %x.le.limit)
+ %cmp = icmp ugt i8 %sub, %limit
+ ret i1 %cmp
+}
+
+define i1 @sub_nuw_ult(i8 %x, i8 %y, i8 %limit) {
+; CHECK-LABEL: define i1 @sub_nuw_ult(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[LIMIT:%.*]]) {
+; CHECK-NEXT: [[X_LT_LIMIT:%.*]] = icmp ult i8 [[X]], [[LIMIT]]
+; CHECK-NEXT: call void @llvm.assume(i1 [[X_LT_LIMIT]])
+; CHECK-NEXT: ret i1 true
+;
+ %sub = sub nuw i8 %x, %y
+ %x.lt.limit = icmp ult i8 %x, %limit
+ call void @llvm.assume(i1 %x.lt.limit)
+ %cmp = icmp ult i8 %sub, %limit
+ ret i1 %cmp
+}
+
+define i1 @sub_nuw_uge(i8 %x, i8 %y, i8 %limit) {
+; CHECK-LABEL: define i1 @sub_nuw_uge(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[LIMIT:%.*]]) {
+; CHECK-NEXT: [[X_LT_LIMIT:%.*]] = icmp ult i8 [[X]], [[LIMIT]]
+; CHECK-NEXT: call void @llvm.assume(i1 [[X_LT_LIMIT]])
+; CHECK-NEXT: ret i1 false
+;
+ %sub = sub nuw i8 %x, %y
+ %x.lt.limit = icmp ult i8 %x, %limit
+ call void @llvm.assume(i1 %x.lt.limit)
+ %cmp = icmp uge i8 %sub, %limit
+ ret i1 %cmp
+}
+
+define <2 x i1> @sub_nuw_vector(<2 x i8> %x, <2 x i8> %y) {
+; CHECK-LABEL: define <2 x i1> @sub_nuw_vector(
+; CHECK-SAME: <2 x i8> [[X:%.*]], <2 x i8> [[Y:%.*]]) {
+; CHECK-NEXT: ret <2 x i1> splat (i1 true)
+;
+ %sub = sub nuw <2 x i8> %x, %y
+ %cmp = icmp ule <2 x i8> %sub, %x
+ ret <2 x i1> %cmp
+}
+
+define i1 @sub_nuw_swapped(i8 %x, i8 %y, i8 %limit) {
+; CHECK-LABEL: define i1 @sub_nuw_swapped(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[LIMIT:%.*]]) {
+; CHECK-NEXT: [[X_LE_LIMIT:%.*]] = icmp ule i8 [[X]], [[LIMIT]]
+; CHECK-NEXT: call void @llvm.assume(i1 [[X_LE_LIMIT]])
+; CHECK-NEXT: ret i1 true
+;
+ %sub = sub nuw i8 %x, %y
+ %x.le.limit = icmp ule i8 %x, %limit
+ call void @llvm.assume(i1 %x.le.limit)
+ %cmp = icmp uge i8 %limit, %sub
+ ret i1 %cmp
+}
+
+define i1 @no_nuw(i8 %x, i8 %y, i8 %limit) {
+; CHECK-LABEL: define i1 @no_nuw(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[LIMIT:%.*]]) {
+; CHECK-NEXT: [[SUB:%.*]] = sub i8 [[X]], [[Y]]
+; CHECK-NEXT: [[X_LE_LIMIT:%.*]] = icmp ule i8 [[X]], [[LIMIT]]
+; CHECK-NEXT: call void @llvm.assume(i1 [[X_LE_LIMIT]])
+; CHECK-NEXT: [[CMP:%.*]] = icmp ule i8 [[SUB]], [[LIMIT]]
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %sub = sub i8 %x, %y
+ %x.le.limit = icmp ule i8 %x, %limit
+ call void @llvm.assume(i1 %x.le.limit)
+ %cmp = icmp ule i8 %sub, %limit
+ ret i1 %cmp
+}
+
+define i1 @no_upper_bound(i8 %x, i8 %y, i8 %limit) {
+; CHECK-LABEL: define i1 @no_upper_bound(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[LIMIT:%.*]]) {
+; CHECK-NEXT: [[SUB:%.*]] = sub nuw i8 [[X]], [[Y]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp ule i8 [[SUB]], [[LIMIT]]
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %sub = sub nuw i8 %x, %y
+ %cmp = icmp ule i8 %sub, %limit
+ ret i1 %cmp
+}
+
+define i1 @strict_needs_strict_bound(i8 %x, i8 %y, i8 %limit) {
+; CHECK-LABEL: define i1 @strict_needs_strict_bound(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[LIMIT:%.*]]) {
+; CHECK-NEXT: [[SUB:%.*]] = sub nuw i8 [[X]], [[Y]]
+; CHECK-NEXT: [[X_LE_LIMIT:%.*]] = icmp ule i8 [[X]], [[LIMIT]]
+; CHECK-NEXT: call void @llvm.assume(i1 [[X_LE_LIMIT]])
+; CHECK-NEXT: [[CMP:%.*]] = icmp ult i8 [[SUB]], [[LIMIT]]
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %sub = sub nuw i8 %x, %y
+ %x.le.limit = icmp ule i8 %x, %limit
+ call void @llvm.assume(i1 %x.le.limit)
+ %cmp = icmp ult i8 %sub, %limit
+ ret i1 %cmp
+}
+
+define i1 @signed_predicate(i8 %x, i8 %y, i8 %limit) {
+; CHECK-LABEL: define i1 @signed_predicate(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[LIMIT:%.*]]) {
+; CHECK-NEXT: [[SUB:%.*]] = sub nuw i8 [[X]], [[Y]]
+; CHECK-NEXT: [[X_LE_LIMIT:%.*]] = icmp sle i8 [[X]], [[LIMIT]]
+; CHECK-NEXT: call void @llvm.assume(i1 [[X_LE_LIMIT]])
+; CHECK-NEXT: [[CMP:%.*]] = icmp sle i8 [[SUB]], [[LIMIT]]
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %sub = sub nuw i8 %x, %y
+ %x.le.limit = icmp sle i8 %x, %limit
+ call void @llvm.assume(i1 %x.le.limit)
+ %cmp = icmp sle i8 %sub, %limit
+ ret i1 %cmp
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/218085
More information about the llvm-commits
mailing list