Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -1225,6 +1225,14 @@ let Documentation = [AssumeAlignedDocs]; } +def AllocAlign : InheritableAttr { + let Spellings = [GCC<"alloc_align">]; + let Subjects = SubjectList<[HasFunctionProto], WarnDiag, + "ExpectedFunctionWithProtoType">; + let Args = [IntArgument<"ParamIndex">]; + let Documentation = [AllocAlignDocs]; +} + def NoReturn : InheritableAttr { let Spellings = [GCC<"noreturn">, Declspec<"noreturn">]; // FIXME: Does GCC allow this on the function instead? Index: include/clang/Basic/AttrDocs.td =================================================================== --- include/clang/Basic/AttrDocs.td +++ include/clang/Basic/AttrDocs.td @@ -244,6 +244,33 @@ }]; } +def AllocAlignDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +Use ``__attribute__((alloc_align())`` on a function +declaration to specify that the return value of the function (which must be a +pointer type) has an alignment specified by the indicated parameter. The +alignment parameter is one-indexed. In the case of member functions, the +implicit ``this`` parameter is considered as as the first function parameter. + +.. code-block:: c++ + + // The returned pointer has the alignment specified by the first parameter + void *a(size_t align) __attribute__((alloc_align(1))); + + // The returned pointer has the alignment specified by the second parameter + void *b(void* v, size_t align) __attribute__((alloc_align(2))); + + // The returned pointer has the alignment specified by the second visible + // parameter, however it must be adjusted for the implicit 'this' parameter. + void *Foo::b(void* v, size_t align) __attribute__((alloc_align(3))); + +Note that this attribute provides information to the compiler regarding a +condition that the code already ensures is true. It does not cause the compiler +to enforce the provided alignment assumption. + }]; +} + def EnableIfDocs : Documentation { let Category = DocCatFunction; let Content = [{ Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -8178,6 +8178,11 @@ void AddAssumeAlignedAttr(SourceRange AttrRange, Decl *D, Expr *E, Expr *OE, unsigned SpellingListIndex); + /// AddAllocAlignAttr - Adds an alloc_align attribute to a particular + /// declaration. + void AddAllocAlignAttr(SourceRange AttrRange, Decl *D, Expr *ParamExpr, + unsigned SpellingListIndex); + /// AddAlignValueAttr - Adds an align_value attribute to a particular /// declaration. void AddAlignValueAttr(SourceRange AttrRange, Decl *D, Expr *E, Index: lib/CodeGen/CGCall.cpp =================================================================== --- lib/CodeGen/CGCall.cpp +++ lib/CodeGen/CGCall.cpp @@ -4348,6 +4348,9 @@ llvm::ConstantInt *AlignmentCI = cast(Alignment); EmitAlignmentAssumption(Ret.getScalarVal(), AlignmentCI->getZExtValue(), OffsetValue); + } else if (const auto *AA = TargetDecl->getAttr()) { + llvm::Value *ParamVal = IRCallArgs[AA->getParamIndex() - 1]; + EmitAlignmentAssumption(Ret.getScalarVal(), ParamVal); } } Index: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ lib/CodeGen/CodeGenFunction.h @@ -2465,6 +2465,12 @@ PeepholeProtection protectFromPeepholes(RValue rvalue); void unprotectFromPeepholes(PeepholeProtection protection); + void EmitAlignmentAssumption(llvm::Value *PtrValue, llvm::Value *Alignment, + llvm::Value *OffsetValue = nullptr) { + Builder.CreateAlignmentAssumption(CGM.getDataLayout(), PtrValue, Alignment, + OffsetValue); + } + //===--------------------------------------------------------------------===// // Statement Emission //===--------------------------------------------------------------------===// Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -218,21 +218,45 @@ std::greater()); } +/// \brief A helper function to provide Attribute Location for the Attr types +/// AND the AttributeList. +template +static typename std::enable_if::value, + SourceLocation>::type +getAttrLoc(const AttrInfo &Attr) { + return Attr.getLocation(); +} +static SourceLocation getAttrLoc(const clang::AttributeList &Attr) { + return Attr.getLoc(); +} + +/// \brief A helper function to provide Attribute Name for the Attr types +/// AND the AttributeList. +template +static typename std::enable_if::value, + const AttrInfo *>::type +getAttrName(const AttrInfo &Attr) { + return &Attr; +} +const IdentifierInfo *getAttrName(const clang::AttributeList &Attr) { + return Attr.getName(); +} + /// \brief If Expr is a valid integer constant, get the value of the integer /// expression and return success or failure. May output an error. -static bool checkUInt32Argument(Sema &S, const AttributeList &Attr, - const Expr *Expr, uint32_t &Val, - unsigned Idx = UINT_MAX) { +template +static bool checkUInt32Argument(Sema &S, const AttrInfo& Attr, const Expr *Expr, + uint32_t &Val, unsigned Idx = UINT_MAX) { llvm::APSInt I(32); if (Expr->isTypeDependent() || Expr->isValueDependent() || !Expr->isIntegerConstantExpr(I, S.Context)) { if (Idx != UINT_MAX) - S.Diag(Attr.getLoc(), diag::err_attribute_argument_n_type) - << Attr.getName() << Idx << AANT_ArgumentIntegerConstant + S.Diag(getAttrLoc(Attr), diag::err_attribute_argument_n_type) + << getAttrName(Attr) << Idx << AANT_ArgumentIntegerConstant << Expr->getSourceRange(); else - S.Diag(Attr.getLoc(), diag::err_attribute_argument_type) - << Attr.getName() << AANT_ArgumentIntegerConstant + S.Diag(getAttrLoc(Attr), diag::err_attribute_argument_type) + << getAttrName(Attr) << AANT_ArgumentIntegerConstant << Expr->getSourceRange(); return false; } @@ -250,9 +274,9 @@ /// \brief Wrapper around checkUInt32Argument, with an extra check to be sure /// that the result will fit into a regular (signed) int. All args have the same /// purpose as they do in checkUInt32Argument. -static bool checkPositiveIntArgument(Sema &S, const AttributeList &Attr, - const Expr *Expr, int &Val, - unsigned Idx = UINT_MAX) { +template +static bool checkPositiveIntArgument(Sema &S, const AttrInfo& Attr, const Expr *Expr, + int &Val, unsigned Idx = UINT_MAX) { uint32_t UVal; if (!checkUInt32Argument(S, Attr, Expr, UVal, Idx)) return false; @@ -287,11 +311,10 @@ /// instance method D. May output an error. /// /// \returns true if IdxExpr is a valid index. -static bool checkFunctionOrMethodParameterIndex(Sema &S, const Decl *D, - const AttributeList &Attr, - unsigned AttrArgNum, - const Expr *IdxExpr, - uint64_t &Idx) { +template +static bool checkFunctionOrMethodParameterIndex( + Sema &S, const Decl *D, const AttrInfo& Attr, + unsigned AttrArgNum, const Expr *IdxExpr, uint64_t &Idx) { assert(isFunctionOrMethodOrBlock(D)); // In C++ the implicit 'this' function parameter also counts. @@ -305,24 +328,24 @@ llvm::APSInt IdxInt; if (IdxExpr->isTypeDependent() || IdxExpr->isValueDependent() || !IdxExpr->isIntegerConstantExpr(IdxInt, S.Context)) { - S.Diag(Attr.getLoc(), diag::err_attribute_argument_n_type) - << Attr.getName() << AttrArgNum << AANT_ArgumentIntegerConstant + S.Diag(getAttrLoc(Attr), diag::err_attribute_argument_n_type) + << getAttrName(Attr) << AttrArgNum << AANT_ArgumentIntegerConstant << IdxExpr->getSourceRange(); return false; } Idx = IdxInt.getLimitedValue(); if (Idx < 1 || (!IV && Idx > NumParams)) { - S.Diag(Attr.getLoc(), diag::err_attribute_argument_out_of_bounds) - << Attr.getName() << AttrArgNum << IdxExpr->getSourceRange(); + S.Diag(getAttrLoc(Attr), diag::err_attribute_argument_out_of_bounds) + << getAttrName(Attr) << AttrArgNum << IdxExpr->getSourceRange(); return false; } Idx--; // Convert to zero-based. if (HasImplicitThisParam) { if (Idx == 0) { - S.Diag(Attr.getLoc(), + S.Diag(getAttrLoc(Attr), diag::err_attribute_invalid_implicit_this_argument) - << Attr.getName() << IdxExpr->getSourceRange(); + << getAttrName(Attr) << IdxExpr->getSourceRange(); return false; } --Idx; @@ -753,31 +776,49 @@ Attr.getAttributeSpellingListIndex())); } -/// \brief Checks to be sure that the given parameter number is inbounds, and is -/// an some integral type. Will emit appropriate diagnostics if this returns +/// \brief Checks to be sure that the given parameter number is in bounds, and is +/// an integral type. Will emit appropriate diagnostics if this returns /// false. /// /// FuncParamNo is expected to be from the user, so is base-1. AttrArgNo is used /// to actually retrieve the argument, so it's base-0. +template static bool checkParamIsIntegerType(Sema &S, const FunctionDecl *FD, - const AttributeList &Attr, - unsigned FuncParamNo, unsigned AttrArgNo) { - assert(Attr.isArgExpr(AttrArgNo) && "Expected expression argument"); + const AttrInfo &Attr, Expr *AttrArg, + unsigned FuncParamNo, unsigned AttrArgNo, + bool AllowDependentType = false) { uint64_t Idx; - if (!checkFunctionOrMethodParameterIndex(S, FD, Attr, FuncParamNo, - Attr.getArgAsExpr(AttrArgNo), Idx)) + if (!checkFunctionOrMethodParameterIndex(S, FD, Attr, FuncParamNo, AttrArg, + Idx)) return false; const ParmVarDecl *Param = FD->getParamDecl(Idx); + if (AllowDependentType && Param->getType()->isDependentType()) + return true; if (!Param->getType()->isIntegerType() && !Param->getType()->isCharType()) { - SourceLocation SrcLoc = Attr.getArgAsExpr(AttrArgNo)->getLocStart(); + SourceLocation SrcLoc = AttrArg->getLocStart(); S.Diag(SrcLoc, diag::err_attribute_integers_only) - << Attr.getName() << Param->getSourceRange(); + << getAttrName(Attr) << Param->getSourceRange(); return false; } return true; } +/// \brief Checks to be sure that the given parameter number is in bounds, and is +/// an integral type. Will emit appropriate diagnostics if this returns +/// false. +/// +/// FuncParamNo is expected to be from the user, so is base-1. AttrArgNo is used +/// to actually retrieve the argument, so it's base-0. +static bool checkParamIsIntegerType(Sema &S, const FunctionDecl *FD, + const AttributeList &Attr, + unsigned FuncParamNo, unsigned AttrArgNo, + bool AllowDependentType = false) { + assert(Attr.isArgExpr(AttrArgNo) && "Expected expression argument"); + return checkParamIsIntegerType(S, FD, Attr, Attr.getArgAsExpr(AttrArgNo), + FuncParamNo, AttrArgNo, AllowDependentType); +} + static void handleAllocSizeAttr(Sema &S, Decl *D, const AttributeList &Attr) { if (!checkAttributeAtLeastNumArgs(S, Attr, 1) || !checkAttributeAtMostNumArgs(S, Attr, 2)) @@ -1484,6 +1525,13 @@ Attr.getAttributeSpellingListIndex()); } +static void handleAllocAlignAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + Expr *E = Attr.getArgAsExpr(0); + S.AddAllocAlignAttr(Attr.getRange(), D, E, + Attr.getAttributeSpellingListIndex()); +} + void Sema::AddAssumeAlignedAttr(SourceRange AttrRange, Decl *D, Expr *E, Expr *OE, unsigned SpellingListIndex) { QualType ResultType = getFunctionOrMethodResultType(D); @@ -1535,6 +1583,38 @@ AssumeAlignedAttr(AttrRange, Context, E, OE, SpellingListIndex)); } +void Sema::AddAllocAlignAttr(SourceRange AttrRange, Decl *D, Expr *ParamExpr, + unsigned SpellingListIndex) { + QualType ResultType = getFunctionOrMethodResultType(D); + + AllocAlignAttr TmpAttr(AttrRange, Context, 0, SpellingListIndex); + SourceLocation AttrLoc = AttrRange.getBegin(); + + if (!ResultType->isDependentType() && + !isValidPointerAttrType(ResultType, /* RefOkay */ true)) { + Diag(AttrLoc, diag::warn_attribute_return_pointers_refs_only) + << &TmpAttr << AttrRange << getFunctionOrMethodResultSourceRange(D); + return; + } + + uint64_t IndexVal; + const auto *FuncDecl = cast(D); + if (!checkFunctionOrMethodParameterIndex(*this, FuncDecl, TmpAttr, + /*AttrArgNo=*/1, ParamExpr, + IndexVal)) + return; + + // Uncorrect for zero index as well as implicit 'this'. + IndexVal += 1 + isInstanceMethod(FuncDecl); + + if (!checkParamIsIntegerType(*this, FuncDecl, TmpAttr, ParamExpr, IndexVal, + /*AttrArgNo=*/0, /*AllowDependentType=*/true)) + return; + + D->addAttr(::new (Context) AllocAlignAttr(AttrRange, Context, IndexVal, + SpellingListIndex)); +} + /// Normalize the attribute, __foo__ becomes foo. /// Returns true if normalization was applied. static bool normalizeName(StringRef &AttrName) { @@ -5937,6 +6017,9 @@ case AttributeList::AT_AssumeAligned: handleAssumeAlignedAttr(S, D, Attr); break; + case AttributeList::AT_AllocAlign: + handleAllocAlignAttr(S, D, Attr); + break; case AttributeList::AT_Overloadable: handleSimpleAttribute(S, D, Attr); break; Index: lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- lib/Sema/SemaTemplateInstantiateDecl.cpp +++ lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -168,6 +168,16 @@ Aligned->getSpellingListIndex()); } +static void instantiateDependentAllocAlignAttr( + Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs, + const AllocAlignAttr *Align, Decl *New) { + Expr *Param = IntegerLiteral::Create( + S.getASTContext(), llvm::APInt(64, Align->getParamIndex()), + S.getASTContext().UnsignedLongLongTy, Align->getLocation()); + S.AddAllocAlignAttr(Align->getLocation(), New, Param, + Align->getSpellingListIndex()); +} + static Expr *instantiateDependentFunctionAttrCondition( Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs, const Attr *A, Expr *OldCond, const Decl *Tmpl, FunctionDecl *New) { @@ -380,6 +390,12 @@ continue; } + if (const auto *AllocAlign = dyn_cast(TmplAttr)) { + instantiateDependentAllocAlignAttr(*this, TemplateArgs, AllocAlign, New); + continue; + } + + if (const auto *EnableIf = dyn_cast(TmplAttr)) { instantiateDependentEnableIfAttr(*this, TemplateArgs, EnableIf, Tmpl, cast(New)); Index: test/CodeGen/alloc-align-attr.c =================================================================== --- test/CodeGen/alloc-align-attr.c +++ test/CodeGen/alloc-align-attr.c @@ -0,0 +1,63 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -o - %s | FileCheck %s + +__INT32_TYPE__*m1(__INT32_TYPE__ i) __attribute__((alloc_align(1))); + +// Condition where parameter to m1 is not size_t. +__INT32_TYPE__ test1(__INT32_TYPE__ a) { +// CHECK: define i32 @test1 + return *m1(a); +// CHECK: call i32* @m1(i32 [[PARAM1:%[^\)]+]]) +// CHECK: [[ALIGNCAST1:%.+]] = sext i32 [[PARAM1]] to i64 +// CHECK: [[ISPOS1:%.+]] = icmp sgt i64 [[ALIGNCAST1]], 0 +// CHECK: [[POSMASK1:%.+]] = sub i64 [[ALIGNCAST1]], 1 +// CHECK: [[MASK1:%.+]] = select i1 [[ISPOS1]], i64 [[POSMASK1]], i64 0 +// CHECK: [[PTRINT1:%.+]] = ptrtoint +// CHECK: [[MASKEDPTR1:%.+]] = and i64 [[PTRINT1]], [[MASK1]] +// CHECK: [[MASKCOND1:%.+]] = icmp eq i64 [[MASKEDPTR1]], 0 +// CHECK: call void @llvm.assume(i1 [[MASKCOND1]]) +} +// Condition where test2 param needs casting. +__INT32_TYPE__ test2(__SIZE_TYPE__ a) { +// CHECK: define i32 @test2 + return *m1(a); +// CHECK: [[CONV2:%.+]] = trunc i64 %{{.+}} to i32 +// CHECK: call i32* @m1(i32 [[CONV2]]) +// CHECK: [[ALIGNCAST2:%.+]] = sext i32 [[CONV2]] to i64 +// CHECK: [[ISPOS2:%.+]] = icmp sgt i64 [[ALIGNCAST2]], 0 +// CHECK: [[POSMASK2:%.+]] = sub i64 [[ALIGNCAST2]], 1 +// CHECK: [[MASK2:%.+]] = select i1 [[ISPOS2]], i64 [[POSMASK2]], i64 0 +// CHECK: [[PTRINT2:%.+]] = ptrtoint +// CHECK: [[MASKEDPTR2:%.+]] = and i64 [[PTRINT2]], [[MASK2]] +// CHECK: [[MASKCOND2:%.+]] = icmp eq i64 [[MASKEDPTR2]], 0 +// CHECK: call void @llvm.assume(i1 [[MASKCOND2]]) +} +__INT32_TYPE__ *m2(__SIZE_TYPE__ i) __attribute__((alloc_align(1))); + +// test3 param needs casting, but 'm2' is correct. +__INT32_TYPE__ test3(__INT32_TYPE__ a) { +// CHECK: define i32 @test3 + return *m2(a); +// CHECK: [[CONV3:%.+]] = sext i32 %{{.+}} to i64 +// CHECK: call i32* @m2(i64 [[CONV3]]) +// CHECK: [[ISPOS3:%.+]] = icmp sgt i64 [[CONV3]], 0 +// CHECK: [[POSMASK3:%.+]] = sub i64 [[CONV3]], 1 +// CHECK: [[MASK3:%.+]] = select i1 [[ISPOS3]], i64 [[POSMASK3]], i64 0 +// CHECK: [[PTRINT3:%.+]] = ptrtoint +// CHECK: [[MASKEDPTR3:%.+]] = and i64 [[PTRINT3]], [[MASK3]] +// CHECK: [[MASKCOND3:%.+]] = icmp eq i64 [[MASKEDPTR3]], 0 +// CHECK: call void @llvm.assume(i1 [[MASKCOND3]]) +} + +// Every type matches, canonical example. +__INT32_TYPE__ test4(__SIZE_TYPE__ a) { +// CHECK: define i32 @test4 + return *m2(a); +// CHECK: call i32* @m2(i64 [[PARAM4:%[^\)]+]]) +// CHECK: [[ISPOS4:%.+]] = icmp sgt i64 [[PARAM4]], 0 +// CHECK: [[POSMASK4:%.+]] = sub i64 [[PARAM4]], 1 +// CHECK: [[MASK4:%.+]] = select i1 [[ISPOS4]], i64 [[POSMASK4]], i64 0 +// CHECK: [[PTRINT4:%.+]] = ptrtoint +// CHECK: [[MASKEDPTR4:%.+]] = and i64 [[PTRINT4]], [[MASK4]] +// CHECK: [[MASKCOND4:%.+]] = icmp eq i64 [[MASKEDPTR4]], 0 +// CHECK: call void @llvm.assume(i1 [[MASKCOND4]]) +} Index: test/Sema/alloc-align-attr.c =================================================================== --- test/Sema/alloc-align-attr.c +++ test/Sema/alloc-align-attr.c @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +// return values +void test_void_alloc_align(void) __attribute__((alloc_align(1))); // expected-warning {{'alloc_align' attribute only applies to return values that are pointers}} +void *test_ptr_alloc_align(int a) __attribute__((alloc_align(1))); // no-warning + +int j __attribute__((alloc_align(1))); // expected-warning {{'alloc_align' attribute only applies to non-K&R-style functions}} +void *test_no_params_zero(void) __attribute__((alloc_align(0))); // expected-error {{'alloc_align' attribute parameter 1 is out of bounds}} +void *test_no_params(void) __attribute__((alloc_align(1))); // expected-error {{'alloc_align' attribute parameter 1 is out of bounds}} +void *test_incorrect_param_type(float a) __attribute__((alloc_align(1))); // expected-error {{'alloc_align' attribute argument may only refer to a function parameter of integer type}} + +// argument type +void *test_bad_param_type(void) __attribute((alloc_align(1.1))); // expected-error {{'alloc_align' attribute requires parameter 1 to be an integer constant}} + +// argument count +void *test_no_fn_proto() __attribute__((alloc_align)); // expected-error {{'alloc_align' attribute takes one argument}} +void *test_no_fn_proto() __attribute__((alloc_align())); // expected-error {{'alloc_align' attribute takes one argument}} +void *test_no_fn_proto() __attribute__((alloc_align(32, 45, 37))); // expected-error {{'alloc_align' attribute takes one argument}} + Index: test/SemaCXX/alloc-align-attr.cpp =================================================================== --- test/SemaCXX/alloc-align-attr.cpp +++ test/SemaCXX/alloc-align-attr.cpp @@ -0,0 +1,40 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +struct param_num { + void* Foo(int a) __attribute__((alloc_align(1))); // expected-error {{'alloc_align' attribute is invalid for the implicit this argument}} +}; + + +template +struct dependent_ret { + T* Foo(int a) __attribute__((alloc_align(2)));// no-warning, ends up being int**. + T Foo2(int a) __attribute__((alloc_align(2)));// expected-warning {{'alloc_align' attribute only applies to return values that are pointers or references}} +}; + +// Following 2 errors associated only with the 'float' versions below. +template +struct dependent_param_struct { + void* Foo(T param) __attribute__((alloc_align(2))); // expected-error {{'alloc_align' attribute argument may only refer to a function parameter of integer type}} +}; + +template +void* dependent_param_func(T param) __attribute__((alloc_align(1)));// expected-error {{'alloc_align' attribute argument may only refer to a function parameter of integer type}} + +template +void* illegal_align_param(int p) __attribute__((alloc_align(T))); // expected-error {{'alloc_align' attribute requires parameter 1 to be an integer constant}} + +void dependent_impl() { + dependent_ret a; // expected-note {{in instantiation of template class 'dependent_ret' requested here}} + a.Foo(1); + a.Foo2(1); + dependent_ret b; + a.Foo(1); + a.Foo2(1); + + dependent_param_struct c; + c.Foo(1); + dependent_param_struct d; // expected-note {{in instantiation of template class 'dependent_param_struct' requested here}} + d.Foo(1.0); + dependent_param_func(1); + dependent_param_func(1); // expected-note {{in instantiation of function template specialization 'dependent_param_func' requested here}} +}