[llvm] 0a8ebdb - [MemCpyOpt] Remove handling for lifetime sizes

Nikita Popov via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 5 08:22:24 PDT 2025


Author: Nikita Popov
Date: 2025-08-05T17:22:12+02:00
New Revision: 0a8ebdb2f0adc60d9fc17d6b3af8933841cca50c

URL: https://github.com/llvm/llvm-project/commit/0a8ebdb2f0adc60d9fc17d6b3af8933841cca50c
DIFF: https://github.com/llvm/llvm-project/commit/0a8ebdb2f0adc60d9fc17d6b3af8933841cca50c.diff

LOG: [MemCpyOpt] Remove handling for lifetime sizes

Split out from #150248:

Since #150944 the size passed to lifetime.start/end is considered
meaningless. The lifetime always applies to the whole alloca.

Accordingly, remove checks of the lifetime size from MemCpyOpt.

Added: 
    

Modified: 
    llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp
    llvm/test/Transforms/MemCpyOpt/capturing-func.ll
    llvm/test/Transforms/MemCpyOpt/memcpy-undef.ll
    llvm/test/Transforms/MemCpyOpt/memset-memcpy-oversized.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp b/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp
index 9220abb974d21..79721dc5f39f0 100644
--- a/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp
+++ b/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp
@@ -1010,8 +1010,7 @@ bool MemCpyOptPass::performCallSlotOptzn(Instruction *cpyLoad,
       // Lifetime of srcAlloca ends at lifetime.end.
       if (auto *II = dyn_cast<IntrinsicInst>(&I)) {
         if (II->getIntrinsicID() == Intrinsic::lifetime_end &&
-            II->getArgOperand(1)->stripPointerCasts() == srcAlloca &&
-            cast<ConstantInt>(II->getArgOperand(0))->uge(srcSize))
+            II->getArgOperand(1) == srcAlloca)
           break;
       }
 
@@ -1384,39 +1383,17 @@ bool MemCpyOptPass::processMemSetMemCpyDependence(MemCpyInst *MemCpy,
   return true;
 }
 
-/// Determine whether the pointer V had only undefined content (due to Def) up
-/// to the given Size, either because it was freshly alloca'd or started its
-/// lifetime.
+/// Determine whether the pointer V had only undefined content (due to Def),
+/// either because it was freshly alloca'd or started its lifetime.
 static bool hasUndefContents(MemorySSA *MSSA, BatchAAResults &AA, Value *V,
-                             MemoryDef *Def, Value *Size) {
+                             MemoryDef *Def) {
   if (MSSA->isLiveOnEntryDef(Def))
     return isa<AllocaInst>(getUnderlyingObject(V));
 
-  if (auto *II = dyn_cast_or_null<IntrinsicInst>(Def->getMemoryInst())) {
-    if (II->getIntrinsicID() == Intrinsic::lifetime_start) {
-      auto *LTSize = cast<ConstantInt>(II->getArgOperand(0));
-
-      if (auto *CSize = dyn_cast<ConstantInt>(Size)) {
-        if (AA.isMustAlias(V, II->getArgOperand(1)) &&
-            LTSize->getZExtValue() >= CSize->getZExtValue())
-          return true;
-      }
-
-      // If the lifetime.start covers a whole alloca (as it almost always
-      // does) and we're querying a pointer based on that alloca, then we know
-      // the memory is definitely undef, regardless of how exactly we alias.
-      // The size also doesn't matter, as an out-of-bounds access would be UB.
-      if (auto *Alloca = dyn_cast<AllocaInst>(getUnderlyingObject(V))) {
-        if (getUnderlyingObject(II->getArgOperand(1)) == Alloca) {
-          const DataLayout &DL = Alloca->getDataLayout();
-          if (std::optional<TypeSize> AllocaSize =
-                  Alloca->getAllocationSize(DL))
-            if (*AllocaSize == LTSize->getValue())
-              return true;
-        }
-      }
-    }
-  }
+  if (auto *II = dyn_cast_or_null<IntrinsicInst>(Def->getMemoryInst()))
+    if (II->getIntrinsicID() == Intrinsic::lifetime_start)
+      if (auto *Alloca = dyn_cast<AllocaInst>(getUnderlyingObject(V)))
+        return II->getArgOperand(1) == Alloca;
 
   return false;
 }
