Skip to content

[InferAttrs] Annotate math and basic string libcalls with nosync#197761

Merged
antoniofrighetto merged 2 commits into
llvm:mainfrom
antoniofrighetto:feature/infer-nosync
May 26, 2026
Merged

[InferAttrs] Annotate math and basic string libcalls with nosync#197761
antoniofrighetto merged 2 commits into
llvm:mainfrom
antoniofrighetto:feature/infer-nosync

Conversation

@antoniofrighetto

Copy link
Copy Markdown
Contributor

Math libcalls as well as some simple string ones do not create synchronizes-with edges, thus nosync may be derived.

(Excluded strings setting locale)

Partial revive of: https://reviews.llvm.org/D98605.

Co-authored-by: Johannes Doerfert jdoerfert.llvm@gmail.com

Math libcalls as well as some string libcalls do not create
synchronizes-with edges, thus `nosync` may be derived.

Co-authored-by: Johannes Doerfert <jdoerfert.llvm@gmail.com>
@llvmorg-github-actions

Copy link
Copy Markdown

@llvm/pr-subscribers-llvm-transforms

Author: Antonio Frighetto (antoniofrighetto)

Changes

Math libcalls as well as some simple string ones do not create synchronizes-with edges, thus nosync may be derived.

(Excluded strings setting locale)

Partial revive of: https://reviews.llvm.org/D98605.

Co-authored-by: Johannes Doerfert <jdoerfert.llvm@gmail.com>


Patch is 56.18 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/197761.diff

6 Files Affected:

  • (modified) llvm/lib/Transforms/Utils/BuildLibCalls.cpp (+30)
  • (modified) llvm/test/Transforms/InferFunctionAttrs/annotate.ll (+147-147)
  • (modified) llvm/test/Transforms/LICM/strlen.ll (+1-1)
  • (modified) llvm/test/Transforms/MergeICmps/X86/alias-merge-blocks.ll (+1-1)
  • (modified) llvm/test/Transforms/MergeICmps/X86/entry-block-shuffled.ll (+1-1)
  • (modified) llvm/test/Transforms/PreISelIntrinsicLowering/X86/memset-pattern.ll (+1-1)
diff --git a/llvm/lib/Transforms/Utils/BuildLibCalls.cpp b/llvm/lib/Transforms/Utils/BuildLibCalls.cpp
index f53b9555a7d1b..93fc1e679b423 100644
--- a/llvm/lib/Transforms/Utils/BuildLibCalls.cpp
+++ b/llvm/lib/Transforms/Utils/BuildLibCalls.cpp
@@ -61,6 +61,7 @@ STATISTIC(NumReturnedArg, "Number of arguments inferred as returned");
 STATISTIC(NumWillReturn, "Number of functions inferred as willreturn");
 STATISTIC(NumCold, "Number of functions inferred as cold");
 STATISTIC(NumNoReturn, "Number of functions inferred as no return");
