Index: llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp =================================================================== --- llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp +++ llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp @@ -231,10 +231,27 @@ } } } - if (auto *CB = dyn_cast(Inst)) + + if (auto *CB = dyn_cast(Inst)) { + LibFunc LF; + if (TLI.getLibFunc(*CB, LF) && TLI.has(LF)) { + switch (LF) { + case LibFunc_strncpy: + if (const auto *Len = dyn_cast(CB->getArgOperand(2))) + return MemoryLocation(CB->getArgOperand(0), + LocationSize::precise(Len->getZExtValue()), + CB->getAAMetadata()); + LLVM_FALLTHROUGH; + default: + break; + } + } + // All the supported TLI functions so far happen to have dest as their // first argument. return MemoryLocation::getAfter(CB->getArgOperand(0)); + } + return MemoryLocation(); } @@ -1114,8 +1131,13 @@ LibFunc LF; if (TLI.getLibFunc(*CB, LF) && TLI.has(LF)) { switch (LF) { - case LibFunc_strcpy: case LibFunc_strncpy: + if (const auto *Len = dyn_cast(CB->getArgOperand(2))) + return MemoryLocation(CB->getArgOperand(0), + LocationSize::precise(Len->getZExtValue()), + CB->getAAMetadata()); + LLVM_FALLTHROUGH; + case LibFunc_strcpy: case LibFunc_strcat: case LibFunc_strncat: return {MemoryLocation::getAfter(CB->getArgOperand(0))}; Index: llvm/test/Transforms/DeadStoreElimination/memset-and-strncpy.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/DeadStoreElimination/memset-and-strncpy.ll @@ -0,0 +1,79 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -inferattrs -dse -S | FileCheck %s + +declare void @llvm.memset.p0i8.i64(i8* nocapture, i8, i64, i1) nounwind +declare i8* @strncpy(i8* noalias nocapture, i8* noalias nocapture readonly, i64) + +; Test that strncpy/memset overwriting each other is optimized out + +; strncpy -> memset, full overwrite +define void @test1(i8* noalias %out, i8* noalias %in) { +; CHECK-LABEL: @test1( +; CHECK-NEXT: tail call void @llvm.memset.p0i8.i64(i8* [[OUT:%.*]], i8 42, i64 100, i1 false) +; CHECK-NEXT: ret void +; + %call = tail call i8* @strncpy(i8* %out, i8* %in, i64 100) + tail call void @llvm.memset.p0i8.i64(i8* %out, i8 42, i64 100, i1 false) + ret void +} + +; strncpy -> memset, partial overwrite +define void @test2(i8* noalias %out, i8* noalias %in) { +; CHECK-LABEL: @test2( +; CHECK-NEXT: [[CALL:%.*]] = tail call i8* @strncpy(i8* [[OUT:%.*]], i8* [[IN:%.*]], i64 100) +; CHECK-NEXT: tail call void @llvm.memset.p0i8.i64(i8* [[OUT]], i8 42, i64 99, i1 false) +; CHECK-NEXT: ret void +; + %call = tail call i8* @strncpy(i8* %out, i8* %in, i64 100) + tail call void @llvm.memset.p0i8.i64(i8* %out, i8 42, i64 99, i1 false) + ret void +} + +; strncpy -> memset, different destination +define void @test3(i8* noalias %out1, i8* noalias %out2, i8* noalias %in) { +; CHECK-LABEL: @test3( +; CHECK-NEXT: [[CALL:%.*]] = tail call i8* @strncpy(i8* [[OUT1:%.*]], i8* [[IN:%.*]], i64 100) +; CHECK-NEXT: tail call void @llvm.memset.p0i8.i64(i8* [[OUT2:%.*]], i8 42, i64 100, i1 false) +; CHECK-NEXT: ret void +; + %call = tail call i8* @strncpy(i8* %out1, i8* %in, i64 100) + tail call void @llvm.memset.p0i8.i64(i8* %out2, i8 42, i64 100, i1 false) + ret void +} + + +; memset -> strncpy, full overwrite +define void @test4(i8* noalias %out, i8* noalias %in) { +; CHECK-LABEL: @test4( +; CHECK-NEXT: [[CALL:%.*]] = tail call i8* @strncpy(i8* [[OUT:%.*]], i8* [[IN:%.*]], i64 100) +; CHECK-NEXT: ret void +; + tail call void @llvm.memset.p0i8.i64(i8* %out, i8 42, i64 100, i1 false) + %call = tail call i8* @strncpy(i8* %out, i8* %in, i64 100) + ret void +} + +; memset -> strncpy, partial overwrite +define void @test5(i8* noalias %out, i8* noalias %in) { +; CHECK-LABEL: @test5( +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, i8* [[OUT:%.*]], i64 99 +; CHECK-NEXT: tail call void @llvm.memset.p0i8.i64(i8* align 1 [[TMP1]], i8 42, i64 1, i1 false) +; CHECK-NEXT: [[CALL:%.*]] = tail call i8* @strncpy(i8* [[OUT]], i8* [[IN:%.*]], i64 99) +; CHECK-NEXT: ret void +; + tail call void @llvm.memset.p0i8.i64(i8* %out, i8 42, i64 100, i1 false) + %call = tail call i8* @strncpy(i8* %out, i8* %in, i64 99) + ret void +} + +; memset -> strncpy, different destination +define void @test6(i8* noalias %out1, i8* noalias %out2, i8* noalias %in) { +; CHECK-LABEL: @test6( +; CHECK-NEXT: tail call void @llvm.memset.p0i8.i64(i8* [[OUT1:%.*]], i8 42, i64 100, i1 false) +; CHECK-NEXT: [[CALL:%.*]] = tail call i8* @strncpy(i8* [[OUT2:%.*]], i8* [[IN:%.*]], i64 100) +; CHECK-NEXT: ret void +; + tail call void @llvm.memset.p0i8.i64(i8* %out1, i8 42, i64 100, i1 false) + %call = tail call i8* @strncpy(i8* %out2, i8* %in, i64 100) + ret void +}