]> granicus.if.org Git - clang/commitdiff
Initial support for the align_value attribute
authorHal Finkel <hfinkel@anl.gov>
Thu, 2 Oct 2014 21:21:25 +0000 (21:21 +0000)
committerHal Finkel <hfinkel@anl.gov>
Thu, 2 Oct 2014 21:21:25 +0000 (21:21 +0000)
This adds support for the align_value attribute. This attribute is supported by
Intel's compiler (versions 14.0+), and several of my HPC users have requested
support in Clang. It specifies an alignment assumption on the values to which a
pointer points, and is used by numerical libraries to encourage efficient
generation of vector code.

Of course, we already have an aligned attribute that can specify enhanced
alignment for a type, so why is this additional attribute important? The
problem is that if you want to specify that an input array of T is, say,
64-byte aligned, you could try this:

  typedef double aligned_double attribute((aligned(64)));
  void foo(aligned_double *P) {
    double x = P[0]; // This is fine.
    double y = P[1]; // What alignment did those doubles have again?
  }

the access here to P[1] causes problems. P was specified as a pointer to type
aligned_double, and any object of type aligned_double must be 64-byte aligned.
But if P[0] is 64-byte aligned, then P[1] cannot be, and this access causes
undefined behavior. Getting round this problem requires a lot of awkward
casting and hand-unrolling of loops, all of which is bad.

With the align_value attribute, we can accomplish what we'd like in a well
defined way:

  typedef double *aligned_double_ptr attribute((align_value(64)));
  void foo(aligned_double_ptr P) {
    double x = P[0]; // This is fine.
    double y = P[1]; // This is fine too.
  }

This attribute does not create a new type (and so it not part of the type
system), and so will only "propagate" through templates, auto, etc. by
optimizer deduction after inlining. This seems consistent with Intel's
implementation (thanks to Alexey for confirming the various Intel-compiler
behaviors).

As a final note, I would have chosen to call this aligned_value, not
align_value, for better naming consistency with the aligned attribute, but I
think it would be more useful to users to adopt Intel's name.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@218910 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Basic/Attr.td
include/clang/Basic/AttrDocs.td
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Sema/AttributeList.h
include/clang/Sema/Sema.h
lib/CodeGen/CGCall.cpp
lib/Sema/SemaDeclAttr.cpp
lib/Sema/SemaTemplateInstantiateDecl.cpp
test/CodeGen/align_value.cpp [new file with mode: 0644]
test/Sema/align_value.c [new file with mode: 0644]
test/SemaCXX/align_value.cpp [new file with mode: 0644]

index 2d73d5e0424433d07b709ad5888a06b51d79c9c8..d4b0063e46e8326ebe231052ce517a7b30bfba42 100644 (file)
@@ -354,6 +354,25 @@ def Aligned : InheritableAttr {
   let Documentation = [Undocumented];
 }
 
+def AlignValue : Attr {
+  let Spellings = [
+    // Unfortunately, this is semantically an assertion, not a
+    // directive (something else must ensure the alignment), so
+    // aligned_value is a probably a better name. We might want
+    // to add an aligned_value spelling in the future (and a
+    // corresponding C++ attribute), but this can be done later
+    // once we decide if we also want them to have
+    // slightly-different semantics than Intel's align_value.
+    GNU<"align_value">
+    // Intel's compiler on Windows also supports:
+    // , Declspec<"align_value">
+  ];
+  let Args = [ExprArgument<"Alignment">];
+  let Subjects = SubjectList<[Var, TypedefName], WarnDiag,
+                             "ExpectedVariableOrTypedef">;
+  let Documentation = [AlignValueDocs];
+}
+
 def AlignMac68k : InheritableAttr {
   // This attribute has no spellings as it is only ever created implicitly.
   let Spellings = [];
index 25858f67880e8829ac25cc1946044969f926b722..c72817f9deeced2feae5cd258f742ec227264f6c 100644 (file)
@@ -1011,6 +1011,26 @@ Clang implements two kinds of checks with this attribute.
   }];
 }
 
