[llvm] [LoopFlatten] Invalidate SCEV after flattening loops (PR #212744)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Jul 29 04:35:23 PDT 2026
https://github.com/Michael-Chen-NJU created https://github.com/llvm/llvm-project/pull/212744
LoopFlatten updates loop structure and replaces the original linear IV expression with the flattened induction variable. SCEV may otherwise keep stale no-wrap information for the scalarized narrowed index, causing later LoopVectorize to omit the required runtime SCEV check.
In the reduced case, the stale SCEV result treats `%flatten.trunciv` as `{0,+,1}<nuw>`, which lets the zext index be modeled as a widened no-wrap AddRec. After invalidation, SCEV recomputes it as a wrapping i8 recurrence and LoopVectorize emits the required `vector.scevcheck`.
Also make `FunctionToLoopPassAdaptor` preserve `ScalarEvolutionAnalysis` only when the loop pipeline's preserved analyses still preserve it, so a loop pass can actually invalidate SCEV at the function level.
Fixes #212176.
>From 41ec6ab12cde0c7b7234e200a73a8e86a071b433 Mon Sep 17 00:00:00 2001
From: Michael-Chen-NJU <2802328816 at qq.com>
Date: Wed, 29 Jul 2026 19:19:32 +0800
Subject: [PATCH] [LoopFlatten] Invalidate SCEV after flattening loops
---
llvm/lib/Transforms/Scalar/LoopFlatten.cpp | 1 +
.../lib/Transforms/Scalar/LoopPassManager.cpp | 6 ++-
.../LoopFlatten/scev-invalidation.ll | 43 +++++++++++++++++++
3 files changed, 48 insertions(+), 2 deletions(-)
create mode 100644 llvm/test/Transforms/LoopFlatten/scev-invalidation.ll
diff --git a/llvm/lib/Transforms/Scalar/LoopFlatten.cpp b/llvm/lib/Transforms/Scalar/LoopFlatten.cpp
index e48c47f1b4b89..933edd8a5bb43 100644
--- a/llvm/lib/Transforms/Scalar/LoopFlatten.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopFlatten.cpp
@@ -1026,6 +1026,7 @@ PreservedAnalyses LoopFlattenPass::run(LoopNest &LN, LoopAnalysisManager &LAM,
AR.MSSA->verifyMemorySSA();
auto PA = getLoopPassPreservedAnalyses();
+ PA.abandon<ScalarEvolutionAnalysis>();
if (AR.MSSA)
PA.preserve<MemorySSAAnalysis>();
return PA;
diff --git a/llvm/lib/Transforms/Scalar/LoopPassManager.cpp b/llvm/lib/Transforms/Scalar/LoopPassManager.cpp
index 978adeedc88d7..5ba47b20ba655 100644
--- a/llvm/lib/Transforms/Scalar/LoopPassManager.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopPassManager.cpp
@@ -332,10 +332,12 @@ PreservedAnalyses FunctionToLoopPassAdaptor::run(Function &F,
// loop analysis manager incrementally above.
PA.preserveSet<AllAnalysesOn<Loop>>();
PA.preserve<LoopAnalysisManagerFunctionProxy>();
- // We also preserve the set of standard analyses.
+ // We also preserve the set of standard analyses, unless the loop pipeline
+ // explicitly invalidated SCEV.
PA.preserve<DominatorTreeAnalysis>();
PA.preserve<LoopAnalysis>();
- PA.preserve<ScalarEvolutionAnalysis>();
+ if (PA.getChecker<ScalarEvolutionAnalysis>().preserved())
+ PA.preserve<ScalarEvolutionAnalysis>();
if (UseMemorySSA)
PA.preserve<MemorySSAAnalysis>();
return PA;
diff --git a/llvm/test/Transforms/LoopFlatten/scev-invalidation.ll b/llvm/test/Transforms/LoopFlatten/scev-invalidation.ll
new file mode 100644
index 0000000000000..875007e5d4688
--- /dev/null
+++ b/llvm/test/Transforms/LoopFlatten/scev-invalidation.ll
@@ -0,0 +1,43 @@
+; RUN: opt < %s -S -passes='module(cgscc(function(loop(loop-flatten)))),function(loop-vectorize)' --force-vector-width=4 --force-vector-interleave=3 | FileCheck %s
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+; Make sure loop-flatten does not preserve stale ScalarEvolution results that
+; make loop-vectorize omit the runtime SCEV check for the scalarized zext index.
+define void @zext_i8(i8 %N, ptr %A) {
+; CHECK-LABEL: @zext_i8(
+; CHECK: for.cond1.preheader.us.preheader:
+; CHECK: %flatten.tripcount = mul i64
+; CHECK: br i1 %min.iters.check, label %scalar.ph, label %vector.scevcheck
+; CHECK: vector.scevcheck:
+; CHECK-NEXT: {{%.*}} = add nsw i64 %flatten.tripcount, -1
+; CHECK-NEXT: {{%.*}} = icmp ugt i64 {{%.*}}, 255
+; CHECK-NEXT: br i1 {{%.*}}, label %scalar.ph, label %vector.ph
+entry:
+ %cmp20.not = icmp eq i8 %N, 0
+ br i1 %cmp20.not, label %common.ret, label %for.cond1.preheader.us
+
+for.cond1.preheader.us:
+ %i.021.us = phi i8 [ %inc8.us, %for.cond1.for.inc7_crit_edge.us ], [ 0, %entry ]
+ br label %for.body3.us
+
+for.body3.us:
+ %j.019.us = phi i8 [ 0, %for.cond1.preheader.us ], [ %inc.us, %for.body3.us ]
+ %mul.us = mul i8 %i.021.us, %N
+ %add.us = add i8 %j.019.us, %mul.us
+ %idxprom.us = zext i8 %add.us to i64
+ %arrayidx.us = getelementptr [2 x i8], ptr %A, i64 %idxprom.us
+ store i16 1, ptr %arrayidx.us, align 2
+ %inc.us = add i8 %j.019.us, 1
+ %cmp2.us = icmp ult i8 %inc.us, %N
+ br i1 %cmp2.us, label %for.body3.us, label %for.cond1.for.inc7_crit_edge.us
+
+for.cond1.for.inc7_crit_edge.us:
+ %inc8.us = add i8 %i.021.us, 1
+ %cmp.us = icmp ult i8 %inc8.us, %N
+ br i1 %cmp.us, label %for.cond1.preheader.us, label %common.ret
+
+common.ret:
+ ret void
+}
More information about the llvm-commits
mailing list