Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -864,6 +864,13 @@ let Documentation = [Undocumented]; } +def AssumeAligned : InheritableAttr { + let Spellings = [GCC<"assume_aligned">]; + let Subjects = SubjectList<[ObjCMethod, Function]>; + let Args = [ExprArgument<"Alignment">, ExprArgument<"Offset", 1>]; + let Documentation = [AssumeAlignedDocs]; +} + 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 @@ -154,6 +154,30 @@ Marks a function as releasing a capability. }]; } + +def AssumeAlignedDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +Use ``__attribute__((assume_aligned([,]))`` on a function +declaration to specify that the return value of the function (which must be a +pointer type) has the specified offset, in bytes, from an address with the +specified alignment. The offset is taken to be zero if omitted. + +.. code-block:: c++ + + // The returned pointer value has 32-byte alignment. + void *a() __attribute__((assume_aligned (32))); + + // The returned pointer value is 4 bytes greater than an address having + // 32-byte alignment. + void *b() __attribute__((assume_aligned (32, 4))); + +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 @@ -2705,8 +2705,9 @@ void checkUnusedDeclAttributes(Declarator &D); - /// Determine if type T is a valid subject for a nonnull attribute. - bool isValidNonNullAttrType(QualType T); + /// Determine if type T is a valid subject for a nonnull and similar + /// attributes. + bool isValidPointerAttrType(QualType T); bool CheckRegparmAttr(const AttributeList &attr, unsigned &value); bool CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC, @@ -7331,6 +7332,11 @@ void AddAlignedAttr(SourceRange AttrRange, Decl *D, TypeSourceInfo *T, unsigned SpellingListIndex, bool IsPackExpansion); + /// AddAssumeAlignedAttr - Adds an assume_aligned attribute to a particular + /// declaration. + void AddAssumeAlignedAttr(SourceRange AttrRange, Decl *D, Expr *E, Expr *OE, + unsigned SpellingListIndex); + // OpenMP directives and clauses. private: void *VarDataSharingAttributesStack; Index: lib/CodeGen/CGExpr.cpp =================================================================== --- lib/CodeGen/CGExpr.cpp +++ lib/CodeGen/CGExpr.cpp @@ -3376,7 +3376,22 @@ Callee = Builder.CreateBitCast(Callee, CalleeTy, "callee.knr.cast"); } - return EmitCall(FnInfo, Callee, ReturnValue, Args, TargetDecl); + RValue Ret = EmitCall(FnInfo, Callee, ReturnValue, Args, TargetDecl); + + if (Ret.isScalar() && TargetDecl) { + if (const auto *AA = TargetDecl->getAttr()) { + llvm::Value *OffsetValue = nullptr; + if (const auto *Offset = AA->getOffset()) + OffsetValue = EmitScalarExpr(Offset); + + llvm::Value *Alignment = EmitScalarExpr(AA->getAlignment()); + llvm::ConstantInt *AlignmentCI = cast(Alignment); + EmitAlignmentAssumption(Ret.getScalarVal(), AlignmentCI->getZExtValue(), + OffsetValue); + } + } + + return Ret; } LValue CodeGenFunction:: Index: lib/Sema/SemaChecking.cpp =================================================================== --- lib/Sema/SemaChecking.cpp +++ lib/Sema/SemaChecking.cpp @@ -777,7 +777,7 @@ if (!NonNull->args_size()) { // Easy case: all pointer arguments are nonnull. for (const auto *Arg : Args) - if (S.isValidNonNullAttrType(Arg->getType())) + if (S.isValidPointerAttrType(Arg->getType())) CheckNonNullArgument(S, Arg, CallSiteLoc); return; } Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -29,6 +29,7 @@ #include "clang/Sema/Lookup.h" #include "clang/Sema/Scope.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Support/MathExtras.h" using namespace clang; using namespace sema; @@ -1125,7 +1126,7 @@ Attr.getAttributeSpellingListIndex())); } -bool Sema::isValidNonNullAttrType(QualType T) { +bool Sema::isValidPointerAttrType(QualType T) { T = T.getNonReferenceType(); // The nonnull attribute can be applied to a transparent union that @@ -1146,13 +1147,13 @@ static bool attrNonNullArgCheck(Sema &S, QualType T, const AttributeList &Attr, SourceRange AttrParmRange, - SourceRange NonNullTypeRange, + SourceRange TypeRange, bool isReturnValue = false) { - if (!S.isValidNonNullAttrType(T)) { + if (!S.isValidPointerAttrType(T)) { S.Diag(Attr.getLoc(), isReturnValue ? diag::warn_attribute_return_pointers_only : diag::warn_attribute_pointers_only) - << Attr.getName() << AttrParmRange << NonNullTypeRange; + << Attr.getName() << AttrParmRange << TypeRange; return false; } return true; @@ -1186,7 +1187,7 @@ for (unsigned I = 0, E = getFunctionOrMethodNumParams(D); I != E && !AnyPointers; ++I) { QualType T = getFunctionOrMethodParamType(D, I); - if (T->isDependentType() || S.isValidNonNullAttrType(T)) + if (T->isDependentType() || S.isValidPointerAttrType(T)) AnyPointers = true; } @@ -1237,6 +1238,65 @@ Attr.getAttributeSpellingListIndex())); } +static void handleAssumeAlignedAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + Expr *E = Attr.getArgAsExpr(0), + *OE = Attr.getNumArgs() > 1 ? Attr.getArgAsExpr(1) : nullptr; + S.AddAssumeAlignedAttr(Attr.getRange(), D, E, OE, + Attr.getAttributeSpellingListIndex()); +} + +void Sema::AddAssumeAlignedAttr(SourceRange AttrRange, Decl *D, Expr *E, + Expr *OE, unsigned SpellingListIndex) { + QualType ResultType = getFunctionOrMethodResultType(D); + SourceRange SR = getFunctionOrMethodResultSourceRange(D); + + AssumeAlignedAttr TmpAttr(AttrRange, Context, E, OE, SpellingListIndex); + SourceLocation AttrLoc = AttrRange.getBegin(); + + if (!isValidPointerAttrType(ResultType)) { + Diag(AttrLoc, diag::warn_attribute_return_pointers_only) + << &TmpAttr << AttrRange << SR; + return; + } + + if (!E->isTypeDependent() && !E->isValueDependent()) { + llvm::APSInt I(64); + if (!E->isIntegerConstantExpr(I, Context)) { + if (OE) + Diag(AttrLoc, diag::err_attribute_argument_n_type) + << &TmpAttr << 1 << AANT_ArgumentIntegerConstant + << E->getSourceRange(); + else + Diag(AttrLoc, diag::err_attribute_argument_type) + << &TmpAttr << AANT_ArgumentIntegerConstant + << E->getSourceRange(); + return; + } + + if (!I.isPowerOf2()) { + Diag(AttrLoc, diag::err_alignment_not_power_of_two) + << E->getSourceRange(); + return; + } + } + + if (OE) { + if (!OE->isTypeDependent() && !OE->isValueDependent()) { + llvm::APSInt I(64); + if (!OE->isIntegerConstantExpr(I, Context)) { + Diag(AttrLoc, diag::err_attribute_argument_n_type) + << &TmpAttr << 2 << AANT_ArgumentIntegerConstant + << OE->getSourceRange(); + return; + } + } + } + + D->addAttr(::new (Context) + AssumeAlignedAttr(AttrRange, Context, E, OE, SpellingListIndex)); +} + static void handleOwnershipAttr(Sema &S, Decl *D, const AttributeList &AL) { // This attribute must be applied to a function declaration. The first // argument to the attribute must be an identifier, the name of the resource, @@ -4212,6 +4272,9 @@ case AttributeList::AT_ReturnsNonNull: handleReturnsNonNullAttr(S, D, Attr); break; + case AttributeList::AT_AssumeAligned: + handleAssumeAlignedAttr(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 @@ -129,6 +129,29 @@ } } +static void instantiateDependentAssumeAlignedAttr( + Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs, + const AssumeAlignedAttr *Aligned, Decl *New) { + // The alignment expression is a constant expression. + EnterExpressionEvaluationContext Unevaluated(S, Sema::ConstantEvaluated); + + Expr *E, *OE = nullptr; + ExprResult Result = S.SubstExpr(Aligned->getAlignment(), TemplateArgs); + if (Result.isInvalid()) + return; + E = Result.getAs(); + + if (Aligned->getOffset()) { + Result = S.SubstExpr(Aligned->getOffset(), TemplateArgs); + if (Result.isInvalid()) + return; + OE = Result.getAs(); + } + + S.AddAssumeAlignedAttr(Aligned->getLocation(), New, E, OE, + Aligned->getSpellingListIndex()); +} + static void instantiateDependentEnableIfAttr( Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs, const EnableIfAttr *A, const Decl *Tmpl, Decl *New) { @@ -176,6 +199,14 @@ continue; } + const AssumeAlignedAttr *AssumeAligned = dyn_cast(TmplAttr); + if (AssumeAligned && (AssumeAligned->getAlignment()->isValueDependent() || + (AssumeAligned->getOffset() && + AssumeAligned->getOffset()->isValueDependent()))) { + instantiateDependentAssumeAlignedAttr(*this, TemplateArgs, AssumeAligned, New); + continue; + } + const EnableIfAttr *EnableIf = dyn_cast(TmplAttr); if (EnableIf && EnableIf->getCond()->isValueDependent()) { instantiateDependentEnableIfAttr(*this, TemplateArgs, EnableIf, Tmpl, Index: test/CodeGen/builtin-assume-aligned.c =================================================================== --- test/CodeGen/builtin-assume-aligned.c +++ test/CodeGen/builtin-assume-aligned.c @@ -42,3 +42,26 @@ return a[0]; } +int *m1() __attribute__((assume_aligned(64))); + +// CHECK-LABEL: @test5 +int test5() { + return *m1(); +// CHECK: %ptrint = ptrtoint +// CHECK: %maskedptr = and i64 %ptrint, 63 +// CHECK: %maskcond = icmp eq i64 %maskedptr, 0 +// CHECK: call void @llvm.assume(i1 %maskcond) +} + +int *m2() __attribute__((assume_aligned(64, 12))); + +// CHECK-LABEL: @test6 +int test6() { + return *m2(); +// CHECK: %ptrint = ptrtoint +// CHECK: %offsetptr = sub i64 %ptrint, 12 +// CHECK: %maskedptr = and i64 %offsetptr, 63 +// CHECK: %maskcond = icmp eq i64 %maskedptr, 0 +// CHECK: call void @llvm.assume(i1 %maskcond) +} + Index: test/Sema/builtin-assume-aligned.c =================================================================== --- test/Sema/builtin-assume-aligned.c +++ test/Sema/builtin-assume-aligned.c @@ -41,3 +41,18 @@ return a[0]; } +void test_void_assume_aligned(void) __attribute__((assume_aligned(32))); // expected-warning {{'assume_aligned' attribute only applies to return values that are pointers}} +int test_int_assume_aligned(void) __attribute__((assume_aligned(16))); // expected-warning {{'assume_aligned' attribute only applies to return values that are pointers}} +void *test_ptr_assume_aligned(void) __attribute__((assume_aligned(64))); // no-warning + +int j __attribute__((assume_aligned(8))); // expected-warning {{'assume_aligned' attribute only applies to functions and methods}} +void *test_no_fn_proto() __attribute__((assume_aligned(32))); // no-warning +void *test_with_fn_proto(void) __attribute__((assume_aligned(128))); // no-warning + +void *test_no_fn_proto() __attribute__((assume_aligned(31))); // expected-error {{requested alignment is not a power of 2}} +void *test_no_fn_proto() __attribute__((assume_aligned(32, 73))); // no-warning + +void *test_no_fn_proto() __attribute__((assume_aligned)); // expected-error {{'assume_aligned' attribute takes at least 1 argument}} +void *test_no_fn_proto() __attribute__((assume_aligned())); // expected-error {{'assume_aligned' attribute takes at least 1 argument}} +void *test_no_fn_proto() __attribute__((assume_aligned(32, 45, 37))); // expected-error {{'assume_aligned' attribute takes no more than 2 arguments}} + Index: test/SemaCXX/builtin-assume-aligned-tmpl.cpp =================================================================== --- test/SemaCXX/builtin-assume-aligned-tmpl.cpp +++ test/SemaCXX/builtin-assume-aligned-tmpl.cpp @@ -20,3 +20,17 @@ return test10(a, 42); // expected-note {{in instantiation of function template specialization 'test10' requested here}} } +template +void *atest() __attribute__((assume_aligned(q))); // expected-error {{requested alignment is not a power of 2}} + +template +void *atest2() __attribute__((assume_aligned(q, o))); // expected-error {{requested alignment is not a power of 2}} + +void test20() { + atest<31>(); // expected-note {{in instantiation of function template specialization 'atest<31>' requested here}} + atest<32>(); + + atest2<31, 5>(); // expected-note {{in instantiation of function template specialization 'atest2<31, 5>' requested here}} + atest2<32, 4>(); +} +