[llvm] [LoopUnroll] Invalidate SCEV after full unrolling (PR #208874)

via llvm-commits llvm-commits at lists.llvm.org
Tue Jul 14 07:37:27 PDT 2026


https://github.com/Michael-Chen-NJU updated https://github.com/llvm/llvm-project/pull/208874

>From 47564d113337bdc85e98ef330f6f2574b0bef9ca Mon Sep 17 00:00:00 2001
From: Michael-Chen-NJU <2802328816 at qq.com>
Date: Sat, 11 Jul 2026 11:39:44 +0800
Subject: [PATCH 1/5] [LoopUnroll] Invalidate SCEV after full unrolling

---
 llvm/lib/Transforms/Utils/LoopUnroll.cpp      |  5 +
 .../full-unroll-scev-invalidation.ll          | 93 +++++++++++++++++++
 2 files changed, 98 insertions(+)
 create mode 100644 llvm/test/Transforms/LoopUnroll/full-unroll-scev-invalidation.ll

diff --git a/llvm/lib/Transforms/Utils/LoopUnroll.cpp b/llvm/lib/Transforms/Utils/LoopUnroll.cpp
index d00de52e1cb0b..293d1b95f111f 100644
--- a/llvm/lib/Transforms/Utils/LoopUnroll.cpp
+++ b/llvm/lib/Transforms/Utils/LoopUnroll.cpp
@@ -1038,6 +1038,11 @@ llvm::UnrollLoop(Loop *L, UnrollLoopOptions ULO, LoopInfo *LI,
     if (ULO.ForgetAllSCEV)
       SE->forgetAllLoops();
     else {
+      // Unrolling rewrites values in the loop body, so drop cached SCEV
+      // expressions for them before later exit-count queries reuse them.
+      for (BasicBlock *BB : L->blocks())
+        for (Instruction &I : *BB)
+          SE->forgetValue(&I);
       SE->forgetTopmostLoop(L);
       SE->forgetBlockAndLoopDispositions();
     }
diff --git a/llvm/test/Transforms/LoopUnroll/full-unroll-scev-invalidation.ll b/llvm/test/Transforms/LoopUnroll/full-unroll-scev-invalidation.ll
new file mode 100644
index 0000000000000..dfd984742d5b2
--- /dev/null
+++ b/llvm/test/Transforms/LoopUnroll/full-unroll-scev-invalidation.ll
@@ -0,0 +1,93 @@
+; RUN: opt -S -passes='loop(loop-idiom,indvars,loop-deletion,loop-unroll-full,loop-idiom,indvars),simplifycfg,instcombine' %s | 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"
+
+ at g0 = global i64 0, align 8
+ at g15 = global i64 0, align 8
+ at g14 = global i64 0, align 8
+ at g5 = global i64 0, align 8
+ at f4_c7 = global i8 0, align 1
+ at g8 = global i64 0, align 8
+ at f4_c15 = global i8 0, align 1
+ at f4_c8 = global i8 0, align 1
+ at __chk = global i64 0, align 8
+
+define void @f4() {
+; CHECK-LABEL: define void @f4(
+; CHECK-NOT: ret void
+; CHECK: store i64 5, ptr @__chk
+entry:
+  %0 = load i64, ptr @g0, align 8
+  %cmp.not = icmp eq i64 %0, 908375363948206739
+  br i1 %cmp.not, label %if.end, label %if.then
+
+if.then:
+  store i64 1, ptr @g15, align 8
+  br label %if.end
+
+if.end:
+  %1 = load i64, ptr @g15, align 8
+  store i64 %1, ptr @g14, align 8
+  store i64 %1, ptr @g0, align 8
+  br label %lbl_b5
+
+lbl_b5:
+  %bb13.0 = phi i32 [ 0, %if.end ], [ %bb13.1.lcssa, %if.then8 ]
+  %ov6.0 = phi i1 [ false, %if.end ], [ true, %if.then8 ]
+  br label %lbl_b10
+
+lbl_b10:
+  %2 = phi i8 [ 0, %lbl_b5 ], [ 1, %lbl_b10 ]
+  %3 = phi i64 [ %1, %lbl_b5 ], [ 0, %lbl_b10 ]
+  %bb13.1 = phi i32 [ %bb13.0, %lbl_b5 ], [ %5, %lbl_b10 ]
+  %conv1 = trunc i64 %3 to i32
+  %4 = tail call range(i32 0, 33) i32 @llvm.ctpop.i32(i32 %conv1)
+  %5 = and i32 %4, 1
+  %loadedv = trunc nuw i8 %2 to i1
+  br i1 %loadedv, label %if.then3, label %lbl_b10
+
+if.then3:
+  %.lcssa38 = phi i8 [ %2, %lbl_b10 ]
+  %.lcssa36 = phi i64 [ %3, %lbl_b10 ]
+  %bb13.1.lcssa = phi i32 [ %bb13.1, %lbl_b10 ]
+  %.lcssa = phi i32 [ %5, %lbl_b10 ]
+  %tobool4.not = icmp eq i32 %bb13.1.lcssa, 0
+  br i1 %tobool4.not, label %cleanup.loopexit, label %if.then8
+
+if.then8:
+  br i1 %ov6.0, label %if.then10, label %lbl_b5
+
+if.then10:
+  %.lcssa38.lcssa39 = phi i8 [ %.lcssa38, %if.then8 ]
+  %.lcssa36.lcssa37 = phi i64 [ %.lcssa36, %if.then8 ]
+  %bb13.1.lcssa.lcssa35 = phi i32 [ %bb13.1.lcssa, %if.then8 ]
+  %.lcssa.lcssa34 = phi i32 [ %.lcssa, %if.then8 ]
+  %conv2.le.le = zext nneg i32 %.lcssa.lcssa34 to i64
+  %storedv5.le = trunc nuw i32 %bb13.1.lcssa.lcssa35 to i8
+  store i64 %.lcssa36.lcssa37, ptr @g5, align 8
+  store i8 %.lcssa38.lcssa39, ptr @f4_c7, align 1
+  store i64 %conv2.le.le, ptr @g8, align 8
+  store i8 1, ptr @f4_c15, align 1
+  store i8 %storedv5.le, ptr @f4_c8, align 1
+  store i64 5, ptr @__chk, align 8
+  br label %cleanup
+
+cleanup.loopexit:
+  %ov6.0.lcssa = phi i1 [ %ov6.0, %if.then3 ]
+  %.lcssa38.lcssa = phi i8 [ %.lcssa38, %if.then3 ]
+  %.lcssa36.lcssa = phi i64 [ %.lcssa36, %if.then3 ]
+  %.lcssa.lcssa = phi i32 [ %.lcssa, %if.then3 ]
+  %conv2.le.le32 = zext nneg i32 %.lcssa.lcssa to i64
+  %storedv.le30 = zext i1 %ov6.0.lcssa to i8
+  store i64 %.lcssa36.lcssa, ptr @g5, align 8
+  store i8 %.lcssa38.lcssa, ptr @f4_c7, align 1
+  store i64 %conv2.le.le32, ptr @g8, align 8
+  store i8 %storedv.le30, ptr @f4_c15, align 1
+  store i8 0, ptr @f4_c8, align 1
+  br label %cleanup
+
+cleanup:
+  ret void
+}
+
+declare i32 @llvm.ctpop.i32(i32)

>From cb526cc3b77f8620ed2f85d329683ebe4d0302db Mon Sep 17 00:00:00 2001
From: Michael-Chen-NJU <2802328816 at qq.com>
Date: Sun, 12 Jul 2026 11:01:58 +0800
Subject: [PATCH 2/5] Narrow full unroll SCEV invalidation

---
 llvm/lib/Transforms/Utils/LoopUnroll.cpp | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Transforms/Utils/LoopUnroll.cpp b/llvm/lib/Transforms/Utils/LoopUnroll.cpp
index 293d1b95f111f..1f9f340a9cb31 100644
--- a/llvm/lib/Transforms/Utils/LoopUnroll.cpp
+++ b/llvm/lib/Transforms/Utils/LoopUnroll.cpp
@@ -1038,11 +1038,6 @@ llvm::UnrollLoop(Loop *L, UnrollLoopOptions ULO, LoopInfo *LI,
     if (ULO.ForgetAllSCEV)
       SE->forgetAllLoops();
     else {
-      // Unrolling rewrites values in the loop body, so drop cached SCEV
-      // expressions for them before later exit-count queries reuse them.
-      for (BasicBlock *BB : L->blocks())
-        for (Instruction &I : *BB)
-          SE->forgetValue(&I);
       SE->forgetTopmostLoop(L);
       SE->forgetBlockAndLoopDispositions();
     }
@@ -1289,6 +1284,10 @@ llvm::UnrollLoop(Loop *L, UnrollLoopOptions ULO, LoopInfo *LI,
   // Loop over the PHI nodes in the original block, setting incoming values.
   for (PHINode *PN : OrigPHINode) {
     if (CompletelyUnroll) {
+      // The RAUW below disconnects the original PHI from its users.
+      // Invalidate cached SCEVs while the def-use chain is still intact.
+      if (SE)
+        SE->forgetValue(PN);
       PN->replaceAllUsesWith(PN->getIncomingValueForBlock(Preheader));
       PN->eraseFromParent();
     } else if (ULO.Count > 1) {

>From 548b4065a51818bd3db2f3c83b1da7a9125ff37d Mon Sep 17 00:00:00 2001
From: Michael-Chen-NJU <2802328816 at qq.com>
Date: Sun, 12 Jul 2026 20:34:29 +0800
Subject: [PATCH 3/5] Reduce full unroll SCEV invalidation test

---
 .../full-unroll-scev-invalidation.ll          | 84 ++++---------------
 1 file changed, 16 insertions(+), 68 deletions(-)

diff --git a/llvm/test/Transforms/LoopUnroll/full-unroll-scev-invalidation.ll b/llvm/test/Transforms/LoopUnroll/full-unroll-scev-invalidation.ll
index dfd984742d5b2..bbff6d6748ac8 100644
--- a/llvm/test/Transforms/LoopUnroll/full-unroll-scev-invalidation.ll
+++ b/llvm/test/Transforms/LoopUnroll/full-unroll-scev-invalidation.ll
@@ -1,92 +1,40 @@
-; RUN: opt -S -passes='loop(loop-idiom,indvars,loop-deletion,loop-unroll-full,loop-idiom,indvars),simplifycfg,instcombine' %s | FileCheck %s
+; RUN: opt -S -passes='loop(indvars,loop-unroll-full,indvars),simplifycfg' %s | 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"
 
- at g0 = global i64 0, align 8
- at g15 = global i64 0, align 8
- at g14 = global i64 0, align 8
- at g5 = global i64 0, align 8
- at f4_c7 = global i8 0, align 1
- at g8 = global i64 0, align 8
- at f4_c15 = global i8 0, align 1
- at f4_c8 = global i8 0, align 1
- at __chk = global i64 0, align 8
+ at __chk = external global i64
 
-define void @f4() {
+define void @f4(i64 %0) {
 ; CHECK-LABEL: define void @f4(
-; CHECK-NOT: ret void
 ; CHECK: store i64 5, ptr @__chk
 entry:
-  %0 = load i64, ptr @g0, align 8
-  %cmp.not = icmp eq i64 %0, 908375363948206739
-  br i1 %cmp.not, label %if.end, label %if.then
-
-if.then:
-  store i64 1, ptr @g15, align 8
-  br label %if.end
-
-if.end:
-  %1 = load i64, ptr @g15, align 8
-  store i64 %1, ptr @g14, align 8
-  store i64 %1, ptr @g0, align 8
   br label %lbl_b5
 
 lbl_b5:
-  %bb13.0 = phi i32 [ 0, %if.end ], [ %bb13.1.lcssa, %if.then8 ]
-  %ov6.0 = phi i1 [ false, %if.end ], [ true, %if.then8 ]
   br label %lbl_b10
 
 lbl_b10:
-  %2 = phi i8 [ 0, %lbl_b5 ], [ 1, %lbl_b10 ]
-  %3 = phi i64 [ %1, %lbl_b5 ], [ 0, %lbl_b10 ]
-  %bb13.1 = phi i32 [ %bb13.0, %lbl_b5 ], [ %5, %lbl_b10 ]
-  %conv1 = trunc i64 %3 to i32
-  %4 = tail call range(i32 0, 33) i32 @llvm.ctpop.i32(i32 %conv1)
-  %5 = and i32 %4, 1
-  %loadedv = trunc nuw i8 %2 to i1
+  %1 = phi i8 [ 0, %lbl_b5 ], [ 1, %lbl_b10 ]
+  %2 = phi i64 [ %0, %lbl_b5 ], [ 0, %lbl_b10 ]
+  %bb13.1 = phi i32 [ 0, %lbl_b5 ], [ %3, %lbl_b10 ]
+  %conv1 = trunc i64 %2 to i32
+  %3 = tail call i32 @llvm.ctpop.i32(i32 %conv1)
+  %loadedv = trunc i8 %1 to i1
   br i1 %loadedv, label %if.then3, label %lbl_b10
 
 if.then3:
-  %.lcssa38 = phi i8 [ %2, %lbl_b10 ]
-  %.lcssa36 = phi i64 [ %3, %lbl_b10 ]
-  %bb13.1.lcssa = phi i32 [ %bb13.1, %lbl_b10 ]
-  %.lcssa = phi i32 [ %5, %lbl_b10 ]
-  %tobool4.not = icmp eq i32 %bb13.1.lcssa, 0
-  br i1 %tobool4.not, label %cleanup.loopexit, label %if.then8
+  %tobool4.not = icmp eq i32 %bb13.1, 0
+  br i1 %tobool4.not, label %common.ret, label %if.then8
 
 if.then8:
-  br i1 %ov6.0, label %if.then10, label %lbl_b5
+  br i1 true, label %if.then10, label %lbl_b5
+
+common.ret:
+  ret void
 
 if.then10:
-  %.lcssa38.lcssa39 = phi i8 [ %.lcssa38, %if.then8 ]
-  %.lcssa36.lcssa37 = phi i64 [ %.lcssa36, %if.then8 ]
-  %bb13.1.lcssa.lcssa35 = phi i32 [ %bb13.1.lcssa, %if.then8 ]
-  %.lcssa.lcssa34 = phi i32 [ %.lcssa, %if.then8 ]
-  %conv2.le.le = zext nneg i32 %.lcssa.lcssa34 to i64
-  %storedv5.le = trunc nuw i32 %bb13.1.lcssa.lcssa35 to i8
-  store i64 %.lcssa36.lcssa37, ptr @g5, align 8
-  store i8 %.lcssa38.lcssa39, ptr @f4_c7, align 1
-  store i64 %conv2.le.le, ptr @g8, align 8
-  store i8 1, ptr @f4_c15, align 1
-  store i8 %storedv5.le, ptr @f4_c8, align 1
+  %.lcssa.lcssa34 = phi i32 [ %3, %if.then8 ]
   store i64 5, ptr @__chk, align 8
-  br label %cleanup
-
-cleanup.loopexit:
-  %ov6.0.lcssa = phi i1 [ %ov6.0, %if.then3 ]
-  %.lcssa38.lcssa = phi i8 [ %.lcssa38, %if.then3 ]
-  %.lcssa36.lcssa = phi i64 [ %.lcssa36, %if.then3 ]
-  %.lcssa.lcssa = phi i32 [ %.lcssa, %if.then3 ]
-  %conv2.le.le32 = zext nneg i32 %.lcssa.lcssa to i64
-  %storedv.le30 = zext i1 %ov6.0.lcssa to i8
-  store i64 %.lcssa36.lcssa, ptr @g5, align 8
-  store i8 %.lcssa38.lcssa, ptr @f4_c7, align 1
-  store i64 %conv2.le.le32, ptr @g8, align 8
-  store i8 %storedv.le30, ptr @f4_c15, align 1
-  store i8 0, ptr @f4_c8, align 1
-  br label %cleanup
-
-cleanup:
   ret void
 }
 

>From c2ca830f1b2538ac01681172eb7f2c92f9940b6c Mon Sep 17 00:00:00 2001
From: Michael-Chen-NJU <2802328816 at qq.com>
Date: Sun, 12 Jul 2026 23:43:42 +0800
Subject: [PATCH 4/5] Further clean up full unroll SCEV test

---
 .../Transforms/LoopUnroll/full-unroll-scev-invalidation.ll  | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/llvm/test/Transforms/LoopUnroll/full-unroll-scev-invalidation.ll b/llvm/test/Transforms/LoopUnroll/full-unroll-scev-invalidation.ll
index bbff6d6748ac8..b439cb27edc47 100644
--- a/llvm/test/Transforms/LoopUnroll/full-unroll-scev-invalidation.ll
+++ b/llvm/test/Transforms/LoopUnroll/full-unroll-scev-invalidation.ll
@@ -1,7 +1,5 @@
 ; RUN: opt -S -passes='loop(indvars,loop-unroll-full,indvars),simplifycfg' %s | 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"
-
 @__chk = external global i64
 
 define void @f4(i64 %0) {
@@ -18,7 +16,7 @@ lbl_b10:
   %2 = phi i64 [ %0, %lbl_b5 ], [ 0, %lbl_b10 ]
   %bb13.1 = phi i32 [ 0, %lbl_b5 ], [ %3, %lbl_b10 ]
   %conv1 = trunc i64 %2 to i32
-  %3 = tail call i32 @llvm.ctpop.i32(i32 %conv1)
+  %3 = call i32 @llvm.ctpop.i32(i32 %conv1)
   %loadedv = trunc i8 %1 to i1
   br i1 %loadedv, label %if.then3, label %lbl_b10
 
@@ -34,7 +32,7 @@ common.ret:
 
 if.then10:
   %.lcssa.lcssa34 = phi i32 [ %3, %if.then8 ]
-  store i64 5, ptr @__chk, align 8
+  store i64 5, ptr @__chk
   ret void
 }
 

>From 5e7e48a11aeb9c62d51fdcb8b6fe31887f9f93d2 Mon Sep 17 00:00:00 2001
From: Michael-Chen-NJU <2802328816 at qq.com>
Date: Tue, 14 Jul 2026 22:37:01 +0800
Subject: [PATCH 5/5] Update full unroll SCEV test checks

---
 .../LoopUnroll/full-unroll-scev-invalidation.ll  | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/llvm/test/Transforms/LoopUnroll/full-unroll-scev-invalidation.ll b/llvm/test/Transforms/LoopUnroll/full-unroll-scev-invalidation.ll
index b439cb27edc47..c925dc86c815e 100644
--- a/llvm/test/Transforms/LoopUnroll/full-unroll-scev-invalidation.ll
+++ b/llvm/test/Transforms/LoopUnroll/full-unroll-scev-invalidation.ll
@@ -1,10 +1,22 @@
-; RUN: opt -S -passes='loop(indvars,loop-unroll-full,indvars),simplifycfg' %s | FileCheck %s
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S -passes='loop(indvars,loop-unroll-full),simplifycfg' %s | FileCheck %s
 
 @__chk = external global i64
 
 define void @f4(i64 %0) {
 ; CHECK-LABEL: define void @f4(
-; CHECK: store i64 5, ptr @__chk
+; CHECK-SAME: i64 [[TMP0:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[CONV1:%.*]] = trunc i64 [[TMP0]] to i32
+; CHECK-NEXT:    [[TMP1:%.*]] = call i32 @llvm.ctpop.i32(i32 [[CONV1]])
+; CHECK-NEXT:    [[TOBOOL4_NOT:%.*]] = icmp eq i32 [[TMP1]], 0
+; CHECK-NEXT:    br i1 [[TOBOOL4_NOT]], label %[[COMMON_RET1:.*]], label %[[IF_THEN8:.*]]
+; CHECK:       [[COMMON_RET1]]:
+; CHECK-NEXT:    ret void
+; CHECK:       [[IF_THEN8]]:
+; CHECK-NEXT:    store i64 5, ptr @__chk, align 4
+; CHECK-NEXT:    br label %[[COMMON_RET1]]
+;
 entry:
   br label %lbl_b5
 



More information about the llvm-commits mailing list