[llvm-branch-commits] [llvm] [DSE] Make DSE eliminate stores to objects with a sized dead_on_return (PR #173694)

Aiden Grossman via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Wed Jan 14 09:22:35 PST 2026


https://github.com/boomanaiden154 updated https://github.com/llvm/llvm-project/pull/173694

>From ba9d3c13c2efe720833d62f1c6cbc150eb399ceb Mon Sep 17 00:00:00 2001
From: Aiden Grossman <aidengrossman at google.com>
Date: Sat, 27 Dec 2025 02:05:36 +0000
Subject: [PATCH 1/2] formatting

Created using spr 1.3.7
---
 llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
index 25d473b5beb72..78734beac4bbe 100644
--- a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
@@ -1214,7 +1214,8 @@ struct DSEState {
     return OW_None;
   }
 
-  bool isInvisibleToCallerAfterRet(const Value *V, const Value *Ptr, const LocationSize StoreSize) {
+  bool isInvisibleToCallerAfterRet(const Value *V, const Value *Ptr,
+                                   const LocationSize StoreSize) {
     if (isa<AllocaInst>(V))
       return true;
 
@@ -1774,7 +1775,8 @@ struct DSEState {
           BasicBlock *MaybeKillingBlock = UseInst->getParent();
           if (PostOrderNumbers.find(MaybeKillingBlock)->second <
               PostOrderNumbers.find(MaybeDeadAccess->getBlock())->second) {
-            if (!isInvisibleToCallerAfterRet(KillingUndObj, KillingLoc.Ptr, KillingLoc.Size)) {
+            if (!isInvisibleToCallerAfterRet(KillingUndObj, KillingLoc.Ptr,
+                                             KillingLoc.Size)) {
               LLVM_DEBUG(dbgs()
                          << "    ... found killing def " << *UseInst << "\n");
               KillingDefs.insert(UseInst);
@@ -1792,7 +1794,8 @@ struct DSEState {
     // For accesses to locations visible after the function returns, make sure
     // that the location is dead (=overwritten) along all paths from
     // MaybeDeadAccess to the exit.
-    if (!isInvisibleToCallerAfterRet(KillingUndObj, KillingLoc.Ptr, KillingLoc.Size)) {
+    if (!isInvisibleToCallerAfterRet(KillingUndObj, KillingLoc.Ptr,
+                                     KillingLoc.Size)) {
       SmallPtrSet<BasicBlock *, 16> KillingBlocks;
       for (Instruction *KD : KillingDefs)
         KillingBlocks.insert(KD->getParent());

>From 805d7b8f4c1bb9d394a6d0760bc7de2a274fda30 Mon Sep 17 00:00:00 2001
From: Aiden Grossman <aidengrossman at google.com>
Date: Wed, 14 Jan 2026 17:22:23 +0000
Subject: [PATCH 2/2] feedback

Created using spr 1.3.7
---
 .../Scalar/DeadStoreElimination.cpp           | 27 ++++++++++------
 .../Transforms/DeadStoreElimination/simple.ll | 31 +++++++++++++++++--
 2 files changed, 46 insertions(+), 12 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
index 78734beac4bbe..20436b92412f9 100644
--- a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
@@ -1018,16 +1018,19 @@ struct DSEState {
     // Treat byval, inalloca or dead on return arguments the same as Allocas,
     // stores to them are dead at the end of the function.
     for (Argument &AI : F.args()) {
-      if (AI.hasPassPointeeByValueCopyAttr() ||
-          (AI.getType()->isPointerTy() &&
-           AI.getDeadOnReturnInfo().coversAllReachableMemory()))
+      if (AI.hasPassPointeeByValueCopyAttr()) {
         InvisibleToCallerAfterRet.insert({&AI, true});
-      if (AI.getType()->isPointerTy() &&
-          !AI.getDeadOnReturnInfo().coversAllReachableMemory()) {
-        if (uint64_t DeadOnReturnBytes =
-                AI.getDeadOnReturnInfo().getNumberOfDeadBytes())
-          InvisibleToCallerAfterRetBounded.insert({&AI, DeadOnReturnBytes});
+        continue;
       }
+
+      if (!AI.getType()->isPointerTy())
+        continue;
+
+      const DeadOnReturnInfo &Info = AI.getDeadOnReturnInfo();
+      if (Info.coversAllReachableMemory())
+        InvisibleToCallerAfterRet.insert({&AI, true});
+      else if (uint64_t DeadBytes = Info.getNumberOfDeadBytes())
+        InvisibleToCallerAfterRetBounded.insert({&AI, DeadBytes});
     }
 
     // Collect whether there is any irreducible control flow in the function.
@@ -1225,8 +1228,11 @@ struct DSEState {
       const Value *BaseValue =
           GetPointerBaseWithConstantOffset(Ptr, ValueOffset, DL);
       assert(BaseValue == V);
-      if (ValueOffset + StoreSize.toRaw() <
-          InvisibleToCallerAfterRetBounded[BaseValue])
+      // This store is only invisible after return if we are in bounds of the
+      // range marked dead.
+      if (ValueOffset + StoreSize.getValue() <=
+              InvisibleToCallerAfterRetBounded[BaseValue] &&
+          ValueOffset >= 0)
         return true;
     }
     if (I.second && isInvisibleToCallerOnUnwind(V) && isNoAliasCall(V))
@@ -1899,6 +1905,7 @@ struct DSEState {
               if (CapturedBeforeReturn.erase(UO))
                 ShouldIterateEndOfFunctionDSE = true;
               InvisibleToCallerAfterRet.erase(UO);
+              InvisibleToCallerAfterRetBounded.erase(UO);
             }
           }
         }
diff --git a/llvm/test/Transforms/DeadStoreElimination/simple.ll b/llvm/test/Transforms/DeadStoreElimination/simple.ll
index 7619842ea18cf..855cae3f70259 100644
--- a/llvm/test/Transforms/DeadStoreElimination/simple.ll
+++ b/llvm/test/Transforms/DeadStoreElimination/simple.ll
@@ -901,6 +901,18 @@ define void @test_dead_on_return_oob(ptr dead_on_return(4) %p) {
   ret void
 }
 
+define void @test_dead_on_return_zero_offset(ptr dead_on_return(8) %p) {
+; CHECK-LABEL: @test_dead_on_return_zero_offset(
+; CHECK-NEXT:    [[LOCAL_VAR:%.*]] = alloca ptr, align 8
+; CHECK-NEXT:    call void @opaque(ptr [[LOCAL_VAR]])
+; CHECK-NEXT:    ret void
+;
+  %local.var = alloca ptr
+  call void @opaque(ptr %local.var)
+  store ptr %local.var, ptr %p
+  ret void
+}
+
 define void @test_dead_on_return_inbounds(ptr dead_on_return(16) %p) {
 ; CHECK-LABEL: @test_dead_on_return_inbounds(
 ; CHECK-NEXT:    [[LOCAL_VAR:%.*]] = alloca ptr, align 8
@@ -914,8 +926,8 @@ define void @test_dead_on_return_inbounds(ptr dead_on_return(16) %p) {
   ret void
 }
 
-define void @test_on_return_overlapping_oob(ptr dead_on_return(8) %p) {
-; CHECK-LABEL: @test_on_return_overlapping_oob(
+define void @test_dead_on_return_overlapping_oob(ptr dead_on_return(8) %p) {
+; CHECK-LABEL: @test_dead_on_return_overlapping_oob(
 ; CHECK-NEXT:    [[LOCAL_VAR:%.*]] = alloca ptr, align 8
 ; CHECK-NEXT:    call void @opaque(ptr [[LOCAL_VAR]])
 ; CHECK-NEXT:    [[P1:%.*]] = getelementptr inbounds i8, ptr [[P:%.*]], i64 4
@@ -929,5 +941,20 @@ define void @test_on_return_overlapping_oob(ptr dead_on_return(8) %p) {
   ret void
 }
 
+define void @test_dead_on_return_negative_oob(ptr dead_on_return(8) %p) {
+; CHECK-LABEL: @test_dead_on_return_negative_oob(
+; CHECK-NEXT:    [[LOCAL_VAR:%.*]] = alloca ptr, align 8
+; CHECK-NEXT:    call void @opaque(ptr [[LOCAL_VAR]])
+; CHECK-NEXT:    [[P1:%.*]] = getelementptr inbounds i8, ptr [[P:%.*]], i64 -4
+; CHECK-NEXT:    store ptr [[LOCAL_VAR]], ptr [[P1]], align 8
+; CHECK-NEXT:    ret void
+;
+  %local.var = alloca ptr
+  call void @opaque(ptr %local.var)
+  %p1 = getelementptr inbounds i8, ptr %p, i64 -4
+  store ptr %local.var, ptr %p1
+  ret void
+}
+
 declare void @opaque(ptr)
 declare void @maythrow() memory(none)



More information about the llvm-branch-commits mailing list