[llvm] [CVP] Infer no-wrap flags from operand ranges at use sites. (PR #207522)

Florian Hahn via llvm-commits llvm-commits at lists.llvm.org
Tue Jul 14 01:50:26 PDT 2026


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

>From 37cc07bd144a33e4b49a20bb041b09cb011586b0 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Fri, 3 Jul 2026 17:47:44 +0100
Subject: [PATCH 1/3] [CVP] Extract computeNoWrapFlags helper from processBinOp
 (NFC).

Pull the per-operand-range no-wrap deduction in processBinOp out into a
standalone computeNoWrapFlags helper to be re-used in
https://github.com/llvm/llvm-project/pull/207522.
---
 .../Scalar/CorrelatedValuePropagation.cpp     | 47 ++++++++++++-------
 1 file changed, 30 insertions(+), 17 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp b/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
index ff0b70b51e5f7..f893f5281ceff 100644
--- a/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
+++ b/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
@@ -1176,9 +1176,33 @@ static bool processSIToFP(SIToFPInst *SIToFP, LazyValueInfo *LVI) {
   return true;
 }
 
-static bool processBinOp(BinaryOperator *BinOp, LazyValueInfo *LVI) {
+namespace {
+struct NoWrapFlags {
+  bool NSW = false;
+  bool NUW = false;
+};
+} // namespace
+
+// Check if the requested no-wrap flags are valid for \p Opcode on \p LRange and
+// \p RRange.
+static NoWrapFlags computeNoWrapFlags(Instruction::BinaryOps Opcode,
+                                      const ConstantRange &LRange,
+                                      const ConstantRange &RRange,
+                                      bool CheckNSW, bool CheckNUW) {
   using OBO = OverflowingBinaryOperator;
+  NoWrapFlags Flags;
+  if (CheckNUW)
+    Flags.NUW = ConstantRange::makeGuaranteedNoWrapRegion(Opcode, RRange,
+                                                          OBO::NoUnsignedWrap)
+                    .contains(LRange);
+  if (CheckNSW)
+    Flags.NSW = ConstantRange::makeGuaranteedNoWrapRegion(Opcode, RRange,
+                                                          OBO::NoSignedWrap)
+                    .contains(LRange);
+  return Flags;
+}
 
+static bool processBinOp(BinaryOperator *BinOp, LazyValueInfo *LVI) {
   bool NSW = BinOp->hasNoSignedWrap();
   bool NUW = BinOp->hasNoUnsignedWrap();
   if (NSW && NUW)
@@ -1190,24 +1214,13 @@ static bool processBinOp(BinaryOperator *BinOp, LazyValueInfo *LVI) {
   ConstantRange RRange = LVI->getConstantRangeAtUse(BinOp->getOperandUse(1),
                                                     /*UndefAllowed=*/false);
 
-  bool Changed = false;
-  bool NewNUW = false, NewNSW = false;
-  if (!NUW) {
-    ConstantRange NUWRange = ConstantRange::makeGuaranteedNoWrapRegion(
-        Opcode, RRange, OBO::NoUnsignedWrap);
-    NewNUW = NUWRange.contains(LRange);
-    Changed |= NewNUW;
-  }
-  if (!NSW) {
-    ConstantRange NSWRange = ConstantRange::makeGuaranteedNoWrapRegion(
-        Opcode, RRange, OBO::NoSignedWrap);
-    NewNSW = NSWRange.contains(LRange);
-    Changed |= NewNSW;
-  }
+  NoWrapFlags New =
+      computeNoWrapFlags(Opcode, LRange, RRange, /*CheckNSW=*/!NSW,
+                         /*CheckNUW=*/!NUW);
 
-  setDeducedOverflowingFlags(BinOp, Opcode, NewNSW, NewNUW);
+  setDeducedOverflowingFlags(BinOp, Opcode, New.NSW, New.NUW);
 
-  return Changed;
+  return New.NSW || New.NUW;
 }
 
 static bool processAnd(BinaryOperator *BinOp, LazyValueInfo *LVI) {

>From f5159fbfcee14e1bf79224949e539f4431c5916e Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Fri, 3 Jul 2026 12:37:57 +0100
Subject: [PATCH 2/3] [CVP] Add tests.

---
 .../infer-nowrap-from-uses.ll                 | 315 ++++++++++++++++++
 1 file changed, 315 insertions(+)
 create mode 100644 llvm/test/Transforms/CorrelatedValuePropagation/infer-nowrap-from-uses.ll

diff --git a/llvm/test/Transforms/CorrelatedValuePropagation/infer-nowrap-from-uses.ll b/llvm/test/Transforms/CorrelatedValuePropagation/infer-nowrap-from-uses.ll
new file mode 100644
index 0000000000000..18c6b5d48a35a
--- /dev/null
+++ b/llvm/test/Transforms/CorrelatedValuePropagation/infer-nowrap-from-uses.ll
@@ -0,0 +1,315 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -passes=correlated-propagation -S < %s | FileCheck %s
+
+; Infer no-wrap flags on a binop whose uses (not its def) are all dominated by a
+; range-constraining guard; the multi-use case relies on the per-use fallback.
+
+declare void @use(i8)
+
+; Def precedes the overflow guard; both uses behind it recover nsw. The guard
+; admits i in [-32, 32), the nsw range for 4*i.
+define i8 @shl_nsw_from_overflow_guard(i8 %i) {
+; CHECK-LABEL: define range(i8 0, 10) i8 @shl_nsw_from_overflow_guard(
+; CHECK-SAME: i8 [[I:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[R:%.*]] = shl i8 [[I]], 2
+; CHECK-NEXT:    [[OV:%.*]] = add i8 [[I]], -32
+; CHECK-NEXT:    [[OVC:%.*]] = icmp ult i8 [[OV]], -64
+; CHECK-NEXT:    br i1 [[OVC]], label %[[EXIT:.*]], label %[[USES1:.*]]
+; CHECK:       [[USES1]]:
+; CHECK-NEXT:    call void @use(i8 [[R]])
+; CHECK-NEXT:    [[BND:%.*]] = icmp ult i8 [[R]], 10
+; CHECK-NEXT:    br i1 [[BND]], label %[[USES2:.*]], label %[[EXIT]]
+; CHECK:       [[USES2]]:
+; CHECK-NEXT:    call void @use(i8 [[R]])
+; CHECK-NEXT:    ret i8 [[R]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret i8 0
+;
+entry:
+  %r = shl i8 %i, 2
+  %ov = add i8 %i, -32
+  %ovc = icmp ult i8 %ov, -64
+  br i1 %ovc, label %exit, label %uses1
+
+uses1:
+  call void @use(i8 %r)
+  %bnd = icmp ult i8 %r, 10
+  br i1 %bnd, label %uses2, label %exit
+
+uses2:
+  call void @use(i8 %r)
+  ret i8 %r
+
+exit:
+  ret i8 0
+}
+
+; One guard (i < 100), four ops, four different results: add is nuw+nsw, sub is
+; nsw only, shl is nuw only (crosses the sign bit), mul overflows so neither.
+define void @same_guard_diff_ops(i8 %i) {
+; CHECK-LABEL: define void @same_guard_diff_ops(
+; CHECK-SAME: i8 [[I:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[A:%.*]] = add i8 [[I]], 3
+; CHECK-NEXT:    [[S:%.*]] = sub i8 [[I]], 3
+; CHECK-NEXT:    [[L:%.*]] = shl i8 [[I]], 1
+; CHECK-NEXT:    [[M:%.*]] = mul i8 [[I]], 30
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i8 [[I]], 100
+; CHECK-NEXT:    br i1 [[C]], label %[[USES:.*]], label %[[EXIT:.*]]
+; CHECK:       [[USES]]:
+; CHECK-NEXT:    call void @use(i8 [[A]])
+; CHECK-NEXT:    call void @use(i8 [[S]])
+; CHECK-NEXT:    call void @use(i8 [[L]])
+; CHECK-NEXT:    call void @use(i8 [[M]])
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %a = add i8 %i, 3
+  %s = sub i8 %i, 3
+  %l = shl i8 %i, 1
+  %m = mul i8 %i, 30
+  %c = icmp ult i8 %i, 100
+  br i1 %c, label %uses, label %exit
+
+uses:
+  call void @use(i8 %a)
+  call void @use(i8 %s)
+  call void @use(i8 %l)
+  call void @use(i8 %m)
+  br label %exit
+
+exit:
+  ret void
+}
+
+; Guarded by i > 100; sub recovers nuw only (nsw needs a signed upper bound).
+define void @sub_nuw_only(i8 %i) {
+; CHECK-LABEL: define void @sub_nuw_only(
+; CHECK-SAME: i8 [[I:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[R:%.*]] = sub i8 [[I]], 3
+; CHECK-NEXT:    [[C:%.*]] = icmp ugt i8 [[I]], 100
+; CHECK-NEXT:    br i1 [[C]], label %[[USES:.*]], label %[[EXIT:.*]]
+; CHECK:       [[USES]]:
+; CHECK-NEXT:    call void @use(i8 [[R]])
+; CHECK-NEXT:    call void @use(i8 [[R]])
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %r = sub i8 %i, 3
+  %c = icmp ugt i8 %i, 100
+  br i1 %c, label %uses, label %exit
+
+uses:
+  call void @use(i8 %r)
+  call void @use(i8 %r)
+  br label %exit
+
+exit:
+  ret void
+}
+
+; Two muls, same guard: 4*i stays in range (both flags), 50*i overflows (none).
+define void @mul_mixed(i8 %i) {
+; CHECK-LABEL: define void @mul_mixed(
+; CHECK-SAME: i8 [[I:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[A:%.*]] = mul i8 [[I]], 4
+; CHECK-NEXT:    [[B:%.*]] = mul i8 [[I]], 50
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i8 [[I]], 10
+; CHECK-NEXT:    br i1 [[C]], label %[[USES:.*]], label %[[EXIT:.*]]
+; CHECK:       [[USES]]:
+; CHECK-NEXT:    call void @use(i8 [[A]])
+; CHECK-NEXT:    call void @use(i8 [[A]])
+; CHECK-NEXT:    call void @use(i8 [[B]])
+; CHECK-NEXT:    call void @use(i8 [[B]])
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %a = mul i8 %i, 4
+  %b = mul i8 %i, 50
+  %c = icmp ult i8 %i, 10
+  br i1 %c, label %uses, label %exit
+
+uses:
+  call void @use(i8 %a)
+  call void @use(i8 %a)
+  call void @use(i8 %b)
+  call void @use(i8 %b)
+  br label %exit
+
+exit:
+  ret void
+}
+
+; Two shls, same guard: i << 2 stays in range (both flags), i << 5 overflows.
+define void @shl_mixed(i8 %i) {
+; CHECK-LABEL: define void @shl_mixed(
+; CHECK-SAME: i8 [[I:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[A:%.*]] = shl i8 [[I]], 2
+; CHECK-NEXT:    [[B:%.*]] = shl i8 [[I]], 5
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i8 [[I]], 10
+; CHECK-NEXT:    br i1 [[C]], label %[[USES:.*]], label %[[EXIT:.*]]
+; CHECK:       [[USES]]:
+; CHECK-NEXT:    call void @use(i8 [[A]])
+; CHECK-NEXT:    call void @use(i8 [[A]])
+; CHECK-NEXT:    call void @use(i8 [[B]])
+; CHECK-NEXT:    call void @use(i8 [[B]])
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %a = shl i8 %i, 2
+  %b = shl i8 %i, 5
+  %c = icmp ult i8 %i, 10
+  br i1 %c, label %uses, label %exit
+
+uses:
+  call void @use(i8 %a)
+  call void @use(i8 %a)
+  call void @use(i8 %b)
+  call void @use(i8 %b)
+  br label %exit
+
+exit:
+  ret void
+}
+
+; Uses behind different guards: nsw fails at the weaker (i < 64), nuw holds at
+; both, so only nuw is recovered.
+define void @nuw_only_across_guards(i8 %i) {
+; CHECK-LABEL: define void @nuw_only_across_guards(
+; CHECK-SAME: i8 [[I:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[R:%.*]] = shl i8 [[I]], 2
+; CHECK-NEXT:    [[C1:%.*]] = icmp ult i8 [[I]], 10
+; CHECK-NEXT:    br i1 [[C1]], label %[[USES1:.*]], label %[[REST:.*]]
+; CHECK:       [[USES1]]:
+; CHECK-NEXT:    call void @use(i8 [[R]])
+; CHECK-NEXT:    ret void
+; CHECK:       [[REST]]:
+; CHECK-NEXT:    [[C2:%.*]] = icmp ult i8 [[I]], 64
+; CHECK-NEXT:    br i1 [[C2]], label %[[USES2:.*]], label %[[EXIT:.*]]
+; CHECK:       [[USES2]]:
+; CHECK-NEXT:    call void @use(i8 [[R]])
+; CHECK-NEXT:    ret void
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %r = shl i8 %i, 2
+  %c1 = icmp ult i8 %i, 10
+  br i1 %c1, label %uses1, label %rest
+
+uses1:
+  call void @use(i8 %r)
+  ret void
+
+rest:
+  %c2 = icmp ult i8 %i, 64
+  br i1 %c2, label %uses2, label %exit
+
+uses2:
+  call void @use(i8 %r)
+  ret void
+
+exit:
+  ret void
+}
+
+define void @neg_use_in_def_block(i8 %i) {
+; CHECK-LABEL: define void @neg_use_in_def_block(
+; CHECK-SAME: i8 [[I:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[R:%.*]] = shl i8 [[I]], 2
+; CHECK-NEXT:    call void @use(i8 [[R]])
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i8 [[I]], 10
+; CHECK-NEXT:    br i1 [[C]], label %[[USES:.*]], label %[[EXIT:.*]]
+; CHECK:       [[USES]]:
+; CHECK-NEXT:    call void @use(i8 [[R]])
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %r = shl i8 %i, 2
+  call void @use(i8 %r)
+  %c = icmp ult i8 %i, 10
+  br i1 %c, label %uses, label %exit
+
+uses:
+  call void @use(i8 %r)
+  br label %exit
+
+exit:
+  ret void
+}
+
+define i8 @neg_phi_use(i8 %i) {
+; CHECK-LABEL: define range(i8 0, -3) i8 @neg_phi_use(
+; CHECK-SAME: i8 [[I:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    [[R:%.*]] = shl i8 [[I]], 2
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i8 [[I]], 10
+; CHECK-NEXT:    br i1 [[C]], label %[[USES:.*]], label %[[EXIT:.*]]
+; CHECK:       [[USES]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    [[P:%.*]] = phi i8 [ [[R]], %[[USES]] ], [ 0, %[[ENTRY]] ]
+; CHECK-NEXT:    ret i8 [[P]]
+;
+entry:
+  %r = shl i8 %i, 2
+  %c = icmp ult i8 %i, 10
+  br i1 %c, label %uses, label %exit
+
+uses:
+  br label %exit
+
+exit:
+  %p = phi i8 [ %r, %uses ], [ 0, %entry ]
+  ret i8 %p
+}
+
+define void @neg_too_many_uses(i8 %i) {
+; CHECK-LABEL: define void @neg_too_many_uses(
+; CHECK-SAME: i8 [[I:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[R:%.*]] = shl i8 [[I]], 2
+; CHECK-NEXT:    [[C:%.*]] = icmp ult i8 [[I]], 10
+; CHECK-NEXT:    br i1 [[C]], label %[[USES:.*]], label %[[EXIT:.*]]
+; CHECK:       [[USES]]:
+; CHECK-NEXT:    call void @use(i8 [[R]])
+; CHECK-NEXT:    call void @use(i8 [[R]])
+; CHECK-NEXT:    call void @use(i8 [[R]])
+; CHECK-NEXT:    call void @use(i8 [[R]])
+; CHECK-NEXT:    call void @use(i8 [[R]])
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %r = shl i8 %i, 2
+  %c = icmp ult i8 %i, 10
+  br i1 %c, label %uses, label %exit
+
+uses:
+  call void @use(i8 %r)
+  call void @use(i8 %r)
+  call void @use(i8 %r)
+  call void @use(i8 %r)
+  call void @use(i8 %r)
+  br label %exit
+
+exit:
+  ret void
+}

>From d55808252d8d6aee0c842c7dfc36bf44056c19e3 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Fri, 3 Jul 2026 17:48:41 +0100
Subject: [PATCH 3/3] [CVP] Infer no-wrap flags from operand ranges at use
 sites.

When processBinOp cannot prove a no-wrap flag from the operand ranges at
the definition, fall back to querying the operand ranges constrained at
the binop's use sites. This recovers nsw/nuw when the guard that
establishes the range dominates the uses but not the definition (e.g. the
def sits in the loop body ahead of the overflow-guard branch that all uses
are behind).

This is triggers in a number of C/C++ workloads and is also quite common
in Swift code which emits overflowing ops extensively, and aborts if the
check fails.
---
 .../Scalar/CorrelatedValuePropagation.cpp     | 51 +++++++++++++++++++
 .../infer-nowrap-from-uses.ll                 | 16 +++---
 .../CorrelatedValuePropagation/range.ll       |  6 +--
 3 files changed, 62 insertions(+), 11 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp b/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
index f893f5281ceff..14f7df24c0433 100644
--- a/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
+++ b/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp
@@ -1202,6 +1202,46 @@ static NoWrapFlags computeNoWrapFlags(Instruction::BinaryOps Opcode,
   return Flags;
 }
 
+// Try to prove that \p BinOp does not wrap by looking at the operand ranges
+// constrained at each of its use sites, rather than at the definition. This
+// improves results, e.g. when all uses are constrained by a runtime check.
+static NoWrapFlags inferNoWrapFromUses(BinaryOperator *BinOp,
+                                       LazyValueInfo *LVI, bool WantNSW,
+                                       bool WantNUW) {
+  // Skip analysis, when there are too many uses to check or any use is in the
+  // same block.
+  const unsigned MaxUsesToInspect = 4;
+  BasicBlock *DefBB = BinOp->getParent();
+  unsigned NumUses = 0;
+  for (Use &U : BinOp->uses()) {
+    if (++NumUses > MaxUsesToInspect)
+      return {};
+    auto *UserI = cast<Instruction>(U.getUser());
+    if (isa<PHINode>(UserI) || UserI->getParent() == DefBB)
+      return {};
+  }
+  if (NumUses == 0)
+    return {};
+
+  Instruction::BinaryOps Opcode = BinOp->getOpcode();
+  NoWrapFlags Flags;
+  Flags.NSW = WantNSW;
+  Flags.NUW = WantNUW;
+  for (Use &U : BinOp->uses()) {
+    auto *UserI = cast<Instruction>(U.getUser());
+    // Constrain both operands at this use site and see which flags still hold.
+    ConstantRange LRange = LVI->getConstantRange(BinOp->getOperand(0), UserI,
+                                                 /*UndefAllowed=*/false);
+    ConstantRange RRange = LVI->getConstantRange(BinOp->getOperand(1), UserI,
+                                                 /*UndefAllowed=*/false);
+    Flags = computeNoWrapFlags(Opcode, LRange, RRange, Flags.NSW, Flags.NUW);
+    if (!Flags.NSW && !Flags.NUW)
+      return {};
+  }
+
+  return Flags;
+}
+
 static bool processBinOp(BinaryOperator *BinOp, LazyValueInfo *LVI) {
   bool NSW = BinOp->hasNoSignedWrap();
   bool NUW = BinOp->hasNoUnsignedWrap();
@@ -1218,6 +1258,17 @@ static bool processBinOp(BinaryOperator *BinOp, LazyValueInfo *LVI) {
       computeNoWrapFlags(Opcode, LRange, RRange, /*CheckNSW=*/!NSW,
                          /*CheckNUW=*/!NUW);
 
+  // If a still-wanted flag could not be proven at the definition, retry using
+  // the operand ranges constrained at the use sites. This is the more
+  // expensive path, so it only runs when the cheap query above came up short.
+  bool WantNSW = !NSW && !New.NSW;
+  bool WantNUW = !NUW && !New.NUW;
+  if (WantNSW || WantNUW) {
+    NoWrapFlags FromUses = inferNoWrapFromUses(BinOp, LVI, WantNSW, WantNUW);
+    New.NSW |= FromUses.NSW;
+    New.NUW |= FromUses.NUW;
+  }
+
   setDeducedOverflowingFlags(BinOp, Opcode, New.NSW, New.NUW);
 
   return New.NSW || New.NUW;
diff --git a/llvm/test/Transforms/CorrelatedValuePropagation/infer-nowrap-from-uses.ll b/llvm/test/Transforms/CorrelatedValuePropagation/infer-nowrap-from-uses.ll
index 18c6b5d48a35a..c797bd15ccbde 100644
--- a/llvm/test/Transforms/CorrelatedValuePropagation/infer-nowrap-from-uses.ll
+++ b/llvm/test/Transforms/CorrelatedValuePropagation/infer-nowrap-from-uses.ll
@@ -12,7 +12,7 @@ define i8 @shl_nsw_from_overflow_guard(i8 %i) {
 ; CHECK-LABEL: define range(i8 0, 10) i8 @shl_nsw_from_overflow_guard(
 ; CHECK-SAME: i8 [[I:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[R:%.*]] = shl i8 [[I]], 2
+; CHECK-NEXT:    [[R:%.*]] = shl nsw i8 [[I]], 2
 ; CHECK-NEXT:    [[OV:%.*]] = add i8 [[I]], -32
 ; CHECK-NEXT:    [[OVC:%.*]] = icmp ult i8 [[OV]], -64
 ; CHECK-NEXT:    br i1 [[OVC]], label %[[EXIT:.*]], label %[[USES1:.*]]
@@ -51,9 +51,9 @@ define void @same_guard_diff_ops(i8 %i) {
 ; CHECK-LABEL: define void @same_guard_diff_ops(
 ; CHECK-SAME: i8 [[I:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[A:%.*]] = add i8 [[I]], 3
-; CHECK-NEXT:    [[S:%.*]] = sub i8 [[I]], 3
-; CHECK-NEXT:    [[L:%.*]] = shl i8 [[I]], 1
+; CHECK-NEXT:    [[A:%.*]] = add nuw nsw i8 [[I]], 3
+; CHECK-NEXT:    [[S:%.*]] = sub nsw i8 [[I]], 3
+; CHECK-NEXT:    [[L:%.*]] = shl nuw i8 [[I]], 1
 ; CHECK-NEXT:    [[M:%.*]] = mul i8 [[I]], 30
 ; CHECK-NEXT:    [[C:%.*]] = icmp ult i8 [[I]], 100
 ; CHECK-NEXT:    br i1 [[C]], label %[[USES:.*]], label %[[EXIT:.*]]
@@ -90,7 +90,7 @@ define void @sub_nuw_only(i8 %i) {
 ; CHECK-LABEL: define void @sub_nuw_only(
 ; CHECK-SAME: i8 [[I:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[R:%.*]] = sub i8 [[I]], 3
+; CHECK-NEXT:    [[R:%.*]] = sub nuw i8 [[I]], 3
 ; CHECK-NEXT:    [[C:%.*]] = icmp ugt i8 [[I]], 100
 ; CHECK-NEXT:    br i1 [[C]], label %[[USES:.*]], label %[[EXIT:.*]]
 ; CHECK:       [[USES]]:
@@ -119,7 +119,7 @@ define void @mul_mixed(i8 %i) {
 ; CHECK-LABEL: define void @mul_mixed(
 ; CHECK-SAME: i8 [[I:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[A:%.*]] = mul i8 [[I]], 4
+; CHECK-NEXT:    [[A:%.*]] = mul nuw nsw i8 [[I]], 4
 ; CHECK-NEXT:    [[B:%.*]] = mul i8 [[I]], 50
 ; CHECK-NEXT:    [[C:%.*]] = icmp ult i8 [[I]], 10
 ; CHECK-NEXT:    br i1 [[C]], label %[[USES:.*]], label %[[EXIT:.*]]
@@ -154,7 +154,7 @@ define void @shl_mixed(i8 %i) {
 ; CHECK-LABEL: define void @shl_mixed(
 ; CHECK-SAME: i8 [[I:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[A:%.*]] = shl i8 [[I]], 2
+; CHECK-NEXT:    [[A:%.*]] = shl nuw nsw i8 [[I]], 2
 ; CHECK-NEXT:    [[B:%.*]] = shl i8 [[I]], 5
 ; CHECK-NEXT:    [[C:%.*]] = icmp ult i8 [[I]], 10
 ; CHECK-NEXT:    br i1 [[C]], label %[[USES:.*]], label %[[EXIT:.*]]
@@ -190,7 +190,7 @@ define void @nuw_only_across_guards(i8 %i) {
 ; CHECK-LABEL: define void @nuw_only_across_guards(
 ; CHECK-SAME: i8 [[I:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[R:%.*]] = shl i8 [[I]], 2
+; CHECK-NEXT:    [[R:%.*]] = shl nuw i8 [[I]], 2
 ; CHECK-NEXT:    [[C1:%.*]] = icmp ult i8 [[I]], 10
 ; CHECK-NEXT:    br i1 [[C1]], label %[[USES1:.*]], label %[[REST:.*]]
 ; CHECK:       [[USES1]]:
diff --git a/llvm/test/Transforms/CorrelatedValuePropagation/range.ll b/llvm/test/Transforms/CorrelatedValuePropagation/range.ll
index 860caf2624d42..6c39aa5184ea0 100644
--- a/llvm/test/Transforms/CorrelatedValuePropagation/range.ll
+++ b/llvm/test/Transforms/CorrelatedValuePropagation/range.ll
@@ -748,7 +748,7 @@ define i8 @test17(i8 %a) {
 ; CHECK-LABEL: define i8 @test17(
 ; CHECK-SAME: i8 [[A:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[C:%.*]] = add i8 [[A]], 3
+; CHECK-NEXT:    [[C:%.*]] = add nuw nsw i8 [[A]], 3
 ; CHECK-NEXT:    br label %[[DISPATCH:.*]]
 ; CHECK:       [[DISPATCH]]:
 ; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[A]], 93
@@ -772,7 +772,7 @@ define i8 @test17_2(i8 %a) {
 ; CHECK-LABEL: define i8 @test17_2(
 ; CHECK-SAME: i8 [[A:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[C:%.*]] = add i8 [[A]], [[A]]
+; CHECK-NEXT:    [[C:%.*]] = add nuw i8 [[A]], [[A]]
 ; CHECK-NEXT:    br label %[[DISPATCH:.*]]
 ; CHECK:       [[DISPATCH]]:
 ; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[A]], 93
@@ -845,7 +845,7 @@ define i8 @test19(i8 %a) {
 ; CHECK-LABEL: define i8 @test19(
 ; CHECK-SAME: i8 [[A:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[C:%.*]] = add i8 [[A]], 3
+; CHECK-NEXT:    [[C:%.*]] = add nuw nsw i8 [[A]], 3
 ; CHECK-NEXT:    br label %[[DISPATCH:.*]]
 ; CHECK:       [[DISPATCH]]:
 ; CHECK-NEXT:    switch i8 [[A]], label %[[DISPATCH]] [



More information about the llvm-commits mailing list