From bc7ea63e9c885fbe71dec29581a206bc0543d22a Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 11 Jun 2025 20:04:27 -0400 Subject: [PATCH] [MemCpyOpt] handle memcpy from memset for non-constant sizes (#143727) Allows forwarding memset to memcpy for mismatching unknown sizes if overread has undef contents. In that case we can refine the undef bytes to the memset value. Refs #140954 which laid some of the groundwork for this. --- .../lib/Transforms/Scalar/MemCpyOptimizer.cpp | 38 +++++++++---------- .../MemCpyOpt/variable-sized-memset-memcpy.ll | 6 +-- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp b/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp index 960001bf880c..1c4ec6aa08b4 100644 --- a/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp +++ b/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp @@ -1440,7 +1440,7 @@ bool MemCpyOptPass::performMemCpyToMemSetOptzn(MemCpyInst *MemCpy, int64_t MOffset = 0; const DataLayout &DL = MemCpy->getModule()->getDataLayout(); // We can only transforms memcpy's where the dest of one is the source of the - // other, or the memory transfer has a known offset from the memset. + // other, or they have a known offset. if (MemCpy->getSource() != MemSet->getDest()) { std::optional Offset = MemCpy->getSource()->getPointerOffsetFrom(MemSet->getDest(), DL); @@ -1451,28 +1451,28 @@ bool MemCpyOptPass::performMemCpyToMemSetOptzn(MemCpyInst *MemCpy, 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. A known memset - // size is required. + // other than undef. Don't worry about sizes larger than i64. auto *CMemSetSize = dyn_cast(MemSetSize); - if (!CMemSetSize) - return false; - - // A known memcpy size is also required. auto *CCopySize = dyn_cast(CopySize); - if (!CCopySize) - return false; - if (CCopySize->getZExtValue() + MOffset > CMemSetSize->getZExtValue()) { + if (!CMemSetSize || !CCopySize || + CCopySize->getZExtValue() + MOffset > CMemSetSize->getZExtValue()) { if (!overreadUndefContents(MSSA, MemCpy, MemSet, BAA)) return false; - // Clip the memcpy to the bounds of the memset - if (MOffset == 0) - CopySize = MemSetSize; - else - CopySize = - ConstantInt::get(CopySize->getType(), - CMemSetSize->getZExtValue() <= (uint64_t)MOffset - ? 0 - : CMemSetSize->getZExtValue() - MOffset); + + 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); + } } } diff --git a/llvm/test/Transforms/MemCpyOpt/variable-sized-memset-memcpy.ll b/llvm/test/Transforms/MemCpyOpt/variable-sized-memset-memcpy.ll index d5b1ab9b2f29..4b44f8b44f74 100644 --- a/llvm/test/Transforms/MemCpyOpt/variable-sized-memset-memcpy.ll +++ b/llvm/test/Transforms/MemCpyOpt/variable-sized-memset-memcpy.ll @@ -19,12 +19,12 @@ define void @test(ptr %src, i8 %c, i64 %size) { } ; Differing sizes, but would be UB if size1 < size2 since the memcpy would reference outside of the first alloca -define void @negative_test(ptr %src, i8 %c, i64 %size1, i64 %size2) { -; CHECK-LABEL: @negative_test( +define void @dynsize_test(ptr %src, i8 %c, i64 %size1, i64 %size2) { +; CHECK-LABEL: @dynsize_test( ; CHECK-NEXT: [[DST1:%.*]] = alloca i8, i64 [[SIZE1:%.*]], align 1 ; CHECK-NEXT: [[DST2:%.*]] = alloca i8, i64 [[SIZE2:%.*]], align 1 ; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[DST1]], i8 [[C:%.*]], i64 [[SIZE1]], i1 false) -; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[DST2]], ptr align 8 [[DST1]], i64 [[SIZE2]], i1 false) +; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[DST2]], i8 [[C]], i64 [[SIZE2]], i1 false) ; CHECK-NEXT: ret void ; %dst1 = alloca i8, i64 %size1