diff options
| -rw-r--r-- | clang/include/clang/AST/ASTContext.h | 4 | ||||
| -rw-r--r-- | clang/include/clang/Basic/Builtins.def | 60 | ||||
| -rw-r--r-- | clang/lib/AST/ASTContext.cpp | 44 | ||||
| -rw-r--r-- | clang/lib/CodeGen/CGCall.cpp | 86 | ||||
| -rw-r--r-- | clang/test/CodeGen/nonnull.c | 84 |
5 files changed, 201 insertions, 77 deletions
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 5fa515d02db..2a6709104ae 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -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; diff --git a/clang/include/clang/Basic/Builtins.def b/clang/include/clang/Basic/Builtins.def index e7084de9ceb..9174d391034 100644 --- a/clang/include/clang/Basic/Builtins.def +++ b/clang/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(_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 diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 071537a051c..1c1f78ad266 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -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) diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 41a29187762..b9090b413c6 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -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; diff --git a/clang/test/CodeGen/nonnull.c b/clang/test/CodeGen/nonnull.c index 7c33e6329fd..63d8b90b349 100644 --- a/clang/test/CodeGen/nonnull.c +++ b/clang/test/CodeGen/nonnull.c @@ -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; +} |