+def AlignValueDocs : Documentation {
+  let Category = DocCatType;
+  let Content = [{
+The align_value attribute can be added to the typedef of a pointer type or the
+declaration of a variable of pointer or reference type. It specifies that the
+pointer will point to, or the reference will bind to, only objects with at
+least the provided alignment. This alignment value must be some positive power
+of 2.
+
+   .. code-block:: c
+
+     typedef double * aligned_double_ptr __attribute__((align_value(64)));
+     void foo(double & x  __attribute__((align_value(128)),
+              aligned_double_ptr y) { ... }
+
+If the pointer value does not have the specified alignment at runtime, the
+behavior of the program is undefined.
+  }];
+}
+
 def MSInheritanceDocs : Documentation {
   let Category = DocCatType;
   let Heading = "__single_inhertiance, __multiple_inheritance, __virtual_inheritance";
index c811db33bc891357258bc4bf4ca2d69d71f6a03c..5fb3af619081851abd312a9a50c3200899adb004 100644 (file)
@@ -1930,8 +1930,12 @@ def err_attribute_bad_neon_vector_size : Error<
   "Neon vector size must be 64 or 128 bits">;
 def err_attribute_unsupported : Error<
   "%0 attribute is not supported for this target">;
+// The err_*_attribute_argument_not_int are seperate because they're used by
+// VerifyIntegerConstantExpression.
 def err_aligned_attribute_argument_not_int : Error<
   "'aligned' attribute requires integer constant">;
+def err_align_value_attribute_argument_not_int : Error<
+  "'align_value' attribute requires integer constant">;
 def err_alignas_attribute_wrong_decl_type : Error<
   "%0 attribute cannot be applied to a %select{function parameter|"
   "variable with 'register' storage class|'catch' variable|bit-field}1">;
@@ -1970,6 +1974,9 @@ def warn_attribute_return_pointers_only : Warning<
 def warn_attribute_return_pointers_refs_only : Warning<
   "%0 attribute only applies to return values that are pointers or references">,
   InGroup<IgnoredAttributes>;
+def warn_attribute_pointer_or_reference_only : Warning<
+  "%0 attribute only applies to a pointer or reference (%1 is invalid)">,
+  InGroup<IgnoredAttributes>;
 def err_attribute_no_member_pointers : Error<
   "%0 attribute cannot be used with pointers to members">;
 def err_attribute_invalid_implicit_this_argument : Error<
@@ -2210,7 +2217,7 @@ def warn_attribute_wrong_decl_type : Warning<
   "functions, methods and blocks|functions, methods, and classes|"
   "functions, methods, and parameters|classes|variables|methods|"
   "variables, functions and labels|fields and global variables|structs|"
-  "variables, functions and tag types|thread-local variables|"
+  "variables and typedefs|thread-local variables|"
   "variables and fields|variables, data members and tag types|"
   "types and namespaces|Objective-C interfaces|methods and properties|"
   "struct or union|struct, union or class|types|"
index b6620d071a0b48364623ff3a95f0f3da5b282102..39e7ccd7b26abd70e7b7f2844a1c66244e0f9d68 100644 (file)
@@ -827,7 +827,7 @@ enum AttributeDeclKind {
   ExpectedVariableFunctionOrLabel,
   ExpectedFieldOrGlobalVar,
   ExpectedStruct,
-  ExpectedVariableFunctionOrTag,
+  ExpectedVariableOrTypedef,
   ExpectedTLSVar,
   ExpectedVariableOrField,
   ExpectedVariableFieldOrTag,
index be781c300d19bfae8691d31839aa6f439e5d8be0..769c18edd963658c69ab09a2bda3344747c9be36 100644 (file)
@@ -7363,6 +7363,11 @@ public:
   void AddAssumeAlignedAttr(SourceRange AttrRange, Decl *D, Expr *E, Expr *OE,
                             unsigned SpellingListIndex);
 
+  /// AddAlignValueAttr - Adds an align_value attribute to a particular
+  /// declaration.
+  void AddAlignValueAttr(SourceRange AttrRange, Decl *D, Expr *E,
+                         unsigned SpellingListIndex);
+
   // OpenMP directives and clauses.
 private:
   void *VarDataSharingAttributesStack;
index 3db23f2bcb73295dd1ce61b5613859bd8e7dd292..b40fa9d93d4a8c01c7ceb8fcafd7c69922b7cc6a 100644 (file)
@@ -1743,6 +1743,25 @@ void CodeGenFunction::EmitFunctionProlog(const CGFunctionInfo &FI,
                                                   AI->getArgNo() + 1,
                                                   llvm::Attribute::NonNull));
           }
+
+          const auto *AVAttr = PVD->getAttr<AlignValueAttr>();
+          if (!AVAttr)
+            if (const auto *TOTy = dyn_cast<TypedefType>(OTy))
+              AVAttr = TOTy->getDecl()->getAttr<AlignValueAttr>();
+          if (AVAttr) {         
+            llvm::Value *AlignmentValue =
+              EmitScalarExpr(AVAttr->getAlignment());
+            llvm::ConstantInt *AlignmentCI =
+              cast<llvm::ConstantInt>(AlignmentValue);
+            unsigned Alignment =
+              std::min((unsigned) AlignmentCI->getZExtValue(),
+                       +llvm::Value::MaximumAlignment);
+
+            llvm::AttrBuilder Attrs;
+            Attrs.addAlignmentAttr(Alignment);
+            AI->addAttr(llvm::AttributeSet::get(getLLVMContext(),
+                                                AI->getArgNo() + 1, Attrs));
+          }
         }
 
         if (Arg->getType().isRestrictQualified())
