[llvm] [ConstraintElim] Strengthen flags on mul and shl with constant operand. (PR #219293)

Florian Hahn via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 28 03:02:07 PDT 2026


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

>From 0d923bc8af45e9d86e67d9b9dd4565a0ade80c6f Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Thu, 27 Aug 2026 18:37:17 +0100
Subject: [PATCH 1/3] [ConstraintElim] Add tests for shl/mul materialization

---
 .../materialize-flags.ll                      | 186 +++++++++++++++++-
 1 file changed, 185 insertions(+), 1 deletion(-)

diff --git a/llvm/test/Transforms/ConstraintElimination/materialize-flags.ll b/llvm/test/Transforms/ConstraintElimination/materialize-flags.ll
index d27ff1b5e3068..862ad52176d25 100644
--- a/llvm/test/Transforms/ConstraintElimination/materialize-flags.ll
+++ b/llvm/test/Transforms/ConstraintElimination/materialize-flags.ll
@@ -1,4 +1,4 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: none --version 6
 ; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s
 
 define i64 @sext_to_zext_nneg(i32 %n, i32 %m) {
@@ -455,3 +455,187 @@ bb.2:
 exit:
   ret ptr null
 }
+
+; %x <u %n <u 1024, hence %x * 3 fits an i64 unsigned and signed.
+define i64 @mul_from_transitive_unsigned_bound(i64 %x, i64 %n) {
+; CHECK-LABEL: define i64 @mul_from_transitive_unsigned_bound(
+; CHECK-SAME: i64 [[X:%.*]], i64 [[N:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[C_0:%.*]] = icmp ult i64 [[N]], 1024
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C_0]])
+; CHECK-NEXT:    [[C_1:%.*]] = icmp ult i64 [[X]], [[N]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C_1]])
+; CHECK-NEXT:    [[MUL:%.*]] = mul i64 [[X]], 3
+; CHECK-NEXT:    ret i64 [[MUL]]
+;
+entry:
+  %c.0 = icmp ult i64 %n, 1024
+  call void @llvm.assume(i1 %c.0)
+  %c.1 = icmp ult i64 %x, %n
+  call void @llvm.assume(i1 %c.1)
+  %mul = mul i64 %x, 3
+  ret i64 %mul
+}
+
+; Same as above, but there is no upper bound for %n.
+define i64 @mul_no_flags_unbounded_transitive_operand(i64 %x, i64 %n) {
+; CHECK-LABEL: define i64 @mul_no_flags_unbounded_transitive_operand(
+; CHECK-SAME: i64 [[X:%.*]], i64 [[N:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[C_1:%.*]] = icmp ult i64 [[X]], [[N]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C_1]])
+; CHECK-NEXT:    [[MUL:%.*]] = mul i64 [[X]], 3
+; CHECK-NEXT:    ret i64 [[MUL]]
+;
+entry:
+  %c.1 = icmp ult i64 %x, %n
+  call void @llvm.assume(i1 %c.1)
+  %mul = mul i64 %x, 3
+  ret i64 %mul
+}
+
+; %x <=u %x + %y <u 1024 due to the nuw add, hence %x << 3 fits an i64 unsigned.
+define i64 @shl_nuw_from_sum_bound(i64 %x, i64 %y) {
+; CHECK-LABEL: define i64 @shl_nuw_from_sum_bound(
+; CHECK-SAME: i64 [[X:%.*]], i64 [[Y:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[S:%.*]] = add nuw i64 [[X]], [[Y]]
+; CHECK-NEXT:    [[C_0:%.*]] = icmp ult i64 [[S]], 1024
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C_0]])
+; CHECK-NEXT:    [[SHL:%.*]] = shl i64 [[X]], 3
+; CHECK-NEXT:    ret i64 [[SHL]]
+;
+entry:
+  %s = add nuw i64 %x, %y
+  %c.0 = icmp ult i64 %s, 1024
+  call void @llvm.assume(i1 %c.0)
+  %shl = shl i64 %x, 3
+  ret i64 %shl
+}
+
+; The bound on %x is one too weak for %x << 3 to fit an i64 unsigned.
+define i64 @shl_no_nuw_bound_too_weak(i64 %x) {
+; CHECK-LABEL: define i64 @shl_no_nuw_bound_too_weak(
+; CHECK-SAME: i64 [[X:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[C_0:%.*]] = icmp ult i64 [[X]], 2305843009213693953
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C_0]])
+; CHECK-NEXT:    [[SHL:%.*]] = shl i64 [[X]], 3
+; CHECK-NEXT:    ret i64 [[SHL]]
+;
+entry:
+  %c.0 = icmp ult i64 %x, 2305843009213693953
+  call void @llvm.assume(i1 %c.0)
+  %shl = shl i64 %x, 3
+  ret i64 %shl
+}
+
+; %x >=s %z >=s 0 and %y >=s 0, hence the nsw product also fits unsigned.
+define i64 @mul_nuw_from_nsw_and_non_negative(i64 %x, i64 %y, i64 %z) {
+; CHECK-LABEL: define i64 @mul_nuw_from_nsw_and_non_negative(
+; CHECK-SAME: i64 [[X:%.*]], i64 [[Y:%.*]], i64 [[Z:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[C_0:%.*]] = icmp sge i64 [[X]], [[Z]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C_0]])
+; CHECK-NEXT:    [[C_1:%.*]] = icmp sge i64 [[Z]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C_1]])
+; CHECK-NEXT:    [[C_2:%.*]] = icmp sge i64 [[Y]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C_2]])
+; CHECK-NEXT:    [[MUL:%.*]] = mul nsw i64 [[X]], [[Y]]
+; CHECK-NEXT:    ret i64 [[MUL]]
+;
+entry:
+  %c.0 = icmp sge i64 %x, %z
+  call void @llvm.assume(i1 %c.0)
+  %c.1 = icmp sge i64 %z, 0
+  call void @llvm.assume(i1 %c.1)
+  %c.2 = icmp sge i64 %y, 0
+  call void @llvm.assume(i1 %c.2)
+  %mul = mul nsw i64 %x, %y
+  ret i64 %mul
+}
+
+; Same as above, but %y may be negative.
+define i64 @mul_no_nuw_from_nsw_second_operand_may_be_negative(i64 %x, i64 %y, i64 %z) {
+; CHECK-LABEL: define i64 @mul_no_nuw_from_nsw_second_operand_may_be_negative(
+; CHECK-SAME: i64 [[X:%.*]], i64 [[Y:%.*]], i64 [[Z:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[C_0:%.*]] = icmp sge i64 [[X]], [[Z]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C_0]])
+; CHECK-NEXT:    [[C_1:%.*]] = icmp sge i64 [[Z]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C_1]])
+; CHECK-NEXT:    [[MUL:%.*]] = mul nsw i64 [[X]], [[Y]]
+; CHECK-NEXT:    ret i64 [[MUL]]
+;
+entry:
+  %c.0 = icmp sge i64 %x, %z
+  call void @llvm.assume(i1 %c.0)
+  %c.1 = icmp sge i64 %z, 0
+  call void @llvm.assume(i1 %c.1)
+  %mul = mul nsw i64 %x, %y
+  ret i64 %mul
+}
+
+; %x >=s %z >=s 0, hence the nsw shift also fits unsigned, for any shift amount.
+define i64 @shl_nuw_from_nsw_and_non_negative(i64 %x, i64 %s, i64 %z) {
+; CHECK-LABEL: define i64 @shl_nuw_from_nsw_and_non_negative(
+; CHECK-SAME: i64 [[X:%.*]], i64 [[S:%.*]], i64 [[Z:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[C_0:%.*]] = icmp sge i64 [[X]], [[Z]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C_0]])
+; CHECK-NEXT:    [[C_1:%.*]] = icmp sge i64 [[Z]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C_1]])
+; CHECK-NEXT:    [[SHL:%.*]] = shl nsw i64 [[X]], [[S]]
+; CHECK-NEXT:    ret i64 [[SHL]]
+;
+entry:
+  %c.0 = icmp sge i64 %x, %z
+  call void @llvm.assume(i1 %c.0)
+  %c.1 = icmp sge i64 %z, 0
+  call void @llvm.assume(i1 %c.1)
+  %shl = shl nsw i64 %x, %s
+  ret i64 %shl
+}
+
+; Same as above, but the shift does not have nsw.
+define i64 @shl_no_nuw_non_negative_without_nsw(i64 %x, i64 %s, i64 %z) {
+; CHECK-LABEL: define i64 @shl_no_nuw_non_negative_without_nsw(
+; CHECK-SAME: i64 [[X:%.*]], i64 [[S:%.*]], i64 [[Z:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[C_0:%.*]] = icmp sge i64 [[X]], [[Z]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C_0]])
+; CHECK-NEXT:    [[C_1:%.*]] = icmp sge i64 [[Z]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C_1]])
+; CHECK-NEXT:    [[SHL:%.*]] = shl i64 [[X]], [[S]]
+; CHECK-NEXT:    ret i64 [[SHL]]
+;
+entry:
+  %c.0 = icmp sge i64 %x, %z
+  call void @llvm.assume(i1 %c.0)
+  %c.1 = icmp sge i64 %z, 0
+  call void @llvm.assume(i1 %c.1)
+  %shl = shl i64 %x, %s
+  ret i64 %shl
+}
+
+; -8 <=s %x <=s 7 implies %x * 16 does not wrap signed, but it may wrap
+; unsigned.
+define i8 @mul_nsw_only_from_signed_bounds(i8 %x) {
+; CHECK-LABEL: define i8 @mul_nsw_only_from_signed_bounds(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[C_0:%.*]] = icmp sle i8 [[X]], 7
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C_0]])
+; CHECK-NEXT:    [[C_1:%.*]] = icmp sge i8 [[X]], -8
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C_1]])
+; CHECK-NEXT:    [[MUL:%.*]] = mul i8 [[X]], 16
+; CHECK-NEXT:    ret i8 [[MUL]]
+;
+entry:
+  %c.0 = icmp sle i8 %x, 7
+  call void @llvm.assume(i1 %c.0)
+  %c.1 = icmp sge i8 %x, -8
+  call void @llvm.assume(i1 %c.1)
+  %mul = mul i8 %x, 16
+  ret i8 %mul
+}

