diff --git a/llvm/lib/Transforms/Utils/BuildLibCalls.cpp b/llvm/lib/Transforms/Utils/BuildLibCalls.cpp --- a/llvm/lib/Transforms/Utils/BuildLibCalls.cpp +++ b/llvm/lib/Transforms/Utils/BuildLibCalls.cpp @@ -36,6 +36,7 @@ STATISTIC(NumNoUnwind, "Number of functions inferred as nounwind"); STATISTIC(NumNoCapture, "Number of arguments inferred as nocapture"); STATISTIC(NumReadOnlyArg, "Number of arguments inferred as readonly"); +STATISTIC(NumWriteOnlyArg, "Number of arguments inferred as writeonly"); STATISTIC(NumNoAlias, "Number of function returns inferred as noalias"); STATISTIC(NumNoUndef, "Number of function returns inferred as noundef returns"); STATISTIC(NumNonNull, "Number of function returns inferred as nonnull returns"); @@ -105,6 +106,14 @@ return true; } +static bool setOnlyWritesMemory(Function &F, unsigned ArgNo) { + if (F.hasParamAttribute(ArgNo, Attribute::WriteOnly)) + return false; + F.addParamAttr(ArgNo, Attribute::WriteOnly); + ++NumWriteOnlyArg; + return true; +} + static bool setRetAndArgsNoUndef(Function &F) { bool Changed = false; if (!F.getReturnType()->isVoidTy() && @@ -203,9 +212,6 @@ return Changed; case LibFunc_strcpy: case LibFunc_strncpy: - Changed |= setDoesNotAlias(F, 0); - Changed |= setDoesNotAlias(F, 1); - LLVM_FALLTHROUGH; case LibFunc_strcat: case LibFunc_strncat: Changed |= setReturnedArg(F, 0); @@ -213,8 +219,12 @@ case LibFunc_stpcpy: case LibFunc_stpncpy: Changed |= setDoesNotThrow(F); + Changed |= setDoesNotCapture(F, 0); Changed |= setDoesNotCapture(F, 1); + Changed |= setOnlyWritesMemory(F, 0); Changed |= setOnlyReadsMemory(F, 1); + Changed |= setDoesNotAlias(F, 0); + Changed |= setDoesNotAlias(F, 1); return Changed; case LibFunc_strxfrm: Changed |= setDoesNotThrow(F); diff --git a/llvm/test/Transforms/InferFunctionAttrs/annotate.ll b/llvm/test/Transforms/InferFunctionAttrs/annotate.ll --- a/llvm/test/Transforms/InferFunctionAttrs/annotate.ll +++ b/llvm/test/Transforms/InferFunctionAttrs/annotate.ll @@ -825,16 +825,16 @@ ; CHECK-LINUX: declare noundef i32 @statvfs64(i8* nocapture noundef readonly, %opaque* nocapture noundef) [[G1]] declare i32 @statvfs64(i8*, %opaque*) -; CHECK: declare i8* @stpcpy(i8*, i8* nocapture readonly) [[G1]] +; CHECK: declare i8* @stpcpy(i8* noalias nocapture writeonly, i8* noalias nocapture readonly) [[G1]] declare i8* @stpcpy(i8*, i8*) -; CHECK: declare i8* @stpncpy(i8*, i8* nocapture readonly, i64) [[G1]] +; CHECK: declare i8* @stpncpy(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64) [[G1]] declare i8* @stpncpy(i8*, i8*, i64) ; CHECK: declare i32 @strcasecmp(i8* nocapture, i8* nocapture) [[G2]] declare i32 @strcasecmp(i8*, i8*) -; CHECK: declare i8* @strcat(i8* returned, i8* nocapture readonly) [[G1]] +; CHECK: declare i8* @strcat(i8* noalias nocapture returned writeonly, i8* noalias nocapture readonly) [[G1]] declare i8* @strcat(i8*, i8*) ; CHECK: declare i8* @strchr(i8*, i32) [[ARGMEMONLY_NOFREE_NOUNWIND_READONLY]] @@ -846,7 +846,7 @@ ; CHECK: declare i32 @strcoll(i8* nocapture, i8* nocapture) [[G2]] declare i32 @strcoll(i8*, i8*) -; CHECK: declare i8* @strcpy(i8* noalias returned, i8* noalias nocapture readonly) [[G1]] +; CHECK: declare i8* @strcpy(i8* noalias nocapture returned writeonly, i8* noalias nocapture readonly) [[G1]] declare i8* @strcpy(i8*, i8*) ; CHECK: declare i64 @strcspn(i8* nocapture, i8* nocapture) [[ARGMEMONLY_NOFREE_NOUNWIND_READONLY]] @@ -861,13 +861,13 @@ ; CHECK: declare i32 @strncasecmp(i8* nocapture, i8* nocapture, i64) [[G2]] declare i32 @strncasecmp(i8*, i8*, i64) -; CHECK: declare i8* @strncat(i8* returned, i8* nocapture readonly, i64) [[G1]] +; CHECK: declare i8* @strncat(i8* noalias nocapture returned writeonly, i8* noalias nocapture readonly, i64) [[G1]] declare i8* @strncat(i8*, i8*, i64) ; CHECK: declare i32 @strncmp(i8* nocapture, i8* nocapture, i64) [[ARGMEMONLY_NOFREE_NOUNWIND_READONLY]] declare i32 @strncmp(i8*, i8*, i64) -; CHECK: declare i8* @strncpy(i8* noalias returned, i8* noalias nocapture readonly, i64) [[G1]] +; CHECK: declare i8* @strncpy(i8* noalias nocapture returned writeonly, i8* noalias nocapture readonly, i64) [[G1]] declare i8* @strncpy(i8*, i8*, i64) ; CHECK: declare noalias i8* @strndup(i8* nocapture readonly, i64) [[G1]] diff --git a/llvm/test/Transforms/PhaseOrdering/dse-libcalls.ll b/llvm/test/Transforms/PhaseOrdering/dse-libcalls.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/PhaseOrdering/dse-libcalls.ll @@ -0,0 +1,93 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -S -O2 | FileCheck %s --check-prefixes=CHECK,OLDPM +; RUN: opt -passes='default' -S < %s | FileCheck %s --check-prefixes=CHECK,NEWPM + + +declare i8* @strcpy(i8*, i8*) +declare i8* @strncpy(i8*, i8*, i64) +declare i8* @strcat(i8*, i8*) +declare i8* @strncat(i8*, i8*, i64) + +define void @dse_strcpy(i8* nocapture readonly %src) { +; OLDPM-LABEL: @dse_strcpy( +; OLDPM-NEXT: ret void +; +; NEWPM-LABEL: @dse_strcpy( +; NEWPM-NEXT: [[A:%.*]] = alloca [256 x i8], align 16 +; NEWPM-NEXT: [[BUF:%.*]] = getelementptr inbounds [256 x i8], [256 x i8]* [[A]], i64 0, i64 0 +; NEWPM-NEXT: call void @llvm.lifetime.start.p0i8(i64 256, i8* nonnull [[BUF]]) +; NEWPM-NEXT: [[TMP1:%.*]] = call i8* @strcpy(i8* nonnull [[BUF]], i8* nonnull dereferenceable(1) [[SRC:%.*]]) +; NEWPM-NEXT: call void @llvm.lifetime.end.p0i8(i64 256, i8* nonnull [[BUF]]) +; NEWPM-NEXT: ret void +; + %a = alloca [256 x i8], align 16 + %buf = getelementptr inbounds [256 x i8], [256 x i8]* %a, i64 0, i64 0 + call void @llvm.lifetime.start.p0i8(i64 256, i8* nonnull %buf) + call i8* @strcpy(i8* nonnull %buf, i8* nonnull dereferenceable(1) %src) + call void @llvm.lifetime.end.p0i8(i64 256, i8* nonnull %buf) + ret void +} + +define void @dse_strncpy(i8* nocapture readonly %src) { +; OLDPM-LABEL: @dse_strncpy( +; OLDPM-NEXT: ret void +; +; NEWPM-LABEL: @dse_strncpy( +; NEWPM-NEXT: [[A:%.*]] = alloca [256 x i8], align 16 +; NEWPM-NEXT: [[BUF:%.*]] = getelementptr inbounds [256 x i8], [256 x i8]* [[A]], i64 0, i64 0 +; NEWPM-NEXT: call void @llvm.lifetime.start.p0i8(i64 256, i8* nonnull [[BUF]]) +; NEWPM-NEXT: [[TMP1:%.*]] = call i8* @strncpy(i8* nonnull [[BUF]], i8* nonnull dereferenceable(1) [[SRC:%.*]], i64 6) +; NEWPM-NEXT: call void @llvm.lifetime.end.p0i8(i64 256, i8* nonnull [[BUF]]) +; NEWPM-NEXT: ret void +; + %a = alloca [256 x i8], align 16 + %buf = getelementptr inbounds [256 x i8], [256 x i8]* %a, i64 0, i64 0 + call void @llvm.lifetime.start.p0i8(i64 256, i8* nonnull %buf) + call i8* @strncpy(i8* nonnull %buf, i8* nonnull dereferenceable(1) %src, i64 6) + call void @llvm.lifetime.end.p0i8(i64 256, i8* nonnull %buf) + ret void +} + +define void @dse_strcat(i8* nocapture readonly %src) { +; OLDPM-LABEL: @dse_strcat( +; OLDPM-NEXT: ret void +; +; NEWPM-LABEL: @dse_strcat( +; NEWPM-NEXT: [[A:%.*]] = alloca [256 x i8], align 16 +; NEWPM-NEXT: [[BUF:%.*]] = getelementptr inbounds [256 x i8], [256 x i8]* [[A]], i64 0, i64 0 +; NEWPM-NEXT: call void @llvm.lifetime.start.p0i8(i64 256, i8* nonnull [[BUF]]) +; NEWPM-NEXT: [[TMP1:%.*]] = call i8* @strcat(i8* nonnull [[BUF]], i8* nonnull dereferenceable(1) [[SRC:%.*]]) +; NEWPM-NEXT: call void @llvm.lifetime.end.p0i8(i64 256, i8* nonnull [[BUF]]) +; NEWPM-NEXT: ret void +; + %a = alloca [256 x i8], align 16 + %buf = getelementptr inbounds [256 x i8], [256 x i8]* %a, i64 0, i64 0 + call void @llvm.lifetime.start.p0i8(i64 256, i8* nonnull %buf) + call i8* @strcat(i8* nonnull %buf, i8* nonnull dereferenceable(1) %src) + call void @llvm.lifetime.end.p0i8(i64 256, i8* nonnull %buf) + ret void +} + +define void @dse_strncat(i8* nocapture readonly %src) { +; OLDPM-LABEL: @dse_strncat( +; OLDPM-NEXT: ret void +; +; NEWPM-LABEL: @dse_strncat( +; NEWPM-NEXT: [[A:%.*]] = alloca [256 x i8], align 16 +; NEWPM-NEXT: [[BUF:%.*]] = getelementptr inbounds [256 x i8], [256 x i8]* [[A]], i64 0, i64 0 +; NEWPM-NEXT: call void @llvm.lifetime.start.p0i8(i64 256, i8* nonnull [[BUF]]) +; NEWPM-NEXT: [[TMP1:%.*]] = call i8* @strncat(i8* nonnull [[BUF]], i8* nonnull dereferenceable(1) [[SRC:%.*]], i64 6) +; NEWPM-NEXT: call void @llvm.lifetime.end.p0i8(i64 256, i8* nonnull [[BUF]]) +; NEWPM-NEXT: ret void +; + %a = alloca [256 x i8], align 16 + %buf = getelementptr inbounds [256 x i8], [256 x i8]* %a, i64 0, i64 0 + call void @llvm.lifetime.start.p0i8(i64 256, i8* nonnull %buf) + call i8* @strncat(i8* nonnull %buf, i8* nonnull dereferenceable(1) %src, i64 6) + call void @llvm.lifetime.end.p0i8(i64 256, i8* nonnull %buf) + ret void +} + +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) +