/// 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;
// 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
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)
// 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
/// 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) {
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!");
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.
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;
}
/// 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)
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) {
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...
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);
}
}
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>();
// 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);
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())
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;
// 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;
+}