[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