[PATCH] Support the assume_aligned function attribute
Aaron Ballman
aaron.ballman at gmail.com
Mon Jul 21 09:20:37 PDT 2014
On Sun, Jul 20, 2014 at 5:49 PM, hfinkel at anl.gov <hfinkel at anl.gov> wrote:
> Hi rsmith, aaron.ballman,
>
> In addition to __builtin_assume_aligned, GCC also supports an assume_aligned attribute which specifies the alignment (and optional offset) of the function's return value. Building on the llvm.assume patches, and also functionality added in http://reviews.llvm.org/D149, this patch implements support for the assume_aligned attribute in Clang.
>
> http://reviews.llvm.org/D4601
>
> Files:
> include/clang/Basic/Attr.td
> lib/CodeGen/CGExpr.cpp
> lib/Sema/SemaDeclAttr.cpp
> test/CodeGen/builtin-assume-aligned.c
> test/Sema/builtin-assume-aligned.c
> Index: include/clang/Basic/Attr.td
> ===================================================================
> --- include/clang/Basic/Attr.td
> +++ include/clang/Basic/Attr.td
> @@ -860,6 +860,14 @@
> let Documentation = [Undocumented];
> }
>
> +def AssumeAligned : InheritableAttr {
> + let Spellings = [GCC<"assume_aligned">];
> + let Subjects = SubjectList<[ObjCMethod, Function], WarnDiag,
> + "ExpectedFunctionOrMethod">;
This can simply be: SubjectList<[ObjCMethod, Function]>; The rest is
handled by default.
> + let Args = [IntArgument<"Alignment">, DefaultIntArgument<"Offset", 0>];
> + let Documentation = [Undocumented];
> +}
> +
> def NoReturn : InheritableAttr {
> let Spellings = [GCC<"noreturn">, Declspec<"noreturn">];
> // FIXME: Does GCC allow this on the function instead?
> Index: lib/CodeGen/CGExpr.cpp
> ===================================================================
> --- lib/CodeGen/CGExpr.cpp
> +++ lib/CodeGen/CGExpr.cpp
> @@ -3347,7 +3347,20 @@
> 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 AssumeAlignedAttr *AA =
> + TargetDecl->getAttr<AssumeAlignedAttr>()) {
auto?
> + llvm::Value *OffsetValue = nullptr;
> + if (int Offset = AA->getOffset())
> + OffsetValue = llvm::ConstantInt::get(IntPtrTy, Offset);
> +
> + EmitAlignmentAssumption(Ret.getScalarVal(), AA->getAlignment(),
> + OffsetValue);
> + }
> +
> + return Ret;
> }
>
> LValue CodeGenFunction::
> 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;
>
> @@ -1115,7 +1116,7 @@
> }
> }
>
> -static bool attrNonNullArgCheck(Sema &S, QualType T, const AttributeList &Attr,
> +static bool attrPointerArgCheck(Sema &S, QualType T, const AttributeList &Attr,
> SourceRange R, bool isReturnValue = false) {
> T = T.getNonReferenceType();
> possibleTransparentUnionPointerType(T);
> @@ -1140,7 +1141,7 @@
>
> // Is the function argument a pointer type?
> // FIXME: Should also highlight argument in decl in the diagnostic.
> - if (!attrNonNullArgCheck(S, getFunctionOrMethodParamType(D, Idx), Attr,
> + if (!attrPointerArgCheck(S, getFunctionOrMethodParamType(D, Idx), Attr,
> Ex->getSourceRange()))
> continue;
>
> @@ -1188,7 +1189,7 @@
> }
>
> // Is the argument a pointer type?
> - if (!attrNonNullArgCheck(S, D->getType(), Attr, D->getSourceRange()))
> + if (!attrPointerArgCheck(S, D->getType(), Attr, D->getSourceRange()))
> return;
>
> D->addAttr(::new (S.Context)
> @@ -1199,15 +1200,51 @@
> static void handleReturnsNonNullAttr(Sema &S, Decl *D,
> const AttributeList &Attr) {
> QualType ResultType = getFunctionOrMethodResultType(D);
> - if (!attrNonNullArgCheck(S, ResultType, Attr, Attr.getRange(),
> + if (!attrPointerArgCheck(S, ResultType, Attr, Attr.getRange(),
> /* isReturnValue */ true))
> return;
>
> D->addAttr(::new (S.Context)
> ReturnsNonNullAttr(Attr.getRange(), S.Context,
> Attr.getAttributeSpellingListIndex()));
> }
>
> +static void handleAssumeAlignedAttr(Sema &S, Decl *D,
> + const AttributeList &Attr) {
> + QualType ResultType = getFunctionOrMethodResultType(D);
> + if (!attrPointerArgCheck(S, ResultType, Attr, Attr.getRange(),
> + /* isReturnValue */ true))
> + return;
> +
> + if (Attr.getNumArgs() > 2) {
> + S.Diag(Attr.getLoc(), diag::err_attribute_too_many_arguments)
> + << Attr.getName() << 2;
> + return;
> + } else if (Attr.getNumArgs() < 1) {
> + S.Diag(Attr.getLoc(), diag::err_attribute_too_few_arguments)
> + << Attr.getName() << 1;
> + return;
> + }
These two checks can be removed entirely -- they're automatically
handled for you.
> +
> + uint32_t Alignment, Offset = 0;
> + if (!checkUInt32Argument(S, Attr, Attr.getArgAsExpr(0), Alignment, 1))
> + return;
If Attr.getNumArgs() == 1, the last parameter could be set to the
default value (UINT_MAX) so that the diagnostic reads a bit better.
> + if (Attr.getNumArgs() > 1 && !checkUInt32Argument(S, Attr,
> + Attr.getArgAsExpr(1),
> + Offset, 2))
> + return;
> +
> + if (!llvm::isPowerOf2_32(Alignment)) {
> + S.Diag(Attr.getLoc(), diag::err_alignment_not_power_of_two)
> + << Attr.getArgAsExpr(0)->getSourceRange();
> + return;
> + }
> +
> + D->addAttr(::new (S.Context)
> + AssumeAlignedAttr(Attr.getRange(), S.Context, Alignment, Offset,
> + Attr.getAttributeSpellingListIndex()));
> +}
> +
> 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,
> @@ -4198,6 +4235,9 @@
> case AttributeList::AT_ReturnsNonNull:
> handleReturnsNonNullAttr(S, D, Attr);
> break;
> + case AttributeList::AT_AssumeAligned:
> + handleAssumeAlignedAttr(S, D, Attr);
> + break;
> case AttributeList::AT_Overloadable:
> handleSimpleAttribute<OverloadableAttr>(S, D, Attr);
> break;
> 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}}
> +
>
~Aaron
More information about the cfe-commits
mailing list