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 @@ -35,6 +35,7 @@ STATISTIC(NumArgMemOnly, "Number of functions inferred as argmemonly"); STATISTIC(NumNoUnwind, "Number of functions inferred as nounwind"); STATISTIC(NumNoCapture, "Number of arguments inferred as nocapture"); +STATISTIC(NumWriteOnlyArg, "Number of arguments inferred as writeonly"); STATISTIC(NumReadOnlyArg, "Number of arguments inferred as readonly"); STATISTIC(NumNoAlias, "Number of function returns inferred as noalias"); STATISTIC(NumNoUndef, "Number of function returns inferred as noundef returns"); @@ -105,6 +106,15 @@ 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 setRetNoUndef(Function &F) { if (!F.getReturnType()->isVoidTy() && !F.hasAttribute(AttributeList::ReturnIndex, Attribute::NoUndef)) { @@ -220,6 +230,7 @@ Changed |= setOnlyAccessesArgMemory(F); Changed |= setDoesNotThrow(F); Changed |= setDoesNotCapture(F, 1); + Changed |= setOnlyWritesMemory(F, 0); Changed |= setOnlyReadsMemory(F, 1); Changed |= setDoesNotAlias(F, 0); Changed |= setDoesNotAlias(F, 1); @@ -303,6 +314,7 @@ Changed |= setDoesNotThrow(F); Changed |= setDoesNotCapture(F, 0); Changed |= setDoesNotAlias(F, 0); + Changed |= setOnlyWritesMemory(F, 0); Changed |= setDoesNotCapture(F, 1); Changed |= setOnlyReadsMemory(F, 1); return Changed; @@ -311,6 +323,7 @@ Changed |= setDoesNotThrow(F); Changed |= setDoesNotCapture(F, 0); Changed |= setDoesNotAlias(F, 0); + Changed |= setOnlyWritesMemory(F, 0); Changed |= setDoesNotCapture(F, 2); Changed |= setOnlyReadsMemory(F, 2); return Changed; @@ -356,6 +369,7 @@ Changed |= setOnlyAccessesArgMemory(F); Changed |= setDoesNotAlias(F, 0); Changed |= setReturnedArg(F, 0); + Changed |= setOnlyWritesMemory(F, 0); Changed |= setDoesNotAlias(F, 1); Changed |= setDoesNotCapture(F, 1); Changed |= setOnlyReadsMemory(F, 1); @@ -364,6 +378,7 @@ Changed |= setDoesNotThrow(F); Changed |= setOnlyAccessesArgMemory(F); Changed |= setReturnedArg(F, 0); + Changed |= setOnlyWritesMemory(F, 0); Changed |= setDoesNotCapture(F, 1); Changed |= setOnlyReadsMemory(F, 1); return Changed; @@ -372,6 +387,7 @@ Changed |= setDoesNotThrow(F); Changed |= setOnlyAccessesArgMemory(F); Changed |= setDoesNotAlias(F, 0); + Changed |= setOnlyWritesMemory(F, 0); Changed |= setDoesNotAlias(F, 1); Changed |= setDoesNotCapture(F, 1); Changed |= setOnlyReadsMemory(F, 1); @@ -450,8 +466,9 @@ Changed |= setDoesNotThrow(F); Changed |= setOnlyAccessesArgMemory(F); Changed |= setDoesNotCapture(F, 0); - Changed |= setDoesNotCapture(F, 1); Changed |= setOnlyReadsMemory(F, 0); + Changed |= setOnlyWritesMemory(F, 1); + Changed |= setDoesNotCapture(F, 1); return Changed; case LibFunc_bcmp: Changed |= setDoesNotThrow(F); @@ -464,6 +481,7 @@ Changed |= setDoesNotThrow(F); Changed |= setOnlyAccessesArgMemory(F); Changed |= setDoesNotCapture(F, 0); + Changed |= setOnlyWritesMemory(F, 0); return Changed; case LibFunc_calloc: Changed |= setRetNoUndef(F); @@ -922,6 +940,7 @@ case LibFunc_memset_pattern16: Changed |= setOnlyAccessesArgMemory(F); Changed |= setDoesNotCapture(F, 0); + Changed |= setOnlyWritesMemory(F, 0); Changed |= setDoesNotCapture(F, 1); Changed |= setOnlyReadsMemory(F, 1); return Changed; diff --git a/llvm/test/Analysis/BasicAA/libfuncs.ll b/llvm/test/Analysis/BasicAA/libfuncs.ll --- a/llvm/test/Analysis/BasicAA/libfuncs.ll +++ b/llvm/test/Analysis/BasicAA/libfuncs.ll @@ -106,13 +106,14 @@ declare i8* @memccpy(i8*, i8*, i32, i64) ; CHECK-LABEL: Function: test_memccpy_const_size -; CHECK: Both ModRef: Ptr: i8* %a <-> %res = call i8* @memccpy(i8* %a, i8* %b, i32 42, i64 4) +; CHECK: Just Mod: Ptr: i8* %a <-> %res = call i8* @memccpy(i8* %a, i8* %b, i32 42, i64 4) ; CHECK-NEXT: Just Ref: Ptr: i8* %b <-> %res = call i8* @memccpy(i8* %a, i8* %b, i32 42, i64 4) -; CHECK-NEXT: Both ModRef: Ptr: i8* %res <-> %res = call i8* @memccpy(i8* %a, i8* %b, i32 42, i64 4) -; CHECK-NEXT: Both ModRef: Ptr: i8* %a.gep.1 <-> %res = call i8* @memccpy(i8* %a, i8* %b, i32 42, i64 4) +; CHECK-NEXT: Just Mod: Ptr: i8* %res <-> %res = call i8* @memccpy(i8* %a, i8* %b, i32 42, i64 4) +; CHECK-NEXT: Just Mod: Ptr: i8* %a.gep.1 <-> %res = call i8* @memccpy(i8* %a, i8* %b, i32 42, i64 4) ; CHECK-NEXT: NoModRef: Ptr: i8* %a.gep.5 <-> %res = call i8* @memccpy(i8* %a, i8* %b, i32 42, i64 4) ; CHECK-NEXT: Just Ref: Ptr: i8* %b.gep.1 <-> %res = call i8* @memccpy(i8* %a, i8* %b, i32 42, i64 4) ; CHECK-NEXT: NoModRef: Ptr: i8* %b.gep.5 <-> %res = call i8* @memccpy(i8* %a, i8* %b, i32 42, i64 4) + define i8* @test_memccpy_const_size(i8* noalias %a, i8* noalias %b) { entry: %res = call i8* @memccpy(i8* %a, i8* %b, i32 42, i64 4) 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 @@ -247,10 +247,10 @@ ; CHECK-NVPTX-NOT: declare i32 @bcmp(i8* nocapture, i8* nocapture, i64) [[G2]] declare i32 @bcmp(i8*, i8*, i64) -; CHECK: declare void @bcopy(i8* nocapture readonly, i8* nocapture, i64) [[ARGMEMONLY_NOFREE_NOUNWIND:#[0-9]+]] +; CHECK: declare void @bcopy(i8* nocapture readonly, i8* nocapture writeonly, i64) [[ARGMEMONLY_NOFREE_NOUNWIND:#[0-9]+]] declare void @bcopy(i8*, i8*, i64) -; CHECK: declare void @bzero(i8* nocapture, i64) [[ARGMEMONLY_NOFREE_NOUNWIND:#[0-9]+]] +; CHECK: declare void @bzero(i8* nocapture writeonly, i64) [[ARGMEMONLY_NOFREE_NOUNWIND:#[0-9]+]] declare void @bzero(i8*, i64) ; CHECK: declare noalias noundef i8* @calloc(i64, i64) [[G1]] @@ -619,7 +619,7 @@ ; CHECK-LINUX: declare noalias i8* @memalign(i64, i64) [[G0]] declare i8* @memalign(i64, i64) -; CHECK: declare i8* @memccpy(i8* noalias, i8* noalias nocapture readonly, i32, i64) [[ARGMEMONLY_NOFREE_NOUNWIND:#[0-9]+]] +; CHECK: declare i8* @memccpy(i8* noalias writeonly, i8* noalias nocapture readonly, i32, i64) [[ARGMEMONLY_NOFREE_NOUNWIND:#[0-9]+]] declare i8* @memccpy(i8*, i8*, i32, i64) ; CHECK-LINUX: declare i8* @memchr(i8*, i32, i64) [[ARGMEMONLY_NOFREE_NOUNWIND_READONLY]] @@ -630,13 +630,13 @@ ; CHECK: declare i32 @memcmp(i8* nocapture, i8* nocapture, i64) [[ARGMEMONLY_NOFREE_NOUNWIND_READONLY]] declare i32 @memcmp(i8*, i8*, i64) -; CHECK: declare i8* @memcpy(i8* noalias returned, i8* noalias nocapture readonly, i64) [[ARGMEMONLY_NOFREE_NOUNWIND]] +; CHECK: declare i8* @memcpy(i8* noalias returned writeonly, i8* noalias nocapture readonly, i64) [[ARGMEMONLY_NOFREE_NOUNWIND]] declare i8* @memcpy(i8*, i8*, i64) -; CHECK: declare i8* @mempcpy(i8* noalias, i8* noalias nocapture readonly, i64) [[ARGMEMONLY_NOFREE_NOUNWIND]] +; CHECK: declare i8* @mempcpy(i8* noalias writeonly, i8* noalias nocapture readonly, i64) [[ARGMEMONLY_NOFREE_NOUNWIND]] declare i8* @mempcpy(i8*, i8*, i64) -; CHECK: declare i8* @memmove(i8* returned, i8* nocapture readonly, i64) [[ARGMEMONLY_NOFREE_NOUNWIND]] +; CHECK: declare i8* @memmove(i8* returned writeonly, i8* nocapture readonly, i64) [[ARGMEMONLY_NOFREE_NOUNWIND]] declare i8* @memmove(i8*, i8*, i64) ; CHECK: declare i8* @memset(i8*, i32, i64) [[G0]] @@ -795,10 +795,10 @@ ; CHECK: declare x86_fp80 @sinl(x86_fp80) [[G0]] declare x86_fp80 @sinl(x86_fp80) -; CHECK: declare noundef i32 @snprintf(i8* noalias nocapture noundef, i64 noundef, i8* nocapture noundef readonly, ...) [[G1]] +; CHECK: declare noundef i32 @snprintf(i8* noalias nocapture noundef writeonly, i64 noundef, i8* nocapture noundef readonly, ...) [[G1]] declare i32 @snprintf(i8*, i64, i8*, ...) -; CHECK: declare noundef i32 @sprintf(i8* noalias nocapture noundef, i8* nocapture noundef readonly, ...) [[G1]] +; CHECK: declare noundef i32 @sprintf(i8* noalias nocapture noundef writeonly, i8* nocapture noundef readonly, ...) [[G1]] declare i32 @sprintf(i8*, i8*, ...) ; CHECK: declare double @sqrt(double) [[G0]] @@ -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* noalias, i8* noalias nocapture readonly) [[ARGMEMONLY_NOFREE_NOUNWIND]] +; CHECK: declare i8* @stpcpy(i8* noalias writeonly, i8* noalias nocapture readonly) [[ARGMEMONLY_NOFREE_NOUNWIND]] declare i8* @stpcpy(i8*, i8*) -; CHECK: declare i8* @stpncpy(i8* noalias, i8* noalias nocapture readonly, i64) [[ARGMEMONLY_NOFREE_NOUNWIND]] +; CHECK: declare i8* @stpncpy(i8* noalias writeonly, i8* noalias nocapture readonly, i64) [[ARGMEMONLY_NOFREE_NOUNWIND]] 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* noalias returned, i8* noalias nocapture readonly) [[ARGMEMONLY_NOFREE_NOUNWIND]] +; CHECK: declare i8* @strcat(i8* noalias returned writeonly, i8* noalias nocapture readonly) [[ARGMEMONLY_NOFREE_NOUNWIND]] 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) [[ARGMEMONLY_NOFREE_NOUNWIND]] +; CHECK: declare i8* @strcpy(i8* noalias returned writeonly, i8* noalias nocapture readonly) [[ARGMEMONLY_NOFREE_NOUNWIND]] 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* noalias returned, i8* noalias nocapture readonly, i64) [[ARGMEMONLY_NOFREE_NOUNWIND]] +; CHECK: declare i8* @strncat(i8* noalias returned writeonly, i8* noalias nocapture readonly, i64) [[ARGMEMONLY_NOFREE_NOUNWIND]] 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) [[ARGMEMONLY_NOFREE_NOUNWIND]] +; CHECK: declare i8* @strncpy(i8* noalias returned writeonly, i8* noalias nocapture readonly, i64) [[ARGMEMONLY_NOFREE_NOUNWIND]] declare i8* @strncpy(i8*, i8*, i64) ; CHECK: declare noalias i8* @strndup(i8* nocapture readonly, i64) [[G1]] @@ -1007,7 +1007,7 @@ ; memset_pattern16 isn't available everywhere. -; CHECK-DARWIN: declare void @memset_pattern16(i8* nocapture, i8* nocapture readonly, i64) [[ARGMEMONLY_NOFREE:#[0-9]+]] +; CHECK-DARWIN: declare void @memset_pattern16(i8* nocapture writeonly, i8* nocapture readonly, i64) [[ARGMEMONLY_NOFREE:#[0-9]+]] declare void @memset_pattern16(i8*, i8*, i64) ; CHECK: attributes [[G0]] = { nofree } diff --git a/llvm/test/Transforms/LoopIdiom/basic.ll b/llvm/test/Transforms/LoopIdiom/basic.ll --- a/llvm/test/Transforms/LoopIdiom/basic.ll +++ b/llvm/test/Transforms/LoopIdiom/basic.ll @@ -1064,5 +1064,5 @@ } ; Validate that "memset_pattern" has the proper attributes. -; CHECK: declare void @memset_pattern16(i8* nocapture, i8* nocapture readonly, i64) [[ATTRS:#[0-9]+]] +; CHECK: declare void @memset_pattern16(i8* nocapture writeonly, i8* nocapture readonly, i64) [[ATTRS:#[0-9]+]] ; CHECK: [[ATTRS]] = { argmemonly nofree }