[llvm] 034e5d6 - [MemCpyOpt] Extend `performMemCpyToMemSetOptzn` to partially memset'd region

Antonio Frighetto via llvm-commits llvm-commits at lists.llvm.org
Fri Jan 30 01:10:06 PST 2026


Author: Antonio Frighetto
Date: 2026-01-30T10:09:08+01:00
New Revision: 034e5d6f8671e84c9fee5a3236bf2ec0cfa4a731

URL: https://github.com/llvm/llvm-project/commit/034e5d6f8671e84c9fee5a3236bf2ec0cfa4a731
DIFF: https://github.com/llvm/llvm-project/commit/034e5d6f8671e84c9fee5a3236bf2ec0cfa4a731.diff

LOG: [MemCpyOpt] Extend `performMemCpyToMemSetOptzn` to partially memset'd region

While doing memset-to-memcpy forwarding, take into account memset
that covers memory regions from a given offset, and the leading
bytes of such a region are undef.

Fixes: https://github.com/llvm/llvm-project/issues/172326.

Added: 
    

Modified: 
    llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp
    llvm/test/Transforms/MemCpyOpt/lifetime-missing.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 5de3dfa148314..0f75e53cb9998 100644
--- a/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp
+++ b/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp
@@ -1427,7 +1427,6 @@ static bool overreadUndefContents(MemorySSA *MSSA, MemCpyInst *MemCpy,
 ///   memset(dst1, c, dst1_size);
 ///   memset(dst2, c, dst2_size);
 /// \endcode
-/// When dst2_size <= dst1_size.
 bool MemCpyOptPass::performMemCpyToMemSetOptzn(MemCpyInst *MemCpy,
                                                MemSetInst *MemSet,
                                                BatchAAResults &BAA) {
@@ -1441,42 +1440,61 @@ bool MemCpyOptPass::performMemCpyToMemSetOptzn(MemCpyInst *MemCpy,
   if (MemCpy->getSource() != MemSet->getDest()) {
     std::optional<int64_t> Offset =
         MemCpy->getSource()->getPointerOffsetFrom(MemSet->getDest(), DL);
-    if (!Offset || *Offset < 0)
+    if (!Offset)
       return false;
+    // On positive offsets, the memcpy source is at a offset into the memset'd
+    // region. On negative offsets, the copy starts at a offset prior to the
+    // previously memset'd area, namely, we memcpy from a partially initialized
+    // region.
     MOffset = *Offset;
   }
 
   if (MOffset != 0 || MemSetSize != CopySize) {
     // Make sure the memcpy doesn't read any more than what the memset wrote,
-    // other than undef. Don't worry about sizes larger than i64.
+    // other than undef. Likewise, the memcpy should not read from an area not
+    // covered by the memset unless undef bytes. Don't worry about sizes larger
+    // than i64.
     auto *CMemSetSize = dyn_cast<ConstantInt>(MemSetSize);
     auto *CCopySize = dyn_cast<ConstantInt>(CopySize);
-    if (!CMemSetSize || !CCopySize ||
+    if (!CMemSetSize || !CCopySize || MOffset < 0 ||
         CCopySize->getZExtValue() + MOffset > CMemSetSize->getZExtValue()) {
       if (!overreadUndefContents(MSSA, MemCpy, MemSet, BAA))
         return false;
 
       if (CMemSetSize && CCopySize) {
-        // If both have constant sizes and offsets, clip the memcpy to the
-        // bounds of the memset if applicable.
-        assert(CCopySize->getZExtValue() + MOffset >
-               CMemSetSize->getZExtValue());
-        if (MOffset == 0)
-          CopySize = MemSetSize;
-        else
-          CopySize =
-              ConstantInt::get(CopySize->getType(),
-                               CMemSetSize->getZExtValue() <= (uint64_t)MOffset
-                                   ? 0
-                                   : CMemSetSize->getZExtValue() - MOffset);
+        uint64_t MemSetSizeVal = CMemSetSize->getZExtValue();
+        uint64_t MemCpySizeVal = CCopySize->getZExtValue();
+        uint64_t NewSize;
+
+        if (MOffset < 0) {
+          // Offset from beginning of the initialized region.
+          uint64_t Offset = -MOffset;
+          NewSize = MemCpySizeVal <= Offset ? 0 : MemCpySizeVal - Offset;
+        } else if (MOffset == 0) {
+          NewSize = MemSetSizeVal;
+        } else {
+          NewSize =
+              MemSetSizeVal <= (uint64_t)MOffset ? 0 : MemSetSizeVal - MOffset;
+        }
+        CopySize = ConstantInt::get(CopySize->getType(), NewSize);
+      } else {
+        if (MOffset < 0)
+          return false;
       }
     }
   }
 
   IRBuilder<> Builder(MemCpy);
+  Value *DestPtr = MemCpy->getRawDest();
+  MaybeAlign Align = MemCpy->getDestAlign();
+  if (MOffset < 0) {
+    DestPtr = Builder.CreatePtrAdd(DestPtr, Builder.getInt64(-MOffset));
+    if (Align)
+      Align = commonAlignment(*Align, -MOffset);
+  }
+
   Instruction *NewM =
-      Builder.CreateMemSet(MemCpy->getRawDest(), MemSet->getOperand(1),
-                           CopySize, MemCpy->getDestAlign());
+      Builder.CreateMemSet(DestPtr, MemSet->getOperand(1), CopySize, Align);
   auto *LastDef = cast<MemoryDef>(MSSA->getMemoryAccess(MemCpy));
   auto *NewAccess = MSSAU->createMemoryAccessAfter(NewM, nullptr, LastDef);
   MSSAU->insertDef(cast<MemoryDef>(NewAccess), /*RenameUses=*/true);

diff  --git a/llvm/test/Transforms/MemCpyOpt/lifetime-missing.ll b/llvm/test/Transforms/MemCpyOpt/lifetime-missing.ll
index 06d9434e8c5c8..35b53f787373e 100644
--- a/llvm/test/Transforms/MemCpyOpt/lifetime-missing.ll
+++ b/llvm/test/Transforms/MemCpyOpt/lifetime-missing.ll
@@ -14,9 +14,13 @@ define void @test() {
 ; CHECK-LABEL: define void @test() {
 ; CHECK-NEXT:  entry:
 ; CHECK-NEXT:    [[AGG_TMP_SROA_14:%.*]] = alloca [20 x i8], align 4
-; CHECK-NEXT:    [[AGG_TMP_SROA_14_128_SROA_IDX:%.*]] = getelementptr i8, ptr [[AGG_TMP_SROA_14]], i64 4
+; CHECK-NEXT:    [[AGG_TMP_SROA_15:%.*]] = alloca [20 x i8], align 4
+; CHECK-NEXT:    [[AGG_TMP_SROA_14_128_SROA_IDX:%.*]] = getelementptr i8, ptr [[AGG_TMP_SROA_15]], i64 4
 ; CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr [[AGG_TMP_SROA_14_128_SROA_IDX]], i8 0, i64 1, i1 false)
+; CHECK-NEXT:    call void @llvm.lifetime.start.p0(ptr [[AGG_TMP_SROA_14]])
 ; CHECK-NEXT:    [[AGG_TMP3_SROA_35_128_SROA_IDX:%.*]] = getelementptr i8, ptr [[AGG_TMP_SROA_14]], i64 4
+; CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr [[AGG_TMP3_SROA_35_128_SROA_IDX]], i8 0, i64 16, i1 false)
+; CHECK-NEXT:    [[AGG_TMP3_SROA_35_128_SROA_IDX1:%.*]] = getelementptr i8, ptr [[AGG_TMP_SROA_14]], i64 4
 ; CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr inttoptr (i64 4 to ptr), i8 0, i64 1, i1 false)
 ; CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr null, i8 0, i64 1, i1 false)
 ; CHECK-NEXT:    ret void

diff  --git a/llvm/test/Transforms/MemCpyOpt/memset-memcpy-oversized.ll b/llvm/test/Transforms/MemCpyOpt/memset-memcpy-oversized.ll
index 1371f2e4bbd16..a9310ef5c1407 100644
--- a/llvm/test/Transforms/MemCpyOpt/memset-memcpy-oversized.ll
+++ b/llvm/test/Transforms/MemCpyOpt/memset-memcpy-oversized.ll
@@ -153,7 +153,8 @@ define void @test_negative_offset_memset(ptr %result) {
 ; CHECK-NEXT:    [[A1:%.*]] = alloca [16 x i8], align 8
 ; CHECK-NEXT:    [[A:%.*]] = getelementptr i8, ptr [[A1]], i32 4
 ; CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr align 4 [[A]], i8 0, i64 12, i1 false)
-; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr [[RESULT:%.*]], ptr align 8 [[A1]], i64 12, i1 false)
+; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr i8, ptr [[RESULT:%.*]], i64 4
+; CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr [[TMP1]], i8 0, i64 8, i1 false)
 ; CHECK-NEXT:    ret void
 ;
   %a = alloca [ 16 x i8 ], align 8