@@ -1428,13 +1405,12 @@ static bool hasUndefContents(MemorySSA *MSSA, BatchAAResults &AA, Value *V,
 // which cannot deal with offsets), we use the full 0..CopySize range.
 static bool overreadUndefContents(MemorySSA *MSSA, MemCpyInst *MemCpy,
                                   MemIntrinsic *MemSrc, BatchAAResults &BAA) {
-  Value *CopySize = MemCpy->getLength();
   MemoryLocation MemCpyLoc = MemoryLocation::getForSource(MemCpy);
   MemoryUseOrDef *MemSrcAccess = MSSA->getMemoryAccess(MemSrc);
   MemoryAccess *Clobber = MSSA->getWalker()->getClobberingMemoryAccess(
       MemSrcAccess->getDefiningAccess(), MemCpyLoc, BAA);
   if (auto *MD = dyn_cast<MemoryDef>(Clobber))
-    if (hasUndefContents(MSSA, BAA, MemCpy->getSource(), MD, CopySize))
+    if (hasUndefContents(MSSA, BAA, MemCpy->getSource(), MD))
       return true;
   return false;
 }
@@ -1836,7 +1812,7 @@ bool MemCpyOptPass::processMemCpy(MemCpyInst *M, BasicBlock::iterator &BBI) {
       }
     }
 
-    if (hasUndefContents(MSSA, BAA, M->getSource(), MD, M->getLength())) {
+    if (hasUndefContents(MSSA, BAA, M->getSource(), MD)) {
       LLVM_DEBUG(dbgs() << "Removed memcpy from undef\n");
       eraseInstruction(M);
       ++NumMemCpyInstr;

diff  --git a/llvm/test/Transforms/MemCpyOpt/capturing-func.ll b/llvm/test/Transforms/MemCpyOpt/capturing-func.ll
index 627dca5ab673f..47c435809f92b 100644
--- a/llvm/test/Transforms/MemCpyOpt/capturing-func.ll
+++ b/llvm/test/Transforms/MemCpyOpt/capturing-func.ll
@@ -67,28 +67,6 @@ define void @test_lifetime_end() {
   ret void
 }
 
-; Lifetime of %ptr2 does not end, because of size mismatch.
-define void @test_lifetime_not_end() {
-; CHECK-LABEL: define {{[^@]+}}@test_lifetime_not_end() {
-; CHECK-NEXT:    [[PTR1:%.*]] = alloca i8, align 1
-; CHECK-NEXT:    [[PTR2:%.*]] = alloca i8, align 1
-; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 1, ptr [[PTR2]])
-; CHECK-NEXT:    call void @foo(ptr [[PTR2]])
-; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr [[PTR1]], ptr [[PTR2]], i32 1, i1 false)
-; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 0, ptr [[PTR2]])
-; CHECK-NEXT:    call void @foo(ptr [[PTR1]])
-; CHECK-NEXT:    ret void
-;
-  %ptr1 = alloca i8
-  %ptr2 = alloca i8
-  call void @llvm.lifetime.start.p0(i64 1, ptr %ptr2)
-  call void @foo(ptr %ptr2)
-  call void @llvm.memcpy.p0.p0.i32(ptr %ptr1, ptr %ptr2, i32 1, i1 false)
-  call void @llvm.lifetime.end.p0(i64 0, ptr %ptr2)
-  call void @foo(ptr %ptr1)
-  ret void
-}
-
 ; Lifetime of %ptr2 ends before any potential use of the capture because we
 ; return from the function.
 define void @test_function_end() {

diff  --git a/llvm/test/Transforms/MemCpyOpt/memcpy-undef.ll b/llvm/test/Transforms/MemCpyOpt/memcpy-undef.ll
index 816e10324179a..84253dca16bae 100644
--- a/llvm/test/Transforms/MemCpyOpt/memcpy-undef.ll
+++ b/llvm/test/Transforms/MemCpyOpt/memcpy-undef.ll
@@ -38,20 +38,6 @@ define void @test2(ptr sret(i8) noalias nocapture %out) nounwind noinline ssp uw
   ret void
 }
 