>From e37d06bccb12b1ba186fc7e61969b7575d14aa7c Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Wed, 26 Aug 2026 09:57:01 +0100
Subject: [PATCH 2/3] [ConstraintElim] Strengthen flags on mul and shl with
 constant operand.

Extend the flag strengthening to mul and shl if the second operand is a
constant. In that case ConstantRange::makeGuaranteedNoWrapRegion gives
the exact range for the first operand so the operation does not wrap.

Query the constraint system, to check if the first operand is within the
no-wrap region.

This enables a number of additional folds end-to-end:
https://github.com/dtcxzyw/llvm-opt-benchmark-nightly/pull/1080

Note that in a few cases, the total number of instructions slightly
increases, mostly due to a few extra truncates.

Compile-time impact is in the noise:
https://llvm-compile-time-tracker.com/compare.php?from=7c9adcf3cd9bab7ebb739f7d10885d3433d6d7df&to=bd0d903e1335c5a5f6deb3d334cf2e9b46cc1b65&stat=instructions:u
---
 .../Scalar/ConstraintElimination.cpp          | 75 ++++++++++++++++++-
 .../constraint-overflow.ll                    |  2 +-
 .../geps-unsigned-predicates.ll               | 18 ++---
 .../materialize-flags.ll                      |  8 +-
 .../Transforms/ConstraintElimination/shl.ll   | 10 +--
 5 files changed, 88 insertions(+), 25 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index 56ff0a543ae93..52df61ae7a261 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -1294,32 +1294,99 @@ static bool getConstraintFromMemoryAccess(GetElementPtrInst &GEP,
 /// Returns true if \p I is a candidate whose poison-generating flags may be
 /// strengthened using the constraint systems.
 static bool canStrengthenFlags(Instruction *I) {
-  switch (I->getOpcode()) {
+  auto *BO = dyn_cast<BinaryOperator>(I);
+  if (!BO || !BO->getType()->isIntegerTy())
+    return false;
+
+  switch (BO->getOpcode()) {
   case Instruction::Sub:
     // A - B does not wrap unsigned, if A >=u B. Subs with constant operands get
     // canonicalized to Add.
-    return I->getType()->isIntegerTy() && !I->hasNoUnsignedWrap() &&
-           !isa<Constant>(I->getOperand(1));
+    return !BO->hasNoUnsignedWrap() && !isa<Constant>(BO->getOperand(1));
+  case Instruction::Mul:
+  case Instruction::Shl:
+    if (BO->hasNoUnsignedWrap() && BO->hasNoSignedWrap())
+      return false;
+    // A constant second operand can be used to bound the first operand to
+    // refine no-wrap flags.
+    return isa<ConstantInt>(BO->getOperand(1));
   default:
     return false;
   }
 }
 
+/// Returns true if \p Info implies that \p Op is in \p R, interpreting \p R as
+/// a signed range if \p Signed is set and as an unsigned range otherwise.
+static bool doesHoldInRange(const ConstraintInfo &Info, Value *Op,
+                            const ConstantRange &R, bool Signed) {
+  if (R.isFullSet() || R.isEmptySet() ||
+      (Signed ? R.isSignWrappedSet() : R.isWrappedSet()))
+    return false;
+
+  unsigned BitWidth = R.getBitWidth();
+  APInt Min = Signed ? R.getSignedMin() : R.getUnsignedMin();
+  APInt Max = Signed ? R.getSignedMax() : R.getUnsignedMax();
+  APInt MinVal = Signed ? APInt::getSignedMinValue(BitWidth)
+                        : APInt::getMinValue(BitWidth);
+  APInt MaxVal = Signed ? APInt::getSignedMaxValue(BitWidth)
+                        : APInt::getMaxValue(BitWidth);
+  Type *Ty = Op->getType();
+  if (Min != MinVal &&
+      !Info.doesHold(Signed ? CmpInst::ICMP_SGE : CmpInst::ICMP_UGE, Op,
+                     ConstantInt::get(Ty, Min)))
+    return false;
+  if (Max != MaxVal &&
+      !Info.doesHold(Signed ? CmpInst::ICMP_SLE : CmpInst::ICMP_ULE, Op,
+                     ConstantInt::get(Ty, Max)))
+    return false;
+  return true;
+}
+
 /// 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: {
     // Op0 - Op1 does not wrap unsigned, if Op0 >=u Op1.
-    if (!Info.doesHold(CmpInst::ICMP_UGE, I->getOperand(0), I->getOperand(1)))
+    if (!Info.doesHold(CmpInst::ICMP_UGE, Op0, Op1))
       return false;
     LLVM_DEBUG(dbgs() << "Adding nuw to " << *I << "\n");
     I->setHasNoUnsignedWrap();
     return true;
   }
+  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.
+    auto *C = 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;
+    }
+    return Changed;
+  }
   default:
     return false;
   }
