[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