+STATISTIC(NumNoSync, "Number of functions inferred as nosync");
 
 static bool setDoesNotAccessMemory(Function &F) {
   if (F.doesNotAccessMemory())
@@ -195,6 +196,14 @@ static bool setDoesNotAlias(Function &F, unsigned ArgNo) {
   return true;
 }
 
+static bool setDoesNotSync(Function &F) {
+  if (F.hasFnAttribute(Attribute::NoSync))
+    return false;
+  F.addFnAttr(Attribute::NoSync);
+  ++NumNoSync;
+  return true;
+}
+
 static bool setOnlyReadsMemory(Function &F, unsigned ArgNo) {
   if (F.hasParamAttribute(ArgNo, Attribute::ReadOnly))
     return false;
@@ -348,6 +357,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
     Changed |= setOnlyAccessesArgMemory(F);
     Changed |= setWillReturn(F);
     Changed |= setDoesNotCapture(F, 0);
+    Changed |= setDoesNotSync(F);
     break;
   case LibFunc_strchr:
   case LibFunc_strrchr:
@@ -356,6 +366,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
     Changed |= setDoesNotThrow(F);
     Changed |= setDoesNotCallback(F);
     Changed |= setWillReturn(F);
+    Changed |= setDoesNotSync(F);
     break;
   case LibFunc_strtol:
   case LibFunc_strtod:
@@ -381,6 +392,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
     Changed |= setOnlyReadsMemory(F, 1);
     Changed |= setDoesNotAlias(F, 0);
     Changed |= setDoesNotAlias(F, 1);
+    Changed |= setDoesNotSync(F);
     break;
   case LibFunc_strcpy:
   case LibFunc_strncpy:
@@ -397,6 +409,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
     Changed |= setOnlyReadsMemory(F, 1);
     Changed |= setDoesNotAlias(F, 0);
     Changed |= setDoesNotAlias(F, 1);
+    Changed |= setDoesNotSync(F);
     break;
   case LibFunc_strxfrm:
     Changed |= setDoesNotThrow(F);
@@ -417,6 +430,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
     Changed |= setOnlyReadsMemory(F);
     Changed |= setDoesNotCapture(F, 0);
     Changed |= setDoesNotCapture(F, 1);
+    Changed |= setDoesNotSync(F);
     break;
   case LibFunc_strcoll:
   case LibFunc_strcasecmp:  // 0,1
@@ -438,6 +452,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
     Changed |= setDoesNotCallback(F);
     Changed |= setWillReturn(F);
     Changed |= setDoesNotCapture(F, 1);
+    Changed |= setDoesNotSync(F);
     break;
   case LibFunc_strtok:
   case LibFunc_strtok_r:
@@ -547,6 +562,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
     Changed |= setWillReturn(F);
     Changed |= setDoesNotCapture(F, 0);
     Changed |= setDoesNotCapture(F, 1);
+    Changed |= setDoesNotSync(F);
     break;
   case LibFunc_memchr:
   case LibFunc_memrchr:
@@ -555,6 +571,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
     Changed |= setOnlyAccessesArgMemory(F);
     Changed |= setOnlyReadsMemory(F);
     Changed |= setWillReturn(F);
+    Changed |= setDoesNotSync(F);
     break;
   case LibFunc_modf:
   case LibFunc_modff:
@@ -565,6 +582,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
     Changed |= setOnlyAccessesArgMemory(F);
     Changed |= setOnlyWritesMemory(F);
     Changed |= setDoesNotCapture(F, 1);
+    Changed |= setDoesNotSync(F);
     break;
   case LibFunc_memcpy:
     Changed |= setDoesNotThrow(F);
@@ -577,6 +595,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
     Changed |= setDoesNotAlias(F, 1);
     Changed |= setDoesNotCapture(F, 1);
     Changed |= setOnlyReadsMemory(F, 1);
+    Changed |= setDoesNotSync(F);
     break;
   case LibFunc_memmove:
     Changed |= setDoesNotThrow(F);
@@ -587,6 +606,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
     Changed |= setOnlyWritesMemory(F, 0);
     Changed |= setDoesNotCapture(F, 1);
     Changed |= setOnlyReadsMemory(F, 1);
+    Changed |= setDoesNotSync(F);
     break;
   case LibFunc_mempcpy:
   case LibFunc_memccpy:
@@ -601,6 +621,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
     Changed |= setDoesNotAlias(F, 1);
     Changed |= setDoesNotCapture(F, 1);
     Changed |= setOnlyReadsMemory(F, 1);
+    Changed |= setDoesNotSync(F);
     break;
   case LibFunc_memalign:
     Changed |= setAllocFamily(F, "malloc");
@@ -704,6 +725,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
     Changed |= setOnlyReadsMemory(F, 0);
     Changed |= setOnlyWritesMemory(F, 1);
     Changed |= setDoesNotCapture(F, 1);
+    Changed |= setDoesNotSync(F);
     break;
   case LibFunc_bcmp:
     Changed |= setDoesNotThrow(F);
@@ -713,6 +735,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
     Changed |= setWillReturn(F);
     Changed |= setDoesNotCapture(F, 0);
     Changed |= setDoesNotCapture(F, 1);
+    Changed |= setDoesNotSync(F);
     break;
   case LibFunc_bzero:
     Changed |= setDoesNotThrow(F);
@@ -721,6 +744,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
     Changed |= setWillReturn(F);
     Changed |= setDoesNotCapture(F, 0);
     Changed |= setOnlyWritesMemory(F, 0);
+    Changed |= setDoesNotSync(F);
     break;
   case LibFunc_calloc:
   case LibFunc_vec_calloc:
@@ -757,6 +781,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
     Changed |= setOnlyReadsMemory(F);
     Changed |= setWillReturn(F);
     Changed |= setDoesNotCapture(F, 0);
+    Changed |= setDoesNotSync(F);
     break;
   case LibFunc_access:
     Changed |= setRetAndArgsNoUndef(F);
@@ -836,6 +861,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
     Changed |= setOnlyAccessesArgMemory(F);
     Changed |= setOnlyWritesMemory(F);
     Changed |= setDoesNotCapture(F, 1);
+    Changed |= setDoesNotSync(F);
     break;
   case LibFunc_fstatvfs:
     Changed |= setRetAndArgsNoUndef(F);
@@ -1197,6 +1223,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
     Changed |= setOnlyWritesMemory(F, 0);
     Changed |= setDoesNotThrow(F);
     Changed |= setDoesNotCallback(F);
+    Changed |= setDoesNotSync(F);
     break;
   case LibFunc_abort:
     Changed |= setIsCold(F);
@@ -1337,6 +1364,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
     Changed |= setDoesNotFreeMemory(F);
     Changed |= setWillReturn(F);
     Changed |= setOnlyWritesErrnoMemory(F);
+    Changed |= setDoesNotSync(F);
     break;
   case LibFunc_abs:
   case LibFunc_cbrt:
@@ -1395,6 +1423,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
     Changed |= setDoesNotCallback(F);
     Changed |= setDoesNotFreeMemory(F);
     Changed |= setWillReturn(F);
+    Changed |= setDoesNotSync(F);
     break;
   case LibFunc_sincos:
   case LibFunc_sincosf:
@@ -1412,6 +1441,7 @@ bool llvm::inferNonMandatoryLibFuncAttrs(Function &F,
     Changed |= setDoesNotCapture(F, 2);
     Changed |= setWillReturn(F);
     Changed |= setOnlyWritesArgMemOrErrnoMem(F);
+    Changed |= setDoesNotSync(F);
     break;
   default:
     // FIXME: It'd be really nice to cover all the library functions we're
diff --git a/llvm/test/Transforms/InferFunctionAttrs/annotate.ll b/llvm/test/Transforms/InferFunctionAttrs/annotate.ll
index ed5d5a56770f9..bb449ab3a71ef 100644
--- a/llvm/test/Transforms/InferFunctionAttrs/annotate.ll
+++ b/llvm/test/Transforms/InferFunctionAttrs/annotate.ll
@@ -201,70 +201,70 @@ declare i32 @abs(i32)
 ; CHECK: declare noundef i32 @access(ptr noundef readonly captures(none), i32 noundef) [[NOFREE_NOUNWIND:#[0-9]+]]
 declare i32 @access(ptr, i32)
 
-; CHECK: declare double @acos(double) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN:#[0-9]+]]
+; CHECK: declare double @acos(double) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN:#[0-9]+]]
 declare double @acos(double)
 
-; CHECK: declare float @acosf(float) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare float @acosf(float) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare float @acosf(float)
 
-; CHECK: declare double @acosh(double) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare double @acosh(double) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare double @acosh(double)
 
-; CHECK: declare float @acoshf(float) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare float @acoshf(float) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare float @acoshf(float)
 
-; CHECK: declare x86_fp80 @acoshl(x86_fp80) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare x86_fp80 @acoshl(x86_fp80) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare x86_fp80 @acoshl(x86_fp80)
 
-; CHECK: declare x86_fp80 @acosl(x86_fp80) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare x86_fp80 @acosl(x86_fp80) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare x86_fp80 @acosl(x86_fp80)
 
 ; CHECK: declare noalias noundef ptr @aligned_alloc(i64 allocalign noundef, i64 noundef) [[INACCESSIBLEMEMORERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN_ALLOCKIND_ALLOCUNINIT_ALLOCSIZE1_FAMILY_MALLOC:#[0-9]+]]
 declare ptr @aligned_alloc(i64, i64)
 
-; CHECK: declare double @asin(double) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare double @asin(double) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare double @asin(double)
 
-; CHECK: declare float @asinf(float) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare float @asinf(float) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare float @asinf(float)
 
-; CHECK: declare double @asinh(double) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare double @asinh(double) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare double @asinh(double)
 
-; CHECK: declare float @asinhf(float) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare float @asinhf(float) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare float @asinhf(float)
 
-; CHECK: declare x86_fp80 @asinhl(x86_fp80) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare x86_fp80 @asinhl(x86_fp80) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare x86_fp80 @asinhl(x86_fp80)
 
-; CHECK: declare x86_fp80 @asinl(x86_fp80) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare x86_fp80 @asinl(x86_fp80) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare x86_fp80 @asinl(x86_fp80)
 
-; CHECK: declare double @atan(double) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare double @atan(double) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare double @atan(double)
 
-; CHECK: declare double @atan2(double, double) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare double @atan2(double, double) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare double @atan2(double, double)
 
-; CHECK: declare float @atan2f(float, float) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare float @atan2f(float, float) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare float @atan2f(float, float)
 
-; CHECK: declare x86_fp80 @atan2l(x86_fp80, x86_fp80) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare x86_fp80 @atan2l(x86_fp80, x86_fp80) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare x86_fp80 @atan2l(x86_fp80, x86_fp80)
 
-; CHECK: declare float @atanf(float) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare float @atanf(float) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare float @atanf(float)
 
-; CHECK: declare double @atanh(double) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare double @atanh(double) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare double @atanh(double)
 
-; CHECK: declare float @atanhf(float) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare float @atanhf(float) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare float @atanhf(float)
 
-; CHECK: declare x86_fp80 @atanhl(x86_fp80) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare x86_fp80 @atanhl(x86_fp80) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare x86_fp80 @atanhl(x86_fp80)
 
-; CHECK: declare x86_fp80 @atanl(x86_fp80) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare x86_fp80 @atanl(x86_fp80) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare x86_fp80 @atanl(x86_fp80)
 
 ; CHECK: declare double @atof(ptr captures(none)) [[NOFREE_NOUNWIND_READONLY_WILLRETURN:#[0-9]+]]
@@ -283,10 +283,10 @@ declare i64 @atoll(ptr)
 ; CHECK-NOLINUX: declare i32 @bcmp(ptr, ptr, i64){{$}}
 declare i32 @bcmp(ptr, ptr, i64)
 
-; CHECK: declare void @bcopy(ptr readonly captures(none), ptr writeonly captures(none), i64)  [[ARGMEMONLY_NOFREE_NOUNWIND_WILLRETURN:#[0-9]+]]
+; CHECK: declare void @bcopy(ptr readonly captures(none), ptr writeonly captures(none), i64)  [[ARGMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN:#[0-9]+]]
 declare void @bcopy(ptr, ptr, i64)
 
-; CHECK: declare void @bzero(ptr writeonly captures(none), i64)  [[ARGMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare void @bzero(ptr writeonly captures(none), i64)  [[ARGMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare void @bzero(ptr, i64)
 
 ; CHECK: declare noalias noundef ptr @calloc(i64 noundef, i64 noundef) [[INACCESSIBLEMEMORERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN_ALLOCKIND_ALLOCZEROED_ALLOCSIZE01_FAMILY_MALLOC:#[0-9]+]]
@@ -339,52 +339,52 @@ declare float @copysignf(float, float)
 ; CHECK: declare x86_fp80 @copysignl(x86_fp80, x86_fp80) [[MEMNONE_NOFREE_NOUNWIND_WILLRETURN]]
 declare x86_fp80 @copysignl(x86_fp80, x86_fp80)
 
-; CHECK: declare double @cos(double) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare double @cos(double) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare double @cos(double)
 
-; CHECK: declare float @cosf(float) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare float @cosf(float) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare float @cosf(float)
 
-; CHECK: declare double @cosh(double) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare double @cosh(double) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare double @cosh(double)
 
-; CHECK: declare float @coshf(float) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare float @coshf(float) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare float @coshf(float)
 
-; CHECK: declare x86_fp80 @coshl(x86_fp80) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare x86_fp80 @coshl(x86_fp80) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare x86_fp80 @coshl(x86_fp80)
 
-; CHECK: declare x86_fp80 @cosl(x86_fp80) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare x86_fp80 @cosl(x86_fp80) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare x86_fp80 @cosl(x86_fp80)
 
 ; CHECK: declare noundef ptr @ctermid(ptr noundef captures(none)) [[NOFREE_NOUNWIND]]
 declare ptr @ctermid(ptr)
 
-; CHECK: declare double @exp(double) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare double @exp(double) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare double @exp(double)
 
-; CHECK: declare double @exp2(double) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare double @exp2(double) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare double @exp2(double)
 
-; CHECK: declare float @exp2f(float) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare float @exp2f(float) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare float @exp2f(float)
 
-; CHECK: declare x86_fp80 @exp2l(x86_fp80) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare x86_fp80 @exp2l(x86_fp80) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare x86_fp80 @exp2l(x86_fp80)
 
-; CHECK: declare float @expf(float) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare float @expf(float) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare float @expf(float)
 
-; CHECK: declare x86_fp80 @expl(x86_fp80) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare x86_fp80 @expl(x86_fp80) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare x86_fp80 @expl(x86_fp80)
 
-; CHECK: declare double @expm1(double) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare double @expm1(double) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare double @expm1(double)
 
-; CHECK: declare float @expm1f(float) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare float @expm1f(float) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare float @expm1f(float)
 
-; CHECK: declare x86_fp80 @expm1l(x86_fp80) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare x86_fp80 @expm1l(x86_fp80) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare x86_fp80 @expm1l(x86_fp80)
 
 ; CHECK: declare double @fabs(double) [[MEMNONE_NOFREE_NOUNWIND_WILLRETURN]]
@@ -491,13 +491,13 @@ declare float @fminimum_numf(float, float)
 ; CHECK: declare x86_fp80 @fminimum_numl(x86_fp80, x86_fp80) [[MEMNONE_NOFREE_NOUNWIND_WILLRETURN]]
 declare x86_fp80 @fminimum_numl(x86_fp80, x86_fp80)
 
-; CHECK: declare double @fmod(double, double) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare double @fmod(double, double) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare double @fmod(double, double)
 
-; CHECK: declare float @fmodf(float, float) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare float @fmodf(float, float) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare float @fmodf(float, float)
 
-; CHECK: declare x86_fp80 @fmodl(x86_fp80, x86_fp80) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare x86_fp80 @fmodl(x86_fp80, x86_fp80) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare x86_fp80 @fmodl(x86_fp80, x86_fp80)
 
 ; CHECK: declare noalias noundef ptr @fopen(ptr noundef readonly captures(none), ptr noundef readonly captures(none)) [[NOFREE_NOUNWIND]]
@@ -521,13 +521,13 @@ declare void @free(ptr)
 ; CHECK-AIX: declare void @vec_free(ptr allocptr noundef captures(none)) [[INACCESSIBLEMEMORARGMEMONLY_NOUNWIND_WILLRETURN_FAMILY_VEC_MALLOC:#[0-9]+]]
 declare void @vec_free(ptr)
 
-; CHECK: declare double @frexp(double, ptr captures(none)) [[ARGMEMONLY_NOFREE_NOUNWIND_WILLRETURN_WRITEONLY:#[0-9]+]]
+; CHECK: declare double @frexp(double, ptr captures(none)) [[ARGMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN_WRITEONLY:#[0-9]+]]
 declare double @frexp(double, ptr)
 
-; CHECK: declare float @frexpf(float, ptr captures(none)) [[ARGMEMONLY_NOFREE_NOUNWIND_WILLRETURN_WRITEONLY]]
+; CHECK: declare float @frexpf(float, ptr captures(none)) [[ARGMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN_WRITEONLY]]
 declare float @frexpf(float, ptr)
 
-; CHECK: declare x86_fp80 @frexpl(x86_fp80, ptr captures(none)) [[ARGMEMONLY_NOFREE_NOUNWIND_WILLRETURN_WRITEONLY]]
+; CHECK: declare x86_fp80 @frexpl(x86_fp80, ptr captures(none)) [[ARGMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN_WRITEONLY]]
 declare x86_fp80 @frexpl(x86_fp80, ptr)
 
 ; CHECK: declare noundef i32 @fscanf(ptr noundef captures(none), ptr noundef readonly captures(none), ...) [[NOFREE_NOUNWIND]]
@@ -607,13 +607,13 @@ declare ptr @gets(ptr)
 ; CHECK: declare noundef i32 @gettimeofday(ptr noundef captures(none), ptr noundef captures(none)) [[NOFREE_NOUNWIND]]
 declare i32 @gettimeofday(ptr, ptr)
 
-; CHECK: declare double @hypot(double, double) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare double @hypot(double, double) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare double @hypot(double, double)
 
-; CHECK: declare float @hypotf(float, float) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare float @hypotf(float, float) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare float @hypotf(float, float)
 
-; CHECK: declare x86_fp80 @hypotl(x86_fp80, x86_fp80) [[ERRNOMEMONLY_NOFREE_NOUNWIND_WILLRETURN]]
+; CHECK: declare x86_fp80 @hypotl(x86_fp80, x86_fp80) [[ERRNOMEMONLY_NOFREE_NOSYNC_NOUNWIND_WILLRETURN]]
 declare x86_fp80 @hypotl(x86_fp80, x86_fp80)
 
 ; CHECK: declare i32 @isascii(i32) [[NOFREE_NOUNWIND_WILLRETURN:#[0-9]+]]
@@ -628,79 +628,79 @@ declare i64 @labs(i64)
 ; CHECK: declare noundef i32 @lchown(ptr noundef readonly c...
[truncated]

@nikic nikic left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

It would be great to have a UTC option to generate checks for function declarations. Manually updating this test is annoying and fragile...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd expect test updates for the fmax family as well?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, good catch. It looks like fmax and friends already get nosync via memory(none) + not convergent (from inferAttributesFromOthers()). I dropped setDoesNotSync() from these functions (and update the test to show the attribute), while leaving it to those that write errno or arg memory.

@jdoerfert

Copy link
Copy Markdown
Member

Co-authored-by: Johannes Doerfert jdoerfert.llvm@gmail.com

I forgot about this. Thanks for picking it up!

Changed |= setOnlyWritesMemory(F, 0);
Changed |= setDoesNotThrow(F);
Changed |= setDoesNotCallback(F);
Changed |= setDoesNotSync(F);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

memset_chk terminates the program when a buffer overflow occurs. Should it be marked as argmem + inaccessiblemem - nocallback - nosync?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess killing the process is synchronization. Depending on how exactly you define "synchronization" in a multi-process environment... if you write to shared memory, then kill the process, and another process waits for the process to die, then reads the shared memory, I guess we should count that as happens-before.

I'm not sure that's really a useful analysis in this context, though. A _chk function failing is a security backstop, not intentional behavior.

The other attributes seem fine.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, I always considered attributes to be "in-process" but that is open to debate.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought IPC via shared memory, waitpid, etc. were quite outside of the LLVM abstract machine / memory model, but even so, I'm not sure how useful LLVM attributes would be if process termination counted as synchronization.

As for inaccessiblemem/nocallback, haven't given much thought, though agree in principle we could touch signal states / run into user code via signal handlers on the abort path. Though, this slightly sounds like to me something broader to how LLVM would model fatal paths rather than something specific to _chk functions.

@antoniofrighetto
antoniofrighetto merged commit 377af85 into llvm:main May 26, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants