[llvm] [DeadStoreElimination] Visit blocks in RPO traversal when initializing DSEState (PR #137815)
Antonio Frighetto via llvm-commits
llvm-commits at lists.llvm.org
Tue Apr 29 07:06:29 PDT 2025
https://github.com/antoniofrighetto updated https://github.com/llvm/llvm-project/pull/137815
>From 96f27233a7051530e3a021ff9f3c535ecf0e1d2a Mon Sep 17 00:00:00 2001
From: Antonio Frighetto <me at antoniofrighetto.com>
Date: Tue, 29 Apr 2025 14:59:40 +0200
Subject: [PATCH 1/3] [DeadStoreElimination] Introduce test for PR137815 (NFC)
---
.../traversal-order-end-of-function.ll | 37 +++++++++++++++++++
1 file changed, 37 insertions(+)
create mode 100644 llvm/test/Transforms/DeadStoreElimination/traversal-order-end-of-function.ll
diff --git a/llvm/test/Transforms/DeadStoreElimination/traversal-order-end-of-function.ll b/llvm/test/Transforms/DeadStoreElimination/traversal-order-end-of-function.ll
new file mode 100644
index 0000000000000..4c81fc8871711
--- /dev/null
+++ b/llvm/test/Transforms/DeadStoreElimination/traversal-order-end-of-function.ll
@@ -0,0 +1,37 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -passes=dse -S -debug -disable-output %s 2>&1 | FileCheck %s
+; REQUIRES: asserts
+
+; Ensure MemoryDefs are visited bottom-up in eliminateDeadWritesAtEndOfFunction.
+
+; CHECK: Trying to eliminate MemoryDefs at the end of the function
+; CHECK-NEXT: Check if def 1 = MemoryDef(liveOnEntry) ( call void @llvm.memset.p0.i64(ptr noundef %array, i8 0, i64 100, i1 false)) is at the end the function
+; CHECK-NEXT: ... hit read clobber %array.curr = load i32, ptr %array.idx, align 4.
+; CHECK-NEXT: Check if def 2 = MemoryDef(4) ( store i32 %sum, ptr %array.idx, align 4) is at the end the function
+define void @reverse_post_order_traversal_visit_test(ptr noundef %A) {
+entry:
+ %array = alloca [25 x i32], align 16
+ call void @llvm.memset.p0.i64(ptr noundef %array, i8 0, i64 100, i1 false)
+ br label %loop
+
+loop: ; preds = %loop, %entry
+ %array.prev = phi i32 [ 0, %entry ], [ %sum, %loop ]
+ %iv = phi i64 [ 1, %entry ], [ %iv.next, %loop ]
+ %A.idx = getelementptr inbounds i32, ptr %A, i64 %iv
+ %A.curr = load i32, ptr %A.idx, align 4
+ %A.curr.sum.array.prev = add nsw i32 %A.curr, %array.prev
+ %array.idx = getelementptr inbounds [25 x i32], ptr %array, i64 0, i64 %iv
+ %array.curr = load i32, ptr %array.idx, align 4
+ %sum = add nsw i32 %A.curr.sum.array.prev, %array.curr
+ store i32 %sum, ptr %array.idx, align 4
+ %iv.next = add nuw nsw i64 %iv, 1
+ %cond = icmp eq i64 %iv.next, 25
+ br i1 %cond, label %exit, label %loop
+
+exit: ; preds = %loop
+ call void @opaque(ptr noundef %array.idx)
+ ret void
+}
+
+declare void @llvm.memset.p0.i64(ptr, i8, i64, i1)
+declare void @opaque(ptr)
>From 35a54ee5e6a7f0c634eaa766155350a1e967f66e Mon Sep 17 00:00:00 2001
From: Antonio Frighetto <me at antoniofrighetto.com>
Date: Tue, 29 Apr 2025 15:42:20 +0200
Subject: [PATCH 2/3] [DeadStoreElimination] Visit blocks in RPO traversal when
initializing DSEState
`eliminateDeadWritesAtEndOfFunction` is supposed to walk MemoryDefs
bottom-up though this may not always be the case as such definitions
are gathered post-order. Switch to reverse post-order when collecting
blocks, thus ensuring last defs are processed first.
---
.../Transforms/Scalar/DeadStoreElimination.cpp | 17 +++++++++--------
.../traversal-order-end-of-function.ll | 3 ++-
2 files changed, 11 insertions(+), 9 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
index 0521df8b818cf..234ba0151ac9f 100644
--- a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
@@ -970,9 +970,9 @@ struct DSEState {
DenseMap<const Value *, bool> InvisibleToCallerAfterRet;
// Keep track of blocks with throwing instructions not modeled in MemorySSA.
SmallPtrSet<BasicBlock *, 16> ThrowingBlocks;
- // Post-order numbers for each basic block. Used to figure out if memory
+ // Reverse post-order numbers for each basic block. Used to figure out if memory
// accesses are executed before another access.
- DenseMap<BasicBlock *, unsigned> PostOrderNumbers;
+ DenseMap<BasicBlock *, unsigned> RPONumbers;
/// Keep track of instructions (partly) overlapping with killing MemoryDefs per
/// basic block.
@@ -1001,9 +1001,10 @@ struct DSEState {
PDT(PDT), TLI(TLI), DL(F.getDataLayout()), LI(LI) {
// Collect blocks with throwing instructions not modeled in MemorySSA and
// alloc-like objects.
- unsigned PO = 0;
- for (BasicBlock *BB : post_order(&F)) {
- PostOrderNumbers[BB] = PO++;
+ unsigned RPO = 0;
+ ReversePostOrderTraversal<Function *> RPOT(&F);
+ for (BasicBlock *BB : RPOT) {
+ RPONumbers[BB] = RPO++;
for (Instruction &I : *BB) {
MemoryAccess *MA = MSSA.getMemoryAccess(&I);
if (I.mayThrow() && !MA)
@@ -1761,8 +1762,8 @@ struct DSEState {
if (MemoryDef *UseDef = dyn_cast<MemoryDef>(UseAccess)) {
if (isCompleteOverwrite(MaybeDeadLoc, MaybeDeadI, UseInst)) {
BasicBlock *MaybeKillingBlock = UseInst->getParent();
- if (PostOrderNumbers.find(MaybeKillingBlock)->second <
- PostOrderNumbers.find(MaybeDeadAccess->getBlock())->second) {
+ if (RPONumbers.find(MaybeKillingBlock)->second >
+ RPONumbers.find(MaybeDeadAccess->getBlock())->second) {
if (!isInvisibleToCallerAfterRet(KillingUndObj)) {
LLVM_DEBUG(dbgs()
<< " ... found killing def " << *UseInst << "\n");
@@ -2451,7 +2452,7 @@ DSEState::eliminateDeadDefs(const MemoryLocationWrapper &KillingLocWrapper) {
// We only consider incoming MemoryAccesses that come before the
// MemoryPhi. Otherwise we could discover candidates that do not
// strictly dominate our starting def.
- if (PostOrderNumbers[IncomingBlock] > PostOrderNumbers[PhiBlock])
+ if (RPONumbers[IncomingBlock] < RPONumbers[PhiBlock])
ToCheck.insert(IncomingAccess);
}
continue;
diff --git a/llvm/test/Transforms/DeadStoreElimination/traversal-order-end-of-function.ll b/llvm/test/Transforms/DeadStoreElimination/traversal-order-end-of-function.ll
index 4c81fc8871711..900f6671637f2 100644
--- a/llvm/test/Transforms/DeadStoreElimination/traversal-order-end-of-function.ll
+++ b/llvm/test/Transforms/DeadStoreElimination/traversal-order-end-of-function.ll
@@ -5,9 +5,10 @@
; Ensure MemoryDefs are visited bottom-up in eliminateDeadWritesAtEndOfFunction.
; CHECK: Trying to eliminate MemoryDefs at the end of the function
+; CHECK-NEXT: Check if def 2 = MemoryDef(4) ( store i32 %sum, ptr %array.idx, align 4) is at the end the function
+; CHECK-NEXT: ... hit read clobber call void @opaque(ptr noundef %array.idx).
; CHECK-NEXT: Check if def 1 = MemoryDef(liveOnEntry) ( call void @llvm.memset.p0.i64(ptr noundef %array, i8 0, i64 100, i1 false)) is at the end the function
; CHECK-NEXT: ... hit read clobber %array.curr = load i32, ptr %array.idx, align 4.
-; CHECK-NEXT: Check if def 2 = MemoryDef(4) ( store i32 %sum, ptr %array.idx, align 4) is at the end the function
define void @reverse_post_order_traversal_visit_test(ptr noundef %A) {
entry:
%array = alloca [25 x i32], align 16
>From 9fb299865d002469c275e437dc306d69eaede090 Mon Sep 17 00:00:00 2001
From: Antonio Frighetto <me at antoniofrighetto.com>
Date: Tue, 29 Apr 2025 16:05:32 +0200
Subject: [PATCH 3/3] !fixup clang-format
---
llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
index 234ba0151ac9f..ab72e7578fe94 100644
--- a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
@@ -970,8 +970,8 @@ struct DSEState {
DenseMap<const Value *, bool> InvisibleToCallerAfterRet;
// Keep track of blocks with throwing instructions not modeled in MemorySSA.
SmallPtrSet<BasicBlock *, 16> ThrowingBlocks;
- // Reverse post-order numbers for each basic block. Used to figure out if memory
- // accesses are executed before another access.
+ // Reverse post-order numbers for each basic block. Used to figure out if
+ // memory accesses are executed before another access.
DenseMap<BasicBlock *, unsigned> RPONumbers;
/// Keep track of instructions (partly) overlapping with killing MemoryDefs per
More information about the llvm-commits
mailing list