chandlerc updated this revision to Diff 92245.
chandlerc added a comment.

Update with fixes suggested in review.


https://reviews.llvm.org/D30806

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

Index: test/CodeGen/nonnull.c
===================================================================
--- test/CodeGen/nonnull.c
+++ test/CodeGen/nonnull.c
@@ -49,3 +49,87 @@
 // 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;
+}
Index: lib/CodeGen/CGCall.cpp
===================================================================
--- lib/CodeGen/CGCall.cpp
+++ lib/CodeGen/CGCall.cpp
@@ -1759,6 +1759,34 @@
   F.addAttributes(llvm::AttributeSet::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 @@
                                      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 @@
     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 @@
     }
     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 @@
     // 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 @@
       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())
@@ -2118,34 +2164,6 @@
   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: lib/AST/ASTContext.cpp
===================================================================
--- lib/AST/ASTContext.cpp
+++ lib/AST/ASTContext.cpp
@@ -8500,21 +8500,25 @@
 /// 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) {
     switch (*Str++) {
     default: Done = true; --Str; break;
     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!");
@@ -8649,8 +8653,8 @@
     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.
@@ -8665,15 +8669,15 @@
     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;
@@ -8755,27 +8759,37 @@
 }
 
 /// 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: include/clang/Basic/Builtins.def
===================================================================
--- include/clang/Basic/Builtins.def
+++ include/clang/Basic/Builtins.def
@@ -55,6 +55,12 @@
 //  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(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 @@
 // 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: include/clang/AST/ASTContext.h
===================================================================
--- include/clang/AST/ASTContext.h
+++ include/clang/AST/ASTContext.h
@@ -1864,7 +1864,9 @@
   /// 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;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to