Index: llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp =================================================================== --- llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp +++ llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp @@ -22,6 +22,7 @@ #include "llvm/Analysis/AssumptionCache.h" #include "llvm/Analysis/CaptureTracking.h" #include "llvm/Analysis/GlobalsModRef.h" +#include "llvm/Analysis/InstructionSimplify.h" #include "llvm/Analysis/Loads.h" #include "llvm/Analysis/MemoryLocation.h" #include "llvm/Analysis/MemorySSA.h" @@ -1390,6 +1391,15 @@ return true; } +static bool isZeroSize(Value *Size) { + if (auto *I = dyn_cast(Size)) + if (auto *Res = SimplifyInstruction(I, I->getModule()->getDataLayout())) + Size = Res; + if (auto *C = dyn_cast(Size)) + return C->isNullValue(); + return false; +} + /// Perform simplification of memcpy's. If we have memcpy A /// which copies X to Y, and memcpy B which copies Y to Z, then we can rewrite /// B to be a memcpy from X to Z (or potentially a memmove, depending on @@ -1406,6 +1416,14 @@ return true; } + // If the size is zero, remove the memcpy. This also prevents infinite loops + // in processMemSetMemCpyDependence, which is a no-op for zero-length memcpys. + if (isZeroSize(M->getLength())) { + ++BBI; + eraseInstruction(M); + return true; + } + // If copying from a constant, try to turn the memcpy into a memset. if (auto *GV = dyn_cast(M->getSource())) if (GV->isConstant() && GV->hasDefinitiveInitializer()) Index: llvm/test/Transforms/MemCpyOpt/memcpy-zero-size.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/MemCpyOpt/memcpy-zero-size.ll @@ -0,0 +1,26 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -memcpyopt < %s | FileCheck %s + +declare void @llvm.memset.p0.i64(ptr, i8, i64, i1) +declare void @llvm.memcpy.p0.p0.i64(ptr, ptr, i64, i1) + +define void @zero_size(ptr %p, ptr %p2) { +; CHECK-LABEL: @zero_size( +; CHECK-NEXT: ret void +; + call void @llvm.memcpy.p0.p0.i64(ptr %p, ptr %p2, i64 0, i1 false) + ret void +} + +; The memcpy size is zero in a way that is non-trivial, but understood by AA. +define void @pr54983(ptr %p, ptr noalias %p2) { +; CHECK-LABEL: @pr54983( +; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr [[P:%.*]], i8 0, i64 1, i1 false) +; CHECK-NEXT: [[SIZE:%.*]] = shl i64 0, 0 +; CHECK-NEXT: ret void +; + call void @llvm.memset.p0.i64(ptr %p, i8 0, i64 1, i1 false) + %size = shl i64 0, 0 + call void @llvm.memcpy.p0.p0.i64(ptr %p, ptr %p2, i64 %size, i1 false) + ret void +}