[llvm] [InstCombine] Avoid rewriting multi-use post-inc latch icmps (preserve nsw/nuw for later passes) (PR #215960)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Aug 12 23:29:05 PDT 2026
https://github.com/yasmincs updated https://github.com/llvm/llvm-project/pull/215960
>From 2df5b8e2e70dafb9668cfd39de493b1955086d60 Mon Sep 17 00:00:00 2001
From: ysarita <ysarita at nvidia.com>
Date: Thu, 13 Aug 2026 05:43:14 +0000
Subject: [PATCH 1/2] Add tests for post-inc latch exit compares
Add InstCombine, LSR, and PhaseOrdering coverage for post-inc latch
icmps with multi-use increments, plus a SCEV baseline for an aligned
post-inc early-exit latch whose exit count is currently CNC.
---
.../ScalarEvolution/exit-count-non-strict.ll | 36 ++++++
llvm/test/Transforms/InstCombine/icmp-add.ll | 46 +++++++
.../LoopStrengthReduce/latch-inc-lsr.ll | 118 ++++++++++++++++++
.../instcombine-latch-inc-benefits.ll | 90 +++++++++++++
4 files changed, 290 insertions(+)
create mode 100644 llvm/test/Transforms/LoopStrengthReduce/latch-inc-lsr.ll
create mode 100644 llvm/test/Transforms/PhaseOrdering/instcombine-latch-inc-benefits.ll
diff --git a/llvm/test/Analysis/ScalarEvolution/exit-count-non-strict.ll b/llvm/test/Analysis/ScalarEvolution/exit-count-non-strict.ll
index 1e15d2d0d6461..fbccaf1d604df 100644
--- a/llvm/test/Analysis/ScalarEvolution/exit-count-non-strict.ll
+++ b/llvm/test/Analysis/ScalarEvolution/exit-count-non-strict.ll
@@ -499,3 +499,39 @@ latch:
exit:
ret void
}
+
+define i1 @postinc_step64_aligned_early_exit(ptr %bits, i64 %n) {
+; CHECK-LABEL: 'postinc_step64_aligned_early_exit'
+; CHECK-NEXT: Determining loop execution counts for: @postinc_step64_aligned_early_exit
+; CHECK-NEXT: Loop %header: <multiple exits> Unpredictable backedge-taken count.
+; CHECK-NEXT: exit count for header: ***COULDNOTCOMPUTE***
+; CHECK-NEXT: exit count for latch: ***COULDNOTCOMPUTE***
+; CHECK-NEXT: Loop %header: Unpredictable constant max backedge-taken count.
+; CHECK-NEXT: Loop %header: Unpredictable symbolic max backedge-taken count.
+; CHECK-NEXT: symbolic max exit count for header: ***COULDNOTCOMPUTE***
+; CHECK-NEXT: symbolic max exit count for latch: ***COULDNOTCOMPUTE***
+;
+entry:
+ %limit = and i64 %n, -64
+ %empty = icmp eq i64 %limit, 0
+ br i1 %empty, label %exit.false, label %header
+
+header:
+ %iv = phi i64 [ 0, %entry ], [ %next, %latch ]
+ %idx = lshr exact i64 %iv, 3
+ %ptr = getelementptr inbounds i8, ptr %bits, i64 %idx
+ %val = load i64, ptr %ptr, align 8
+ %all.ones = icmp eq i64 %val, -1
+ br i1 %all.ones, label %latch, label %exit.false
+
+latch:
+ %next = add nuw nsw i64 %iv, 64
+ %done = icmp ugt i64 %next, %limit
+ br i1 %done, label %exit.true, label %header
+
+exit.true:
+ ret i1 true
+
+exit.false:
+ ret i1 false
+}
diff --git a/llvm/test/Transforms/InstCombine/icmp-add.ll b/llvm/test/Transforms/InstCombine/icmp-add.ll
index 1b66a50c26e59..6692ca2bf3fb2 100644
--- a/llvm/test/Transforms/InstCombine/icmp-add.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-add.ll
@@ -1979,6 +1979,52 @@ define <2 x i1> @slt_zero_add_nsw_splat_vec(<2 x i8> %a) {
ret <2 x i1> %cmp
}
+define void @latch_inc_phi_user(i32 %sub) {
+; CHECK-LABEL: @latch_inc_phi_user(
+; CHECK: [[INC:%.*]] = add nuw nsw i32 [[J:%.*]], 1
+; CHECK: icmp slt i32 [[J]], [[SUB:%.*]]
+ entry:
+ br label %loop
+
+loop:
+ %j = phi i32 [ 0, %entry ], [ %inc, %loop ]
+ %inc = add nuw nsw i32 %j, 1
+ %cmp = icmp sgt i32 %inc, %sub
+ br i1 %cmp, label %exit, label %loop
+
+exit:
+ ret void
+}
+
+define void @latch_inc_phi_user_sle(i32 %sub) {
+; CHECK-LABEL: @latch_inc_phi_user_sle(
+; CHECK: [[INC:%.*]] = add nuw nsw i32 [[J:%.*]], 1
+; CHECK: icmp slt i32 [[J]], [[SUB:%.*]]
+ entry:
+ br label %loop
+loop:
+ %j = phi i32 [ 0, %entry ], [ %inc, %loop ]
+ %inc = add nsw i32 %j, 1
+ %cmp = icmp sle i32 %inc, %sub
+ br i1 %cmp, label %exit, label %loop
+exit:
+ ret void
+}
+
+define void @latch_inc_phi_user_ugt(i32 %sub) {
+; CHECK-LABEL: @latch_inc_phi_user_ugt(
+; CHECK: icmp ult i32 [[J:%.*]], [[SUB:%.*]]
+ entry:
+ br label %loop
+loop:
+ %j = phi i32 [ 0, %entry ], [ %inc, %loop ]
+ %inc = add nuw i32 %j, 1
+ %cmp = icmp ugt i32 %inc, %sub
+ br i1 %cmp, label %exit, label %loop
+exit:
+ ret void
+}
+
; Test the edges - instcombine should not interfere with simplification to constants.
; Constant subtraction does not overflow, but this is false.
diff --git a/llvm/test/Transforms/LoopStrengthReduce/latch-inc-lsr.ll b/llvm/test/Transforms/LoopStrengthReduce/latch-inc-lsr.ll
new file mode 100644
index 0000000000000..de9ccb343ff8c
--- /dev/null
+++ b/llvm/test/Transforms/LoopStrengthReduce/latch-inc-lsr.ll
@@ -0,0 +1,118 @@
+; RUN: opt < %s -passes='instcombine,indvars' -indvars-widen-indvars=false -S \
+; RUN: | opt -passes=loop-reduce -S | FileCheck %s
+
+target datalayout = "e-m:w-p:64:64-i32:32-i64:64-i128:128-n32:64-S128"
+
+declare void @usef(float)
+
+define void @gpu_addr_hoist(i32 %tmp20, i64 %sub47, i64 %stride, i32 %sub, ptr %tau1) {
+; CHECK-LABEL: @gpu_addr_hoist(
+; CHECK: call i32 @llvm.smax.i32(i32 [[SUB:%.*]], i32 0)
+; CHECK: body:
+; CHECK: sext i32 {{%.*}} to i64
+; CHECK: mul nsw i64 [[STRIDE:%.*]], {{%.*}}
+; CHECK: add nsw i64 [[SUB47:%.*]], {{%.*}}
+; CHECK: shl nsw i64 {{%.*}}, 2
+; CHECK: getelementptr inbounds i8, ptr [[TAU1:%.*]], i64 {{%.*}}
+; CHECK: load float, ptr {{%.*}}
+; CHECK: icmp ne i32 {{%.*}}, 0
+entry:
+ br label %pre
+pre:
+ br label %body
+body:
+ %j = phi i32 [ 0, %pre ], [ %inc, %cond ]
+ %add43 = add nsw i32 %j, %tmp20
+ %conv50 = sext i32 %add43 to i64
+ %mul51 = mul nsw i64 %stride, %conv50
+ %add52 = add nsw i64 %sub47, %mul51
+ %idx = shl nsw i64 %add52, 2
+ %gep = getelementptr inbounds i8, ptr %tau1, i64 %idx
+ %val = load float, ptr %gep, align 4
+ call void @usef(float %val)
+ br label %cond
+cond:
+ %inc = add nuw nsw i32 %j, 1
+ %cmp = icmp sgt i32 %inc, %sub
+ br i1 %cmp, label %exit, label %body
+exit:
+ ret void
+}
+
+define void @gpu_twostream_hoist(i32 %tmp20, i64 %sub47, i64 %stride, i32 %sub, ptr %tau1, ptr %tau2) {
+; CHECK-LABEL: @gpu_twostream_hoist(
+; CHECK: body:
+; CHECK: sext i32 {{%.*}} to i64
+; CHECK: mul nsw i64 [[STRIDE:%.*]], {{%.*}}
+; CHECK: getelementptr inbounds i8, ptr [[TAU1:%.*]], i64 {{%.*}}
+; CHECK: getelementptr inbounds i8, ptr [[TAU2:%.*]], i64 {{%.*}}
+; CHECK: load float, ptr {{%.*}}
+; CHECK: load float, ptr {{%.*}}
+; CHECK: icmp ne i32 {{%.*}}, 0
+entry:
+ br label %pre
+pre:
+ br label %body
+body:
+ %j = phi i32 [ 0, %pre ], [ %inc, %cond ]
+ %add43 = add nsw i32 %j, %tmp20
+ %conv50 = sext i32 %add43 to i64
+ %mul51 = mul nsw i64 %stride, %conv50
+ %add52 = add nsw i64 %sub47, %mul51
+ %idx = shl nsw i64 %add52, 2
+ %gep1 = getelementptr inbounds i8, ptr %tau1, i64 %idx
+ %gep2 = getelementptr inbounds i8, ptr %tau2, i64 %idx
+ %val1 = load float, ptr %gep1, align 4
+ %val2 = load float, ptr %gep2, align 4
+ call void @usef(float %val1)
+ call void @usef(float %val2)
+ br label %cond
+cond:
+ %inc = add nuw nsw i32 %j, 1
+ %cmp = icmp sgt i32 %inc, %sub
+ br i1 %cmp, label %exit, label %body
+exit:
+ ret void
+}
+
+define void @gpu_four_load(i32 %tmp20, i64 %sub47, i64 %stride, i32 %sub, ptr %tau1) {
+; CHECK-LABEL: @gpu_four_load(
+; CHECK: body:
+; CHECK: sext i32 {{%.*}} to i64
+; CHECK: mul nsw i64 [[STRIDE:%.*]], {{%.*}}
+; CHECK: load float, ptr {{%.*}}
+; CHECK: load float, ptr {{%.*}}
+; CHECK: load float, ptr {{%.*}}
+; CHECK: load float, ptr {{%.*}}
+; CHECK: icmp ne i32 {{%.*}}, 0
+entry:
+ br label %pre
+pre:
+ br label %body
+body:
+ %j = phi i32 [ 0, %pre ], [ %inc, %cond ]
+ %add43 = add nsw i32 %j, %tmp20
+ %conv50 = sext i32 %add43 to i64
+ %mul51 = mul nsw i64 %stride, %conv50
+ %add52 = add nsw i64 %sub47, %mul51
+ %idx = shl nsw i64 %add52, 2
+ %gep = getelementptr inbounds i8, ptr %tau1, i64 %idx
+ %gep1 = getelementptr inbounds i8, ptr %gep, i64 4
+ %gep2 = getelementptr inbounds i8, ptr %gep, i64 8
+ %gep3 = getelementptr inbounds i8, ptr %gep, i64 12
+ %v0 = load float, ptr %gep, align 4
+ %v1 = load float, ptr %gep1, align 4
+ %v2 = load float, ptr %gep2, align 4
+ %v3 = load float, ptr %gep3, align 4
+ call void @usef(float %v0)
+ call void @usef(float %v1)
+ call void @usef(float %v2)
+ call void @usef(float %v3)
+ br label %cond
+cond:
+ %inc = add nuw nsw i32 %j, 1
+ %cmp = icmp sgt i32 %inc, %sub
+ br i1 %cmp, label %exit, label %body
+exit:
+ ret void
+}
diff --git a/llvm/test/Transforms/PhaseOrdering/instcombine-latch-inc-benefits.ll b/llvm/test/Transforms/PhaseOrdering/instcombine-latch-inc-benefits.ll
new file mode 100644
index 0000000000000..403744d53c4a4
--- /dev/null
+++ b/llvm/test/Transforms/PhaseOrdering/instcombine-latch-inc-benefits.ll
@@ -0,0 +1,90 @@
+; SCEV gains from preserving post-inc latch exit compares
+
+; RUN: opt < %s -passes='instcombine,print<scalar-evolution>' -disable-output 2>&1 \
+; RUN: | FileCheck %s
+; RUN: opt < %s -passes='instcombine,indvars,print<scalar-evolution>' \
+; RUN: -indvars-widen-indvars=false -disable-output 2>&1 \
+; RUN: | FileCheck %s --check-prefix=INDVARS
+
+target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64"
+
+ at a = external global [1024 x i32]
+declare void @use(i32)
+declare void @usef(float)
+
+define void @scev_postinc_nsw(i32 %sub) {
+; CHECK-LABEL: 'scev_postinc_nsw'
+; CHECK: %inc = add nuw nsw i32 %j, 1
+; CHECK-NEXT: --> {1,+,1}<nuw><%loop>
+entry:
+ br label %loop
+loop:
+ %j = phi i32 [ 0, %entry ], [ %inc, %loop ]
+ %inc = add nuw nsw i32 %j, 1
+ %cmp = icmp sgt i32 %inc, %sub
+ br i1 %cmp, label %exit, label %loop
+exit:
+ ret void
+}
+
+define void @scev_gep_nsw(i32 %sub) {
+; CHECK-LABEL: 'scev_gep_nsw'
+; CHECK: %inc = add nuw nsw i32 %j, 1
+; CHECK-NEXT: --> {1,+,1}<nuw><%loop>
+entry:
+ br label %loop
+loop:
+ %j = phi i32 [ 0, %entry ], [ %inc, %loop ]
+ %idx = zext i32 %j to i64
+ %off = mul nuw nsw i64 %idx, 4
+ %p = getelementptr i8, ptr @a, i64 %off
+ %v = load i32, ptr %p
+ %inc = add nuw nsw i32 %j, 1
+ %cmp = icmp sgt i32 %inc, %sub
+ br i1 %cmp, label %exit, label %loop
+exit:
+ ret void
+}
+
+define void @scev_gpu_nssw(i32 %tmp20, i64 %sub47, i64 %stride, i32 %sub, ptr %tau1) {
+; CHECK-LABEL: 'scev_gpu_nssw'
+; CHECK: %conv50 = sext i32 %add43 to i64
+; CHECK-NEXT: --> {(sext i32 %tmp20 to i64),+,1}<nsw>
+; CHECK: {1,+,1}<nuw><%body>
+entry:
+ br label %pre
+pre:
+ br label %body
+body:
+ %j = phi i32 [ 0, %pre ], [ %inc, %cond ]
+ %add43 = add nsw i32 %j, %tmp20
+ %conv50 = sext i32 %add43 to i64
+ %mul51 = mul nsw i64 %stride, %conv50
+ %add52 = add nsw i64 %sub47, %mul51
+ %gep.idx = shl nsw i64 %add52, 2
+ %gep = getelementptr inbounds i8, ptr %tau1, i64 %gep.idx
+ %val = load float, ptr %gep, align 4
+ call void @usef(float %val)
+ br label %cond
+cond:
+ %inc = add nuw nsw i32 %j, 1
+ %cmp = icmp sgt i32 %inc, %sub
+ br i1 %cmp, label %exit, label %body
+exit:
+ ret void
+}
+
+define void @scev_nssw_indvars(i32 %sub) {
+; INDVARS-LABEL: 'scev_nssw_indvars'
+; INDVARS: {1,+,1}<nuw><%loop>
+entry:
+ br label %loop
+loop:
+ %j = phi i32 [ 0, %entry ], [ %inc, %loop ]
+ call void @use(i32 %j)
+ %inc = add nuw nsw i32 %j, 1
+ %cmp = icmp sgt i32 %inc, %sub
+ br i1 %cmp, label %exit, label %loop
+exit:
+ ret void
+}
>From 2b7f5f883614f6262e4d18458f8167da9fc36418 Mon Sep 17 00:00:00 2001
From: ysarita <ysarita at nvidia.com>
Date: Thu, 13 Aug 2026 05:43:15 +0000
Subject: [PATCH 2/2] Preserve post-inc latch exit compares and improve SCEV
exit counts
InstCombine: only fold icmp (add X, C) when the add is one-use, so
multi-use latch increments keep their nsw/nuw on the exit path.
SCEV: when a non-strict compare's X < (Y+1) form lacks an exact count,
retry via the aligned pre-inc IV, and still return any max BTC the
direct analysis found if ViaPreInc does not help.
---
llvm/include/llvm/Analysis/ScalarEvolution.h | 7 ++
llvm/lib/Analysis/ScalarEvolution.cpp | 87 ++++++++++++++++++-
.../InstCombine/InstCombineCompares.cpp | 8 +-
.../ScalarEvolution/exit-count-non-strict.ll | 8 +-
llvm/test/Transforms/InstCombine/icmp-add.ll | 7 +-
.../LoopStrengthReduce/latch-inc-lsr.ll | 48 +++++-----
.../instcombine-latch-inc-benefits.ll | 8 +-
7 files changed, 134 insertions(+), 39 deletions(-)
diff --git a/llvm/include/llvm/Analysis/ScalarEvolution.h b/llvm/include/llvm/Analysis/ScalarEvolution.h
index 50af763614a31..7207d189ebf73 100644
--- a/llvm/include/llvm/Analysis/ScalarEvolution.h
+++ b/llvm/include/llvm/Analysis/ScalarEvolution.h
@@ -2186,6 +2186,13 @@ class ScalarEvolution {
/// CouldNotCompute.
ExitLimit howFarToNonZero(const SCEV *V, const Loop *L);
+ /// Try to compute the number of times a non-strict post-inc comparison
+ /// executes by expressing it as a strict comparison of the pre-inc sibling.
+ ExitLimit howManyLessThansViaPreInc(const SCEV *LHS, const SCEV *RHS,
+ const Loop *L, bool IsSigned,
+ bool ControlsOnlyExit,
+ bool AllowPredicates);
+
/// Return the number of times an exit condition containing the specified
/// less-than comparison will execute. If not computable, return
/// CouldNotCompute.
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index c0cdce982e623..2a6792398c0be 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -9497,6 +9497,12 @@ ScalarEvolution::ExitLimit ScalarEvolution::computeExitLimitFromICmp(
bool ControllingFiniteLoop = ControlsOnlyExit && loopHasNoAbnormalExits(L) &&
loopIsFiniteByAssumption(L);
+
+ // Preserve the original operands
+ SCEVUse OrigLHS = LHS;
+ SCEVUse OrigRHS = RHS;
+ CmpPredicate OrigPred = Pred;
+
// Simplify the operands before analyzing them.
(void)SimplifyICmpOperands(Pred, LHS, RHS, /*Depth=*/0);
@@ -9592,7 +9598,9 @@ ScalarEvolution::ExitLimit ScalarEvolution::computeExitLimitFromICmp(
break;
}
case ICmpInst::ICMP_SLE:
- case ICmpInst::ICMP_ULE:
+ case ICmpInst::ICMP_ULE: {
+ bool IsSigned = ICmpInst::isSigned(Pred);
+
// Since the loop is finite, an invariant RHS cannot include the boundary
// value, otherwise it would loop forever.
if (!EnableFiniteLoopControl || !ControllingFiniteLoop ||
@@ -9607,7 +9615,7 @@ ScalarEvolution::ExitLimit ScalarEvolution::computeExitLimitFromICmp(
// likely that we use a legal type.
auto *NewType =
Type::getIntNTy(OldType->getContext(), OldType->getBitWidth() * 2);
- if (ICmpInst::isSigned(Pred)) {
+ if (IsSigned) {
LHS = getSignExtendExpr(LHS, NewType);
RHS = getSignExtendExpr(RHS, NewType);
} else {
@@ -9616,12 +9624,46 @@ ScalarEvolution::ExitLimit ScalarEvolution::computeExitLimitFromICmp(
}
}
RHS = getAddExpr(getOne(RHS->getType()), RHS);
- [[fallthrough]];
+ ExitLimit EL = howManyLessThans(LHS, RHS, L, IsSigned, ControlsOnlyExit,
+ AllowPredicates);
+ if (EL.hasFullInfo())
+ return EL;
+
+ // X < (Y+1) failed to get an exact count. Try the pre-inc form.
+ ExitLimit PreEL = howManyLessThansViaPreInc(
+ OrigLHS, OrigRHS, L, IsSigned, ControlsOnlyExit, AllowPredicates);
+ if (PreEL.hasFullInfo())
+ return PreEL;
+
+ // Neither form found an exact count. Preserve whatever partial (e.g. max)
+ // information the direct analysis produced.
+ if (EL.hasAnyInfo())
+ return EL;
+ if (PreEL.hasAnyInfo())
+ return PreEL;
+ break;
+ }
case ICmpInst::ICMP_SLT:
case ICmpInst::ICMP_ULT: { // while (X < Y)
bool IsSigned = ICmpInst::isSigned(Pred);
ExitLimit EL = howManyLessThans(LHS, RHS, L, IsSigned, ControlsOnlyExit,
AllowPredicates);
+ if (EL.hasFullInfo())
+ return EL;
+
+ // SimplifyICmpOperands may have rewritten an original X <= Y into
+ // X < (Y + 1). Retry with the pre-inc form of the original compare.
+ if (OrigPred == ICmpInst::ICMP_ULE || OrigPred == ICmpInst::ICMP_SLE) {
+ ExitLimit PreEL = howManyLessThansViaPreInc(
+ OrigLHS, OrigRHS, L, ICmpInst::isSigned(OrigPred), ControlsOnlyExit,
+ AllowPredicates);
+ if (PreEL.hasFullInfo())
+ return PreEL;
+ if (!EL.hasAnyInfo() && PreEL.hasAnyInfo())
+ EL = PreEL;
+ }
+
+ // Preserve whatever partial (e.g. max) information was found.
if (EL.hasAnyInfo())
return EL;
break;
@@ -13317,6 +13359,45 @@ const SCEV *ScalarEvolution::computeMaxBECountForLT(const SCEV *Start,
getConstant(StrideForMaxBECount) /* Step */);
}
+ScalarEvolution::ExitLimit ScalarEvolution::howManyLessThansViaPreInc(
+ const SCEV *LHS, const SCEV *RHS, const Loop *L, bool IsSigned,
+ bool ControlsOnlyExit, bool AllowPredicates) {
+ if (!isLoopInvariant(RHS, L))
+ return getCouldNotCompute();
+
+ const auto *AR = dyn_cast<SCEVAddRecExpr>(LHS);
+ SCEV::NoWrapFlags NoWrapFlag = IsSigned ? SCEV::FlagNSW : SCEV::FlagNUW;
+ if (!AR || AR->getLoop() != L || !AR->isAffine() ||
+ !AR->getNoWrapFlags(NoWrapFlag))
+ return getCouldNotCompute();
+
+ const SCEV *Step = AR->getStepRecurrence(*this);
+ const auto *StepC = dyn_cast<SCEVConstant>(Step);
+ if (!StepC || !StepC->getAPInt().isStrictlyPositive() ||
+ !willNotOverflow(Instruction::Sub, IsSigned, AR->getStart(), Step))
+ return getCouldNotCompute();
+
+ const SCEV *PreStart = getMinusSCEV(AR->getStart(), Step);
+ // Do not transfer nowrap flags from the post-inc AddRec to its sibling.
+ const SCEV *PreAR = getAddRecExpr(PreStart, Step, L, SCEV::FlagAnyWrap);
+
+ const APInt &StepV = StepC->getAPInt();
+ auto IsMultipleOfStep = [&](const SCEV *S) {
+ if (StepV.isOne())
+ return true;
+ if (StepV.isPowerOf2())
+ return getMinTrailingZeros(S) >= StepV.logBase2();
+ return getConstantMultiple(S).urem(StepV).isZero();
+ };
+ if (!IsMultipleOfStep(PreAR) || !IsMultipleOfStep(RHS))
+ return getCouldNotCompute();
+
+ // With both sides aligned to Step, this is the unit-stride identity
+ // (X + 1) <= Y <=> X < Y, scaled by Step.
+ return howManyLessThans(PreAR, RHS, L, IsSigned, ControlsOnlyExit,
+ AllowPredicates);
+}
+
ScalarEvolution::ExitLimit
ScalarEvolution::howManyLessThans(const SCEV *LHS, const SCEV *RHS,
const Loop *L, bool IsSigned,
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index bff3f8126aab0..5efc8ed123ea9 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -5385,9 +5385,12 @@ Instruction *InstCombinerImpl::foldICmpBinOp(ICmpInst &I,
// icmp sgt (A + 1), Op1 -> icmp sge A, Op1
// icmp ule (A + 1), Op0 -> icmp ult A, Op1
// icmp ugt (A + 1), Op0 -> icmp uge A, Op1
+ // Do not strictness-fold icmp (add X, C), Y to icmp on X when the add has
+ // other users (e.g. a phi backedge on the latch increment).
if (A && NoOp0WrapProblem &&
ShareCommonDivisor(A, Op1, B,
- ICmpInst::isLT(Pred) || ICmpInst::isGE(Pred)))
+ ICmpInst::isLT(Pred) || ICmpInst::isGE(Pred)) &&
+ (!BO0 || BO0->hasOneUse()))
return new ICmpInst(ICmpInst::getFlippedStrictnessPredicate(Pred), A,
Op1);
@@ -5399,7 +5402,8 @@ Instruction *InstCombinerImpl::foldICmpBinOp(ICmpInst &I,
// icmp ult Op0, (C + 1) -> icmp ule Op0, C
if (C && NoOp1WrapProblem &&
ShareCommonDivisor(Op0, C, D,
- ICmpInst::isGT(Pred) || ICmpInst::isLE(Pred)))
+ ICmpInst::isGT(Pred) || ICmpInst::isLE(Pred)) &&
+ (!BO1 || BO1->hasOneUse()))
return new ICmpInst(ICmpInst::getFlippedStrictnessPredicate(Pred), Op0,
C);
}
diff --git a/llvm/test/Analysis/ScalarEvolution/exit-count-non-strict.ll b/llvm/test/Analysis/ScalarEvolution/exit-count-non-strict.ll
index fbccaf1d604df..401c03e23e459 100644
--- a/llvm/test/Analysis/ScalarEvolution/exit-count-non-strict.ll
+++ b/llvm/test/Analysis/ScalarEvolution/exit-count-non-strict.ll
@@ -505,11 +505,11 @@ define i1 @postinc_step64_aligned_early_exit(ptr %bits, i64 %n) {
; CHECK-NEXT: Determining loop execution counts for: @postinc_step64_aligned_early_exit
; CHECK-NEXT: Loop %header: <multiple exits> Unpredictable backedge-taken count.
; CHECK-NEXT: exit count for header: ***COULDNOTCOMPUTE***
-; CHECK-NEXT: exit count for latch: ***COULDNOTCOMPUTE***
-; CHECK-NEXT: Loop %header: Unpredictable constant max backedge-taken count.
-; CHECK-NEXT: Loop %header: Unpredictable symbolic max backedge-taken count.
+; CHECK-NEXT: exit count for latch: ((63 + (64 * (%n /u 64))<nuw>)<nuw><nsw> /u 64)
+; CHECK-NEXT: Loop %header: constant max backedge-taken count is i64 288230376151711743
+; CHECK-NEXT: Loop %header: symbolic max backedge-taken count is ((63 + (64 * (%n /u 64))<nuw>)<nuw><nsw> /u 64)
; CHECK-NEXT: symbolic max exit count for header: ***COULDNOTCOMPUTE***
-; CHECK-NEXT: symbolic max exit count for latch: ***COULDNOTCOMPUTE***
+; CHECK-NEXT: symbolic max exit count for latch: ((63 + (64 * (%n /u 64))<nuw>)<nuw><nsw> /u 64)
;
entry:
%limit = and i64 %n, -64
diff --git a/llvm/test/Transforms/InstCombine/icmp-add.ll b/llvm/test/Transforms/InstCombine/icmp-add.ll
index 6692ca2bf3fb2..c5caf35241546 100644
--- a/llvm/test/Transforms/InstCombine/icmp-add.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-add.ll
@@ -1982,7 +1982,7 @@ define <2 x i1> @slt_zero_add_nsw_splat_vec(<2 x i8> %a) {
define void @latch_inc_phi_user(i32 %sub) {
; CHECK-LABEL: @latch_inc_phi_user(
; CHECK: [[INC:%.*]] = add nuw nsw i32 [[J:%.*]], 1
-; CHECK: icmp slt i32 [[J]], [[SUB:%.*]]
+; CHECK: icmp sgt i32 [[INC]], [[SUB:%.*]]
entry:
br label %loop
@@ -1998,8 +1998,7 @@ exit:
define void @latch_inc_phi_user_sle(i32 %sub) {
; CHECK-LABEL: @latch_inc_phi_user_sle(
-; CHECK: [[INC:%.*]] = add nuw nsw i32 [[J:%.*]], 1
-; CHECK: icmp slt i32 [[J]], [[SUB:%.*]]
+; CHECK: icmp sgt i32 [[INC:%.*]], [[SUB:%.*]]
entry:
br label %loop
loop:
@@ -2013,7 +2012,7 @@ exit:
define void @latch_inc_phi_user_ugt(i32 %sub) {
; CHECK-LABEL: @latch_inc_phi_user_ugt(
-; CHECK: icmp ult i32 [[J:%.*]], [[SUB:%.*]]
+; CHECK: icmp ugt i32 [[INC:%.*]], [[SUB:%.*]]
entry:
br label %loop
loop:
diff --git a/llvm/test/Transforms/LoopStrengthReduce/latch-inc-lsr.ll b/llvm/test/Transforms/LoopStrengthReduce/latch-inc-lsr.ll
index de9ccb343ff8c..b402bd9630279 100644
--- a/llvm/test/Transforms/LoopStrengthReduce/latch-inc-lsr.ll
+++ b/llvm/test/Transforms/LoopStrengthReduce/latch-inc-lsr.ll
@@ -7,15 +7,14 @@ declare void @usef(float)
define void @gpu_addr_hoist(i32 %tmp20, i64 %sub47, i64 %stride, i32 %sub, ptr %tau1) {
; CHECK-LABEL: @gpu_addr_hoist(
-; CHECK: call i32 @llvm.smax.i32(i32 [[SUB:%.*]], i32 0)
+; CHECK: mul i64 [[STRIDE:%.*]], {{%.*}}
+; CHECK: shl i64 {{%.*}}, 2
; CHECK: body:
-; CHECK: sext i32 {{%.*}} to i64
-; CHECK: mul nsw i64 [[STRIDE:%.*]], {{%.*}}
-; CHECK: add nsw i64 [[SUB47:%.*]], {{%.*}}
-; CHECK: shl nsw i64 {{%.*}}, 2
-; CHECK: getelementptr inbounds i8, ptr [[TAU1:%.*]], i64 {{%.*}}
-; CHECK: load float, ptr {{%.*}}
-; CHECK: icmp ne i32 {{%.*}}, 0
+; CHECK: [[LSR_IV:%.*]] = phi ptr [ {{%.*}} ], [ {{%.*}} ]
+; CHECK-NEXT: [[J:%.*]] = phi i32 [ 0, {{%.*}} ], [ [[INC:%.*]], {{.*}} ]
+; CHECK-NEXT: load float, ptr [[LSR_IV]]
+; CHECK: [[INC]] = add nuw i32 [[J]], 1
+; CHECK: icmp sgt i32 [[INC]], [[SUB:%.*]]
entry:
br label %pre
pre:
@@ -41,14 +40,15 @@ exit:
define void @gpu_twostream_hoist(i32 %tmp20, i64 %sub47, i64 %stride, i32 %sub, ptr %tau1, ptr %tau2) {
; CHECK-LABEL: @gpu_twostream_hoist(
+; CHECK: mul i64 [[STRIDE:%.*]], {{%.*}}
; CHECK: body:
-; CHECK: sext i32 {{%.*}} to i64
-; CHECK: mul nsw i64 [[STRIDE:%.*]], {{%.*}}
-; CHECK: getelementptr inbounds i8, ptr [[TAU1:%.*]], i64 {{%.*}}
-; CHECK: getelementptr inbounds i8, ptr [[TAU2:%.*]], i64 {{%.*}}
-; CHECK: load float, ptr {{%.*}}
-; CHECK: load float, ptr {{%.*}}
-; CHECK: icmp ne i32 {{%.*}}, 0
+; CHECK: [[LSR_IV:%.*]] = phi i64 [ {{%.*}} ], [ {{%.*}} ]
+; CHECK-NEXT: [[J:%.*]] = phi i32 [ 0, {{%.*}} ], [ [[INC:%.*]], {{.*}} ]
+; CHECK-NEXT: getelementptr i8, ptr [[TAU1:%.*]], i64 [[LSR_IV]]
+; CHECK-NEXT: getelementptr i8, ptr [[TAU2:%.*]], i64 [[LSR_IV]]
+; CHECK-NEXT: load float, ptr {{%.*}}
+; CHECK-NEXT: load float, ptr {{%.*}}
+; CHECK: icmp sgt i32 [[INC:%.*]], [[SUB:%.*]]
entry:
br label %pre
pre:
@@ -77,14 +77,18 @@ exit:
define void @gpu_four_load(i32 %tmp20, i64 %sub47, i64 %stride, i32 %sub, ptr %tau1) {
; CHECK-LABEL: @gpu_four_load(
+; CHECK: mul i64 [[STRIDE:%.*]], {{%.*}}
; CHECK: body:
-; CHECK: sext i32 {{%.*}} to i64
-; CHECK: mul nsw i64 [[STRIDE:%.*]], {{%.*}}
-; CHECK: load float, ptr {{%.*}}
-; CHECK: load float, ptr {{%.*}}
-; CHECK: load float, ptr {{%.*}}
-; CHECK: load float, ptr {{%.*}}
-; CHECK: icmp ne i32 {{%.*}}, 0
+; CHECK: [[LSR_IV:%.*]] = phi ptr [ {{%.*}} ], [ {{%.*}} ]
+; CHECK-NEXT: [[J:%.*]] = phi i32 [ 0, {{%.*}} ], [ [[INC:%.*]], {{.*}} ]
+; CHECK-NEXT: getelementptr i8, ptr [[LSR_IV]], i64 4
+; CHECK-NEXT: getelementptr i8, ptr [[LSR_IV]], i64 8
+; CHECK-NEXT: getelementptr i8, ptr [[LSR_IV]], i64 12
+; CHECK-NEXT: load float, ptr [[LSR_IV]]
+; CHECK-NEXT: load float, ptr {{%.*}}
+; CHECK-NEXT: load float, ptr {{%.*}}
+; CHECK-NEXT: load float, ptr {{%.*}}
+; CHECK: icmp sgt i32 [[INC:%.*]], [[SUB:%.*]]
entry:
br label %pre
pre:
diff --git a/llvm/test/Transforms/PhaseOrdering/instcombine-latch-inc-benefits.ll b/llvm/test/Transforms/PhaseOrdering/instcombine-latch-inc-benefits.ll
index 403744d53c4a4..80dc589140117 100644
--- a/llvm/test/Transforms/PhaseOrdering/instcombine-latch-inc-benefits.ll
+++ b/llvm/test/Transforms/PhaseOrdering/instcombine-latch-inc-benefits.ll
@@ -15,7 +15,7 @@ declare void @usef(float)
define void @scev_postinc_nsw(i32 %sub) {
; CHECK-LABEL: 'scev_postinc_nsw'
; CHECK: %inc = add nuw nsw i32 %j, 1
-; CHECK-NEXT: --> {1,+,1}<nuw><%loop>
+; CHECK-NEXT: --> {1,+,1}<nuw><nsw>
entry:
br label %loop
loop:
@@ -30,7 +30,7 @@ exit:
define void @scev_gep_nsw(i32 %sub) {
; CHECK-LABEL: 'scev_gep_nsw'
; CHECK: %inc = add nuw nsw i32 %j, 1
-; CHECK-NEXT: --> {1,+,1}<nuw><%loop>
+; CHECK-NEXT: --> {1,+,1}<nuw><nsw>
entry:
br label %loop
loop:
@@ -50,7 +50,7 @@ define void @scev_gpu_nssw(i32 %tmp20, i64 %sub47, i64 %stride, i32 %sub, ptr %t
; CHECK-LABEL: 'scev_gpu_nssw'
; CHECK: %conv50 = sext i32 %add43 to i64
; CHECK-NEXT: --> {(sext i32 %tmp20 to i64),+,1}<nsw>
-; CHECK: {1,+,1}<nuw><%body>
+; CHECK: {1,+,1}<nuw><{{.*}}> Added Flags: <nssw>
entry:
br label %pre
pre:
@@ -76,7 +76,7 @@ exit:
define void @scev_nssw_indvars(i32 %sub) {
; INDVARS-LABEL: 'scev_nssw_indvars'
-; INDVARS: {1,+,1}<nuw><%loop>
+; INDVARS: {1,+,1}<nuw><{{.*}}> Added Flags: <nssw>
entry:
br label %loop
loop:
More information about the llvm-commits
mailing list