[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