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/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -1952,6 +1952,9 @@ def warn_attribute_return_pointers_only : Warning< "%0 attribute only applies to return values that are pointers">, InGroup; +def warn_attribute_return_pointers_refs_only : Warning< + "%0 attribute only applies to return values that are pointers or references">, + InGroup; def err_attribute_no_member_pointers : Error< "%0 attribute cannot be used with pointers to members">; def err_attribute_invalid_implicit_this_argument : Error< Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -2711,8 +2711,11 @@ 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. By default, we look through references (the behavior used by + /// nonnull), but if the second parameter is true, then we treat a reference + /// type as valid. + bool isValidPointerAttrType(QualType T, bool RefOkay = false); bool CheckRegparmAttr(const AttributeList &attr, unsigned &value); bool CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC, @@ -7338,6 +7341,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/CGCall.cpp =================================================================== --- lib/CodeGen/CGCall.cpp +++ lib/CodeGen/CGCall.cpp @@ -3144,75 +3144,92 @@ // lexical order, so deactivate it and run it manually here. CallArgs.freeArgumentMemory(*this); - switch (RetAI.getKind()) { - case ABIArgInfo::InAlloca: - case ABIArgInfo::Indirect: - return convertTempToRValue(SRetPtr, RetTy, SourceLocation()); + RValue Ret = [&] { + switch (RetAI.getKind()) { + case ABIArgInfo::InAlloca: + case ABIArgInfo::Indirect: + return convertTempToRValue(SRetPtr, RetTy, SourceLocation()); - case ABIArgInfo::Ignore: - // If we are ignoring an argument that had a result, make sure to - // construct the appropriate return value for our caller. - return GetUndefRValue(RetTy); + case ABIArgInfo::Ignore: + // If we are ignoring an argument that had a result, make sure to + // construct the appropriate return value for our caller. + return GetUndefRValue(RetTy); - case ABIArgInfo::Extend: - case ABIArgInfo::Direct: { - llvm::Type *RetIRTy = ConvertType(RetTy); - if (RetAI.getCoerceToType() == RetIRTy && RetAI.getDirectOffset() == 0) { - switch (getEvaluationKind(RetTy)) { - case TEK_Complex: { - llvm::Value *Real = Builder.CreateExtractValue(CI, 0); - llvm::Value *Imag = Builder.CreateExtractValue(CI, 1); - return RValue::getComplex(std::make_pair(Real, Imag)); - } - case TEK_Aggregate: { - llvm::Value *DestPtr = ReturnValue.getValue(); - bool DestIsVolatile = ReturnValue.isVolatile(); + case ABIArgInfo::Extend: + case ABIArgInfo::Direct: { + llvm::Type *RetIRTy = ConvertType(RetTy); + if (RetAI.getCoerceToType() == RetIRTy && RetAI.getDirectOffset() == 0) { + switch (getEvaluationKind(RetTy)) { + case TEK_Complex: { + llvm::Value *Real = Builder.CreateExtractValue(CI, 0); + llvm::Value *Imag = Builder.CreateExtractValue(CI, 1); + return RValue::getComplex(std::make_pair(Real, Imag)); + } + case TEK_Aggregate: { + llvm::Value *DestPtr = ReturnValue.getValue(); + bool DestIsVolatile = ReturnValue.isVolatile(); - if (!DestPtr) { - DestPtr = CreateMemTemp(RetTy, "agg.tmp"); - DestIsVolatile = false; + if (!DestPtr) { + DestPtr = CreateMemTemp(RetTy, "agg.tmp"); + DestIsVolatile = false; + } + BuildAggStore(*this, CI, DestPtr, DestIsVolatile, false); + return RValue::getAggregate(DestPtr); } - BuildAggStore(*this, CI, DestPtr, DestIsVolatile, false); - return RValue::getAggregate(DestPtr); - } - case TEK_Scalar: { - // If the argument doesn't match, perform a bitcast to coerce it. This - // can happen due to trivial type mismatches. - llvm::Value *V = CI; - if (V->getType() != RetIRTy) - V = Builder.CreateBitCast(V, RetIRTy); - return RValue::get(V); + case TEK_Scalar: { + // If the argument doesn't match, perform a bitcast to coerce it. This + // can happen due to trivial type mismatches. + llvm::Value *V = CI; + if (V->getType() != RetIRTy) + V = Builder.CreateBitCast(V, RetIRTy); + return RValue::get(V); + } + } + llvm_unreachable("bad evaluation kind"); } + + llvm::Value *DestPtr = ReturnValue.getValue(); + bool DestIsVolatile = ReturnValue.isVolatile(); + + if (!DestPtr) { + DestPtr = CreateMemTemp(RetTy, "coerce"); + DestIsVolatile = false; } - llvm_unreachable("bad evaluation kind"); - } - llvm::Value *DestPtr = ReturnValue.getValue(); - bool DestIsVolatile = ReturnValue.isVolatile(); + // If the value is offset in memory, apply the offset now. + llvm::Value *StorePtr = DestPtr; + if (unsigned Offs = RetAI.getDirectOffset()) { + StorePtr = Builder.CreateBitCast(StorePtr, Builder.getInt8PtrTy()); + StorePtr = Builder.CreateConstGEP1_32(StorePtr, Offs); + StorePtr = Builder.CreateBitCast(StorePtr, + llvm::PointerType::getUnqual(RetAI.getCoerceToType())); + } + CreateCoercedStore(CI, StorePtr, DestIsVolatile, *this); - if (!DestPtr) { - DestPtr = CreateMemTemp(RetTy, "coerce"); - DestIsVolatile = false; + return convertTempToRValue(DestPtr, RetTy, SourceLocation()); } - // If the value is offset in memory, apply the offset now. - llvm::Value *StorePtr = DestPtr; - if (unsigned Offs = RetAI.getDirectOffset()) { - StorePtr = Builder.CreateBitCast(StorePtr, Builder.getInt8PtrTy()); - StorePtr = Builder.CreateConstGEP1_32(StorePtr, Offs); - StorePtr = Builder.CreateBitCast(StorePtr, - llvm::PointerType::getUnqual(RetAI.getCoerceToType())); + case ABIArgInfo::Expand: + llvm_unreachable("Invalid ABI kind for return argument"); } - CreateCoercedStore(CI, StorePtr, DestIsVolatile, *this); - return convertTempToRValue(DestPtr, RetTy, SourceLocation()); - } + llvm_unreachable("Unhandled ABIArgInfo::Kind"); + } (); - case ABIArgInfo::Expand: - llvm_unreachable("Invalid ABI kind for return argument"); + 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); + } } - llvm_unreachable("Unhandled ABIArgInfo::Kind"); + return Ret; } /* VarArg handling */ 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,11 +1126,16 @@ Attr.getAttributeSpellingListIndex())); } -bool Sema::isValidNonNullAttrType(QualType T) { - T = T.getNonReferenceType(); +bool Sema::isValidPointerAttrType(QualType T, bool RefOkay) { + if (RefOkay) { + if (T->isReferenceType()) + return true; + } else { + T = T.getNonReferenceType(); + } - // The nonnull attribute can be applied to a transparent union that - // contains a pointer type. + // The nonnull attribute, and other similar attributes, can be applied to a + // transparent union that contains a pointer type. if (const RecordType *UT = T->getAsUnionType()) { if (UT && UT->getDecl()->hasAttr()) { RecordDecl *UD = UT->getDecl(); @@ -1146,13 +1152,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 +1192,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 +1243,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, /* RefOkay */ true)) { + Diag(AttrLoc, diag::warn_attribute_return_pointers_refs_only) + << &TmpAttr << AttrRange << SR; + return; + } + + if (!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->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 +4277,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,12 @@ continue; } + const AssumeAlignedAttr *AssumeAligned = dyn_cast(TmplAttr); + if (AssumeAligned) { + 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 @@ -43,3 +43,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,48 @@ 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>(); +} + +// expected-error@+1 {{invalid application of 'sizeof' to a function type}} +template __attribute__((assume_aligned(sizeof(int(T()))))) T *f(); +void test21() { + void *p = f(); // expected-note {{in instantiation of function template specialization 'f' requested here}} +} + +// expected-error@+1 {{functional-style cast from 'void' to 'int' is not allowed}} +template __attribute__((assume_aligned(sizeof((int(T())))))) T *g(); +void test23() { + void *p = g(); // expected-note {{in instantiation of function template specialization 'g' requested here}} +} + +template +T *atest3() __attribute__((assume_aligned(31, o))); // expected-error {{requested alignment is not a power of 2}} + +template +T *atest4() __attribute__((assume_aligned(32, o))); + +void test22() { + atest3(); + atest4(); +} + +// expected-warning@+1 {{'assume_aligned' attribute only applies to functions and methods}} +class __attribute__((assume_aligned(32))) x { + int y; +}; + +// expected-warning@+1 {{'assume_aligned' attribute only applies to return values that are pointers or references}} +x foo() __attribute__((assume_aligned(32))); +