-; Check that the memcpy is not removed.
-define void @test3(ptr sret(i8) noalias nocapture %out) nounwind noinline ssp uwtable {
-; CHECK-LABEL: @test3(
-; CHECK-NEXT:    [[IN:%.*]] = alloca i64, align 8
-; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 4, ptr [[IN]])
-; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr [[OUT:%.*]], ptr [[IN]], i64 8, i1 false)
-; CHECK-NEXT:    ret void
-;
-  %in = alloca i64
-  call void @llvm.lifetime.start.p0(i64 4, ptr %in)
-  call void @llvm.memcpy.p0.p0.i64(ptr %out, ptr %in, i64 8, i1 false)
-  ret void
-}
-
 ; Check that the memcpy is not removed.
 define void @test_lifetime_may_alias(ptr %src, ptr %dst) {
 ; CHECK-LABEL: @test_lifetime_may_alias(
@@ -96,38 +82,6 @@ define void @test_lifetime_partial_alias_2(ptr noalias %dst) {
   ret void
 }
 
-; lifetime.start on part of alloca, copy in range.
-define void @test_lifetime_partial_alias_3(ptr noalias %dst) {
-; CHECK-LABEL: @test_lifetime_partial_alias_3(
-; CHECK-NEXT:    [[A:%.*]] = alloca [16 x i8], align 1
-; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 12, ptr [[A]])
-; CHECK-NEXT:    [[GEP:%.*]] = getelementptr i8, ptr [[A]], i64 8
-; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr [[DST:%.*]], ptr [[GEP]], i64 4, i1 false)
-; CHECK-NEXT:    ret void
-;
-  %a = alloca [16 x i8]
-  call void @llvm.lifetime.start.p0(i64 12, ptr %a)
-  %gep = getelementptr i8, ptr %a, i64 8
-  call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr %gep, i64 4, i1 false)
-  ret void
-}
-
-; lifetime.start on part of alloca, copy out of range.
-define void @test_lifetime_partial_alias_4(ptr noalias %dst) {
-; CHECK-LABEL: @test_lifetime_partial_alias_4(
-; CHECK-NEXT:    [[A:%.*]] = alloca [16 x i8], align 1
-; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 12, ptr [[A]])
-; CHECK-NEXT:    [[GEP:%.*]] = getelementptr i8, ptr [[A]], i64 8
-; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr [[DST:%.*]], ptr [[GEP]], i64 8, i1 false)
-; CHECK-NEXT:    ret void
-;
-  %a = alloca [16 x i8]
-  call void @llvm.lifetime.start.p0(i64 12, ptr %a)
-  %gep = getelementptr i8, ptr %a, i64 8
-  call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr %gep, i64 8, i1 false)
-  ret void
-}
-
 declare void @llvm.memcpy.p0.p0.i64(ptr nocapture, ptr nocapture, i64, i1) nounwind
 
 declare void @llvm.lifetime.start.p0(i64, ptr nocapture) nounwind

diff  --git a/llvm/test/Transforms/MemCpyOpt/memset-memcpy-oversized.ll b/llvm/test/Transforms/MemCpyOpt/memset-memcpy-oversized.ll
index 7ea63bb5d3c6b..343f95181a8e5 100644
--- a/llvm/test/Transforms/MemCpyOpt/memset-memcpy-oversized.ll
+++ b/llvm/test/Transforms/MemCpyOpt/memset-memcpy-oversized.ll
@@ -37,26 +37,6 @@ define void @test_alloca_with_lifetimes(ptr %result) {
   ret void
 }
 
-; memcpy size is larger than lifetime, don't optimize.
-define void @test_copy_larger_than_lifetime_size(ptr %result) {
-; CHECK-LABEL: @test_copy_larger_than_lifetime_size(
-; CHECK-NEXT:    [[A:%.*]] = alloca [[T:%.*]], align 8
-; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 12, ptr [[A]])
-; CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr align 8 [[A]], i8 0, i64 12, i1 false)
-; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr [[RESULT:%.*]], ptr align 8 [[A]], i64 16, i1 false)
-; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 12, ptr [[A]])
-; CHECK-NEXT:    call void @free(ptr [[A]])
-; CHECK-NEXT:    ret void
-;
-  %a = alloca %T, align 8
-  call void @llvm.lifetime.start.p0(i64 12, ptr %a)
-  call void @llvm.memset.p0.i64(ptr align 8 %a, i8 0, i64 12, i1 false)
-  call void @llvm.memcpy.p0.p0.i64(ptr %result, ptr align 8 %a, i64 16, i1 false)
-  call void @llvm.lifetime.end.p0(i64 12, ptr %a)
-  call void @free(ptr %a)
-  ret void
-}
-
 ; The trailing bytes are not known to be undef, we can't ignore them.
 define void @test_not_undef_memory(ptr %result, ptr %input) {
 ; CHECK-LABEL: @test_not_undef_memory(


        


More information about the llvm-commits mailing list