[llvm] [ConstraintElim] Use range-based NUW/NSW inferrence for Add. (PR #221487)

Florian Hahn via llvm-commits llvm-commits at lists.llvm.org
Mon Sep 7 14:05:45 PDT 2026


https://github.com/fhahn updated https://github.com/llvm/llvm-project/pull/221487

>From d73409e9649964201cb3df7b4130a3b25182d163 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Fri, 4 Sep 2026 20:01:46 +0100
Subject: [PATCH 1/4] Add tests

add test
---
 .../ConstraintElimination/add-nsw.ll          | 270 ++++++++++++++++++
 1 file changed, 270 insertions(+)

diff --git a/llvm/test/Transforms/ConstraintElimination/add-nsw.ll b/llvm/test/Transforms/ConstraintElimination/add-nsw.ll
index 4b8ac09801ab2..98c070893d257 100644
--- a/llvm/test/Transforms/ConstraintElimination/add-nsw.ll
+++ b/llvm/test/Transforms/ConstraintElimination/add-nsw.ll
@@ -897,3 +897,273 @@ entry:
   %c = icmp ult i32 %sub, %a
   ret i1 %c
 }
+
+; Tests below need to materialize the nsw flag first.
+
+define i1 @add_neg_1_slt(i64 %x, i64 %b) {
+; CHECK-LABEL: @add_neg_1_slt(
+; CHECK-NEXT:    [[X_NON_NEG:%.*]] = icmp sge i64 [[X:%.*]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[X_NON_NEG]])
+; CHECK-NEXT:    [[C:%.*]] = icmp slt i64 [[X]], [[B:%.*]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    [[SUB:%.*]] = add i64 [[X]], -1
+; CHECK-NEXT:    [[T:%.*]] = icmp slt i64 [[SUB]], [[B]]
+; CHECK-NEXT:    ret i1 [[T]]
+;
+  %x.non.neg = icmp sge i64 %x, 0
+  call void @llvm.assume(i1 %x.non.neg)
+  %c = icmp slt i64 %x, %b
+  call void @llvm.assume(i1 %c)
+  %sub = add i64 %x, -1
+  %t = icmp slt i64 %sub, %b
+  ret i1 %t
+}
+
+define i1 @add_neg_20_slt(i64 %x, i64 %b) {
+; CHECK-LABEL: @add_neg_20_slt(
+; CHECK-NEXT:    [[X_NON_NEG:%.*]] = icmp sge i64 [[X:%.*]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[X_NON_NEG]])
+; CHECK-NEXT:    [[C:%.*]] = icmp slt i64 [[X]], [[B:%.*]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    [[SUB:%.*]] = add i64 [[X]], -20
+; CHECK-NEXT:    [[T:%.*]] = icmp slt i64 [[SUB]], [[B]]
+; CHECK-NEXT:    ret i1 [[T]]
+;
+  %x.non.neg = icmp sge i64 %x, 0
+  call void @llvm.assume(i1 %x.non.neg)
+  %c = icmp slt i64 %x, %b
+  call void @llvm.assume(i1 %c)
+  %sub = add i64 %x, -20
+  %t = icmp slt i64 %sub, %b
+  ret i1 %t
+}
+
+define i1 @add_neg_1_nonneg_from_ult(i64 %x, i64 %c) {
+; CHECK-LABEL: @add_neg_1_nonneg_from_ult(
+; CHECK-NEXT:    [[X_NON_NEG:%.*]] = icmp sge i64 [[C:%.*]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[X_NON_NEG]])
+; CHECK-NEXT:    [[B:%.*]] = icmp ult i64 [[X:%.*]], [[C]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[B]])
+; CHECK-NEXT:    [[SUB:%.*]] = add i64 [[X]], -1
+; CHECK-NEXT:    [[T:%.*]] = icmp slt i64 [[SUB]], [[C]]
+; CHECK-NEXT:    ret i1 [[T]]
+;
+  %x.non.neg = icmp sge i64 %c, 0
+  call void @llvm.assume(i1 %x.non.neg)
+  %b = icmp ult i64 %x, %c
+  call void @llvm.assume(i1 %b)
+  %sub = add i64 %x, -1
+  %t = icmp slt i64 %sub, %c
+  ret i1 %t
+}
+
+define i1 @add_neg_1_no_nonneg(i64 %x, i64 %b) {
+; CHECK-LABEL: @add_neg_1_no_nonneg(
+; CHECK-NEXT:    [[C:%.*]] = icmp slt i64 [[X:%.*]], [[B:%.*]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    [[SUB:%.*]] = add i64 [[X]], -1
+; CHECK-NEXT:    [[T:%.*]] = icmp slt i64 [[SUB]], [[B]]
+; CHECK-NEXT:    ret i1 [[T]]
+;
+  %c = icmp slt i64 %x, %b
+  call void @llvm.assume(i1 %c)
+  %sub = add i64 %x, -1
+  %t = icmp slt i64 %sub, %b
+  ret i1 %t
+}
+
+define i1 @add_non_positive_slt(i64 %x, i64 %y, i64 %b) {
+; CHECK-LABEL: @add_non_positive_slt(
+; CHECK-NEXT:    [[X_NON_NEG:%.*]] = icmp sge i64 [[X:%.*]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[X_NON_NEG]])
+; CHECK-NEXT:    [[Y_NON_POS:%.*]] = icmp sle i64 [[Y:%.*]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[Y_NON_POS]])
+; CHECK-NEXT:    [[C:%.*]] = icmp slt i64 [[X]], [[B:%.*]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    [[SUB:%.*]] = add i64 [[X]], [[Y]]
+; CHECK-NEXT:    [[T:%.*]] = icmp slt i64 [[SUB]], [[B]]
+; CHECK-NEXT:    ret i1 [[T]]
+;
+  %x.non.neg = icmp sge i64 %x, 0
+  call void @llvm.assume(i1 %x.non.neg)
+  %y.non.pos = icmp sle i64 %y, 0
+  call void @llvm.assume(i1 %y.non.pos)
+  %c = icmp slt i64 %x, %b
+  call void @llvm.assume(i1 %c)
+  %sub = add i64 %x, %y
+  %t = icmp slt i64 %sub, %b
+  ret i1 %t
+}
+
+define i1 @add_non_positive_slt_commuted(i64 %x, i64 %y, i64 %b) {
+; CHECK-LABEL: @add_non_positive_slt_commuted(
+; CHECK-NEXT:    [[X_NON_NEG:%.*]] = icmp sge i64 [[X:%.*]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[X_NON_NEG]])
+; CHECK-NEXT:    [[Y_NON_POS:%.*]] = icmp sle i64 [[Y:%.*]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[Y_NON_POS]])
+; CHECK-NEXT:    [[C:%.*]] = icmp slt i64 [[X]], [[B:%.*]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    [[SUB:%.*]] = add i64 [[Y]], [[X]]
+; CHECK-NEXT:    [[T:%.*]] = icmp slt i64 [[SUB]], [[B]]
+; CHECK-NEXT:    ret i1 [[T]]
+;
+  %x.non.neg = icmp sge i64 %x, 0
+  call void @llvm.assume(i1 %x.non.neg)
+  %y.non.pos = icmp sle i64 %y, 0
+  call void @llvm.assume(i1 %y.non.pos)
+  %c = icmp slt i64 %x, %b
+  call void @llvm.assume(i1 %c)
+  %sub = add i64 %y, %x
+  %t = icmp slt i64 %sub, %b
+  ret i1 %t
+}
+
+define i1 @add_non_positive_slt_hoisted(i64 %x, i64 %y, i64 %b) {
+; CHECK-LABEL: @add_non_positive_slt_hoisted(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[X_NON_NEG:%.*]] = icmp sge i64 [[X:%.*]], 0
+; CHECK-NEXT:    [[Y_NON_POS:%.*]] = icmp sle i64 [[Y:%.*]], 0
+; CHECK-NEXT:    [[C:%.*]] = icmp slt i64 [[X]], [[B:%.*]]
+; CHECK-NEXT:    [[AND_1:%.*]] = and i1 [[X_NON_NEG]], [[Y_NON_POS]]
+; CHECK-NEXT:    [[AND_2:%.*]] = and i1 [[AND_1]], [[C]]
+; CHECK-NEXT:    [[SUB:%.*]] = add i64 [[X]], [[Y]]
+; CHECK-NEXT:    br i1 [[AND_2]], label [[IF_END:%.*]], label [[EXIT:%.*]]
+; CHECK:       if.end:
+; CHECK-NEXT:    [[T:%.*]] = icmp slt i64 [[SUB]], [[B]]
+; CHECK-NEXT:    ret i1 [[T]]
+; CHECK:       exit:
+; CHECK-NEXT:    ret i1 false
+;
+entry:
+  %x.non.neg = icmp sge i64 %x, 0
+  %y.non.pos = icmp sle i64 %y, 0
+  %c = icmp slt i64 %x, %b
+  %and.1 = and i1 %x.non.neg, %y.non.pos
+  %and.2 = and i1 %and.1, %c
+  %sub = add i64 %x, %y
+  br i1 %and.2, label %if.end, label %exit
+
+if.end:
+  %t = icmp slt i64 %sub, %b
+  ret i1 %t
+
+exit:
+  ret i1 false
+}
+
+; %y is not known to be non-positive.
+define i1 @add_unknown_sign_slt(i64 %x, i64 %y, i64 %b) {
+; CHECK-LABEL: @add_unknown_sign_slt(
+; CHECK-NEXT:    [[X_NON_NEG:%.*]] = icmp sge i64 [[X:%.*]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[X_NON_NEG]])
+; CHECK-NEXT:    [[C:%.*]] = icmp slt i64 [[X]], [[B:%.*]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    [[SUB:%.*]] = add i64 [[X]], [[Y:%.*]]
+; CHECK-NEXT:    [[T:%.*]] = icmp slt i64 [[SUB]], [[B]]
+; CHECK-NEXT:    ret i1 [[T]]
+;
+  %x.non.neg = icmp sge i64 %x, 0
+  call void @llvm.assume(i1 %x.non.neg)
+  %c = icmp slt i64 %x, %b
+  call void @llvm.assume(i1 %c)
+  %sub = add i64 %x, %y
+  %t = icmp slt i64 %sub, %b
+  ret i1 %t
+}
+
+; %x is not known to be non-negative.
+define i1 @add_non_positive_no_nonneg(i64 %x, i64 %y, i64 %b) {
+; CHECK-LABEL: @add_non_positive_no_nonneg(
+; CHECK-NEXT:    [[Y_NON_POS:%.*]] = icmp sle i64 [[Y:%.*]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[Y_NON_POS]])
+; CHECK-NEXT:    [[C:%.*]] = icmp slt i64 [[X:%.*]], [[B:%.*]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    [[SUB:%.*]] = add i64 [[X]], [[Y]]
+; CHECK-NEXT:    [[T:%.*]] = icmp slt i64 [[SUB]], [[B]]
+; CHECK-NEXT:    ret i1 [[T]]
+;
+  %y.non.pos = icmp sle i64 %y, 0
+  call void @llvm.assume(i1 %y.non.pos)
+  %c = icmp slt i64 %x, %b
+  call void @llvm.assume(i1 %c)
+  %sub = add i64 %x, %y
+  %t = icmp slt i64 %sub, %b
+  ret i1 %t
+}
+
+; Both operands are non-negative, so the add can wrap.
+define i1 @add_both_non_negative_slt(i64 %x, i64 %y, i64 %b) {
+; CHECK-LABEL: @add_both_non_negative_slt(
+; CHECK-NEXT:    [[X_NON_NEG:%.*]] = icmp sge i64 [[X:%.*]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[X_NON_NEG]])
+; CHECK-NEXT:    [[Y_NON_NEG:%.*]] = icmp sge i64 [[Y:%.*]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[Y_NON_NEG]])
+; CHECK-NEXT:    [[C:%.*]] = icmp slt i64 [[X]], [[B:%.*]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    [[SUB:%.*]] = add i64 [[X]], [[Y]]
+; CHECK-NEXT:    [[T:%.*]] = icmp slt i64 [[SUB]], [[B]]
+; CHECK-NEXT:    ret i1 [[T]]
+;
+  %x.non.neg = icmp sge i64 %x, 0
+  call void @llvm.assume(i1 %x.non.neg)
+  %y.non.neg = icmp sge i64 %y, 0
+  call void @llvm.assume(i1 %y.non.neg)
+  %c = icmp slt i64 %x, %b
+  call void @llvm.assume(i1 %c)
+  %sub = add i64 %x, %y
+  %t = icmp slt i64 %sub, %b
+  ret i1 %t
+}
+
+; Both operands are non-positive, so the add can wrap.
+define i1 @add_both_non_positive_slt(i64 %x, i64 %y, i64 %b) {
+; CHECK-LABEL: @add_both_non_positive_slt(
+; CHECK-NEXT:    [[X_NON_POS:%.*]] = icmp sle i64 [[X:%.*]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[X_NON_POS]])
+; CHECK-NEXT:    [[Y_NON_POS:%.*]] = icmp sle i64 [[Y:%.*]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[Y_NON_POS]])
+; CHECK-NEXT:    [[C:%.*]] = icmp slt i64 [[X]], [[B:%.*]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    [[SUB:%.*]] = add i64 [[X]], [[Y]]
+; CHECK-NEXT:    [[T:%.*]] = icmp slt i64 [[SUB]], [[B]]
+; CHECK-NEXT:    ret i1 [[T]]
+;
+  %x.non.pos = icmp sle i64 %x, 0
+  call void @llvm.assume(i1 %x.non.pos)
+  %y.non.pos = icmp sle i64 %y, 0
+  call void @llvm.assume(i1 %y.non.pos)
+  %c = icmp slt i64 %x, %b
+  call void @llvm.assume(i1 %c)
+  %sub = add i64 %x, %y
+  %t = icmp slt i64 %sub, %b
+  ret i1 %t
+}
+
+; %x <=u 100 implies %x + 20 does not wrap unsigned, so nuw can be added to an
+; add that already has nsw.
+define i8 @add_nsw_gains_nuw(i8 %x) {
+; CHECK-LABEL: @add_nsw_gains_nuw(
+; CHECK-NEXT:    [[C:%.*]] = icmp ule i8 [[X:%.*]], 100
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    [[ADD:%.*]] = add nsw i8 [[X]], 20
+; CHECK-NEXT:    ret i8 [[ADD]]
+;
+  %c = icmp ule i8 %x, 100
+  call void @llvm.assume(i1 %c)
+  %add = add nsw i8 %x, 20
+  ret i8 %add
+}
+
+; %x <=u 250 does not imply %x + 20 does not wrap unsigned, so no nuw.
+define i8 @add_nsw_no_nuw(i8 %x) {
+; CHECK-LABEL: @add_nsw_no_nuw(
+; CHECK-NEXT:    [[C:%.*]] = icmp ule i8 [[X:%.*]], -6
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    [[ADD:%.*]] = add nsw i8 [[X]], 20
+; CHECK-NEXT:    ret i8 [[ADD]]
+;
+  %c = icmp ule i8 %x, 250
+  call void @llvm.assume(i1 %c)
+  %add = add nsw i8 %x, 20
+  ret i8 %add
+}

>From ac78169d101911b3477b98ffb36c088fa9970816 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Wed, 2 Sep 2026 14:58:52 +0100
Subject: [PATCH 2/4] [ConstraintElim] Add nsw to adds with non-negative and
 non-positive operand.

---
 .../Scalar/ConstraintElimination.cpp          | 29 +++++++++++++++++++
 .../ConstraintElimination/add-nsw.ll          | 25 +++++++---------
 ...on-condition-in-loop-exit-latch-counted.ll |  2 +-
 ...nduction-condition-in-loop-exit-postinc.ll |  4 +--
 .../induction-nowrap-from-scev-not-ir.ll      |  2 +-
 .../monotonic-int-phis-decrement.ll           | 12 ++++----
 .../monotonic-int-phis-wrapping.ll            |  4 +--
 .../ConstraintElimination/wrapping-math.ll    |  8 ++---
 8 files changed, 55 insertions(+), 31 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index 26b9ad4073dfe..14c3d84c31588 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -318,6 +318,10 @@ class ConstraintInfo {
   /// signed system implies it or because ValueTracking can prove it.
   bool isKnownNonNegative(Value *V) const;
 
+  /// Returns true if \p V is known to be non-positive, either because the
+  /// signed system implies it or because ValueTracking can prove it.
+  bool isKnownNonPositive(Value *V) const;
+
   void addFact(CmpInst::Predicate Pred, Value *A, Value *B, unsigned NumIn,
                unsigned NumOut, SmallVectorImpl<StackEntry> &DFSInStack);
 
@@ -913,6 +917,12 @@ bool ConstraintInfo::isKnownNonNegative(Value *V) const {
          ::isKnownNonNegative(V, DL, /*Depth=*/MaxAnalysisRecursionDepth - 1);
 }
 
+bool ConstraintInfo::isKnownNonPositive(Value *V) const {
+  return doesHold(CmpInst::ICMP_SLE, V, ConstantInt::get(V->getType(), 0)) ||
+         computeKnownBits(V, DL, /*Depth=*/MaxAnalysisRecursionDepth - 1)
+             .isNonPositive();
+}
+
 void ConstraintInfo::transferToOtherSystem(
     CmpInst::Predicate Pred, Value *A, Value *B, unsigned NumIn,
     unsigned NumOut, SmallVectorImpl<StackEntry> &DFSInStack) {
@@ -1315,6 +1325,10 @@ static bool canStrengthenFlags(Instruction *I) {
     // A - B does not wrap unsigned, if A >=u B. Subs with constant operands get
     // canonicalized to Add.
     return !BO->hasNoUnsignedWrap() && !isa<Constant>(BO->getOperand(1));
+  case Instruction::Add:
+    // A + B does not wrap signed, if one operand is non-negative and the
+    // other is non-positive.
+    return !BO->hasNoSignedWrap();
   case Instruction::Mul:
   case Instruction::Shl:
     if (BO->hasNoUnsignedWrap() && BO->hasNoSignedWrap())
@@ -1374,6 +1388,21 @@ static bool tryToStrengthenFlags(Instruction *I, ConstraintInfo &Info,
     I->setHasNoUnsignedWrap();
     return true;
   }
+  case Instruction::Add: {
+    // Op0 + Op1 does not wrap signed, if one operand is non-negative and the
+    // other is non-positive.
+    auto IsNonNegativeAndNonPositive = [&Info](Value *NonNegOp,
+                                               Value *NonPosOp) {
+      return Info.isKnownNonNegative(NonNegOp) &&
+             Info.isKnownNonPositive(NonPosOp);
+    };
+    if (!IsNonNegativeAndNonPositive(Op0, Op1) &&
+        !IsNonNegativeAndNonPositive(Op1, Op0))
+      return false;
+    LLVM_DEBUG(dbgs() << "Adding nsw to " << *I << "\n");
+    I->setHasNoSignedWrap();
+    return true;
+  }
   case Instruction::Mul:
   case Instruction::Shl: {
     auto Opcode = static_cast<Instruction::BinaryOps>(I->getOpcode());
diff --git a/llvm/test/Transforms/ConstraintElimination/add-nsw.ll b/llvm/test/Transforms/ConstraintElimination/add-nsw.ll
index 98c070893d257..8c129858a95a4 100644
--- a/llvm/test/Transforms/ConstraintElimination/add-nsw.ll
+++ b/llvm/test/Transforms/ConstraintElimination/add-nsw.ll
@@ -906,9 +906,8 @@ define i1 @add_neg_1_slt(i64 %x, i64 %b) {
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[X_NON_NEG]])
 ; CHECK-NEXT:    [[C:%.*]] = icmp slt i64 [[X]], [[B:%.*]]
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
-; CHECK-NEXT:    [[SUB:%.*]] = add i64 [[X]], -1
-; CHECK-NEXT:    [[T:%.*]] = icmp slt i64 [[SUB]], [[B]]
-; CHECK-NEXT:    ret i1 [[T]]
+; CHECK-NEXT:    [[SUB:%.*]] = add nsw i64 [[X]], -1
+; CHECK-NEXT:    ret i1 true
 ;
   %x.non.neg = icmp sge i64 %x, 0
   call void @llvm.assume(i1 %x.non.neg)
@@ -925,9 +924,8 @@ define i1 @add_neg_20_slt(i64 %x, i64 %b) {
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[X_NON_NEG]])
 ; CHECK-NEXT:    [[C:%.*]] = icmp slt i64 [[X]], [[B:%.*]]
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
-; CHECK-NEXT:    [[SUB:%.*]] = add i64 [[X]], -20
-; CHECK-NEXT:    [[T:%.*]] = icmp slt i64 [[SUB]], [[B]]
-; CHECK-NEXT:    ret i1 [[T]]
+; CHECK-NEXT:    [[SUB:%.*]] = add nsw i64 [[X]], -20
+; CHECK-NEXT:    ret i1 true
 ;
   %x.non.neg = icmp sge i64 %x, 0
   call void @llvm.assume(i1 %x.non.neg)
@@ -944,9 +942,8 @@ define i1 @add_neg_1_nonneg_from_ult(i64 %x, i64 %c) {
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[X_NON_NEG]])
 ; CHECK-NEXT:    [[B:%.*]] = icmp ult i64 [[X:%.*]], [[C]]
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[B]])
-; CHECK-NEXT:    [[SUB:%.*]] = add i64 [[X]], -1
-; CHECK-NEXT:    [[T:%.*]] = icmp slt i64 [[SUB]], [[C]]
-; CHECK-NEXT:    ret i1 [[T]]
+; CHECK-NEXT:    [[SUB:%.*]] = add nsw i64 [[X]], -1
+; CHECK-NEXT:    ret i1 true
 ;
   %x.non.neg = icmp sge i64 %c, 0
   call void @llvm.assume(i1 %x.non.neg)
@@ -980,9 +977,8 @@ define i1 @add_non_positive_slt(i64 %x, i64 %y, i64 %b) {
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[Y_NON_POS]])
 ; CHECK-NEXT:    [[C:%.*]] = icmp slt i64 [[X]], [[B:%.*]]
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
-; CHECK-NEXT:    [[SUB:%.*]] = add i64 [[X]], [[Y]]
-; CHECK-NEXT:    [[T:%.*]] = icmp slt i64 [[SUB]], [[B]]
-; CHECK-NEXT:    ret i1 [[T]]
+; CHECK-NEXT:    [[SUB:%.*]] = add nsw i64 [[X]], [[Y]]
+; CHECK-NEXT:    ret i1 true
 ;
   %x.non.neg = icmp sge i64 %x, 0
   call void @llvm.assume(i1 %x.non.neg)
@@ -1003,9 +999,8 @@ define i1 @add_non_positive_slt_commuted(i64 %x, i64 %y, i64 %b) {
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[Y_NON_POS]])
 ; CHECK-NEXT:    [[C:%.*]] = icmp slt i64 [[X]], [[B:%.*]]
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
-; CHECK-NEXT:    [[SUB:%.*]] = add i64 [[Y]], [[X]]
-; CHECK-NEXT:    [[T:%.*]] = icmp slt i64 [[SUB]], [[B]]
-; CHECK-NEXT:    ret i1 [[T]]
+; CHECK-NEXT:    [[SUB:%.*]] = add nsw i64 [[Y]], [[X]]
+; CHECK-NEXT:    ret i1 true
 ;
   %x.non.neg = icmp sge i64 %x, 0
   call void @llvm.assume(i1 %x.non.neg)
diff --git a/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-latch-counted.ll b/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-latch-counted.ll
index 31ae62c564dd6..9d60fc7270482 100644
--- a/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-latch-counted.ll
+++ b/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-latch-counted.ll
@@ -8,7 +8,7 @@ define i1 @latch_counted_header_check_removable(ptr %p, i64 %n, i64 %lim) {
 ; CHECK-NEXT:    [[POS:%.*]] = icmp sgt i64 [[N]], 0
 ; CHECK-NEXT:    br i1 [[POS]], label %[[PH:.*]], label %[[EXIT_0:.*]]
 ; CHECK:       [[PH]]:
-; CHECK-NEXT:    [[NM1:%.*]] = add i64 [[N]], -1
+; CHECK-NEXT:    [[NM1:%.*]] = add nsw i64 [[N]], -1
 ; CHECK-NEXT:    [[G4:%.*]] = shl nuw nsw i64 [[NM1]], 2
 ; CHECK-NEXT:    [[G:%.*]] = icmp ult i64 [[G4]], [[LIM]]
 ; CHECK-NEXT:    br i1 [[G]], label %[[LOOP_HEADER:.*]], label %[[EXIT_1:.*]]
diff --git a/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-postinc.ll b/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-postinc.ll
index 1fa5344c9911d..5083728fe3489 100644
--- a/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-postinc.ll
+++ b/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-postinc.ll
@@ -681,7 +681,7 @@ define void @latch_postdec_negative_step_folds() {
 ; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    br label %[[LOOP_LATCH]]
 ; CHECK:       [[LOOP_LATCH]]:
-; CHECK-NEXT:    [[IV_NEXT]] = add i8 [[IV]], -1
+; CHECK-NEXT:    [[IV_NEXT]] = add nsw i8 [[IV]], -1
 ; CHECK-NEXT:    [[EC:%.*]] = icmp eq i8 [[IV_NEXT]], 2
 ; CHECK-NEXT:    br i1 [[EC]], label %[[EXIT:.*]], label %[[LOOP]]
 ; CHECK:       [[EXIT]]:
@@ -946,7 +946,7 @@ define void @latch_postdec_redundant_header_guard(i32 %n) {
 ; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[N]], %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
 ; CHECK-NEXT:    br i1 true, label %[[LOOP_LATCH]], label %[[EXIT]]
 ; CHECK:       [[LOOP_LATCH]]:
-; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], -1
+; CHECK-NEXT:    [[IV_NEXT]] = add nsw i32 [[IV]], -1
 ; CHECK-NEXT:    call void @launch(i32 [[IV_NEXT]])
 ; CHECK-NEXT:    [[EC:%.*]] = icmp eq i32 [[IV_NEXT]], 0
 ; CHECK-NEXT:    br i1 [[EC]], label %[[LAST:.*]], label %[[LOOP]]
diff --git a/llvm/test/Transforms/ConstraintElimination/induction-nowrap-from-scev-not-ir.ll b/llvm/test/Transforms/ConstraintElimination/induction-nowrap-from-scev-not-ir.ll
index 393d5b626d807..7f8ee517bb579 100644
--- a/llvm/test/Transforms/ConstraintElimination/induction-nowrap-from-scev-not-ir.ll
+++ b/llvm/test/Transforms/ConstraintElimination/induction-nowrap-from-scev-not-ir.ll
@@ -74,7 +74,7 @@ define void @zero_step_constant_start(i32 %n) {
 ; CHECK-NEXT:    [[C:%.*]] = icmp ne i32 [[IV]], [[N]]
 ; CHECK-NEXT:    br i1 [[C]], label %[[LATCH]], label %[[EXIT:.*]]
 ; CHECK:       [[LATCH]]:
-; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 0
+; CHECK-NEXT:    [[IV_NEXT]] = add nsw i32 [[IV]], 0
 ; CHECK-NEXT:    br label %[[LOOP]]
 ; CHECK:       [[EXIT]]:
 ; CHECK-NEXT:    ret void
diff --git a/llvm/test/Transforms/ConstraintElimination/monotonic-int-phis-decrement.ll b/llvm/test/Transforms/ConstraintElimination/monotonic-int-phis-decrement.ll
index 32d38acb8bac9..e8d0e0a4b697b 100644
--- a/llvm/test/Transforms/ConstraintElimination/monotonic-int-phis-decrement.ll
+++ b/llvm/test/Transforms/ConstraintElimination/monotonic-int-phis-decrement.ll
@@ -15,7 +15,7 @@ define void @add_rec_decreasing_cond_true_constant(i8 noundef %len) {
 ; CHECK-NEXT:    br i1 [[CMP2_NOT]], label [[EXIT:%.*]], label [[LOOP_LATCH]]
 ; CHECK:       loop.latch:
 ; CHECK-NEXT:    call void @use(i1 true)
-; CHECK-NEXT:    [[K_DEC]] = add i8 [[K_0]], -1
+; CHECK-NEXT:    [[K_DEC]] = add nsw i8 [[K_0]], -1
 ; CHECK-NEXT:    br label [[LOOP_HEADER]]
 ; CHECK:       exit:
 ; CHECK-NEXT:    ret void
@@ -50,7 +50,7 @@ define void @add_rec_decreasing_cond_not_true_constant(i8 noundef %len) {
 ; CHECK:       loop.latch:
 ; CHECK-NEXT:    [[CMP_NOT_I:%.*]] = icmp ult i8 [[K_0]], 4
 ; CHECK-NEXT:    call void @use(i1 [[CMP_NOT_I]])
-; CHECK-NEXT:    [[K_DEC]] = add i8 [[K_0]], -1
+; CHECK-NEXT:    [[K_DEC]] = add nsw i8 [[K_0]], -1
 ; CHECK-NEXT:    br label [[LOOP_HEADER]]
 ; CHECK:       exit:
 ; CHECK-NEXT:    ret void
@@ -79,7 +79,7 @@ define void @add_rec_decreasing_cond_true_start_signed_positive(i8 noundef %star
 ; CHECK-NEXT:  entry:
 ; CHECK-NEXT:    [[PRECOND:%.*]] = icmp sge i8 [[START]], 1
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[PRECOND]])
-; CHECK-NEXT:    [[START_1:%.*]] = add i8 [[START]], -1
+; CHECK-NEXT:    [[START_1:%.*]] = add nsw i8 [[START]], -1
 ; CHECK-NEXT:    br label [[LOOP_HEADER:%.*]]
 ; CHECK:       loop.header:
 ; CHECK-NEXT:    [[K_0:%.*]] = phi i8 [ [[START_1]], [[ENTRY:%.*]] ], [ [[K_DEC:%.*]], [[LOOP_LATCH:%.*]] ]
@@ -87,7 +87,7 @@ define void @add_rec_decreasing_cond_true_start_signed_positive(i8 noundef %star
 ; CHECK-NEXT:    br i1 [[CMP2_NOT]], label [[EXIT:%.*]], label [[LOOP_LATCH]]
 ; CHECK:       loop.latch:
 ; CHECK-NEXT:    call void @use(i1 true)
-; CHECK-NEXT:    [[K_DEC]] = add i8 [[K_0]], -1
+; CHECK-NEXT:    [[K_DEC]] = add nsw i8 [[K_0]], -1
 ; CHECK-NEXT:    br label [[LOOP_HEADER]]
 ; CHECK:       exit:
 ; CHECK-NEXT:    ret void
@@ -127,7 +127,7 @@ define void @add_rec_decreasing_cond_not_true_start_signed_positive(i8 noundef %
 ; CHECK:       loop.latch:
 ; CHECK-NEXT:    [[CMP_NOT_I:%.*]] = icmp ult i8 [[K_0]], [[START]]
 ; CHECK-NEXT:    call void @use(i1 [[CMP_NOT_I]])
-; CHECK-NEXT:    [[K_DEC]] = add i8 [[K_0]], -1
+; CHECK-NEXT:    [[K_DEC]] = add nsw i8 [[K_0]], -1
 ; CHECK-NEXT:    br label [[LOOP_HEADER]]
 ; CHECK:       exit:
 ; CHECK-NEXT:    ret void
@@ -471,7 +471,7 @@ define void @add_rec_decreasing_nsw_from_scev(i8 %b) {
 ; CHECK-NEXT:    [[POSITIVE:%.*]] = icmp sgt i8 [[K_0]], 0
 ; CHECK-NEXT:    br i1 [[POSITIVE]], label [[LOOP_LATCH]], label [[EXIT]]
 ; CHECK:       loop.latch:
-; CHECK-NEXT:    [[K_DEC]] = add i8 [[K_0]], -1
+; CHECK-NEXT:    [[K_DEC]] = add nsw i8 [[K_0]], -1
 ; CHECK-NEXT:    br label [[LOOP_HEADER]]
 ; CHECK:       exit:
 ; CHECK-NEXT:    ret void
diff --git a/llvm/test/Transforms/ConstraintElimination/monotonic-int-phis-wrapping.ll b/llvm/test/Transforms/ConstraintElimination/monotonic-int-phis-wrapping.ll
index cd4afed80df59..1c0de052402af 100644
--- a/llvm/test/Transforms/ConstraintElimination/monotonic-int-phis-wrapping.ll
+++ b/llvm/test/Transforms/ConstraintElimination/monotonic-int-phis-wrapping.ll
@@ -20,7 +20,7 @@ define void @test_iv_wraps_1(i8 %len.n, i8 %a) {
 ; CHECK:       loop.latch:
 ; CHECK-NEXT:    [[T_1:%.*]] = icmp uge i8 [[IV]], -1
 ; CHECK-NEXT:    call void @use.i1(i1 [[T_1]])
-; CHECK-NEXT:    [[IV_NEXT]] = add i8 [[IV]], 1
+; CHECK-NEXT:    [[IV_NEXT]] = add nsw i8 [[IV]], 1
 ; CHECK-NEXT:    br label [[LOOP_HEADER]]
 ; CHECK:       exit:
 ; CHECK-NEXT:    ret void
@@ -85,7 +85,7 @@ exit:
 define void @test_iv_nuw_nsw_2_uge_start(i8 %len.n, i8 %a) {
 ; CHECK-LABEL: @test_iv_nuw_nsw_2_uge_start(
 ; CHECK-NEXT:  loop.ph:
-; CHECK-NEXT:    [[START:%.*]] = add i8 -2, 1
+; CHECK-NEXT:    [[START:%.*]] = add nsw i8 -2, 1
 ; CHECK-NEXT:    br label [[LOOP_HEADER:%.*]]
 ; CHECK:       loop.header:
 ; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ [[START]], [[LOOP_PH:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ]
diff --git a/llvm/test/Transforms/ConstraintElimination/wrapping-math.ll b/llvm/test/Transforms/ConstraintElimination/wrapping-math.ll
index 09dea55ba01c7..6aa55412330ea 100644
--- a/llvm/test/Transforms/ConstraintElimination/wrapping-math.ll
+++ b/llvm/test/Transforms/ConstraintElimination/wrapping-math.ll
@@ -20,7 +20,7 @@ define i1 @wrapping_add_known_1(i8 %a) {
 ; CHECK-NEXT:    [[PRE:%.*]] = icmp eq i8 [[A:%.*]], 1
 ; CHECK-NEXT:    br i1 [[PRE]], label [[THEN:%.*]], label [[ELSE:%.*]]
 ; CHECK:       then:
-; CHECK-NEXT:    [[SUB_1:%.*]] = add i8 [[A]], -1
+; CHECK-NEXT:    [[SUB_1:%.*]] = add nsw i8 [[A]], -1
 ; CHECK-NEXT:    ret i1 true
 ; CHECK:       else:
 ; CHECK-NEXT:    [[SUB_2:%.*]] = add i8 [[A]], -1
@@ -48,7 +48,7 @@ define i1 @wrapping_add_unknown_2(i8 %a) {
 ; CHECK-NEXT:    [[PRE:%.*]] = icmp eq i8 [[A:%.*]], 0
 ; CHECK-NEXT:    br i1 [[PRE]], label [[THEN:%.*]], label [[ELSE:%.*]]
 ; CHECK:       then:
-; CHECK-NEXT:    [[SUB_1:%.*]] = add i8 [[A]], -1
+; CHECK-NEXT:    [[SUB_1:%.*]] = add nsw i8 [[A]], -1
 ; CHECK-NEXT:    [[C_1:%.*]] = icmp eq i8 [[SUB_1]], 0
 ; CHECK-NEXT:    ret i1 [[C_1]]
 ; CHECK:       else:
@@ -272,8 +272,8 @@ define i1 @wrapping_add_known_1_add_nuw(i8 %a) {
 ; CHECK-NEXT:  entry:
 ; CHECK-NEXT:    [[PRE:%.*]] = icmp eq i8 [[A:%.*]], 1
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[PRE]])
-; CHECK-NEXT:    [[SUB_1:%.*]] = add i8 [[A]], -1
-; CHECK-NEXT:    [[ADD:%.*]] = add nuw i8 [[SUB_1]], 10
+; CHECK-NEXT:    [[SUB_1:%.*]] = add nsw i8 [[A]], -1
+; CHECK-NEXT:    [[ADD:%.*]] = add nuw nsw i8 [[SUB_1]], 10
 ; CHECK-NEXT:    [[RES_1:%.*]] = xor i1 true, true
 ; CHECK-NEXT:    [[RES_2:%.*]] = xor i1 [[RES_1]], false
 ; CHECK-NEXT:    ret i1 [[RES_2]]

>From 1073aa82e947a5d2aa0b349f2c5fa8adf94d12ef Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Mon, 7 Sep 2026 10:11:37 +0100
Subject: [PATCH 3/4] !fixup common-ize flag strengthening via ranges

---
 .../Scalar/ConstraintElimination.cpp          | 92 ++++++++-----------
 .../ConstraintElimination/add-nsw.ll          | 22 +++--
 .../ConstraintElimination/cond-used-in-phi.ll |  2 +-
 ...on-condition-in-loop-exit-latch-counted.ll |  2 +-
 .../induction-condition-in-loop-exit.ll       |  2 +-
 .../induction-nowrap-from-scev-not-ir.ll      |  6 +-
 .../Transforms/ConstraintElimination/loops.ll |  4 +-
 .../monotonic-int-phis-cfg.ll                 |  2 +-
 .../monotonic-int-phis-decrement.ll           |  2 +-
 .../monotonic-int-phis-wrapping.ll            |  8 +-
 .../ConstraintElimination/pr70510.ll          |  4 +-
 ...facts-to-unsigned-is-known-non-negative.ll |  2 +-
 .../ConstraintElimination/wrapping-math.ll    |  9 +-
 13 files changed, 72 insertions(+), 85 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index 14c3d84c31588..2741ce5b3fe88 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -318,10 +318,6 @@ class ConstraintInfo {
   /// signed system implies it or because ValueTracking can prove it.
   bool isKnownNonNegative(Value *V) const;
 
-  /// Returns true if \p V is known to be non-positive, either because the
-  /// signed system implies it or because ValueTracking can prove it.
-  bool isKnownNonPositive(Value *V) const;
-
   void addFact(CmpInst::Predicate Pred, Value *A, Value *B, unsigned NumIn,
                unsigned NumOut, SmallVectorImpl<StackEntry> &DFSInStack);
 
@@ -917,12 +913,6 @@ bool ConstraintInfo::isKnownNonNegative(Value *V) const {
          ::isKnownNonNegative(V, DL, /*Depth=*/MaxAnalysisRecursionDepth - 1);
 }
 
-bool ConstraintInfo::isKnownNonPositive(Value *V) const {
-  return doesHold(CmpInst::ICMP_SLE, V, ConstantInt::get(V->getType(), 0)) ||
-         computeKnownBits(V, DL, /*Depth=*/MaxAnalysisRecursionDepth - 1)
-             .isNonPositive();
-}
-
 void ConstraintInfo::transferToOtherSystem(
     CmpInst::Predicate Pred, Value *A, Value *B, unsigned NumIn,
     unsigned NumOut, SmallVectorImpl<StackEntry> &DFSInStack) {
@@ -1326,9 +1316,9 @@ static bool canStrengthenFlags(Instruction *I) {
     // canonicalized to Add.
     return !BO->hasNoUnsignedWrap() && !isa<Constant>(BO->getOperand(1));
   case Instruction::Add:
-    // A + B does not wrap signed, if one operand is non-negative and the
-    // other is non-positive.
-    return !BO->hasNoSignedWrap();
+    // NSW/NUW can be refined using constant ranges.
+    return (!BO->hasNoUnsignedWrap() || !BO->hasNoSignedWrap()) &&
+           isa<Constant>(BO->getOperand(1));
   case Instruction::Mul:
   case Instruction::Shl:
     if (BO->hasNoUnsignedWrap() && BO->hasNoSignedWrap())
@@ -1371,13 +1361,45 @@ static bool doesHoldInRange(const ConstraintInfo &Info, Value *Op,
   return true;
 }
 
+static bool tryToStrengthenBinOpFlags(Instruction *I, Value *Op0, Value *Op1,
+                                      ConstraintInfo &Info) {
+  auto *C = dyn_cast<ConstantInt>(Op1);
+  if (!C)
+    return false;
+
+  // For a constant Op1, the ranges of Op0 for which the operation does not
+  // wrap are known exactly; check if the systems imply one of them.
+  bool Changed = false;
+  auto Opcode = static_cast<Instruction::BinaryOps>(I->getOpcode());
+  using OBO = OverflowingBinaryOperator;
+  ConstantRange Other(C->getValue());
+  if (!I->hasNoUnsignedWrap() &&
+      doesHoldInRange(Info, Op0,
+                      ConstantRange::makeGuaranteedNoWrapRegion(
+                          Opcode, Other, OBO::NoUnsignedWrap),
+                      /*Signed=*/false)) {
+    LLVM_DEBUG(dbgs() << "Adding nuw to " << *I << "\n");
+    I->setHasNoUnsignedWrap();
+    Changed = true;
+  }
+  if (!I->hasNoSignedWrap() &&
+      doesHoldInRange(Info, Op0,
+                      ConstantRange::makeGuaranteedNoWrapRegion(
+                          Opcode, Other, OBO::NoSignedWrap),
+                      /*Signed=*/true)) {
+    LLVM_DEBUG(dbgs() << "Adding nsw to " << *I << "\n");
+    I->setHasNoSignedWrap();
+    Changed = true;
+  }
+  return Changed;
+}
+
 /// Try to strengthen \p I's poison generating flags using \p Info. Returns
 /// true if \p I was modified.
 static bool tryToStrengthenFlags(Instruction *I, ConstraintInfo &Info,
                                  SmallVectorImpl<Instruction *> &ToRemove) {
   assert(canStrengthenFlags(I) && "not a candidate for flag strengthening");
 
-  using OBO = OverflowingBinaryOperator;
   Value *Op0 = I->getOperand(0), *Op1 = I->getOperand(1);
   switch (I->getOpcode()) {
   case Instruction::Sub: {
@@ -1388,48 +1410,12 @@ static bool tryToStrengthenFlags(Instruction *I, ConstraintInfo &Info,
     I->setHasNoUnsignedWrap();
     return true;
   }
-  case Instruction::Add: {
-    // Op0 + Op1 does not wrap signed, if one operand is non-negative and the
-    // other is non-positive.
-    auto IsNonNegativeAndNonPositive = [&Info](Value *NonNegOp,
-                                               Value *NonPosOp) {
-      return Info.isKnownNonNegative(NonNegOp) &&
-             Info.isKnownNonPositive(NonPosOp);
-    };
-    if (!IsNonNegativeAndNonPositive(Op0, Op1) &&
-        !IsNonNegativeAndNonPositive(Op1, Op0))
-      return false;
-    LLVM_DEBUG(dbgs() << "Adding nsw to " << *I << "\n");
-    I->setHasNoSignedWrap();
-    return true;
-  }
+  case Instruction::Add:
+    return tryToStrengthenBinOpFlags(I, Op0, Op1, Info);
   case Instruction::Mul:
   case Instruction::Shl: {
     auto Opcode = static_cast<Instruction::BinaryOps>(I->getOpcode());
-    bool Changed = false;
-    // For a constant Op1, the ranges of Op0 for which the operation does not
-    // wrap are known exactly; check if the systems imply one of them.
-    if (auto *C = dyn_cast<ConstantInt>(Op1)) {
-      ConstantRange Other(C->getValue());
-      if (!I->hasNoUnsignedWrap() &&
-          doesHoldInRange(Info, Op0,
-                          ConstantRange::makeGuaranteedNoWrapRegion(
-                              Opcode, Other, OBO::NoUnsignedWrap),
-                          /*Signed=*/false)) {
-        LLVM_DEBUG(dbgs() << "Adding nuw to " << *I << "\n");
-        I->setHasNoUnsignedWrap();
-        Changed = true;
-      }
-      if (!I->hasNoSignedWrap() &&
-          doesHoldInRange(Info, Op0,
-                          ConstantRange::makeGuaranteedNoWrapRegion(
-                              Opcode, Other, OBO::NoSignedWrap),
-                          /*Signed=*/true)) {
-        LLVM_DEBUG(dbgs() << "Adding nsw to " << *I << "\n");
-        I->setHasNoSignedWrap();
-        Changed = true;
-      }
-    }
+    bool Changed = tryToStrengthenBinOpFlags(I, Op0, Op1, Info);
     if (!I->hasNoUnsignedWrap() && I->hasNoSignedWrap() &&
         Info.isKnownNonNegative(Op0) &&
         (Opcode == Instruction::Shl || Info.isKnownNonNegative(Op1))) {
diff --git a/llvm/test/Transforms/ConstraintElimination/add-nsw.ll b/llvm/test/Transforms/ConstraintElimination/add-nsw.ll
index 8c129858a95a4..34a6ea0910e03 100644
--- a/llvm/test/Transforms/ConstraintElimination/add-nsw.ll
+++ b/llvm/test/Transforms/ConstraintElimination/add-nsw.ll
@@ -906,8 +906,9 @@ define i1 @add_neg_1_slt(i64 %x, i64 %b) {
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[X_NON_NEG]])
 ; CHECK-NEXT:    [[C:%.*]] = icmp slt i64 [[X]], [[B:%.*]]
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
-; CHECK-NEXT:    [[SUB:%.*]] = add nsw i64 [[X]], -1
-; CHECK-NEXT:    ret i1 true
+; CHECK-NEXT:    [[SUB:%.*]] = add i64 [[X]], -1
+; CHECK-NEXT:    [[T:%.*]] = icmp slt i64 [[SUB]], [[B]]
+; CHECK-NEXT:    ret i1 [[T]]
 ;
   %x.non.neg = icmp sge i64 %x, 0
   call void @llvm.assume(i1 %x.non.neg)
@@ -942,8 +943,9 @@ define i1 @add_neg_1_nonneg_from_ult(i64 %x, i64 %c) {
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[X_NON_NEG]])
 ; CHECK-NEXT:    [[B:%.*]] = icmp ult i64 [[X:%.*]], [[C]]
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[B]])
-; CHECK-NEXT:    [[SUB:%.*]] = add nsw i64 [[X]], -1
-; CHECK-NEXT:    ret i1 true
+; CHECK-NEXT:    [[SUB:%.*]] = add i64 [[X]], -1
+; CHECK-NEXT:    [[T:%.*]] = icmp slt i64 [[SUB]], [[C]]
+; CHECK-NEXT:    ret i1 [[T]]
 ;
   %x.non.neg = icmp sge i64 %c, 0
   call void @llvm.assume(i1 %x.non.neg)
@@ -977,8 +979,9 @@ define i1 @add_non_positive_slt(i64 %x, i64 %y, i64 %b) {
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[Y_NON_POS]])
 ; CHECK-NEXT:    [[C:%.*]] = icmp slt i64 [[X]], [[B:%.*]]
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
-; CHECK-NEXT:    [[SUB:%.*]] = add nsw i64 [[X]], [[Y]]
-; CHECK-NEXT:    ret i1 true
+; CHECK-NEXT:    [[SUB:%.*]] = add i64 [[X]], [[Y]]
+; CHECK-NEXT:    [[T:%.*]] = icmp slt i64 [[SUB]], [[B]]
+; CHECK-NEXT:    ret i1 [[T]]
 ;
   %x.non.neg = icmp sge i64 %x, 0
   call void @llvm.assume(i1 %x.non.neg)
@@ -999,8 +1002,9 @@ define i1 @add_non_positive_slt_commuted(i64 %x, i64 %y, i64 %b) {
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[Y_NON_POS]])
 ; CHECK-NEXT:    [[C:%.*]] = icmp slt i64 [[X]], [[B:%.*]]
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
-; CHECK-NEXT:    [[SUB:%.*]] = add nsw i64 [[Y]], [[X]]
-; CHECK-NEXT:    ret i1 true
+; CHECK-NEXT:    [[SUB:%.*]] = add i64 [[Y]], [[X]]
+; CHECK-NEXT:    [[T:%.*]] = icmp slt i64 [[SUB]], [[B]]
+; CHECK-NEXT:    ret i1 [[T]]
 ;
   %x.non.neg = icmp sge i64 %x, 0
   call void @llvm.assume(i1 %x.non.neg)
@@ -1140,7 +1144,7 @@ define i8 @add_nsw_gains_nuw(i8 %x) {
 ; CHECK-LABEL: @add_nsw_gains_nuw(
 ; CHECK-NEXT:    [[C:%.*]] = icmp ule i8 [[X:%.*]], 100
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
-; CHECK-NEXT:    [[ADD:%.*]] = add nsw i8 [[X]], 20
+; CHECK-NEXT:    [[ADD:%.*]] = add nuw nsw i8 [[X]], 20
 ; CHECK-NEXT:    ret i8 [[ADD]]
 ;
   %c = icmp ule i8 %x, 100
diff --git a/llvm/test/Transforms/ConstraintElimination/cond-used-in-phi.ll b/llvm/test/Transforms/ConstraintElimination/cond-used-in-phi.ll
index d38c46b025638..7f3a282dc6f3b 100644
--- a/llvm/test/Transforms/ConstraintElimination/cond-used-in-phi.ll
+++ b/llvm/test/Transforms/ConstraintElimination/cond-used-in-phi.ll
@@ -103,7 +103,7 @@ define void @phi_loop_3(i8 %x, i1 %c) {
 ; CHECK-NEXT:    br i1 [[C:%.*]], label [[LOOP_LATCH]], label [[LOOP_EXIT:%.*]]
 ; CHECK:       loop.latch:
 ; CHECK-NEXT:    call void @use(i1 true)
-; CHECK-NEXT:    [[IV_NEXT]] = add i8 [[IV]], 1
+; CHECK-NEXT:    [[IV_NEXT]] = add nuw nsw i8 [[IV]], 1
 ; CHECK-NEXT:    [[EC:%.*]] = icmp ult i8 [[IV_NEXT]], 3
 ; CHECK-NEXT:    br i1 [[EC]], label [[LOOP]], label [[LOOP_EXIT]]
 ; CHECK:       loop.exit:
diff --git a/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-latch-counted.ll b/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-latch-counted.ll
index 9d60fc7270482..31ae62c564dd6 100644
--- a/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-latch-counted.ll
+++ b/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-latch-counted.ll
@@ -8,7 +8,7 @@ define i1 @latch_counted_header_check_removable(ptr %p, i64 %n, i64 %lim) {
 ; CHECK-NEXT:    [[POS:%.*]] = icmp sgt i64 [[N]], 0
 ; CHECK-NEXT:    br i1 [[POS]], label %[[PH:.*]], label %[[EXIT_0:.*]]
 ; CHECK:       [[PH]]:
-; CHECK-NEXT:    [[NM1:%.*]] = add nsw i64 [[N]], -1
+; CHECK-NEXT:    [[NM1:%.*]] = add i64 [[N]], -1
 ; CHECK-NEXT:    [[G4:%.*]] = shl nuw nsw i64 [[NM1]], 2
 ; CHECK-NEXT:    [[G:%.*]] = icmp ult i64 [[G4]], [[LIM]]
 ; CHECK-NEXT:    br i1 [[G]], label %[[LOOP_HEADER:.*]], label %[[EXIT_1:.*]]
diff --git a/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit.ll b/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit.ll
index de63cd07df6b3..c5eaafad81b9f 100644
--- a/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit.ll
+++ b/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit.ll
@@ -95,7 +95,7 @@ define i1 @multi_exiting_loop_eq_same_unique_exit_const_compare_known_due_to_pre
 ; CHECK-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds i8, ptr [[S]], i32 [[IV]]
 ; CHECK-NEXT:    [[TMP0:%.*]] = load i8, ptr [[ARRAYIDX]], align 1
 ; CHECK-NEXT:    [[LATCH_C:%.*]] = icmp ult i8 [[TMP0]], 10
-; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
+; CHECK-NEXT:    [[IV_NEXT]] = add nuw nsw i32 [[IV]], 1
 ; CHECK-NEXT:    br i1 [[LATCH_C]], label %[[LOOP_HEADER]], label %[[EXIT]]
 ; CHECK:       [[EXIT]]:
 ; CHECK-NEXT:    ret i1 true
diff --git a/llvm/test/Transforms/ConstraintElimination/induction-nowrap-from-scev-not-ir.ll b/llvm/test/Transforms/ConstraintElimination/induction-nowrap-from-scev-not-ir.ll
index 7f8ee517bb579..ffd96bdbfb4a4 100644
--- a/llvm/test/Transforms/ConstraintElimination/induction-nowrap-from-scev-not-ir.ll
+++ b/llvm/test/Transforms/ConstraintElimination/induction-nowrap-from-scev-not-ir.ll
@@ -74,7 +74,7 @@ define void @zero_step_constant_start(i32 %n) {
 ; CHECK-NEXT:    [[C:%.*]] = icmp ne i32 [[IV]], [[N]]
 ; CHECK-NEXT:    br i1 [[C]], label %[[LATCH]], label %[[EXIT:.*]]
 ; CHECK:       [[LATCH]]:
-; CHECK-NEXT:    [[IV_NEXT]] = add nsw i32 [[IV]], 0
+; CHECK-NEXT:    [[IV_NEXT]] = add nuw nsw i32 [[IV]], 0
 ; CHECK-NEXT:    br label %[[LOOP]]
 ; CHECK:       [[EXIT]]:
 ; CHECK-NEXT:    ret void
@@ -105,7 +105,7 @@ define void @zero_step_unknown_start(i32 %n, i32 %start) {
 ; CHECK-NEXT:    [[C:%.*]] = icmp ne i32 [[IV]], [[N]]
 ; CHECK-NEXT:    br i1 [[C]], label %[[LATCH]], label %[[EXIT:.*]]
 ; CHECK:       [[LATCH]]:
-; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 0
+; CHECK-NEXT:    [[IV_NEXT]] = add nuw nsw i32 [[IV]], 0
 ; CHECK-NEXT:    br label %[[LOOP]]
 ; CHECK:       [[EXIT]]:
 ; CHECK-NEXT:    ret void
@@ -140,7 +140,7 @@ define void @zero_step_addrec_of_outer_loop(i32 %n) {
 ; CHECK-NEXT:    br i1 [[CMP]], label %[[INNER_LATCH]], label %[[OUTER_LATCH]]
 ; CHECK:       [[INNER_LATCH]]:
 ; CHECK-NEXT:    call void @use(i1 true)
-; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 0
+; CHECK-NEXT:    [[IV_NEXT]] = add nuw nsw i32 [[IV]], 0
 ; CHECK-NEXT:    br label %[[INNER]]
 ; CHECK:       [[OUTER_LATCH]]:
 ; CHECK-NEXT:    [[O_NEXT]] = add i32 [[O]], 1
diff --git a/llvm/test/Transforms/ConstraintElimination/loops.ll b/llvm/test/Transforms/ConstraintElimination/loops.ll
index 25d4ac436ce8c..124ae0a630ae9 100644
--- a/llvm/test/Transforms/ConstraintElimination/loops.ll
+++ b/llvm/test/Transforms/ConstraintElimination/loops.ll
@@ -66,7 +66,7 @@ define i32 @loop_header_dom(i32 %y, i1 %c) {
 ; CHECK-NEXT:    call void @use(i1 [[C_2]])
 ; CHECK-NEXT:    [[C_3:%.*]] = icmp ugt i32 [[X]], 9
 ; CHECK-NEXT:    call void @use(i1 [[C_3]])
-; CHECK-NEXT:    [[X_NEXT]] = add i32 [[X]], 1
+; CHECK-NEXT:    [[X_NEXT]] = add nuw nsw i32 [[X]], 1
 ; CHECK-NEXT:    br label [[LOOP_HEADER]]
 ; CHECK:       exit:
 ; CHECK-NEXT:    [[C_4:%.*]] = icmp ugt i32 [[Y:%.*]], 10
@@ -323,7 +323,7 @@ define void @loop_header_dom_and(i32 %y, i1 %c) {
 ; CHECK-NEXT:    call void @use(i1 [[C_3]])
 ; CHECK-NEXT:    [[C_4:%.*]] = icmp ugt i32 [[Y]], 100
 ; CHECK-NEXT:    call void @use(i1 [[C_4]])
-; CHECK-NEXT:    [[X_NEXT]] = add i32 [[X]], 1
+; CHECK-NEXT:    [[X_NEXT]] = add nuw nsw i32 [[X]], 1
 ; CHECK-NEXT:    br label [[LOOP_HEADER]]
 ; CHECK:       exit.1:
 ; CHECK-NEXT:    [[C_6:%.*]] = icmp ugt i32 [[Y]], 10
diff --git a/llvm/test/Transforms/ConstraintElimination/monotonic-int-phis-cfg.ll b/llvm/test/Transforms/ConstraintElimination/monotonic-int-phis-cfg.ll
index bf35d3c3228ea..2eeef3c2aec4d 100644
--- a/llvm/test/Transforms/ConstraintElimination/monotonic-int-phis-cfg.ll
+++ b/llvm/test/Transforms/ConstraintElimination/monotonic-int-phis-cfg.ll
@@ -21,7 +21,7 @@ define void @test_phi_not_in_loop_header() {
 ; CHECK-NEXT:    call void @use(i1 [[C]])
 ; CHECK-NEXT:    br label [[INNER_HEADER]]
 ; CHECK:       outer.latch:
-; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
+; CHECK-NEXT:    [[IV_NEXT]] = add nuw nsw i32 [[IV]], 1
 ; CHECK-NEXT:    br label [[OUTER_HEADER]]
 ;
 entry:
diff --git a/llvm/test/Transforms/ConstraintElimination/monotonic-int-phis-decrement.ll b/llvm/test/Transforms/ConstraintElimination/monotonic-int-phis-decrement.ll
index e8d0e0a4b697b..2f6a8a3433e1f 100644
--- a/llvm/test/Transforms/ConstraintElimination/monotonic-int-phis-decrement.ll
+++ b/llvm/test/Transforms/ConstraintElimination/monotonic-int-phis-decrement.ll
@@ -164,7 +164,7 @@ define void @add_rec_decreasing_add_rec_positive_to_negative(i8 noundef %len) {
 ; CHECK:       loop.latch:
 ; CHECK-NEXT:    [[CMP_NOT_I:%.*]] = icmp ult i8 [[K_0]], 5
 ; CHECK-NEXT:    call void @use(i1 [[CMP_NOT_I]])
-; CHECK-NEXT:    [[K_DEC]] = add i8 [[K_0]], -1
+; CHECK-NEXT:    [[K_DEC]] = add nsw i8 [[K_0]], -1
 ; CHECK-NEXT:    br label [[LOOP_HEADER]]
 ; CHECK:       exit:
 ; CHECK-NEXT:    ret void
diff --git a/llvm/test/Transforms/ConstraintElimination/monotonic-int-phis-wrapping.ll b/llvm/test/Transforms/ConstraintElimination/monotonic-int-phis-wrapping.ll
index 1c0de052402af..88a924664d332 100644
--- a/llvm/test/Transforms/ConstraintElimination/monotonic-int-phis-wrapping.ll
+++ b/llvm/test/Transforms/ConstraintElimination/monotonic-int-phis-wrapping.ll
@@ -85,18 +85,16 @@ exit:
 define void @test_iv_nuw_nsw_2_uge_start(i8 %len.n, i8 %a) {
 ; CHECK-LABEL: @test_iv_nuw_nsw_2_uge_start(
 ; CHECK-NEXT:  loop.ph:
-; CHECK-NEXT:    [[START:%.*]] = add nsw i8 -2, 1
+; CHECK-NEXT:    [[START:%.*]] = add nuw nsw i8 -2, 1
 ; CHECK-NEXT:    br label [[LOOP_HEADER:%.*]]
 ; CHECK:       loop.header:
 ; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ [[START]], [[LOOP_PH:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ]
-; CHECK-NEXT:    [[C:%.*]] = icmp eq i8 [[IV]], 1
-; CHECK-NEXT:    br i1 [[C]], label [[EXIT:%.*]], label [[FOR_BODY:%.*]]
+; CHECK-NEXT:    br i1 false, label [[EXIT:%.*]], label [[FOR_BODY:%.*]]
 ; CHECK:       for.body:
 ; CHECK-NEXT:    [[C_2:%.*]] = call i1 @cond()
 ; CHECK-NEXT:    br i1 [[C_2]], label [[LOOP_LATCH]], label [[EXIT]]
 ; CHECK:       loop.latch:
-; CHECK-NEXT:    [[T_1:%.*]] = icmp uge i8 [[IV]], -1
-; CHECK-NEXT:    call void @use.i1(i1 [[T_1]])
+; CHECK-NEXT:    call void @use.i1(i1 true)
 ; CHECK-NEXT:    [[IV_NEXT]] = add nuw nsw i8 [[IV]], 1
 ; CHECK-NEXT:    br label [[LOOP_HEADER]]
 ; CHECK:       exit:
diff --git a/llvm/test/Transforms/ConstraintElimination/pr70510.ll b/llvm/test/Transforms/ConstraintElimination/pr70510.ll
index 32427d7a0474c..31d0a85d8c210 100644
--- a/llvm/test/Transforms/ConstraintElimination/pr70510.ll
+++ b/llvm/test/Transforms/ConstraintElimination/pr70510.ll
@@ -13,7 +13,7 @@ define i32 @test_loop_varying_bound_add_rec() {
 ; CHECK-NEXT:    br i1 [[CMP6]], label [[EXIT1:%.*]], label [[FOR_LATCH]]
 ; CHECK:       for.latch:
 ; CHECK-NEXT:    [[IND2_I]] = add i64 [[IND2]], 1
-; CHECK-NEXT:    [[IND1_I]] = add i64 [[IND1]], 1
+; CHECK-NEXT:    [[IND1_I]] = add nsw i64 [[IND1]], 1
 ; CHECK-NEXT:    [[COND:%.*]] = icmp eq i64 [[IND1_I]], 8
 ; CHECK-NEXT:    br i1 [[COND]], label [[EXIT2:%.*]], label [[FOR_HEADER]]
 ; CHECK:       exit2:
@@ -60,7 +60,7 @@ define i32 @test_loop_varying_bound_call() {
 ; CHECK-NEXT:    [[CMP6:%.*]] = icmp eq i64 [[IND1]], [[VAL]]
 ; CHECK-NEXT:    br i1 [[CMP6]], label [[EXIT1:%.*]], label [[FOR_LATCH]]
 ; CHECK:       for.latch:
-; CHECK-NEXT:    [[IND1_I]] = add i64 [[IND1]], 1
+; CHECK-NEXT:    [[IND1_I]] = add nsw i64 [[IND1]], 1
 ; CHECK-NEXT:    [[COND:%.*]] = icmp eq i64 [[IND1_I]], 8
 ; CHECK-NEXT:    br i1 [[COND]], label [[EXIT2:%.*]], label [[FOR_HEADER]]
 ; CHECK:       exit2:
diff --git a/llvm/test/Transforms/ConstraintElimination/transfer-signed-facts-to-unsigned-is-known-non-negative.ll b/llvm/test/Transforms/ConstraintElimination/transfer-signed-facts-to-unsigned-is-known-non-negative.ll
index efb218ff099fb..d415845ee5a5d 100644
--- a/llvm/test/Transforms/ConstraintElimination/transfer-signed-facts-to-unsigned-is-known-non-negative.ll
+++ b/llvm/test/Transforms/ConstraintElimination/transfer-signed-facts-to-unsigned-is-known-non-negative.ll
@@ -67,7 +67,7 @@ define void @iv_known_non_negative_constant_trip_count_no_nsw_flag(i8 %N) {
 ; CHECK-NEXT:    call void @use(i1 [[F_2]])
 ; CHECK-NEXT:    [[C_0:%.*]] = icmp ugt i8 [[IV]], 2
 ; CHECK-NEXT:    call void @use(i1 [[C_0]])
-; CHECK-NEXT:    [[IV_NEXT]] = add nuw i8 [[IV]], 1
+; CHECK-NEXT:    [[IV_NEXT]] = add nuw nsw i8 [[IV]], 1
 ; CHECK-NEXT:    br label [[LOOP_HEADER]]
 ; CHECK:       exit.1:
 ; CHECK-NEXT:    ret void
diff --git a/llvm/test/Transforms/ConstraintElimination/wrapping-math.ll b/llvm/test/Transforms/ConstraintElimination/wrapping-math.ll
index 6aa55412330ea..febcdce18ca10 100644
--- a/llvm/test/Transforms/ConstraintElimination/wrapping-math.ll
+++ b/llvm/test/Transforms/ConstraintElimination/wrapping-math.ll
@@ -48,9 +48,8 @@ define i1 @wrapping_add_unknown_2(i8 %a) {
 ; CHECK-NEXT:    [[PRE:%.*]] = icmp eq i8 [[A:%.*]], 0
 ; CHECK-NEXT:    br i1 [[PRE]], label [[THEN:%.*]], label [[ELSE:%.*]]
 ; CHECK:       then:
-; CHECK-NEXT:    [[SUB_1:%.*]] = add nsw i8 [[A]], -1
-; CHECK-NEXT:    [[C_1:%.*]] = icmp eq i8 [[SUB_1]], 0
-; CHECK-NEXT:    ret i1 [[C_1]]
+; CHECK-NEXT:    [[SUB_1:%.*]] = add nuw nsw i8 [[A]], -1
+; CHECK-NEXT:    ret i1 false
 ; CHECK:       else:
 ; CHECK-NEXT:    [[SUB_2:%.*]] = add i8 [[A]], -1
 ; CHECK-NEXT:    [[C_2:%.*]] = icmp eq i8 [[SUB_2]], 0
@@ -296,8 +295,8 @@ define i1 @add_nuw_wrapping_add_known_1(i8 %a) {
 ; CHECK-NEXT:  entry:
 ; CHECK-NEXT:    [[PRE:%.*]] = icmp eq i8 [[A:%.*]], 1
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[PRE]])
-; CHECK-NEXT:    [[ADD:%.*]] = add nuw i8 [[A]], 10
-; CHECK-NEXT:    [[SUB_1:%.*]] = add i8 [[ADD]], -1
+; CHECK-NEXT:    [[ADD:%.*]] = add nuw nsw i8 [[A]], 10
+; CHECK-NEXT:    [[SUB_1:%.*]] = add nsw i8 [[ADD]], -1
 ; CHECK-NEXT:    [[RES_1:%.*]] = xor i1 true, true
 ; CHECK-NEXT:    [[RES_2:%.*]] = xor i1 [[RES_1]], false
 ; CHECK-NEXT:    ret i1 [[RES_2]]

>From 299eb79001dd5f2a2110b042e379544144a83985 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Mon, 7 Sep 2026 22:05:18 +0100
Subject: [PATCH 4/4] !fixup update tests after merge

---
 .../test/Transforms/ConstraintElimination/cond-used-in-phi.ll | 4 ++--
 .../induction-condition-in-loop-exit-postinc.ll               | 2 +-
 .../test/Transforms/PhaseOrdering/AArch64/trunc-intrinsics.ll | 4 ++--
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/llvm/test/Transforms/ConstraintElimination/cond-used-in-phi.ll b/llvm/test/Transforms/ConstraintElimination/cond-used-in-phi.ll
index 7f3a282dc6f3b..381bdb02e91f3 100644
--- a/llvm/test/Transforms/ConstraintElimination/cond-used-in-phi.ll
+++ b/llvm/test/Transforms/ConstraintElimination/cond-used-in-phi.ll
@@ -13,7 +13,7 @@ define void @phi_loop_1(i8 %x) {
 ; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
 ; CHECK-NEXT:    [[C_PHI:%.*]] = phi i1 [ false, [[ENTRY]] ], [ true, [[LOOP]] ]
 ; CHECK-NEXT:    call void @use(i1 true)
-; CHECK-NEXT:    [[IV_NEXT]] = add i8 [[IV]], 1
+; CHECK-NEXT:    [[IV_NEXT]] = add nuw nsw i8 [[IV]], 1
 ; CHECK-NEXT:    [[EC:%.*]] = icmp ult i8 [[IV_NEXT]], 3
 ; CHECK-NEXT:    br i1 [[EC]], label [[LOOP]], label [[LOOP_EXIT:%.*]]
 ; CHECK:       loop.exit:
@@ -54,7 +54,7 @@ define void @phi_loop_2(i8 %x) {
 ; CHECK:       loop.2:
 ; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ 0, [[LOOP_1_HEADER]] ], [ [[IV_NEXT:%.*]], [[LOOP_2]] ]
 ; CHECK-NEXT:    call void @use(i1 true)
-; CHECK-NEXT:    [[IV_NEXT]] = add i8 [[IV]], 1
+; CHECK-NEXT:    [[IV_NEXT]] = add nuw nsw i8 [[IV]], 1
 ; CHECK-NEXT:    [[EC:%.*]] = icmp ult i8 [[IV_NEXT]], 3
 ; CHECK-NEXT:    br i1 [[EC]], label [[LOOP_2]], label [[LOOP_1_LATCH]]
 ; CHECK:       loop.1.latch:
diff --git a/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-postinc.ll b/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-postinc.ll
index 28e85c03b2b01..71215d91b4a2a 100644
--- a/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-postinc.ll
+++ b/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-postinc.ll
@@ -722,7 +722,7 @@ define void @latch_postdec_negative_step_header_is_latch() {
 ; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    call void @use(i1 true)
-; CHECK-NEXT:    [[IV_NEXT]] = add i8 [[IV]], -1
+; CHECK-NEXT:    [[IV_NEXT]] = add nsw i8 [[IV]], -1
 ; CHECK-NEXT:    [[EC:%.*]] = icmp eq i8 [[IV_NEXT]], 2
 ; CHECK-NEXT:    br i1 [[EC]], label %[[EXIT:.*]], label %[[LOOP]]
 ; CHECK:       [[EXIT]]:
diff --git a/llvm/test/Transforms/PhaseOrdering/AArch64/trunc-intrinsics.ll b/llvm/test/Transforms/PhaseOrdering/AArch64/trunc-intrinsics.ll
index bd660e5375bca..a955642d52989 100644
--- a/llvm/test/Transforms/PhaseOrdering/AArch64/trunc-intrinsics.ll
+++ b/llvm/test/Transforms/PhaseOrdering/AArch64/trunc-intrinsics.ll
@@ -10,7 +10,7 @@ define void @mul3_clamp_u8_select(ptr noalias %out, ptr noalias %in) {
 ; CHECK-NEXT:    br label %[[VECTOR_BODY:.*]]
 ; CHECK:       [[VECTOR_BODY]]:
 ; CHECK-NEXT:    [[INDEX:%.*]] = phi i64 [ 0, %[[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], %[[VECTOR_BODY]] ]
-; CHECK-NEXT:    [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[IN]], i64 [[INDEX]]
+; CHECK-NEXT:    [[TMP0:%.*]] = getelementptr inbounds nuw i8, ptr [[IN]], i64 [[INDEX]]
 ; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr inbounds nuw i8, ptr [[TMP0]], i64 16
 ; CHECK-NEXT:    [[WIDE_LOAD:%.*]] = load <16 x i8>, ptr [[TMP0]], align 1
 ; CHECK-NEXT:    [[WIDE_LOAD2:%.*]] = load <16 x i8>, ptr [[TMP1]], align 1
@@ -22,7 +22,7 @@ define void @mul3_clamp_u8_select(ptr noalias %out, ptr noalias %in) {
 ; CHECK-NEXT:    [[TMP7:%.*]] = tail call <16 x i16> @llvm.umin.v16i16(<16 x i16> [[TMP5]], <16 x i16> splat (i16 255))
 ; CHECK-NEXT:    [[TMP8:%.*]] = trunc nuw <16 x i16> [[TMP6]] to <16 x i8>
 ; CHECK-NEXT:    [[TMP9:%.*]] = trunc nuw <16 x i16> [[TMP7]] to <16 x i8>
-; CHECK-NEXT:    [[TMP10:%.*]] = getelementptr inbounds i8, ptr [[OUT]], i64 [[INDEX]]
+; CHECK-NEXT:    [[TMP10:%.*]] = getelementptr inbounds nuw i8, ptr [[OUT]], i64 [[INDEX]]
 ; CHECK-NEXT:    [[TMP11:%.*]] = getelementptr inbounds nuw i8, ptr [[TMP10]], i64 16
 ; CHECK-NEXT:    store <16 x i8> [[TMP8]], ptr [[TMP10]], align 1
 ; CHECK-NEXT:    store <16 x i8> [[TMP9]], ptr [[TMP11]], align 1



More information about the llvm-commits mailing list