diff --git a/llvm/test/Transforms/ConstraintElimination/constraint-overflow.ll b/llvm/test/Transforms/ConstraintElimination/constraint-overflow.ll
index bf2e4a933cf6d..41756fd4b16e3 100644
--- a/llvm/test/Transforms/ConstraintElimination/constraint-overflow.ll
+++ b/llvm/test/Transforms/ConstraintElimination/constraint-overflow.ll
@@ -88,7 +88,7 @@ define i1 @fm_overflow_recovery(i64 %n, i64 %i, i64 %lim) {
 ; CHECK-NEXT:    [[N4M4:%.*]] = add i64 [[N4]], -4
 ; CHECK-NEXT:    [[F2:%.*]] = icmp ult i64 [[N4M4]], [[LIM]]
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[F2]])
-; CHECK-NEXT:    [[I4:%.*]] = shl nuw i64 [[I]], 2
+; CHECK-NEXT:    [[I4:%.*]] = shl nuw nsw i64 [[I]], 2
 ; CHECK-NEXT:    ret i1 true
 ;
 entry:
diff --git a/llvm/test/Transforms/ConstraintElimination/geps-unsigned-predicates.ll b/llvm/test/Transforms/ConstraintElimination/geps-unsigned-predicates.ll
index 612bf2f0a8337..a7210b3a80121 100644
--- a/llvm/test/Transforms/ConstraintElimination/geps-unsigned-predicates.ll
+++ b/llvm/test/Transforms/ConstraintElimination/geps-unsigned-predicates.ll
@@ -490,15 +490,14 @@ define void @test.ult.gep.shl(ptr readonly %src, ptr readnone %max, i8 %idx) {
 ; CHECK-NEXT:    [[IDX_SHL_1:%.*]] = shl nuw nsw i8 [[IDX]], 1
 ; CHECK-NEXT:    [[ADD_PTR_SHL_1:%.*]] = getelementptr inbounds i32, ptr [[SRC]], i8 [[IDX_SHL_1]]
 ; CHECK-NEXT:    call void @use(i1 true)
-; CHECK-NEXT:    [[IDX_SHL_2:%.*]] = shl nuw i8 [[IDX]], 2
+; CHECK-NEXT:    [[IDX_SHL_2:%.*]] = shl nuw nsw i8 [[IDX]], 2
 ; CHECK-NEXT:    [[ADD_PTR_SHL_2:%.*]] = getelementptr inbounds i32, ptr [[SRC]], i8 [[IDX_SHL_2]]
 ; CHECK-NEXT:    [[C_MAX_1:%.*]] = icmp ult ptr [[ADD_PTR_SHL_2]], [[MAX]]
 ; CHECK-NEXT:    call void @use(i1 [[C_MAX_1]])
-; CHECK-NEXT:    [[IDX_SHL_NOT_NUW:%.*]] = shl i8 [[IDX]], 1
+; CHECK-NEXT:    [[IDX_SHL_NOT_NUW:%.*]] = shl nuw nsw i8 [[IDX]], 1
 ; CHECK-NEXT:    [[ADD_PTR_SHL_NOT_NUW:%.*]] = getelementptr inbounds i32, ptr [[SRC]], i8 [[IDX_SHL_NOT_NUW]]
-; CHECK-NEXT:    [[C_MAX_2:%.*]] = icmp ult ptr [[ADD_PTR_SHL_NOT_NUW]], [[MAX]]
-; CHECK-NEXT:    call void @use(i1 [[C_MAX_2]])
-; CHECK-NEXT:    [[IDX_SHL_3:%.*]] = shl nuw i8 [[IDX]], 3
+; CHECK-NEXT:    call void @use(i1 true)
+; CHECK-NEXT:    [[IDX_SHL_3:%.*]] = shl nuw nsw i8 [[IDX]], 3
 ; CHECK-NEXT:    [[ADD_PTR_SHL_3:%.*]] = getelementptr inbounds i32, ptr [[SRC]], i8 [[IDX_SHL_3]]
 ; CHECK-NEXT:    [[C_MAX_3:%.*]] = icmp ult ptr [[ADD_PTR_SHL_3]], [[MAX]]
 ; CHECK-NEXT:    call void @use(i1 [[C_MAX_3]])
@@ -549,16 +548,15 @@ define void @test.ult.gep.shl.zext(ptr readonly %src, ptr readnone %max, i32 %id
 ; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i32 [[IDX:%.*]], 5
 ; CHECK-NEXT:    br i1 [[CMP]], label [[CHECK_MAX:%.*]], label [[TRAP]]
 ; CHECK:       check.max:
-; CHECK-NEXT:    [[IDX_SHL:%.*]] = shl nuw i32 [[IDX]], 1
+; CHECK-NEXT:    [[IDX_SHL:%.*]] = shl nuw nsw i32 [[IDX]], 1
 ; CHECK-NEXT:    [[EXT_1:%.*]] = zext i32 [[IDX_SHL]] to i64
 ; CHECK-NEXT:    [[ADD_PTR_SHL:%.*]] = getelementptr inbounds i32, ptr [[SRC]], i64 [[EXT_1]]
 ; CHECK-NEXT:    call void @use(i1 true)
-; CHECK-NEXT:    [[IDX_SHL_NOT_NUW:%.*]] = shl i32 [[IDX]], 1
+; CHECK-NEXT:    [[IDX_SHL_NOT_NUW:%.*]] = shl nuw nsw i32 [[IDX]], 1
 ; CHECK-NEXT:    [[EXT_2:%.*]] = zext i32 [[IDX_SHL_NOT_NUW]] to i64
 ; CHECK-NEXT:    [[ADD_PTR_SHL_NOT_NUW:%.*]] = getelementptr inbounds i32, ptr [[SRC]], i64 [[EXT_2]]
-; CHECK-NEXT:    [[C_MAX_1:%.*]] = icmp ult ptr [[ADD_PTR_SHL_NOT_NUW]], [[MAX]]
-; CHECK-NEXT:    call void @use(i1 [[C_MAX_1]])
-; CHECK-NEXT:    [[IDX_SHL_3:%.*]] = shl nuw i32 [[IDX]], 2
+; CHECK-NEXT:    call void @use(i1 true)
+; CHECK-NEXT:    [[IDX_SHL_3:%.*]] = shl nuw nsw i32 [[IDX]], 2
 ; CHECK-NEXT:    [[EXT_3:%.*]] = zext i32 [[IDX_SHL_3]] to i64
 ; CHECK-NEXT:    [[ADD_PTR_SHL_3:%.*]] = getelementptr inbounds i32, ptr [[SRC]], i64 [[EXT_3]]
 ; CHECK-NEXT:    [[C_MAX_2:%.*]] = icmp ult ptr [[ADD_PTR_SHL_3]], [[MAX]]
diff --git a/llvm/test/Transforms/ConstraintElimination/materialize-flags.ll b/llvm/test/Transforms/ConstraintElimination/materialize-flags.ll
index 862ad52176d25..2ec78db674b04 100644
--- a/llvm/test/Transforms/ConstraintElimination/materialize-flags.ll
+++ b/llvm/test/Transforms/ConstraintElimination/materialize-flags.ll
@@ -1,4 +1,4 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: none --version 6
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
 ; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s
 
 define i64 @sext_to_zext_nneg(i32 %n, i32 %m) {
@@ -465,7 +465,7 @@ define i64 @mul_from_transitive_unsigned_bound(i64 %x, i64 %n) {
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[C_0]])
 ; CHECK-NEXT:    [[C_1:%.*]] = icmp ult i64 [[X]], [[N]]
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[C_1]])
-; CHECK-NEXT:    [[MUL:%.*]] = mul i64 [[X]], 3
+; CHECK-NEXT:    [[MUL:%.*]] = mul nuw nsw i64 [[X]], 3
 ; CHECK-NEXT:    ret i64 [[MUL]]
 ;
 entry:
@@ -502,7 +502,7 @@ define i64 @shl_nuw_from_sum_bound(i64 %x, i64 %y) {
 ; CHECK-NEXT:    [[S:%.*]] = add nuw i64 [[X]], [[Y]]
 ; CHECK-NEXT:    [[C_0:%.*]] = icmp ult i64 [[S]], 1024
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[C_0]])
-; CHECK-NEXT:    [[SHL:%.*]] = shl i64 [[X]], 3
+; CHECK-NEXT:    [[SHL:%.*]] = shl nuw i64 [[X]], 3
 ; CHECK-NEXT:    ret i64 [[SHL]]
 ;
 entry:
@@ -628,7 +628,7 @@ define i8 @mul_nsw_only_from_signed_bounds(i8 %x) {
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[C_0]])
 ; CHECK-NEXT:    [[C_1:%.*]] = icmp sge i8 [[X]], -8
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[C_1]])
-; CHECK-NEXT:    [[MUL:%.*]] = mul i8 [[X]], 16
+; CHECK-NEXT:    [[MUL:%.*]] = mul nsw i8 [[X]], 16
 ; CHECK-NEXT:    ret i8 [[MUL]]
 ;
 entry:
diff --git a/llvm/test/Transforms/ConstraintElimination/shl.ll b/llvm/test/Transforms/ConstraintElimination/shl.ll
index 3affdd2a5291f..a6d5ff396e94e 100644
--- a/llvm/test/Transforms/ConstraintElimination/shl.ll
+++ b/llvm/test/Transforms/ConstraintElimination/shl.ll
@@ -1399,9 +1399,8 @@ define i1 @shl_nuw_signed_bounded(i64 %c) {
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[BND]])
 ; CHECK-NEXT:    [[NN:%.*]] = icmp sge i64 [[C]], 0
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[NN]])
-; CHECK-NEXT:    [[M:%.*]] = shl nuw i64 [[C]], 2
-; CHECK-NEXT:    [[T:%.*]] = icmp slt i64 [[M]], 0
-; CHECK-NEXT:    ret i1 [[T]]
+; CHECK-NEXT:    [[M:%.*]] = shl nuw nsw i64 [[C]], 2
+; CHECK-NEXT:    ret i1 false
 ;
 entry:
   %bnd = icmp slt i64 %c, 2305843009213693952
