[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