[llvm] [LoopFlatten] Invalidate SCEV on each widening (PR #211819)

Arda Serdar Pektezol via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 24 23:53:54 PDT 2026


https://github.com/pektezol updated https://github.com/llvm/llvm-project/pull/211819

>From cfc89366017a30619836a940ed804d4dfea0c49f Mon Sep 17 00:00:00 2001
From: Arda Serdar Pektezol <arda at pektezol.dev>
Date: Tue, 25 Aug 2026 00:35:38 +0300
Subject: [PATCH 1/2] baseline test

---
 .../LoopFlatten/invalidate-scev-pr203176.ll   | 94 +++++++++++++++++++
 1 file changed, 94 insertions(+)
 create mode 100644 llvm/test/Transforms/LoopFlatten/invalidate-scev-pr203176.ll

diff --git a/llvm/test/Transforms/LoopFlatten/invalidate-scev-pr203176.ll b/llvm/test/Transforms/LoopFlatten/invalidate-scev-pr203176.ll
new file mode 100644
index 0000000000000..83bce7418438b
--- /dev/null
+++ b/llvm/test/Transforms/LoopFlatten/invalidate-scev-pr203176.ll
@@ -0,0 +1,94 @@
+; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes='loop(loop-flatten),print<scalar-evolution>' -disable-output 2>&1 | FileCheck %s
+
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+
+define i64 @f(i64 %n) {
+; CHECK-LABEL: 'f'
+; CHECK-NEXT:  Classifying expressions for: @f
+; CHECK-NEXT:    %i = phi i64 [ 0, %entry ], [ %i.next, %outer.latch ]
+; CHECK-NEXT:    --> {0,+,1}<nuw><%outer.loop> U: full-set S: full-set Exits: %n LoopDispositions: { %outer.loop: Computable, %mid.loop: Invariant, %inner.loop: Invariant }
+; CHECK-NEXT:    %tc = trunc i64 %i to i32
+; CHECK-NEXT:    --> {0,+,1}<%outer.loop> U: full-set S: full-set Exits: (trunc i64 %n to i32) LoopDispositions: { %outer.loop: Computable, %mid.loop: Invariant, %inner.loop: Invariant }
+; CHECK-NEXT:    %0 = zext i32 %tc to i64
+; CHECK-NEXT:    --> (zext i32 {0,+,1}<%outer.loop> to i64) U: [0,4294967296) S: [0,4294967296) Exits: (zext i32 (trunc i64 %n to i32) to i64) LoopDispositions: { %outer.loop: Computable, %mid.loop: Invariant, %inner.loop: Invariant }
+; CHECK-NEXT:    %1 = sext i32 %tc to i64
+; CHECK-NEXT:    --> (sext i32 {0,+,1}<%outer.loop> to i64) U: [-2147483648,2147483648) S: [-2147483648,2147483648) Exits: (sext i32 (trunc i64 %n to i32) to i64) LoopDispositions: { %outer.loop: Computable, %mid.loop: Invariant, %inner.loop: Invariant }
+; CHECK-NEXT:    %indvar1 = phi i64 [ 0, %mid.loop.preheader ], [ %indvar.next2, %mid.latch ]
+; CHECK-NEXT:    --> {0,+,1}<nuw><nsw><%mid.loop> U: [0,1) S: [0,1) Exits: 0 LoopDispositions: { %mid.loop: Computable, %outer.loop: Uniform, %inner.loop: Invariant }
+; CHECK-NEXT:    %k = phi i32 [ %k.next, %mid.latch ], [ 0, %mid.loop.preheader ]
+; CHECK-NEXT:    --> {0,+,1}<nuw><nsw><%mid.loop> U: [0,1) S: [0,1) Exits: 0 LoopDispositions: { %mid.loop: Computable, %outer.loop: Uniform, %inner.loop: Invariant }
+; CHECK-NEXT:    %indvar = phi i64 [ %indvar.next, %inner.loop ], [ 0, %mid.loop ]
+; CHECK-NEXT:    --> {0,+,1}<nuw><nsw><%inner.loop> U: [0,4294967295) S: [0,4294967295) Exits: (zext i32 {-1,+,1}<%outer.loop> to i64) LoopDispositions: { %inner.loop: Computable, %mid.loop: Uniform, %outer.loop: Uniform }
+; CHECK-NEXT:    %2 = mul i64 %indvar1, %1
+; CHECK-NEXT:    --> {0,+,(sext i32 {0,+,1}<%outer.loop> to i64)}<nsw><%mid.loop> U: [0,1) S: [0,1) Exits: {0,+,(sext i32 {0,+,1}<%outer.loop> to i64)}<nuw><nsw><%mid.loop> LoopDispositions: { %inner.loop: Invariant, %mid.loop: Computable, %outer.loop: Variant }
+; CHECK-NEXT:    %mul = mul i32 %k, %tc
+; CHECK-NEXT:    --> {0,+,{0,+,1}<%outer.loop>}<nsw><%mid.loop> U: [0,1) S: [0,1) Exits: {0,+,{0,+,1}<%outer.loop>}<nuw><nsw><%mid.loop> LoopDispositions: { %inner.loop: Invariant, %mid.loop: Computable, %outer.loop: Variant }
+; CHECK-NEXT:    %3 = trunc nuw nsw i64 %2 to i32
+; CHECK-NEXT:    --> {0,+,{0,+,1}<%outer.loop>}<nuw><nsw><%mid.loop> U: [0,1) S: [0,1) Exits: {0,+,{0,+,1}<%outer.loop>}<nuw><nsw><%mid.loop> LoopDispositions: { %inner.loop: Invariant, %mid.loop: Computable, %outer.loop: Variant }
+; CHECK-NEXT:    %gep = getelementptr i32, ptr null, i32 %3
+; CHECK-NEXT:    --> {null,+,(4 * (sext i32 {0,+,1}<%outer.loop> to i64))<nsw>}<nw><%mid.loop> U: [0,1) S: [0,1) Exits: {null,+,(4 * (sext i32 {0,+,1}<%outer.loop> to i64))<nsw>}<nuw><nsw><%mid.loop> LoopDispositions: { %inner.loop: Invariant, %mid.loop: Computable, %outer.loop: Variant }
+; CHECK-NEXT:    %4 = trunc nuw i64 %indvar to i32
+; CHECK-NEXT:    --> {0,+,1}<nuw><%inner.loop> U: [0,-1) S: [0,-1) Exits: {-1,+,1}<%outer.loop> LoopDispositions: { %inner.loop: Computable, %mid.loop: Uniform, %outer.loop: Uniform }
+; CHECK-NEXT:    %arrayidx = getelementptr i32, ptr %gep, i32 %4
+; CHECK-NEXT:    --> ((4 * (sext i32 {0,+,1}<nuw><%inner.loop> to i64))<nsw> + {null,+,(4 * (sext i32 {0,+,1}<%outer.loop> to i64))<nsw>}<nuw><nsw><%mid.loop>) U: [0,-3) S: [-8589934592,8589934589) Exits: {((4 * (sext i32 {-1,+,1}<%outer.loop> to i64))<nsw> + null),+,(4 * (sext i32 {0,+,1}<%outer.loop> to i64))<nsw>}<nw><%mid.loop> LoopDispositions: { %inner.loop: Computable, %mid.loop: Variant, %outer.loop: Variant }
+; CHECK-NEXT:    %indvar.next = add i64 %indvar, 1
+; CHECK-NEXT:    --> {1,+,1}<nuw><nsw><%inner.loop> U: [1,4294967296) S: [1,4294967296) Exits: (1 + (zext i32 {-1,+,1}<%outer.loop> to i64))<nuw><nsw> LoopDispositions: { %inner.loop: Computable, %mid.loop: Uniform, %outer.loop: Uniform }
+; CHECK-NEXT:    %indvar.next2 = add i64 %indvar1, 1
+; CHECK-NEXT:    --> {1,+,1}<nuw><nsw><%mid.loop> U: [1,2) S: [1,2) Exits: 1 LoopDispositions: { %mid.loop: Computable, %outer.loop: Uniform, %inner.loop: Invariant }
+; CHECK-NEXT:    %k.next = add i32 %k, 1
+; CHECK-NEXT:    --> {1,+,1}<nuw><nsw><%mid.loop> U: [1,2) S: [1,2) Exits: 1 LoopDispositions: { %mid.loop: Computable, %outer.loop: Uniform, %inner.loop: Invariant }
+; CHECK-NEXT:    %i.next = add i64 %i, 1
+; CHECK-NEXT:    --> {1,+,1}<nw><%outer.loop> U: full-set S: full-set Exits: (1 + %n) LoopDispositions: { %outer.loop: Computable, %mid.loop: Invariant, %inner.loop: Invariant }
+; CHECK-NEXT:  Determining loop execution counts for: @f
+; CHECK-NEXT:  Loop %inner.loop: backedge-taken count is {-1,+,1}<%outer.loop>
+; CHECK-NEXT:  Loop %inner.loop: constant max backedge-taken count is i32 -2
+; CHECK-NEXT:  Loop %inner.loop: symbolic max backedge-taken count is {-1,+,1}<%outer.loop>
+; CHECK-NEXT:  Loop %inner.loop: Trip multiple is 1
+; CHECK-NEXT:  Loop %mid.loop: backedge-taken count is i1 false
+; CHECK-NEXT:  Loop %mid.loop: constant max backedge-taken count is i1 false
+; CHECK-NEXT:  Loop %mid.loop: symbolic max backedge-taken count is i1 false
+; CHECK-NEXT:  Loop %mid.loop: Trip multiple is 1
+; CHECK-NEXT:  Loop %outer.loop: backedge-taken count is %n
+; CHECK-NEXT:  Loop %outer.loop: constant max backedge-taken count is i64 -1
+; CHECK-NEXT:  Loop %outer.loop: symbolic max backedge-taken count is %n
+; CHECK-NEXT:  Loop %outer.loop: Trip multiple is 1
+;
+entry:
+  br label %outer.loop
+
+outer.loop:                                       ; preds = %outer.latch, %entry
+  %i = phi i64 [ 0, %entry ], [ %i.next, %outer.latch ]
+  %outer.cond = icmp ult i64 %i, %n
+  br i1 %outer.cond, label %outer.body, label %exit
+
+outer.body:                                       ; preds = %outer.loop
+  %tc = trunc i64 %i to i32
+  br i1 false, label %mid.loop, label %outer.latch
+
+mid.loop:                                         ; preds = %mid.latch, %outer.body
+  %k = phi i32 [ %k.next, %mid.latch ], [ 0, %outer.body ]
+  br label %inner.loop
+
+inner.loop:                                       ; preds = %inner.loop, %mid.loop
+  %j = phi i32 [ 0, %mid.loop ], [ %j.next, %inner.loop ]
+  %mul = mul i32 %k, %tc
+  %gep = getelementptr i32, ptr null, i32 %mul
+  %arrayidx = getelementptr i32, ptr %gep, i32 %j
+  store i32 0, ptr %gep, align 4
+  %j.next = add i32 %j, 1
+  %inner.cond = icmp ult i32 %j.next, %tc
+  br i1 %inner.cond, label %inner.loop, label %mid.latch
+
+mid.latch:                                        ; preds = %inner.loop
+  %k.next = add i32 %k, 1
+  %mid.cond = icmp ult i32 %k.next, 0
+  br i1 %mid.cond, label %mid.loop, label %outer.latch
+
+outer.latch:                                      ; preds = %mid.latch, %outer.body
+  %i.next = add i64 %i, 1
+  br label %outer.loop
+
+exit:                                             ; preds = %outer.loop
+  ret i64 0
+}

>From 6bf1e613cb622f3735fb5fb18f463cac632b2b57 Mon Sep 17 00:00:00 2001
From: Arda Serdar Pektezol <arda at pektezol.dev>
Date: Tue, 25 Aug 2026 00:37:53 +0300
Subject: [PATCH 2/2] invalidate scev

---
 llvm/lib/Transforms/Scalar/LoopFlatten.cpp             |  2 ++
 .../Transforms/LoopFlatten/invalidate-scev-pr203176.ll | 10 +++++-----
 2 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/LoopFlatten.cpp b/llvm/lib/Transforms/Scalar/LoopFlatten.cpp
index e48c47f1b4b89..7a6a8ce48cebd 100644
--- a/llvm/lib/Transforms/Scalar/LoopFlatten.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopFlatten.cpp
@@ -873,6 +873,8 @@ static bool CanWidenIV(FlattenInfo &FI, DominatorTree *DT, LoopInfo *LI,
                      true /* HasGuards */, true /* UsePostIncrementRanges */);
     if (!WidePhi)
       return false;
+    SE->forgetLoop(FI.OuterLoop);
+    SE->forgetBlockAndLoopDispositions();
     LLVM_DEBUG(dbgs() << "Created wide phi: "; WidePhi->dump());
     LLVM_DEBUG(dbgs() << "Deleting old phi: "; WideIV.NarrowIV->dump());
     Deleted = RecursivelyDeleteDeadPHINode(WideIV.NarrowIV);
diff --git a/llvm/test/Transforms/LoopFlatten/invalidate-scev-pr203176.ll b/llvm/test/Transforms/LoopFlatten/invalidate-scev-pr203176.ll
index 83bce7418438b..293bd491a5a8d 100644
--- a/llvm/test/Transforms/LoopFlatten/invalidate-scev-pr203176.ll
+++ b/llvm/test/Transforms/LoopFlatten/invalidate-scev-pr203176.ll
@@ -19,7 +19,7 @@ define i64 @f(i64 %n) {
 ; CHECK-NEXT:    %k = phi i32 [ %k.next, %mid.latch ], [ 0, %mid.loop.preheader ]
 ; CHECK-NEXT:    --> {0,+,1}<nuw><nsw><%mid.loop> U: [0,1) S: [0,1) Exits: 0 LoopDispositions: { %mid.loop: Computable, %outer.loop: Uniform, %inner.loop: Invariant }
 ; CHECK-NEXT:    %indvar = phi i64 [ %indvar.next, %inner.loop ], [ 0, %mid.loop ]
-; CHECK-NEXT:    --> {0,+,1}<nuw><nsw><%inner.loop> U: [0,4294967295) S: [0,4294967295) Exits: (zext i32 {-1,+,1}<%outer.loop> to i64) LoopDispositions: { %inner.loop: Computable, %mid.loop: Uniform, %outer.loop: Uniform }
+; CHECK-NEXT:    --> {0,+,1}<nuw><nsw><%inner.loop> U: [0,4294967295) S: [0,4294967295) Exits: (-1 + (zext i32 {0,+,1}<%outer.loop> to i64))<nsw> LoopDispositions: { %inner.loop: Computable, %mid.loop: Uniform, %outer.loop: Uniform }
 ; CHECK-NEXT:    %2 = mul i64 %indvar1, %1
 ; CHECK-NEXT:    --> {0,+,(sext i32 {0,+,1}<%outer.loop> to i64)}<nsw><%mid.loop> U: [0,1) S: [0,1) Exits: {0,+,(sext i32 {0,+,1}<%outer.loop> to i64)}<nuw><nsw><%mid.loop> LoopDispositions: { %inner.loop: Invariant, %mid.loop: Computable, %outer.loop: Variant }
 ; CHECK-NEXT:    %mul = mul i32 %k, %tc
@@ -33,7 +33,7 @@ define i64 @f(i64 %n) {
 ; CHECK-NEXT:    %arrayidx = getelementptr i32, ptr %gep, i32 %4
 ; CHECK-NEXT:    --> ((4 * (sext i32 {0,+,1}<nuw><%inner.loop> to i64))<nsw> + {null,+,(4 * (sext i32 {0,+,1}<%outer.loop> to i64))<nsw>}<nuw><nsw><%mid.loop>) U: [0,-3) S: [-8589934592,8589934589) Exits: {((4 * (sext i32 {-1,+,1}<%outer.loop> to i64))<nsw> + null),+,(4 * (sext i32 {0,+,1}<%outer.loop> to i64))<nsw>}<nw><%mid.loop> LoopDispositions: { %inner.loop: Computable, %mid.loop: Variant, %outer.loop: Variant }
 ; CHECK-NEXT:    %indvar.next = add i64 %indvar, 1
-; CHECK-NEXT:    --> {1,+,1}<nuw><nsw><%inner.loop> U: [1,4294967296) S: [1,4294967296) Exits: (1 + (zext i32 {-1,+,1}<%outer.loop> to i64))<nuw><nsw> LoopDispositions: { %inner.loop: Computable, %mid.loop: Uniform, %outer.loop: Uniform }
+; CHECK-NEXT:    --> {1,+,1}<nuw><nsw><%inner.loop> U: [1,4294967296) S: [1,4294967296) Exits: (zext i32 {0,+,1}<%outer.loop> to i64) LoopDispositions: { %inner.loop: Computable, %mid.loop: Uniform, %outer.loop: Uniform }
 ; CHECK-NEXT:    %indvar.next2 = add i64 %indvar1, 1
 ; CHECK-NEXT:    --> {1,+,1}<nuw><nsw><%mid.loop> U: [1,2) S: [1,2) Exits: 1 LoopDispositions: { %mid.loop: Computable, %outer.loop: Uniform, %inner.loop: Invariant }
 ; CHECK-NEXT:    %k.next = add i32 %k, 1
@@ -41,9 +41,9 @@ define i64 @f(i64 %n) {
 ; CHECK-NEXT:    %i.next = add i64 %i, 1
 ; CHECK-NEXT:    --> {1,+,1}<nw><%outer.loop> U: full-set S: full-set Exits: (1 + %n) LoopDispositions: { %outer.loop: Computable, %mid.loop: Invariant, %inner.loop: Invariant }
 ; CHECK-NEXT:  Determining loop execution counts for: @f
-; CHECK-NEXT:  Loop %inner.loop: backedge-taken count is {-1,+,1}<%outer.loop>
-; CHECK-NEXT:  Loop %inner.loop: constant max backedge-taken count is i32 -2
-; CHECK-NEXT:  Loop %inner.loop: symbolic max backedge-taken count is {-1,+,1}<%outer.loop>
+; CHECK-NEXT:  Loop %inner.loop: backedge-taken count is (-1 + (zext i32 {0,+,1}<%outer.loop> to i64))<nsw>
+; CHECK-NEXT:  Loop %inner.loop: constant max backedge-taken count is i64 4294967294
+; CHECK-NEXT:  Loop %inner.loop: symbolic max backedge-taken count is (-1 + (zext i32 {0,+,1}<%outer.loop> to i64))<nsw>
 ; CHECK-NEXT:  Loop %inner.loop: Trip multiple is 1
 ; CHECK-NEXT:  Loop %mid.loop: backedge-taken count is i1 false
 ; CHECK-NEXT:  Loop %mid.loop: constant max backedge-taken count is i1 false



More information about the llvm-commits mailing list