[llvm] [SCEV] Add missing Map.lookup in SCEVLoopGuardRewriter visitAddExpr/v… (PR #197433)
via llvm-commits
llvm-commits at lists.llvm.org
Wed May 13 06:12:59 PDT 2026
https://github.com/RonDahan101 created https://github.com/llvm/llvm-project/pull/197433
…isitMulExpr
SCEVLoopGuardRewriter's visitAddExpr and visitMulExpr methods never check the rewrite map for the full expression before rewriting operands. All other visit methods (visitUnknown, visitUMinExpr, visitSMinExpr, visitZeroExtendExpr, visitSignExtendExpr) correctly call Map.lookup(Expr) at entry, but visitAddExpr and visitMulExpr were missing this lookup.
The bug manifests when applyLoopGuards creates rewrite map entries keyed by a MulExpr or AddExpr. For example, divisibility-aware NE-zero guard handling creates a map entry for the full MulExpr (16 * (n /u 16)) derived from (n & -16), but visitMulExpr only rewrites individual operands and never finds the map entry for the whole expression. This causes downstream passes like LoopVectorize to generate unnecessary runtime checks (e.g., min.iters.check) even when the trip count is provably >= VF.
The fix adds Map.lookup(Expr) at the entry of both visitAddExpr and visitMulExpr, consistent with all other visitor methods in SCEVLoopGuardRewriter.
This also removes a now-redundant diff-based memory check in runtime-checks-difference.ll, where the improved guard propagation makes the vector.memcheck block unnecessary.
>From 6e6a3aec267a63c4e8974aea891b93ef33d919e2 Mon Sep 17 00:00:00 2001
From: rond <ron.dahan at mobileye.com>
Date: Mon, 11 May 2026 17:19:34 +0300
Subject: [PATCH] [SCEV] Add missing Map.lookup in SCEVLoopGuardRewriter
visitAddExpr/visitMulExpr
SCEVLoopGuardRewriter's visitAddExpr and visitMulExpr methods never
check the rewrite map for the full expression before rewriting
operands. All other visit methods (visitUnknown, visitUMinExpr,
visitSMinExpr, visitZeroExtendExpr, visitSignExtendExpr) correctly
call Map.lookup(Expr) at entry, but visitAddExpr and visitMulExpr
were missing this lookup.
The bug manifests when applyLoopGuards creates rewrite map entries
keyed by a MulExpr or AddExpr. For example, divisibility-aware NE-zero
guard handling creates a map entry for the full MulExpr
(16 * (n /u 16)) derived from (n & -16), but visitMulExpr only
rewrites individual operands and never finds the map entry for the
whole expression. This causes downstream passes like LoopVectorize to
generate unnecessary runtime checks (e.g., min.iters.check) even when
the trip count is provably >= VF.
The fix adds Map.lookup(Expr) at the entry of both visitAddExpr and
visitMulExpr, consistent with all other visitor methods in
SCEVLoopGuardRewriter.
This also removes a now-redundant diff-based memory check in
runtime-checks-difference.ll, where the improved guard propagation
makes the vector.memcheck block unnecessary.
---
llvm/lib/Analysis/ScalarEvolution.cpp | 4 ++
.../IndVarSimplify/canonicalize-cmp.ll | 6 +-
.../Transforms/LoopIdiom/add-nsw-zext-fold.ll | 6 +-
.../peel-last-iteration-with-guards.ll | 66 +++----------------
.../runtime-checks-difference.ll | 7 +-
.../scev-loop-guard-rewriter-add-expr.ll | 35 ++++++++++
.../scev-loop-guard-rewriter-mul-expr.ll | 36 ++++++++++
7 files changed, 89 insertions(+), 71 deletions(-)
create mode 100644 llvm/test/Transforms/LoopVectorize/scev-loop-guard-rewriter-add-expr.ll
create mode 100644 llvm/test/Transforms/LoopVectorize/scev-loop-guard-rewriter-mul-expr.ll
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 94fdd36dbcb22..a686d3516874e 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -16238,6 +16238,8 @@ const SCEV *ScalarEvolution::LoopGuards::rewrite(const SCEV *Expr) const {
}
const SCEV *visitAddExpr(const SCEVAddExpr *Expr) {
+ if (const SCEV *S = Map.lookup(Expr))
+ return S;
// Helper to check if S is a subtraction (A - B) where A != B, and if so,
// return UMax(S, 1).
auto RewriteSubtraction = [&](const SCEV *S) -> const SCEV * {
@@ -16289,6 +16291,8 @@ const SCEV *ScalarEvolution::LoopGuards::rewrite(const SCEV *Expr) const {
}
const SCEV *visitMulExpr(const SCEVMulExpr *Expr) {
+ if (const SCEV *S = Map.lookup(Expr))
+ return S;
SmallVector<SCEVUse, 2> Operands;
bool Changed = false;
for (SCEVUse Op : Expr->operands()) {
diff --git a/llvm/test/Transforms/IndVarSimplify/canonicalize-cmp.ll b/llvm/test/Transforms/IndVarSimplify/canonicalize-cmp.ll
index 6ac09fafcb7a4..b8fd0e46592ac 100644
--- a/llvm/test/Transforms/IndVarSimplify/canonicalize-cmp.ll
+++ b/llvm/test/Transforms/IndVarSimplify/canonicalize-cmp.ll
@@ -343,14 +343,13 @@ define void @slt_no_smax_needed(i64 %n, ptr %dst) {
; CHECK-NEXT: [[PRE:%.*]] = icmp ult i32 [[ADD_1]], 8
; CHECK-NEXT: br i1 [[PRE]], label [[EXIT:%.*]], label [[LOOP_PREHEADER:%.*]]
; CHECK: loop.preheader:
-; CHECK-NEXT: [[SMAX:%.*]] = call i32 @llvm.smax.i32(i32 [[SHR]], i32 1)
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[IV_NEXT:%.*]], [[LOOP]] ], [ 0, [[LOOP_PREHEADER]] ]
; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i8, ptr [[DST:%.*]], i32 [[IV]]
; CHECK-NEXT: store i8 0, ptr [[GEP]], align 1
; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i32 [[IV]], 1
-; CHECK-NEXT: [[EXITCOND:%.*]] = icmp ne i32 [[IV_NEXT]], [[SMAX]]
+; CHECK-NEXT: [[EXITCOND:%.*]] = icmp ne i32 [[IV_NEXT]], [[SHR]]
; CHECK-NEXT: br i1 [[EXITCOND]], label [[LOOP]], label [[EXIT_LOOPEXIT:%.*]]
; CHECK: exit.loopexit:
; CHECK-NEXT: br label [[EXIT]]
@@ -385,14 +384,13 @@ define void @ult_no_umax_needed(i64 %n, ptr %dst) {
; CHECK-NEXT: [[PRE:%.*]] = icmp ult i32 [[ADD_1]], 8
; CHECK-NEXT: br i1 [[PRE]], label [[EXIT:%.*]], label [[LOOP_PREHEADER:%.*]]
; CHECK: loop.preheader:
-; CHECK-NEXT: [[UMAX:%.*]] = call i32 @llvm.umax.i32(i32 [[SHR]], i32 1)
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[IV_NEXT:%.*]], [[LOOP]] ], [ 0, [[LOOP_PREHEADER]] ]
; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i8, ptr [[DST:%.*]], i32 [[IV]]
; CHECK-NEXT: store i8 0, ptr [[GEP]], align 1
; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i32 [[IV]], 1
-; CHECK-NEXT: [[EXITCOND:%.*]] = icmp ne i32 [[IV_NEXT]], [[UMAX]]
+; CHECK-NEXT: [[EXITCOND:%.*]] = icmp ne i32 [[IV_NEXT]], [[SHR]]
; CHECK-NEXT: br i1 [[EXITCOND]], label [[LOOP]], label [[EXIT_LOOPEXIT:%.*]]
; CHECK: exit.loopexit:
; CHECK-NEXT: br label [[EXIT]]
diff --git a/llvm/test/Transforms/LoopIdiom/add-nsw-zext-fold.ll b/llvm/test/Transforms/LoopIdiom/add-nsw-zext-fold.ll
index bc1543d8361a7..09419c13aaeb0 100644
--- a/llvm/test/Transforms/LoopIdiom/add-nsw-zext-fold.ll
+++ b/llvm/test/Transforms/LoopIdiom/add-nsw-zext-fold.ll
@@ -61,9 +61,9 @@ define void @test_memset_size_can_use_info_from_guards(i32 %x, ptr %dst) {
; CHECK: [[LOOP1_BACKEDGE]]:
; CHECK-NEXT: br label %[[LOOP1]]
; CHECK: [[LOOP2_PREHEADER]]:
-; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[SUB]] to i64
-; CHECK-NEXT: [[TMP1:%.*]] = lshr i64 [[TMP0]], 1
-; CHECK-NEXT: [[UMAX:%.*]] = call i64 @llvm.umax.i64(i64 [[TMP1]], i64 1)
+; CHECK-NEXT: [[TMP0:%.*]] = add nsw i32 [[SHR]], -1
+; CHECK-NEXT: [[TMP1:%.*]] = zext i32 [[TMP0]] to i64
+; CHECK-NEXT: [[UMAX:%.*]] = add nuw nsw i64 [[TMP1]], 1
; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 1 [[DST]], i8 0, i64 [[UMAX]], i1 false)
; CHECK-NEXT: br label %[[LOOP2:.*]]
; CHECK: [[LOOP2]]:
diff --git a/llvm/test/Transforms/LoopUnroll/peel-last-iteration-with-guards.ll b/llvm/test/Transforms/LoopUnroll/peel-last-iteration-with-guards.ll
index c1d8395965577..876b9e7756e69 100644
--- a/llvm/test/Transforms/LoopUnroll/peel-last-iteration-with-guards.ll
+++ b/llvm/test/Transforms/LoopUnroll/peel-last-iteration-with-guards.ll
@@ -141,42 +141,18 @@ define void @peel_with_guard2(i32 %n) {
; CHECK-NEXT: [[PRECOND:%.*]] = icmp eq i32 [[SUB]], 0
; CHECK-NEXT: br i1 [[PRECOND]], label %[[EXIT:.*]], label %[[LOOP_HEADER_PREHEADER:.*]]
; CHECK: [[LOOP_HEADER_PREHEADER]]:
-; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[N]], -2
-; CHECK-NEXT: [[TMP1:%.*]] = icmp ne i32 [[TMP0]], 0
-; CHECK-NEXT: br i1 [[TMP1]], label %[[LOOP_HEADER_PREHEADER_SPLIT:.*]], label %[[EXIT_LOOPEXIT_PEEL_BEGIN:.*]]
-; CHECK: [[LOOP_HEADER_PREHEADER_SPLIT]]:
-; CHECK-NEXT: br label %[[LOOP_HEADER:.*]]
-; CHECK: [[LOOP_HEADER]]:
-; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ], [ 1, %[[LOOP_HEADER_PREHEADER_SPLIT]] ]
-; CHECK-NEXT: br i1 false, label %[[THEN:.*]], label %[[LOOP_LATCH]]
-; CHECK: [[THEN]]:
-; CHECK-NEXT: call void @foo()
-; CHECK-NEXT: br label %[[LOOP_LATCH]]
-; CHECK: [[LOOP_LATCH]]:
-; CHECK-NEXT: [[IV_NEXT]] = add nuw i32 [[IV]], 1
-; CHECK-NEXT: [[TMP2:%.*]] = sub i32 [[N]], 1
-; CHECK-NEXT: [[EC:%.*]] = icmp eq i32 [[IV_NEXT]], [[TMP2]]
-; CHECK-NEXT: br i1 [[EC]], label %[[EXIT_LOOPEXIT_PEEL_BEGIN_LOOPEXIT:.*]], label %[[LOOP_HEADER]], !llvm.loop [[LOOP2:![0-9]+]]
-; CHECK: [[EXIT_LOOPEXIT_PEEL_BEGIN_LOOPEXIT]]:
-; CHECK-NEXT: [[DOTPH:%.*]] = phi i32 [ [[IV_NEXT]], %[[LOOP_LATCH]] ]
-; CHECK-NEXT: br label %[[EXIT_LOOPEXIT_PEEL_BEGIN]]
-; CHECK: [[EXIT_LOOPEXIT_PEEL_BEGIN]]:
-; CHECK-NEXT: [[TMP3:%.*]] = phi i32 [ 1, %[[LOOP_HEADER_PREHEADER]] ], [ [[DOTPH]], %[[EXIT_LOOPEXIT_PEEL_BEGIN_LOOPEXIT]] ]
; CHECK-NEXT: br label %[[LOOP_HEADER_PEEL:.*]]
; CHECK: [[LOOP_HEADER_PEEL]]:
+; CHECK-NEXT: [[TMP3:%.*]] = phi i32 [ [[IV_NEXT_PEEL:%.*]], %[[LOOP_LATCH_PEEL:.*]] ], [ 1, %[[LOOP_HEADER_PREHEADER]] ]
; CHECK-NEXT: [[C_PEEL:%.*]] = icmp eq i32 [[TMP3]], [[SUB]]
-; CHECK-NEXT: br i1 [[C_PEEL]], label %[[THEN_PEEL:.*]], label %[[LOOP_LATCH_PEEL:.*]]
+; CHECK-NEXT: br i1 [[C_PEEL]], label %[[THEN_PEEL:.*]], label %[[LOOP_LATCH_PEEL]]
; CHECK: [[THEN_PEEL]]:
; CHECK-NEXT: call void @foo()
; CHECK-NEXT: br label %[[LOOP_LATCH_PEEL]]
; CHECK: [[LOOP_LATCH_PEEL]]:
-; CHECK-NEXT: [[IV_NEXT_PEEL:%.*]] = add nuw i32 [[TMP3]], 1
+; CHECK-NEXT: [[IV_NEXT_PEEL]] = add nuw i32 [[TMP3]], 1
; CHECK-NEXT: [[EC_PEEL:%.*]] = icmp eq i32 [[IV_NEXT_PEEL]], [[N]]
-; CHECK-NEXT: br i1 [[EC_PEEL]], label %[[EXIT_LOOPEXIT_PEEL_NEXT:.*]], label %[[EXIT_LOOPEXIT_PEEL_NEXT]]
-; CHECK: [[EXIT_LOOPEXIT_PEEL_NEXT]]:
-; CHECK-NEXT: br label %[[LOOP_HEADER_PEEL_NEXT:.*]]
-; CHECK: [[LOOP_HEADER_PEEL_NEXT]]:
-; CHECK-NEXT: br label %[[EXIT_LOOPEXIT:.*]]
+; CHECK-NEXT: br i1 [[EC_PEEL]], label %[[EXIT_LOOPEXIT:.*]], label %[[LOOP_HEADER_PEEL]]
; CHECK: [[EXIT_LOOPEXIT]]:
; CHECK-NEXT: br label %[[EXIT]]
; CHECK: [[EXIT]]:
@@ -213,42 +189,18 @@ define void @test_peel_guard_sub_1_btc(i32 %n) {
; CHECK-NEXT: [[PRE:%.*]] = icmp eq i32 [[SUB]], 0
; CHECK-NEXT: br i1 [[PRE]], label %[[EXIT:.*]], label %[[LOOP_HEADER_PREHEADER:.*]]
; CHECK: [[LOOP_HEADER_PREHEADER]]:
-; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[N]], -2
-; CHECK-NEXT: [[TMP1:%.*]] = icmp ne i32 [[TMP0]], 0
-; CHECK-NEXT: br i1 [[TMP1]], label %[[LOOP_HEADER_PREHEADER_SPLIT:.*]], label %[[EXIT_LOOPEXIT_PEEL_BEGIN:.*]]
-; CHECK: [[LOOP_HEADER_PREHEADER_SPLIT]]:
-; CHECK-NEXT: br label %[[LOOP_HEADER:.*]]
-; CHECK: [[LOOP_HEADER]]:
-; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ], [ 1, %[[LOOP_HEADER_PREHEADER_SPLIT]] ]
-; CHECK-NEXT: br i1 false, label %[[LOOP_LATCH]], label %[[THEN:.*]]
-; CHECK: [[THEN]]:
-; CHECK-NEXT: [[CALL136:%.*]] = load volatile ptr, ptr null, align 4294967296
-; CHECK-NEXT: br label %[[LOOP_LATCH]]
-; CHECK: [[LOOP_LATCH]]:
-; CHECK-NEXT: [[IV_NEXT]] = add nuw i32 [[IV]], 1
-; CHECK-NEXT: [[TMP2:%.*]] = sub i32 [[N]], 1
-; CHECK-NEXT: [[EC:%.*]] = icmp eq i32 [[IV_NEXT]], [[TMP2]]
-; CHECK-NEXT: br i1 [[EC]], label %[[EXIT_LOOPEXIT_PEEL_BEGIN_LOOPEXIT:.*]], label %[[LOOP_HEADER]], !llvm.loop [[LOOP3:![0-9]+]]
-; CHECK: [[EXIT_LOOPEXIT_PEEL_BEGIN_LOOPEXIT]]:
-; CHECK-NEXT: [[DOTPH:%.*]] = phi i32 [ [[IV_NEXT]], %[[LOOP_LATCH]] ]
-; CHECK-NEXT: br label %[[EXIT_LOOPEXIT_PEEL_BEGIN]]
-; CHECK: [[EXIT_LOOPEXIT_PEEL_BEGIN]]:
-; CHECK-NEXT: [[TMP3:%.*]] = phi i32 [ 1, %[[LOOP_HEADER_PREHEADER]] ], [ [[DOTPH]], %[[EXIT_LOOPEXIT_PEEL_BEGIN_LOOPEXIT]] ]
; CHECK-NEXT: br label %[[LOOP_HEADER_PEEL:.*]]
; CHECK: [[LOOP_HEADER_PEEL]]:
+; CHECK-NEXT: [[TMP3:%.*]] = phi i32 [ [[IV_NEXT_PEEL:%.*]], %[[LOOP_LATCH_PEEL:.*]] ], [ 1, %[[LOOP_HEADER_PREHEADER]] ]
; CHECK-NEXT: [[CMP115_PEEL:%.*]] = icmp eq i32 [[TMP3]], [[SUB]]
-; CHECK-NEXT: br i1 [[CMP115_PEEL]], label %[[LOOP_LATCH_PEEL:.*]], label %[[THEN_PEEL:.*]]
+; CHECK-NEXT: br i1 [[CMP115_PEEL]], label %[[LOOP_LATCH_PEEL]], label %[[THEN_PEEL:.*]]
; CHECK: [[THEN_PEEL]]:
; CHECK-NEXT: [[CALL136_PEEL:%.*]] = load volatile ptr, ptr null, align 4294967296
; CHECK-NEXT: br label %[[LOOP_LATCH_PEEL]]
; CHECK: [[LOOP_LATCH_PEEL]]:
-; CHECK-NEXT: [[IV_NEXT_PEEL:%.*]] = add nuw i32 [[TMP3]], 1
+; CHECK-NEXT: [[IV_NEXT_PEEL]] = add nuw i32 [[TMP3]], 1
; CHECK-NEXT: [[EC_PEEL:%.*]] = icmp eq i32 [[IV_NEXT_PEEL]], [[N]]
-; CHECK-NEXT: br i1 [[EC_PEEL]], label %[[EXIT_LOOPEXIT_PEEL_NEXT:.*]], label %[[EXIT_LOOPEXIT_PEEL_NEXT]]
-; CHECK: [[EXIT_LOOPEXIT_PEEL_NEXT]]:
-; CHECK-NEXT: br label %[[LOOP_HEADER_PEEL_NEXT:.*]]
-; CHECK: [[LOOP_HEADER_PEEL_NEXT]]:
-; CHECK-NEXT: br label %[[EXIT_LOOPEXIT:.*]]
+; CHECK-NEXT: br i1 [[EC_PEEL]], label %[[EXIT_LOOPEXIT:.*]], label %[[LOOP_HEADER_PEEL]]
; CHECK: [[EXIT_LOOPEXIT]]:
; CHECK-NEXT: br label %[[EXIT]]
; CHECK: [[EXIT]]:
@@ -279,6 +231,4 @@ exit:
;.
; CHECK: [[LOOP0]] = distinct !{[[LOOP0]], [[META1:![0-9]+]]}
; CHECK: [[META1]] = !{!"llvm.loop.peeled.count", i32 1}
-; CHECK: [[LOOP2]] = distinct !{[[LOOP2]], [[META1]]}
-; CHECK: [[LOOP3]] = distinct !{[[LOOP3]], [[META1]]}
;.
diff --git a/llvm/test/Transforms/LoopVectorize/runtime-checks-difference.ll b/llvm/test/Transforms/LoopVectorize/runtime-checks-difference.ll
index 16afd3044d180..8c074a1f88d63 100644
--- a/llvm/test/Transforms/LoopVectorize/runtime-checks-difference.ll
+++ b/llvm/test/Transforms/LoopVectorize/runtime-checks-difference.ll
@@ -461,12 +461,7 @@ define void @remove_diff_checks_via_guards(i32 %x, i32 %y, ptr %A) {
; CHECK-NEXT: [[TMP13:%.*]] = icmp ugt i64 [[SMAX]], 4294967295
; CHECK-NEXT: [[TMP14:%.*]] = or i1 [[TMP12]], [[TMP13]]
; CHECK-NEXT: [[TMP15:%.*]] = or i1 [[TMP9]], [[TMP14]]
-; CHECK-NEXT: br i1 [[TMP15]], [[SCALAR_PH]], label %[[VECTOR_MEMCHECK:.*]]
-; CHECK: [[VECTOR_MEMCHECK]]:
-; CHECK-NEXT: [[TMP16:%.*]] = sext i32 [[OFFSET]] to i64
-; CHECK-NEXT: [[TMP17:%.*]] = shl nsw i64 [[TMP16]], 2
-; CHECK-NEXT: [[DIFF_CHECK:%.*]] = icmp ult i64 [[TMP17]], 16
-; CHECK-NEXT: br i1 [[DIFF_CHECK]], [[SCALAR_PH]], [[VECTOR_PH1:label %.*]]
+; CHECK-NEXT: br i1 [[TMP15]], [[SCALAR_PH]], [[VECTOR_MEMCHECK:label %.*]]
;
entry:
%offset = sub i32 %x, %y
diff --git a/llvm/test/Transforms/LoopVectorize/scev-loop-guard-rewriter-add-expr.ll b/llvm/test/Transforms/LoopVectorize/scev-loop-guard-rewriter-add-expr.ll
new file mode 100644
index 0000000000000..116b81799b7dd
--- /dev/null
+++ b/llvm/test/Transforms/LoopVectorize/scev-loop-guard-rewriter-add-expr.ll
@@ -0,0 +1,35 @@
+; RUN: opt -passes=loop-vectorize -force-vector-width=8 -S < %s | FileCheck %s
+;
+; Test that applyLoopGuards correctly rewrites SCEVAddExpr trip counts.
+;
+; The trip count is `a + b` (a SCEVAddExpr). The loop guard `tc >= 8` means
+; inside the loop we know tc >= 8. The SCEVLoopGuardRewriter must look up the
+; full AddExpr in the rewrite map to propagate this constraint to LV.
+;
+; Without the fix, visitAddExpr only rewrites operands and misses the map
+; entry for the whole expression, causing LV to emit a redundant runtime check.
+
+; CHECK-LABEL: @add_expr_loop_guard
+; CHECK-NOT: min.iters.check
+; CHECK: vector.body:
+
+define void @add_expr_loop_guard(i32 %a, i32 %b, ptr %p) {
+entry:
+ %tc = add nuw i32 %a, %b
+ %guard = icmp uge i32 %tc, 8
+ br i1 %guard, label %loop.ph, label %exit
+
+loop.ph:
+ br label %loop
+
+loop:
+ %iv = phi i32 [ 0, %loop.ph ], [ %iv.next, %loop ]
+ %gep = getelementptr inbounds i8, ptr %p, i32 %iv
+ store i8 0, ptr %gep, align 1
+ %iv.next = add nuw nsw i32 %iv, 1
+ %exitcond = icmp eq i32 %iv.next, %tc
+ br i1 %exitcond, label %exit, label %loop
+
+exit:
+ ret void
+}
diff --git a/llvm/test/Transforms/LoopVectorize/scev-loop-guard-rewriter-mul-expr.ll b/llvm/test/Transforms/LoopVectorize/scev-loop-guard-rewriter-mul-expr.ll
new file mode 100644
index 0000000000000..1564f750e1c41
--- /dev/null
+++ b/llvm/test/Transforms/LoopVectorize/scev-loop-guard-rewriter-mul-expr.ll
@@ -0,0 +1,36 @@
+; RUN: opt -passes=loop-vectorize -force-vector-width=16 -S < %s | FileCheck %s
+;
+; Test that applyLoopGuards correctly rewrites SCEVMulExpr trip counts.
+;
+; The trip count here is `n & -16` which SCEV models as `(16 * (n /u 16))`.
+; The loop guard `tc != 0` combined with divisibility info means tc >= 16.
+; The SCEVLoopGuardRewriter must look up the full MulExpr in the rewrite map
+; (not just rewrite its operands) to propagate this constraint.
+;
+; Without the fix, visitMulExpr only rewrites operands and misses the map
+; entry for the whole expression, causing LV to emit a redundant runtime check.
+
+; CHECK-LABEL: @mul_expr_loop_guard
+; CHECK-NOT: min.iters.check
+; CHECK: vector.body:
+
+define void @mul_expr_loop_guard(i32 %n, ptr %p) {
+entry:
+ %tc = and i32 %n, -16
+ %cmp = icmp ne i32 %tc, 0
+ br i1 %cmp, label %loop.ph, label %exit
+
+loop.ph:
+ br label %loop
+
+loop:
+ %iv = phi i32 [ 0, %loop.ph ], [ %iv.next, %loop ]
+ %gep = getelementptr inbounds i8, ptr %p, i32 %iv
+ store i8 0, ptr %gep, align 1
+ %iv.next = add nuw nsw i32 %iv, 1
+ %exitcond = icmp eq i32 %iv.next, %tc
+ br i1 %exitcond, label %exit, label %loop
+
+exit:
+ ret void
+}
More information about the llvm-commits
mailing list