index 227ceaaee297f3f7188a803226c257e026e6aa23..406e511651f9011ece3652697b5bf218db26197c 100644 (file)
@@ -2757,6 +2757,58 @@ static void handleAnnotateAttr(Sema &S, Decl *D, const AttributeList &Attr) {
                           Attr.getAttributeSpellingListIndex()));
 }
 
+static void handleAlignValueAttr(Sema &S, Decl *D,
+                                 const AttributeList &Attr) {
+  S.AddAlignValueAttr(Attr.getRange(), D, Attr.getArgAsExpr(0),
+                      Attr.getAttributeSpellingListIndex());
+}
+
+void Sema::AddAlignValueAttr(SourceRange AttrRange, Decl *D, Expr *E,
+                             unsigned SpellingListIndex) {
+  AlignValueAttr TmpAttr(AttrRange, Context, E, SpellingListIndex);
+  SourceLocation AttrLoc = AttrRange.getBegin();
+
+  QualType T;
+  if (TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(D))
+    T = TD->getUnderlyingType();
+  else if (ValueDecl *VD = dyn_cast<ValueDecl>(D))
+    T = VD->getType();
+  else
+    llvm_unreachable("Unknown decl type for align_value");
+
+  if (!T->isDependentType() && !T->isAnyPointerType() &&
+      !T->isReferenceType() && !T->isMemberPointerType()) {
+    Diag(AttrLoc, diag::warn_attribute_pointer_or_reference_only)
+      << &TmpAttr /*TmpAttr.getName()*/ << T << D->getSourceRange();
+    return;
+  }
+
+  if (!E->isValueDependent()) {
+    llvm::APSInt Alignment(32);
+    ExprResult ICE
+      = VerifyIntegerConstantExpression(E, &Alignment,
+          diag::err_align_value_attribute_argument_not_int,
+            /*AllowFold*/ false);
+    if (ICE.isInvalid())
+      return;
+
+    if (!Alignment.isPowerOf2()) {
+      Diag(AttrLoc, diag::err_alignment_not_power_of_two)
+        << E->getSourceRange();
+      return;
+    }
+
+    D->addAttr(::new (Context)
+               AlignValueAttr(AttrRange, Context, ICE.get(),
+               SpellingListIndex));
+    return;
+  }
+
+  // Save dependent expressions in the AST to be instantiated.
+  D->addAttr(::new (Context) AlignValueAttr(TmpAttr));
+  return;
+}
+
 static void handleAlignedAttr(Sema &S, Decl *D, const AttributeList &Attr) {
   // check the attribute arguments.
   if (Attr.getNumArgs() > 1) {
@@ -4181,6 +4233,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
   case AttributeList::AT_Aligned:
     handleAlignedAttr(S, D, Attr);
     break;
+  case AttributeList::AT_AlignValue:
+    handleAlignValueAttr(S, D, Attr);
+    break;
   case AttributeList::AT_AlwaysInline:
     handleAlwaysInlineAttr(S, D, Attr);
     break;
index 9d194a3ad147f9c07c346f7d3d43804987d2dea9..dce8ca4c8a9bb97028881b7e12a5f2c317e812eb 100644 (file)
@@ -152,6 +152,17 @@ static void instantiateDependentAssumeAlignedAttr(
                          Aligned->getSpellingListIndex());
 }
 
+static void instantiateDependentAlignValueAttr(
+    Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs,
+    const AlignValueAttr *Aligned, Decl *New) {
+  // The alignment expression is a constant expression.
+  EnterExpressionEvaluationContext Unevaluated(S, Sema::ConstantEvaluated);
+  ExprResult Result = S.SubstExpr(Aligned->getAlignment(), TemplateArgs);
+  if (!Result.isInvalid())
+    S.AddAlignValueAttr(Aligned->getLocation(), New, Result.getAs<Expr>(),
+                        Aligned->getSpellingListIndex());
+}
+
 static void instantiateDependentEnableIfAttr(
     Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs,
     const EnableIfAttr *A, const Decl *Tmpl, Decl *New) {
@@ -205,6 +216,12 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs,
       continue;
     }
 
+    const AlignValueAttr *AlignValue = dyn_cast<AlignValueAttr>(TmplAttr);
+    if (AlignValue) {
+      instantiateDependentAlignValueAttr(*this, TemplateArgs, AlignValue, New);
+      continue;
+    }
+
     const EnableIfAttr *EnableIf = dyn_cast<EnableIfAttr>(TmplAttr);
     if (EnableIf && EnableIf->getCond()->isValueDependent()) {
       instantiateDependentEnableIfAttr(*this, TemplateArgs, EnableIf, Tmpl,
diff --git a/test/CodeGen/align_value.cpp b/test/CodeGen/align_value.cpp
new file mode 100644 (file)
index 0000000..90bb37f
--- /dev/null
@@ -0,0 +1,8 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -o - %s | FileCheck %s
+
+typedef double * __attribute__((align_value(64))) aligned_double;
+
+void foo(aligned_double x, double * y __attribute__((align_value(32))),
+         double & z __attribute__((align_value(128)))) { };
+// CHECK: define void @_Z3fooPdS_Rd(double* align 64 %x, double* align 32 %y, double* dereferenceable(8) align 128 %z)
+
diff --git a/test/Sema/align_value.c b/test/Sema/align_value.c
new file mode 100644 (file)
index 0000000..92b84db
--- /dev/null
@@ -0,0 +1,32 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+typedef double * __attribute__((align_value(64))) aligned_double;
+
+void foo(aligned_double x, double * y __attribute__((align_value(32)))) { };
+
+// expected-error@+1 {{requested alignment is not a power of 2}}
+typedef double * __attribute__((align_value(63))) aligned_double1;
+
+// expected-error@+1 {{requested alignment is not a power of 2}}
+typedef double * __attribute__((align_value(-2))) aligned_double2;
+
+// expected-error@+1 {{attribute takes one argument}}
+typedef double * __attribute__((align_value(63, 4))) aligned_double3;
+
+// expected-error@+1 {{attribute takes one argument}}
+typedef double * __attribute__((align_value())) aligned_double3a;
+
+// expected-error@+1 {{attribute takes one argument}}
+typedef double * __attribute__((align_value)) aligned_double3b;
+
+// expected-error@+1 {{'align_value' attribute requires integer constant}}
+typedef double * __attribute__((align_value(4.5))) aligned_double4;
+
+// expected-warning@+1 {{'align_value' attribute only applies to a pointer or reference ('int' is invalid)}}
+typedef int __attribute__((align_value(32))) aligned_int;
+
+typedef double * __attribute__((align_value(32*2))) aligned_double5;
+
+// expected-warning@+1 {{'align_value' attribute only applies to variables and typedefs}}
+void foo() __attribute__((align_value(32)));
+
diff --git a/test/SemaCXX/align_value.cpp b/test/SemaCXX/align_value.cpp
new file mode 100644 (file)
index 0000000..5198682
--- /dev/null
@@ -0,0 +1,26 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+typedef double * __attribute__((align_value(64))) aligned_double;
+
+void foo(aligned_double x, double * y __attribute__((align_value(32))),
+         double & z __attribute__((align_value(128)))) { };
+
+template <typename T, int Q>
+struct x {
+  typedef T* aligned_int __attribute__((align_value(32+8*Q)));
+  aligned_int V;
+
+  void foo(aligned_int a, T &b __attribute__((align_value(sizeof(T)*4))));
+};
+
+x<float, 4> y;
+
+template <typename T, int Q>
+struct nope {
+  // expected-error@+1 {{requested alignment is not a power of 2}}
+  void foo(T &b __attribute__((align_value(sizeof(T)+1))));
+};
+
+// expected-note@+1 {{in instantiation of template class 'nope<long double, 4>' requested here}}
+nope<long double, 4> y2;
+