@@ -1419,9 +1418,8 @@ define i1 @shl_nuw_signed_tight_bound(i8 %x) {
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[B]])
 ; CHECK-NEXT:    [[NN:%.*]] = icmp sge i8 [[X]], 0
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[NN]])
-; CHECK-NEXT:    [[M:%.*]] = shl nuw i8 [[X]], 2
-; CHECK-NEXT:    [[T:%.*]] = icmp slt i8 [[M]], 0
-; CHECK-NEXT:    ret i1 [[T]]
+; CHECK-NEXT:    [[M:%.*]] = shl nuw nsw i8 [[X]], 2
+; CHECK-NEXT:    ret i1 false
 ;
   %b = icmp slt i8 %x, 32
   call void @llvm.assume(i1 %b)

>From a95a1b74805cd92581572c48f3d5c7906a90e86d Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Fri, 28 Aug 2026 11:01:37 +0100
Subject: [PATCH 3/3] !fixup dont bail out on full set

---
 .../Scalar/ConstraintElimination.cpp          |  5 +++-
 .../large-constant-ints.ll                    |  8 ++---
 .../ConstraintElimination/mul-nsw.ll          | 30 +++++++++----------
 .../Transforms/ConstraintElimination/shl.ll   |  6 ++--
 4 files changed, 26 insertions(+), 23 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index 52df61ae7a261..b2827ca844518 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -1319,10 +1319,13 @@ static bool canStrengthenFlags(Instruction *I) {
 /// a signed range if \p Signed is set and as an unsigned range otherwise.
 static bool doesHoldInRange(const ConstraintInfo &Info, Value *Op,
                             const ConstantRange &R, bool Signed) {
-  if (R.isFullSet() || R.isEmptySet() ||
+  if (R.isEmptySet() ||
       (Signed ? R.isSignWrappedSet() : R.isWrappedSet()))
     return false;
 
+  if (R.isFullSet())
+    return true;
+
   unsigned BitWidth = R.getBitWidth();
   APInt Min = Signed ? R.getSignedMin() : R.getUnsignedMin();
   APInt Max = Signed ? R.getSignedMax() : R.getUnsignedMax();
diff --git a/llvm/test/Transforms/ConstraintElimination/large-constant-ints.ll b/llvm/test/Transforms/ConstraintElimination/large-constant-ints.ll
index a80e492e08246..8ee55f98f0e36 100644
--- a/llvm/test/Transforms/ConstraintElimination/large-constant-ints.ll
+++ b/llvm/test/Transforms/ConstraintElimination/large-constant-ints.ll
@@ -152,13 +152,13 @@ else:
 define i1 @gep_zext_shl_decomp_i80(ptr %a, i80 %v) {
 ; CHECK-LABEL: @gep_zext_shl_decomp_i80(
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[SHL:%.*]] = shl nuw i80 [[V:%.*]], 1973801615886922022913
+; CHECK-NEXT:    [[SHL:%.*]] = shl nuw nsw i80 [[V:%.*]], 1973801615886922022913
 ; CHECK-NEXT:    [[EXT:%.*]] = zext i80 [[SHL]] to i128
 ; CHECK-NEXT:    [[GEP:%.*]] = getelementptr inbounds i8, ptr [[A:%.*]], i128 [[EXT]]
 ; CHECK-NEXT:    [[C:%.*]] = icmp eq ptr [[GEP]], null
 ; CHECK-NEXT:    br i1 [[C]], label [[THEN:%.*]], label [[ELSE:%.*]]
 ; CHECK:       then:
-; CHECK-NEXT:    [[SHL_1:%.*]] = shl nuw i80 [[V]], 1973801615886922022913
+; CHECK-NEXT:    [[SHL_1:%.*]] = shl nuw nsw i80 [[V]], 1973801615886922022913
 ; CHECK-NEXT:    [[EXT_1:%.*]] = zext i80 [[SHL_1]] to i128
 ; CHECK-NEXT:    [[GEP_1:%.*]] = getelementptr inbounds i8, ptr [[A]], i128 [[EXT_1]]
 ; CHECK-NEXT:    [[C_1:%.*]] = icmp eq ptr [[GEP_1]], null
@@ -222,12 +222,12 @@ else:
 define i1 @gep_shl_decomp_i80(ptr %a, i80 %v) {
 ; CHECK-LABEL: @gep_shl_decomp_i80(
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[SHL:%.*]] = shl nuw i80 [[V:%.*]], 1973801615886922022913
+; CHECK-NEXT:    [[SHL:%.*]] = shl nuw nsw i80 [[V:%.*]], 1973801615886922022913
 ; CHECK-NEXT:    [[GEP:%.*]] = getelementptr inbounds i8, ptr [[A:%.*]], i80 [[SHL]]
 ; CHECK-NEXT:    [[C:%.*]] = icmp eq ptr [[GEP]], null
 ; CHECK-NEXT:    br i1 [[C]], label [[THEN:%.*]], label [[ELSE:%.*]]
 ; CHECK:       then:
-; CHECK-NEXT:    [[SHL_1:%.*]] = shl nuw i80 [[V]], 1973801615886922022913
+; CHECK-NEXT:    [[SHL_1:%.*]] = shl nuw nsw i80 [[V]], 1973801615886922022913
 ; CHECK-NEXT:    [[GEP_1:%.*]] = getelementptr inbounds i8, ptr [[A]], i80 [[SHL_1]]
 ; CHECK-NEXT:    [[C_1:%.*]] = icmp eq ptr [[GEP_1]], null
 ; CHECK-NEXT:    ret i1 [[C_1]]
diff --git a/llvm/test/Transforms/ConstraintElimination/mul-nsw.ll b/llvm/test/Transforms/ConstraintElimination/mul-nsw.ll
index f2f6a91ebc9e8..4bae51d2fcb04 100644
--- a/llvm/test/Transforms/ConstraintElimination/mul-nsw.ll
+++ b/llvm/test/Transforms/ConstraintElimination/mul-nsw.ll
@@ -14,7 +14,7 @@ define void @slt_mul_nsw_3_known_positive_1(i8 %start, i8 %high) {
 ; CHECK-NEXT:    br i1 [[AND]], label [[THEN:%.*]], label [[ELSE:%.*]]
 ; CHECK:       then:
 ; CHECK-NEXT:    call void @use(i1 true)
-; CHECK-NEXT:    [[START_1:%.*]] = mul nsw i8 [[START]], 1
+; CHECK-NEXT:    [[START_1:%.*]] = mul nuw nsw i8 [[START]], 1
 ; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    [[START_2:%.*]] = mul nsw i8 [[START]], 2
 ; CHECK-NEXT:    call void @use(i1 true)
@@ -68,7 +68,7 @@ define void @slt_mul_nsw_3_known_positive_2(i8 %start, i8 %high) {
 ; CHECK:       else:
 ; CHECK-NEXT:    [[C_4:%.*]] = icmp slt i8 [[START]], [[HIGH]]
 ; CHECK-NEXT:    call void @use(i1 [[C_4]])
-; CHECK-NEXT:    [[ELSE_START_1:%.*]] = mul nsw i8 [[START]], 1
+; CHECK-NEXT:    [[ELSE_START_1:%.*]] = mul nuw nsw i8 [[START]], 1
 ; CHECK-NEXT:    [[C_5:%.*]] = icmp slt i8 [[ELSE_START_1]], [[HIGH]]
 ; CHECK-NEXT:    call void @use(i1 [[C_5]])
 ; CHECK-NEXT:    [[ELSE_START_2:%.*]] = mul nsw i8 [[START]], 2
@@ -121,7 +121,7 @@ define void @slt_mul_no_nsw_3_known_positive_1(i8 %start, i8 %high) {
 ; CHECK:       then:
 ; CHECK-NEXT:    [[C_3:%.*]] = icmp slt i8 [[START]], [[HIGH]]
 ; CHECK-NEXT:    call void @use(i1 [[C_3]])
-; CHECK-NEXT:    [[START_1:%.*]] = mul nsw i8 [[START]], 1
+; CHECK-NEXT:    [[START_1:%.*]] = mul nuw nsw i8 [[START]], 1
 ; CHECK-NEXT:    [[C_4:%.*]] = icmp slt i8 [[START_1]], [[HIGH]]
 ; CHECK-NEXT:    call void @use(i1 [[C_4]])
 ; CHECK-NEXT:    [[START_2:%.*]] = mul nsw i8 [[START]], 2
@@ -178,7 +178,7 @@ define void @slt_mul_no_nsw_3_known_positive_2(i8 %start, i8 %high) {
 ; CHECK:       else:
 ; CHECK-NEXT:    [[C_8:%.*]] = icmp slt i8 [[START]], [[HIGH]]
 ; CHECK-NEXT:    call void @use(i1 [[C_8]])
-; CHECK-NEXT:    [[ELSE_START_1:%.*]] = mul nsw i8 [[START]], 1
+; CHECK-NEXT:    [[ELSE_START_1:%.*]] = mul nuw nsw i8 [[START]], 1
 ; CHECK-NEXT:    [[C_9:%.*]] = icmp slt i8 [[ELSE_START_1]], [[HIGH]]
 ; CHECK-NEXT:    call void @use(i1 [[C_9]])
 ; CHECK-NEXT:    [[ELSE_START_2:%.*]] = mul nsw i8 [[START]], 2
@@ -229,7 +229,7 @@ define void @slt_mul_nsw_3_not_known_positive_1(i8 %start, i8 %high) {
 ; CHECK:       then:
 ; CHECK-NEXT:    [[C_3:%.*]] = icmp slt i8 [[START]], [[HIGH]]
 ; CHECK-NEXT:    call void @use(i1 [[C_3]])
-; CHECK-NEXT:    [[START_1:%.*]] = mul nsw i8 [[START]], 1
+; CHECK-NEXT:    [[START_1:%.*]] = mul nuw nsw i8 [[START]], 1
 ; CHECK-NEXT:    [[C_4:%.*]] = icmp slt i8 [[START_1]], [[HIGH]]
 ; CHECK-NEXT:    call void @use(i1 [[C_4]])
 ; CHECK-NEXT:    [[START_2:%.*]] = mul nsw i8 [[START]], 2
@@ -282,7 +282,7 @@ define void @slt_mul_nsw_3_not_known_positive_2(i8 %start, i8 %high) {
 ; CHECK:       else:
 ; CHECK-NEXT:    [[C_8:%.*]] = icmp slt i8 [[START]], [[HIGH]]
 ; CHECK-NEXT:    call void @use(i1 [[C_8]])
-; CHECK-NEXT:    [[ELSE_START_1:%.*]] = mul nsw i8 [[START]], 1
+; CHECK-NEXT:    [[ELSE_START_1:%.*]] = mul nuw nsw i8 [[START]], 1
 ; CHECK-NEXT:    [[C_9:%.*]] = icmp slt i8 [[ELSE_START_1]], [[HIGH]]
 ; CHECK-NEXT:    call void @use(i1 [[C_9]])
 ; CHECK-NEXT:    [[ELSE_START_2:%.*]] = mul nsw i8 [[START]], 2
@@ -439,7 +439,7 @@ define void @slt_mul_nsw_3_known_negative_1(i8 %start, i8 %high) {
 ; CHECK:       then:
 ; CHECK-NEXT:    [[C_3:%.*]] = icmp slt i8 [[START]], [[HIGH]]
 ; CHECK-NEXT:    call void @use(i1 [[C_3]])
-; CHECK-NEXT:    [[START_1:%.*]] = mul nsw i8 [[START]], 1
+; CHECK-NEXT:    [[START_1:%.*]] = mul nuw nsw i8 [[START]], 1
 ; CHECK-NEXT:    [[C_4:%.*]] = icmp slt i8 [[START_1]], [[HIGH]]
 ; CHECK-NEXT:    call void @use(i1 [[C_4]])
 ; CHECK-NEXT:    [[START_2:%.*]] = mul nsw i8 [[START]], 2
@@ -494,7 +494,7 @@ define void @slt_mul_nsw_3_known_negative_2(i8 %start, i8 %high) {
 ; CHECK:       else:
 ; CHECK-NEXT:    [[C_6:%.*]] = icmp slt i8 [[START]], [[HIGH]]
 ; CHECK-NEXT:    call void @use(i1 [[C_6]])
-; CHECK-NEXT:    [[ELSE_START_1:%.*]] = mul nsw i8 [[START]], 1
+; CHECK-NEXT:    [[ELSE_START_1:%.*]] = mul nuw nsw i8 [[START]], 1
 ; CHECK-NEXT:    [[C_7:%.*]] = icmp slt i8 [[ELSE_START_1]], [[HIGH]]
 ; CHECK-NEXT:    call void @use(i1 [[C_7]])
 ; CHECK-NEXT:    [[ELSE_START_2:%.*]] = mul nsw i8 [[START]], 2
@@ -654,7 +654,7 @@ define void @slt_mul_nsw_3_known_nonnegative_1(i8 %start, i8 %high) {
 ; CHECK-NEXT:    br i1 [[AND]], label [[THEN:%.*]], label [[ELSE:%.*]]
 ; CHECK:       then:
 ; CHECK-NEXT:    call void @use(i1 true)
-; CHECK-NEXT:    [[START_1:%.*]] = mul nsw i8 [[START]], 1
+; CHECK-NEXT:    [[START_1:%.*]] = mul nuw nsw i8 [[START]], 1
 ; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    [[START_2:%.*]] = mul nsw i8 [[START]], 2
 ; CHECK-NEXT:    call void @use(i1 true)
@@ -708,7 +708,7 @@ define void @slt_mul_nsw_3_known_nonnegative_2(i8 %start, i8 %high) {
 ; CHECK:       else:
 ; CHECK-NEXT:    [[C_4:%.*]] = icmp slt i8 [[START]], [[HIGH]]
 ; CHECK-NEXT:    call void @use(i1 [[C_4]])
-; CHECK-NEXT:    [[ELSE_START_1:%.*]] = mul nsw i8 [[START]], 1
+; CHECK-NEXT:    [[ELSE_START_1:%.*]] = mul nuw nsw i8 [[START]], 1
 ; CHECK-NEXT:    [[C_5:%.*]] = icmp slt i8 [[ELSE_START_1]], [[HIGH]]
 ; CHECK-NEXT:    call void @use(i1 [[C_5]])
 ; CHECK-NEXT:    [[ELSE_START_2:%.*]] = mul nsw i8 [[START]], 2
@@ -765,7 +765,7 @@ define void @slt_mul_nsw_both_var_non_negative_1(i8 %start, i8 %scale, i8 %high)
 ; CHECK:       then:
 ; CHECK-NEXT:    [[T_0:%.*]] = icmp slt i8 [[START]], [[HIGH]]
 ; CHECK-NEXT:    call void @use(i1 [[T_0]])
-; CHECK-NEXT:    [[START_1:%.*]] = mul nsw i8 [[START]], 1
+; CHECK-NEXT:    [[START_1:%.*]] = mul nuw nsw i8 [[START]], 1
 ; CHECK-NEXT:    [[T_1:%.*]] = icmp slt i8 [[START_1]], [[HIGH]]
 ; CHECK-NEXT:    call void @use(i1 [[T_1]])
 ; CHECK-NEXT:    [[START_2:%.*]] = mul nsw i8 [[START]], 2
@@ -781,7 +781,7 @@ define void @slt_mul_nsw_both_var_non_negative_1(i8 %start, i8 %scale, i8 %high)
 ; CHECK:       else:
 ; CHECK-NEXT:    [[C_4:%.*]] = icmp slt i8 [[START]], [[HIGH]]
 ; CHECK-NEXT:    call void @use(i1 [[C_4]])
-; CHECK-NEXT:    [[ELSE_START_1:%.*]] = mul nsw i8 [[START]], 1
+; CHECK-NEXT:    [[ELSE_START_1:%.*]] = mul nuw nsw i8 [[START]], 1
 ; CHECK-NEXT:    [[C_5:%.*]] = icmp slt i8 [[ELSE_START_1]], [[HIGH]]
 ; CHECK-NEXT:    call void @use(i1 [[C_5]])
 ; CHECK-NEXT:    [[ELSE_START_2:%.*]] = mul nsw i8 [[START]], 2
@@ -858,7 +858,7 @@ define void @slt_mul_nsw_both_var_non_negative_2(i8 %start, i8 %scale, i8 %high)
 ; CHECK:       else:
 ; CHECK-NEXT:    [[C_4:%.*]] = icmp slt i8 [[START]], [[HIGH]]
 ; CHECK-NEXT:    call void @use(i1 [[C_4]])
-; CHECK-NEXT:    [[ELSE_START_1:%.*]] = mul nsw i8 [[START]], 1
+; CHECK-NEXT:    [[ELSE_START_1:%.*]] = mul nuw nsw i8 [[START]], 1
 ; CHECK-NEXT:    [[C_5:%.*]] = icmp slt i8 [[ELSE_START_1]], [[HIGH]]
 ; CHECK-NEXT:    call void @use(i1 [[C_5]])
 ; CHECK-NEXT:    [[ELSE_START_2:%.*]] = mul nsw i8 [[START]], 2
@@ -915,7 +915,7 @@ define void @ult_mul_nsw_3_known_positive_1(i8 %start, i8 %high) {
 ; CHECK:       then:
 ; CHECK-NEXT:    [[T_0:%.*]] = icmp ult i8 [[START]], [[HIGH]]
 ; CHECK-NEXT:    call void @use(i1 [[T_0]])
-; CHECK-NEXT:    [[START_1:%.*]] = mul nsw i8 [[START]], 1
+; CHECK-NEXT:    [[START_1:%.*]] = mul nuw nsw i8 [[START]], 1
 ; CHECK-NEXT:    [[T_1:%.*]] = icmp ult i8 [[START_1]], [[HIGH]]
 ; CHECK-NEXT:    call void @use(i1 [[T_1]])
 ; CHECK-NEXT:    [[START_2:%.*]] = mul nsw i8 [[START]], 2
@@ -972,7 +972,7 @@ define void @ult_mul_nsw_3_known_positive_2(i8 %start, i8 %high) {
 ; CHECK:       else:
 ; CHECK-NEXT:    [[C_4:%.*]] = icmp slt i8 [[START]], [[HIGH]]
 ; CHECK-NEXT:    call void @use(i1 [[C_4]])
-; CHECK-NEXT:    [[ELSE_START_1:%.*]] = mul nsw i8 [[START]], 1
+; CHECK-NEXT:    [[ELSE_START_1:%.*]] = mul nuw nsw i8 [[START]], 1
 ; CHECK-NEXT:    [[C_5:%.*]] = icmp slt i8 [[ELSE_START_1]], [[HIGH]]
 ; CHECK-NEXT:    call void @use(i1 [[C_5]])
 ; CHECK-NEXT:    [[ELSE_START_2:%.*]] = mul nsw i8 [[START]], 2
diff --git a/llvm/test/Transforms/ConstraintElimination/shl.ll b/llvm/test/Transforms/ConstraintElimination/shl.ll
index a6d5ff396e94e..a09af84a64b56 100644
--- a/llvm/test/Transforms/ConstraintElimination/shl.ll
+++ b/llvm/test/Transforms/ConstraintElimination/shl.ll
@@ -1360,7 +1360,7 @@ define i1 @shl_nsw_by_bw_minus_1(i64 %x) {
 ; Shift returns poison in this case, just make sure we don't crash.
 define i1 @shl_nsw_by_bw(i64 %x) {
 ; CHECK-LABEL: @shl_nsw_by_bw(
-; CHECK-NEXT:    [[X_SHL:%.*]] = shl nsw i64 [[X:%.*]], 64
+; CHECK-NEXT:    [[X_SHL:%.*]] = shl nuw nsw i64 [[X:%.*]], 64
 ; CHECK-NEXT:    [[C_1:%.*]] = icmp slt i64 [[X_SHL]], 0
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[C_1]])
 ; CHECK-NEXT:    [[T_1:%.*]] = icmp slt i64 [[X]], 0
@@ -1377,7 +1377,7 @@ define i1 @shl_nsw_by_bw(i64 %x) {
 ; Shift returns poison in this case, just make sure we don't crash.
 define i1 @shl_nsw_by_bw_plus_1(i64 %x) {
 ; CHECK-LABEL: @shl_nsw_by_bw_plus_1(
-; CHECK-NEXT:    [[X_SHL:%.*]] = shl nsw i64 [[X:%.*]], 65
+; CHECK-NEXT:    [[X_SHL:%.*]] = shl nuw nsw i64 [[X:%.*]], 65
 ; CHECK-NEXT:    [[C_1:%.*]] = icmp slt i64 [[X_SHL]], 0
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[C_1]])
 ; CHECK-NEXT:    [[T_1:%.*]] = icmp slt i64 [[X]], 0
@@ -1464,7 +1464,7 @@ define i1 @shl_nuw_signed_shift_zero(i8 %x) {
 ; CHECK-LABEL: @shl_nuw_signed_shift_zero(
 ; CHECK-NEXT:    [[B:%.*]] = icmp slt i8 [[X:%.*]], 32
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[B]])
-; CHECK-NEXT:    [[M:%.*]] = shl nuw i8 [[X]], 0
+; CHECK-NEXT:    [[M:%.*]] = shl nuw nsw i8 [[X]], 0
 ; CHECK-NEXT:    [[T:%.*]] = icmp slt i8 [[M]], 0
 ; CHECK-NEXT:    ret i1 [[T]]
 ;



More information about the llvm-commits mailing list