]> granicus.if.org Git - clang/commitdiff
[nonnull] Teach Clang to attach the nonnull LLVM attribute to
authorChandler Carruth <chandlerc@gmail.com>
Wed, 22 Mar 2017 09:09:13 +0000 (09:09 +0000)
committerChandler Carruth <chandlerc@gmail.com>
Wed, 22 Mar 2017 09:09:13 +0000 (09:09 +0000)
declarations and calls instead of just definitions, and then teach it to
*not* attach such attributes even if the source code contains them.

This follows the design direction discussed on cfe-dev here:
http://lists.llvm.org/pipermail/cfe-dev/2017-January/052066.html

The idea is that for C standard library builtins, even if the library
vendor chooses to annotate their routines with __attribute__((nonnull)),
we will ignore those attributes which pertain to pointer arguments that
have an associated size. This allows the widespread (and seemingly
reasonable) pattern of calling these routines with a null pointer and
a zero size. I have only done this for the library builtins currently
recognized by Clang, but we can now trivially add to this set. This will
be controllable with -fno-builtin if anyone should care to do so.

Note that this does *not* change the AST. As a consequence, warnings,
static analysis, and source code rewriting are not impacted.

This isn't even a regression on any platform as neither Clang nor LLVM
have ever put 'nonnull' onto these arguments for declarations. All this
patch does is enable it on other declarations while preventing us from
ever accidentally enabling it on these libc functions due to a library
vendor.

It will also allow any other libraries using this annotation to gain
optimizations based on the annotation even when only a declaration is
visible.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@298491 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/ASTContext.h
include/clang/Basic/Builtins.def
lib/AST/ASTContext.cpp
lib/CodeGen/CGCall.cpp
test/CodeGen/nonnull.c

index 5fa515d02db9540c12d370a40da392753dfe2964..2a6709104ae1ea486d4486f796fff6afd5eb6c6e 100644 (file)
@@ -1864,7 +1864,9 @@ public:
   /// arguments to the builtin that are required to be integer constant
   /// expressions.
   QualType GetBuiltinType(unsigned ID, GetBuiltinTypeError &Error,
-                          unsigned *IntegerConstantArgs = nullptr) const;
+                          unsigned *IntegerConstantArgs = nullptr,
+                          bool *OverrideNonnullReturn = nullptr,
+                          unsigned *OverrideNonnullArgs = nullptr) const;
 
 private:
   CanQualType getFromTargetType(unsigned Type) const;
index e7084de9ceb3535339411ad9a6c30fcaab4d4859..9174d391034058ae4c28ade7babeaf18f688cfa6 100644 (file)
 //  S   -> signed
 //  U   -> unsigned
 //  I   -> Required to constant fold to an integer constant expression.
+//  N   -> Do not assume non-null for optimizations even if attributed nonnull.
+//         This can be used when a relevant standard requires arguments or
+//         returns to be non-null and they are attributed accordingly but in
+//         practice are not used in this way. This typically used when a size
+//         parameter is also provided and when zero it would be reasonable to
+//         give an invalid pointer.
 //
 // Types may be postfixed with the following modifiers:
 // * -> pointer (optionally followed by an address space number, if no address
@@ -787,27 +793,27 @@ LIBBUILTIN(_Exit, "vi",           "fr",    "stdlib.h", ALL_LANGUAGES)
 LIBBUILTIN(malloc, "v*z",         "f",     "stdlib.h", ALL_LANGUAGES)
 LIBBUILTIN(realloc, "v*v*z",      "f",     "stdlib.h", ALL_LANGUAGES)
 // C99 string.h
-LIBBUILTIN(memcpy, "v*v*vC*z",    "f",     "string.h", ALL_LANGUAGES)
-LIBBUILTIN(memcmp, "ivC*vC*z",    "f",     "string.h", ALL_LANGUAGES)
-LIBBUILTIN(memmove, "v*v*vC*z",   "f",     "string.h", ALL_LANGUAGES)
-LIBBUILTIN(strcpy, "c*c*cC*",     "f",     "string.h", ALL_LANGUAGES)
-LIBBUILTIN(strncpy, "c*c*cC*z",   "f",     "string.h", ALL_LANGUAGES)
-LIBBUILTIN(strcmp, "icC*cC*",     "f",     "string.h", ALL_LANGUAGES)
-LIBBUILTIN(strncmp, "icC*cC*z",   "f",     "string.h", ALL_LANGUAGES)
-LIBBUILTIN(strcat, "c*c*cC*",     "f",     "string.h", ALL_LANGUAGES)
-LIBBUILTIN(strncat, "c*c*cC*z",   "f",     "string.h", ALL_LANGUAGES)
-LIBBUILTIN(strxfrm, "zc*cC*z",    "f",     "string.h", ALL_LANGUAGES)
-LIBBUILTIN(memchr, "v*vC*iz",     "f",     "string.h", ALL_LANGUAGES)
-LIBBUILTIN(strchr, "c*cC*i",      "f",     "string.h", ALL_LANGUAGES)
-LIBBUILTIN(strcspn, "zcC*cC*",    "f",     "string.h", ALL_LANGUAGES)
-LIBBUILTIN(strpbrk, "c*cC*cC*",   "f",     "string.h", ALL_LANGUAGES)
-LIBBUILTIN(strrchr, "c*cC*i",     "f",     "string.h", ALL_LANGUAGES)
-LIBBUILTIN(strspn, "zcC*cC*",     "f",     "string.h", ALL_LANGUAGES)
-LIBBUILTIN(strstr, "c*cC*cC*",    "f",     "string.h", ALL_LANGUAGES)
-LIBBUILTIN(strtok, "c*c*cC*",     "f",     "string.h", ALL_LANGUAGES)
-LIBBUILTIN(memset, "v*v*iz",      "f",     "string.h", ALL_LANGUAGES)
-LIBBUILTIN(strerror, "c*i",       "f",     "string.h", ALL_LANGUAGES)
-LIBBUILTIN(strlen, "zcC*",        "f",     "string.h", ALL_LANGUAGES)
+LIBBUILTIN(memcpy, "Nv*Nv*NvC*z",  "f",     "string.h", ALL_LANGUAGES)
+LIBBUILTIN(memcmp, "iNvC*NvC*z",   "f",     "string.h", ALL_LANGUAGES)
+LIBBUILTIN(memmove, "Nv*Nv*NvC*z", "f",     "string.h", ALL_LANGUAGES)
+LIBBUILTIN(strcpy, "c*c*cC*",      "f",     "string.h", ALL_LANGUAGES)
+LIBBUILTIN(strncpy, "Nc*Nc*NcC*z", "f",     "string.h", ALL_LANGUAGES)
+LIBBUILTIN(strcmp, "icC*cC*",      "f",     "string.h", ALL_LANGUAGES)
+LIBBUILTIN(strncmp, "iNcC*NcC*z",  "f",     "string.h", ALL_LANGUAGES)
+LIBBUILTIN(strcat, "c*c*cC*",      "f",     "string.h", ALL_LANGUAGES)
+LIBBUILTIN(strncat, "c*c*NcC*z",   "f",     "string.h", ALL_LANGUAGES)
+LIBBUILTIN(strxfrm, "zc*cC*z",     "f",     "string.h", ALL_LANGUAGES)
+LIBBUILTIN(memchr, "Nv*NvC*iz",    "f",     "string.h", ALL_LANGUAGES)
+LIBBUILTIN(strchr, "c*cC*i",       "f",     "string.h", ALL_LANGUAGES)
+LIBBUILTIN(strcspn, "zcC*cC*",     "f",     "string.h", ALL_LANGUAGES)
+LIBBUILTIN(strpbrk, "c*cC*cC*",    "f",     "string.h", ALL_LANGUAGES)
+LIBBUILTIN(strrchr, "c*cC*i",      "f",     "string.h", ALL_LANGUAGES)
+LIBBUILTIN(strspn, "zcC*cC*",      "f",     "string.h", ALL_LANGUAGES)
+LIBBUILTIN(strstr, "c*cC*cC*",     "f",     "string.h", ALL_LANGUAGES)
+LIBBUILTIN(strtok, "c*c*cC*",      "f",     "string.h", ALL_LANGUAGES)
+LIBBUILTIN(memset, "Nv*Nv*iz",     "f",     "string.h", ALL_LANGUAGES)
+LIBBUILTIN(strerror, "c*i",        "f",     "string.h", ALL_LANGUAGES)
+LIBBUILTIN(strlen, "zcC*",         "f",     "string.h", ALL_LANGUAGES)
 // C99 stdio.h
 LIBBUILTIN(printf, "icC*.",       "fp:0:", "stdio.h", ALL_LANGUAGES)
 LIBBUILTIN(fprintf, "iP*cC*.",    "fp:1:", "stdio.h", ALL_LANGUAGES)
@@ -841,12 +847,12 @@ LIBBUILTIN(toupper, "ii", "fnU", "ctype.h", ALL_LANGUAGES)
 // C99 wchar.h
 // FIXME: This list is incomplete. We should cover at least the functions that
 // take format strings.
-LIBBUILTIN(wcschr,  "w*wC*w",   "f", "wchar.h", ALL_LANGUAGES)
-LIBBUILTIN(wcscmp,  "iwC*wC*",  "f", "wchar.h", ALL_LANGUAGES)
-LIBBUILTIN(wcslen,  "zwC*",     "f", "wchar.h", ALL_LANGUAGES)
-LIBBUILTIN(wcsncmp, "iwC*wC*z", "f", "wchar.h", ALL_LANGUAGES)
-LIBBUILTIN(wmemchr, "w*wC*wz",  "f", "wchar.h", ALL_LANGUAGES)
-LIBBUILTIN(wmemcmp, "iwC*wC*z", "f", "wchar.h", ALL_LANGUAGES)
+LIBBUILTIN(wcschr,  "w*wC*w",     "f", "wchar.h", ALL_LANGUAGES)
+LIBBUILTIN(wcscmp,  "iwC*wC*",    "f", "wchar.h", ALL_LANGUAGES)
+LIBBUILTIN(wcslen,  "zwC*",       "f", "wchar.h", ALL_LANGUAGES)
+LIBBUILTIN(wcsncmp, "iNwC*NwC*z", "f", "wchar.h", ALL_LANGUAGES)
+LIBBUILTIN(wmemchr, "Nw*NwC*wz",  "f", "wchar.h", ALL_LANGUAGES)
+LIBBUILTIN(wmemcmp, "iNwC*NwC*z", "f", "wchar.h", ALL_LANGUAGES)
 
 // C99
 // In some systems setjmp is a macro that expands to _setjmp. We undefine
index 071537a051cc1d45cd0a28685615bc9fc2ae441f..1c1f78ad2669aa607a05702d594eeef41fa06a1a 100644 (file)
@@ -8499,13 +8499,14 @@ void ASTMutationListener::DeducedReturnType(const FunctionDecl *FD,
 /// to be an Integer Constant Expression.
 static QualType DecodeTypeFromStr(const char *&Str, const ASTContext &Context,
                                   ASTContext::GetBuiltinTypeError &Error,
-                                  bool &RequiresICE,
+                                  bool &RequiresICE, bool &OverrideNonnull,
                                   bool AllowTypeModifiers) {
   // Modifiers.
   int HowLong = 0;
   bool Signed = false, Unsigned = false;
   RequiresICE = false;
-  
+  OverrideNonnull = false;
+
   // Read the prefixed modifiers first.
   bool Done = false;
   while (!Done) {
@@ -8514,6 +8515,9 @@ static QualType DecodeTypeFromStr(const char *&Str, const ASTContext &Context,
     case 'I':
       RequiresICE = true;
       break;
+    case 'N':
+      OverrideNonnull = true;
+      break;
     case 'S':
       assert(!Unsigned && "Can't use both 'S' and 'U' modifiers!");
       assert(!Signed && "Can't use 'S' modifier multiple times!");
@@ -8648,8 +8652,8 @@ static QualType DecodeTypeFromStr(const char *&Str, const ASTContext &Context,
     assert(End != Str && "Missing vector size");
     Str = End;
 
-    QualType ElementType = DecodeTypeFromStr(Str, Context, Error, 
-                                             RequiresICE, false);
+    QualType ElementType = DecodeTypeFromStr(Str, Context, Error, RequiresICE,
+                                             OverrideNonnull, false);
     assert(!RequiresICE && "Can't require vector ICE");
     
     // TODO: No way to make AltiVec vectors in builtins yet.
@@ -8664,15 +8668,15 @@ static QualType DecodeTypeFromStr(const char *&Str, const ASTContext &Context,
     assert(End != Str && "Missing vector size");
     
     Str = End;
-    
+
     QualType ElementType = DecodeTypeFromStr(Str, Context, Error, RequiresICE,
-                                             false);
+                                             OverrideNonnull, false);
     Type = Context.getExtVectorType(ElementType, NumElements);
     break;    
   }
   case 'X': {
     QualType ElementType = DecodeTypeFromStr(Str, Context, Error, RequiresICE,
-                                             false);
+                                             OverrideNonnull, false);
     assert(!RequiresICE && "Can't require complex ICE");
     Type = Context.getComplexType(ElementType);
     break;
@@ -8754,27 +8758,37 @@ static QualType DecodeTypeFromStr(const char *&Str, const ASTContext &Context,
 }
 
 /// GetBuiltinType - Return the type for the specified builtin.
-QualType ASTContext::GetBuiltinType(unsigned Id,
-                                    GetBuiltinTypeError &Error,
-                                    unsigned *IntegerConstantArgs) const {
+QualType ASTContext::GetBuiltinType(unsigned Id, GetBuiltinTypeError &Error,
+                                    unsigned *IntegerConstantArgs,
+                                    bool *OverrideNonnullReturn,
+                                    unsigned *OverrideNonnullArgs) const {
   const char *TypeStr = BuiltinInfo.getTypeString(Id);
 
   SmallVector<QualType, 8> ArgTypes;
 
   bool RequiresICE = false;
+  bool OverrideNonnull = false;
   Error = GE_None;
-  QualType ResType = DecodeTypeFromStr(TypeStr, *this, Error,
-                                       RequiresICE, true);
+  QualType ResType = DecodeTypeFromStr(TypeStr, *this, Error, RequiresICE,
+                                       OverrideNonnull, true);
   if (Error != GE_None)
     return QualType();
-  
+
+  if (OverrideNonnullReturn)
+    *OverrideNonnullReturn = OverrideNonnull;
   assert(!RequiresICE && "Result of intrinsic cannot be required to be an ICE");
-  
+
   while (TypeStr[0] && TypeStr[0] != '.') {
-    QualType Ty = DecodeTypeFromStr(TypeStr, *this, Error, RequiresICE, true);
+    QualType Ty = DecodeTypeFromStr(TypeStr, *this, Error, RequiresICE,
+                                    OverrideNonnull, true);
     if (Error != GE_None)
       return QualType();
 
+    // If this argument should have any nonnull annotations overriden, fill in
+    // the bitmask.
+    if (OverrideNonnull && OverrideNonnullArgs)
+      *OverrideNonnullArgs |= 1 << ArgTypes.size();
+
     // If this argument is required to be an IntegerConstantExpression and the
     // caller cares, fill in the bitmask we return.
     if (RequiresICE && IntegerConstantArgs)
index 41a291877622484f42ebad74cb06f6a2430d9be9..b9090b413c6c2010358e39b73a39bc8c9cb2b9c7 100644 (file)
@@ -1759,6 +1759,34 @@ void CodeGenModule::AddDefaultFnAttrs(llvm::Function &F) {
   F.addAttributes(llvm::AttributeList::FunctionIndex, AS);
 }
 
+/// Returns the attribute (either parameter attribute, or function
+/// attribute), which declares argument ArgNo to be non-null.
+static const NonNullAttr *getNonNullAttr(const Decl *FD, const ParmVarDecl *PVD,
+                                         QualType ArgType, unsigned ArgNo) {
+  // FIXME: __attribute__((nonnull)) can also be applied to:
+  //   - references to pointers, where the pointee is known to be
+  //     nonnull (apparently a Clang extension)
+  //   - transparent unions containing pointers
+  // In the former case, LLVM IR cannot represent the constraint. In
+  // the latter case, we have no guarantee that the transparent union
+  // is in fact passed as a pointer.
+  if (!ArgType->isAnyPointerType() && !ArgType->isBlockPointerType())
+    return nullptr;
+  // First, check attribute on parameter itself.
+  if (PVD) {
+    if (auto ParmNNAttr = PVD->getAttr<NonNullAttr>())
+      return ParmNNAttr;
+  }
+  // Check function attributes.
+  if (!FD)
+    return nullptr;
+  for (const auto *NNAttr : FD->specific_attrs<NonNullAttr>()) {
+    if (NNAttr->isNonNull(ArgNo))
+      return NNAttr;
+  }
+  return nullptr;
+}
+
 void CodeGenModule::ConstructAttributeList(
     StringRef Name, const CGFunctionInfo &FI, CGCalleeInfo CalleeInfo,
     AttributeListType &PAL, unsigned &CallingConv, bool AttrOnCallSite) {
@@ -1775,6 +1803,18 @@ void CodeGenModule::ConstructAttributeList(
                                      CalleeInfo.getCalleeFunctionProtoType());
 
   const Decl *TargetDecl = CalleeInfo.getCalleeDecl();
+  const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(TargetDecl);
+
+  // Check if this is a builtin function that might override some attributes.
+  unsigned BuiltinID = FD ? FD->getBuiltinID() : 0;
+  bool OverrideNonnullReturn = false;
+  unsigned OverrideNonnullArgs = 0;
+  if (BuiltinID) {
+    ASTContext::GetBuiltinTypeError Error;
+    getContext().GetBuiltinType(BuiltinID, Error, nullptr,
+                                &OverrideNonnullReturn, &OverrideNonnullArgs);
+    assert(Error == ASTContext::GE_None && "Should not codegen an error");
+  }
 
   bool HasOptnone = false;
   // FIXME: handle sseregparm someday...
@@ -1790,13 +1830,13 @@ void CodeGenModule::ConstructAttributeList(
     if (TargetDecl->hasAttr<ConvergentAttr>())
       FuncAttrs.addAttribute(llvm::Attribute::Convergent);
 
-    if (const FunctionDecl *Fn = dyn_cast<FunctionDecl>(TargetDecl)) {
+    if (FD) {
       AddAttributesFromFunctionProtoType(
-          getContext(), FuncAttrs, Fn->getType()->getAs<FunctionProtoType>());
+          getContext(), FuncAttrs, FD->getType()->getAs<FunctionProtoType>());
       // Don't use [[noreturn]] or _Noreturn for a call to a virtual function.
       // These attributes are not inherited by overloads.
-      const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(Fn);
-      if (Fn->isNoReturn() && !(AttrOnCallSite && MD && MD->isVirtual()))
+      const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD);
+      if (FD->isNoReturn() && !(AttrOnCallSite && MD && MD->isVirtual()))
         FuncAttrs.addAttribute(llvm::Attribute::NoReturn);
     }
 
@@ -1813,7 +1853,7 @@ void CodeGenModule::ConstructAttributeList(
     }
     if (TargetDecl->hasAttr<RestrictAttr>())
       RetAttrs.addAttribute(llvm::Attribute::NoAlias);
-    if (TargetDecl->hasAttr<ReturnsNonNullAttr>())
+    if (TargetDecl->hasAttr<ReturnsNonNullAttr>() && !OverrideNonnullReturn)
       RetAttrs.addAttribute(llvm::Attribute::NonNull);
 
     HasOptnone = TargetDecl->hasAttr<OptimizeNoneAttr>();
@@ -1845,7 +1885,6 @@ void CodeGenModule::ConstructAttributeList(
     // we have a decl for the function and it has a target attribute then
     // parse that and add it to the feature set.
     StringRef TargetCPU = getTarget().getTargetOpts().CPU;
-    const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(TargetDecl);
     if (FD && FD->hasAttr<TargetAttr>()) {
       llvm::StringMap<bool> FeatureMap;
       getFunctionFeatureMap(FeatureMap, FD);
@@ -2037,6 +2076,13 @@ void CodeGenModule::ConstructAttributeList(
       continue;
     }
 
+    if (FD && ArgNo < FD->param_size() && ParamType->isPointerType()) {
+      auto *PVD = FD->getParamDecl(ArgNo);
+      if (getNonNullAttr(FD, PVD, ParamType, PVD->getFunctionScopeIndex()) &&
+          (!BuiltinID || (OverrideNonnullArgs & (1 << ArgNo)) == 0))
+        Attrs.addAttribute(llvm::Attribute::NonNull);
+    }
+
     if (const auto *RefTy = ParamType->getAs<ReferenceType>()) {
       QualType PTy = RefTy->getPointeeType();
       if (!PTy->isIncompleteType() && PTy->isConstantSizeType())
@@ -2116,34 +2162,6 @@ static llvm::Value *emitArgumentDemotion(CodeGenFunction &CGF,
   return CGF.Builder.CreateFPCast(value, varType, "arg.unpromote");
 }
 
-/// Returns the attribute (either parameter attribute, or function
-/// attribute), which declares argument ArgNo to be non-null.
-static const NonNullAttr *getNonNullAttr(const Decl *FD, const ParmVarDecl *PVD,
-                                         QualType ArgType, unsigned ArgNo) {
-  // FIXME: __attribute__((nonnull)) can also be applied to:
-  //   - references to pointers, where the pointee is known to be
-  //     nonnull (apparently a Clang extension)
-  //   - transparent unions containing pointers
-  // In the former case, LLVM IR cannot represent the constraint. In
-  // the latter case, we have no guarantee that the transparent union
-  // is in fact passed as a pointer.
-  if (!ArgType->isAnyPointerType() && !ArgType->isBlockPointerType())
-    return nullptr;
-  // First, check attribute on parameter itself.
-  if (PVD) {
-    if (auto ParmNNAttr = PVD->getAttr<NonNullAttr>())
-      return ParmNNAttr;
-  }
-  // Check function attributes.
-  if (!FD)
-    return nullptr;
-  for (const auto *NNAttr : FD->specific_attrs<NonNullAttr>()) {
-    if (NNAttr->isNonNull(ArgNo))
-      return NNAttr;
-  }
-  return nullptr;
-}
-
 namespace {
   struct CopyBackSwiftError final : EHScopeStack::Cleanup {
     Address Temp;
index 7c33e6329fddff8e07fd98ef03a99b7d9dca70dc..63d8b90b349805fbae7d02e19e3a4ed7c3778363 100644 (file)
@@ -49,3 +49,87 @@ __attribute__((nonnull(2))) {}
 // CHECK: define void @bar8(i32* nonnull %a, i32* nonnull %b)
 void bar8(int *a, int *b) __attribute__((nonnull))
 __attribute__((nonnull(1))) {}
+
+// CHECK: declare void @foo_decl(i32* nonnull)
+void foo_decl(int *__attribute__((nonnull)));
+
+// CHECK: declare void @bar_decl(i32* nonnull)
+void bar_decl(int *) __attribute__((nonnull(1)));
+
+// CHECK: declare void @bar2_decl(i32*, i32* nonnull)
+void bar2_decl(int *, int *) __attribute__((nonnull(2)));
+
+// CHECK: declare nonnull i32* @bar3_decl()
+int *bar3_decl(void) __attribute__((returns_nonnull));
+
+// CHECK: declare i32 @bar4_decl(i32, i32* nonnull)
+int bar4_decl(int, int *) __attribute__((nonnull));
+
+// CHECK: declare i32 @bar5_decl(i32, i32* nonnull)
+int bar5_decl(int, int *) __attribute__((nonnull(1, 2)));
+
+// CHECK: declare i32 @bar6_decl(i64)
+int bar6_decl(TransparentUnion) __attribute__((nonnull(1)));
+
+// CHECK: declare void @bar7_decl(i32* nonnull, i32* nonnull)
+void bar7_decl(int *, int *)
+    __attribute__((nonnull(1))) __attribute__((nonnull(2)));
+
+// CHECK: declare void @bar8_decl(i32* nonnull, i32* nonnull)
+void bar8_decl(int *, int *)
+    __attribute__((nonnull)) __attribute__((nonnull(1)));
+
+// Clang specially disables nonnull attributes on some library builtin
+// functions to work around the fact that the standard and some vendors mark
+// them as nonnull even though they are frequently called in practice with null
+// arguments if a corresponding size argument is zero.
+
+// CHECK: declare i8* @memcpy(i8*, i8*, i64)
+void *memcpy(void *, const void *, unsigned long)
+    __attribute__((nonnull(1, 2))) __attribute__((returns_nonnull));
+
+// CHECK: declare i32 @memcmp(i8*, i8*, i64)
+int memcmp(const void *, const void *, unsigned long) __attribute__((nonnull(1, 2)));
+
+// CHECK: declare i8* @memmove(i8*, i8*, i64)
+void *memmove(void *, const void *, unsigned long)
+    __attribute__((nonnull(1, 2))) __attribute__((returns_nonnull));
+
+// CHECK: declare i8* @strncpy(i8*, i8*, i64)
+char *strncpy(char *, const char *, unsigned long)
+    __attribute__((nonnull(1, 2))) __attribute__((returns_nonnull));
+
+// CHECK: declare i32 @strncmp(i8*, i8*, i64)
+int strncmp(const char *, const char *, unsigned long) __attribute__((nonnull(1, 2)));
+
+// CHECK: declare nonnull i8* @strncat(i8* nonnull, i8*, i64)
+char *strncat(char *, const char *, unsigned long)
+    __attribute__((nonnull(1, 2))) __attribute__((returns_nonnull));
+
+// CHECK: declare i8* @memchr(i8*, i32, i64)
+void *memchr(const void *__attribute__((nonnull)), int, unsigned long)
+    __attribute__((returns_nonnull));
+
+// CHECK: declare i8* @memset(i8*, i32, i64)
+void *memset(void *__attribute__((nonnull)), int, unsigned long)
+    __attribute__((returns_nonnull));
+
+void use_declarations(int *p, void *volatile *sink) {
+  foo_decl(p);
+  bar_decl(p);
+  bar2_decl(p, p);
+  (void)bar3_decl();
+  bar4_decl(42, p);
+  bar5_decl(42, p);
+  bar6_decl(p);
+  bar7_decl(p, p);
+  bar8_decl(p, p);
+  *sink = (void *)&memcpy;
+  *sink = (void *)&memcmp;
+  *sink = (void *)&memmove;
+  *sink = (void *)&strncpy;
+  *sink = (void *)&strncmp;
+  *sink = (void *)&strncat;
+  *sink = (void *)&memchr;
+  *sink = (void *)&memset;
+}