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; 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: lib/AST/ASTContext.cpp =================================================================== --- lib/AST/ASTContext.cpp +++ lib/AST/ASTContext.cpp @@ -8500,13 +8500,14 @@ /// 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) { @@ -8515,6 +8516,9 @@ 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 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: 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()) + return ParmNNAttr; + } + // Check function attributes. + if (!FD) + return nullptr; + for (const auto *NNAttr : FD->specific_attrs()) { + 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(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()) FuncAttrs.addAttribute(llvm::Attribute::Convergent); - if (const FunctionDecl *Fn = dyn_cast(TargetDecl)) { + if (FD) { AddAttributesFromFunctionProtoType( - getContext(), FuncAttrs, Fn->getType()->getAs()); + getContext(), FuncAttrs, FD->getType()->getAs()); // 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(Fn); - if (Fn->isNoReturn() && !(AttrOnCallSite && MD && MD->isVirtual())) + const CXXMethodDecl *MD = dyn_cast(FD); + if (FD->isNoReturn() && !(AttrOnCallSite && MD && MD->isVirtual())) FuncAttrs.addAttribute(llvm::Attribute::NoReturn); } @@ -1813,7 +1853,7 @@ } if (TargetDecl->hasAttr()) RetAttrs.addAttribute(llvm::Attribute::NoAlias); - if (TargetDecl->hasAttr()) + if (TargetDecl->hasAttr() && !OverrideNonnullReturn) RetAttrs.addAttribute(llvm::Attribute::NonNull); HasOptnone = TargetDecl->hasAttr(); @@ -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(TargetDecl); if (FD && FD->hasAttr()) { llvm::StringMap 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()) { 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()) - return ParmNNAttr; - } - // Check function attributes. - if (!FD) - return nullptr; - for (const auto *NNAttr : FD->specific_attrs()) { - if (NNAttr->isNonNull(ArgNo)) - return NNAttr; - } - return nullptr; -} - namespace { struct CopyBackSwiftError final : EHScopeStack::Cleanup { Address Temp; 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; +}