@@ -201,7 +202,8 @@ define void @test_negative_offset_memset_2(ptr %out) {
 ; CHECK-NEXT:    [[ALLOCA:%.*]] = alloca <{ [2 x i8], i64, i64, i64 }>, align 8
 ; CHECK-NEXT:    [[OFFSET:%.*]] = getelementptr i8, ptr [[ALLOCA]], i64 2
 ; CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr [[OFFSET]], i8 0, i64 24, i1 false)
-; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr [[OUT:%.*]], ptr [[ALLOCA]], i64 26, i1 false)
+; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr i8, ptr [[OUT:%.*]], i64 2
+; CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr [[TMP1]], i8 0, i64 24, i1 false)
 ; CHECK-NEXT:    ret void
 ;
   %alloca = alloca <{ [2 x i8], i64, i64, i64 }>
@@ -218,7 +220,8 @@ define void @test_negative_offset_memset_3(ptr %out) {
 ; CHECK-NEXT:    [[ALLOCA:%.*]] = alloca [20 x i8], align 1
 ; CHECK-NEXT:    [[OFFSET:%.*]] = getelementptr i8, ptr [[ALLOCA]], i64 10
 ; CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr [[OFFSET]], i8 0, i64 10, i1 false)
-; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr [[OUT:%.*]], ptr [[ALLOCA]], i64 15, i1 false)
+; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr i8, ptr [[OUT:%.*]], i64 10
+; CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr [[TMP1]], i8 0, i64 5, i1 false)
 ; CHECK-NEXT:    ret void
 ;
   %alloca = alloca [20 x i8]
@@ -234,7 +237,8 @@ define void @test_negative_offset_memset_
diff erent_alignment(ptr %out) {
 ; CHECK-NEXT:    [[ALLOCA:%.*]] = alloca [20 x i8], align 1
 ; CHECK-NEXT:    [[OFFSET:%.*]] = getelementptr i8, ptr [[ALLOCA]], i64 4
 ; CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr align 1 [[OFFSET]], i8 0, i64 16, i1 false)
-; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[OUT:%.*]], ptr align 1 [[ALLOCA]], i64 20, i1 false)
+; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr i8, ptr [[OUT:%.*]], i64 4
+; CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr align 4 [[TMP1]], i8 0, i64 16, i1 false)
 ; CHECK-NEXT:    ret void
 ;
   %alloca = alloca [20 x i8], align 1
@@ -252,7 +256,8 @@ define void @test_negative_offset_memset_and_negative_offset_memcpy_dest(ptr %ou
 ; CHECK-NEXT:    [[OFFSET:%.*]] = getelementptr i8, ptr [[ALLOCA]], i64 10
 ; CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr [[OFFSET]], i8 0, i64 10, i1 false)
 ; CHECK-NEXT:    [[OFFSET_OUT:%.*]] = getelementptr i8, ptr [[OUT:%.*]], i64 -10
-; CHECK-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr [[OFFSET_OUT]], ptr [[ALLOCA]], i64 15, i1 false)
+; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr i8, ptr [[OFFSET_OUT]], i64 10
+; CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr [[TMP1]], i8 0, i64 5, i1 false)
 ; CHECK-NEXT:    ret void
 ;
   %alloca = alloca [20 x i8]


        


More information about the llvm-commits mailing list