[clang] 48ff354 - [clang] Add support for new loop attribute [[clang::code_align()]] (#70762)

via cfe-commits cfe-commits at lists.llvm.org
Mon Nov 20 14:09:47 PST 2023


Author: smanna12
Date: 2023-11-20T16:09:42-06:00
New Revision: 48ff35415c06cdbd9115bfe5318449afddcc7ff5

URL: https://github.com/llvm/llvm-project/commit/48ff35415c06cdbd9115bfe5318449afddcc7ff5
DIFF: https://github.com/llvm/llvm-project/commit/48ff35415c06cdbd9115bfe5318449afddcc7ff5.diff

LOG: [clang] Add support for new loop attribute [[clang::code_align()]] (#70762)

This patch adds support for new loop attribute:
[[clang::code_align(N)]].
This attribute applies to a loop and specifies the byte alignment for a
loop.
The attribute accepts a positive integer constant initialization
expression
indicating the number of bytes for the minimum alignment boundary.
Its value must be a power of 2, between 1 and 4096 (inclusive).

Added: 
    clang/test/CodeGen/code_align.c
    clang/test/Sema/code_align.c
    clang/test/Sema/code_align_ast.c

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/include/clang/Basic/Attr.td
    clang/include/clang/Basic/AttrDocs.td
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/include/clang/Sema/Sema.h
    clang/lib/CodeGen/CGLoopInfo.cpp
    clang/lib/CodeGen/CGLoopInfo.h
    clang/lib/Sema/SemaStmtAttr.cpp
    clang/lib/Sema/SemaTemplateInstantiate.cpp
    clang/lib/Sema/TreeTransform.h

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 5fdc061c32cb998..324dd3410beb5d2 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -317,6 +317,23 @@ Attribute Changes in Clang
   should be a coroutine. A non-coroutine function marked with ``[[clang::coro_wrapper]]``
   is still allowed to return the such a type. This is helpful for analyzers to recognize coroutines from the function signatures.
 
+- Clang now supports ``[[clang::code_align(N)]]`` as an attribute which can be
+  applied to a loop and specifies the byte alignment for a loop. This attribute
+  accepts a positive integer constant initialization expression indicating the
+  number of bytes for the minimum alignment boundary. Its value must be a power
+  of 2, between 1 and 4096(inclusive).
+
+  .. code-block:: c++
+
+      void Array(int *array, size_t n) {
+        [[clang::code_align(64)]] for (int i = 0; i < n; ++i) array[i] = 0;
+      }
+
+      template<int A>
+      void func() {
+        [[clang::code_align(A)]] for(;;) { }
+      }
+
 Improvements to Clang's diagnostics
 -----------------------------------
 - Clang constexpr evaluator now prints template arguments when displaying

diff  --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index f7a2b83b15ef5bc..c2fbdfc66c540d6 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -4313,3 +4313,15 @@ def PreferredType: InheritableAttr {
   let Args = [TypeArgument<"Type", 1>];
   let Documentation = [PreferredTypeDocumentation];
 }
+
+def CodeAlign: StmtAttr {
+  let Spellings = [Clang<"code_align">];
+  let Subjects = SubjectList<[ForStmt, CXXForRangeStmt, WhileStmt, DoStmt],
+                              ErrorDiag, "'for', 'while', and 'do' statements">;
+  let Args = [ExprArgument<"Alignment">];
+  let Documentation = [CodeAlignAttrDocs];
+  let AdditionalMembers = [{
+    static constexpr int MinimumAlignment = 1;
+    static constexpr int MaximumAlignment = 4096;
+  }];
+}

diff  --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 438d846c39eaa57..b5ceb47b6b8ad55 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -7540,3 +7540,44 @@ Note: ``a_promise_type::get_return_object`` is exempted from this analysis as it
 implementation detail of any coroutine library.
 }];
 }
+
+def CodeAlignAttrDocs : Documentation {
+  let Category = DocCatVariable;
+  let Heading = "clang::code_align";
+  let Content = [{
+The ``clang::code_align(N)`` attribute applies to a loop and specifies the byte
+alignment for a loop. The attribute accepts a positive integer constant
+initialization expression indicating the number of bytes for the minimum
+alignment boundary. Its value must be a power of 2, between 1 and 4096
+(inclusive).
+
+.. code-block:: c++
+
+  void foo() {
+    int var = 0;
+    [[clang::code_align(16)]] for (int i = 0; i < 10; ++i) var++;
+  }
+
+  void Array(int *array, size_t n) {
+    [[clang::code_align(64)]] for (int i = 0; i < n; ++i) array[i] = 0;
+  }
+
+  void count () {
+    int a1[10], int i = 0;
+    [[clang::code_align(32)]] while (i < 10) { a1[i] += 3; }
+  }
+
+  void check() {
+    int a = 10;
+    [[clang::code_align(8)]] do {
+      a = a + 1;
+    } while (a < 20);
+  }
+
+  template<int A>
+  void func() {
+    [[clang::code_align(A)]] for(;;) { }
+  }
+
+  }];
+}

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 3321ccaa8dab677..990692c06d7d3a8 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10025,6 +10025,11 @@ def err_duplicate_case_
diff ering_expr : Error<
 def warn_case_empty_range : Warning<"empty case range specified">;
 def warn_missing_case_for_condition :
   Warning<"no case matching constant switch condition '%0'">;
+def err_loop_attr_conflict : Error<
+  "conflicting loop attribute %0">;
+def err_attribute_power_of_two_in_range : Error<
+  "%0 attribute requires an integer argument which is a constant power of two "
+  "between %1 and %2 inclusive; provided argument was %3">;
 
 def warn_def_missing_case : Warning<"%plural{"
   "1:enumeration value %1 not explicitly handled in switch|"

diff  --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index e6a9d8da6d911cc..8a4fe2cc6dbe04f 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2099,6 +2099,9 @@ class Sema final {
   QualType BuildAddressSpaceAttr(QualType &T, Expr *AddrSpace,
                                  SourceLocation AttrLoc);
 
+  CodeAlignAttr *BuildCodeAlignAttr(const AttributeCommonInfo &CI, Expr *E);
+  bool CheckRebuiltCodeAlignStmtAttributes(ArrayRef<const Attr *> Attrs);
+
   bool CheckQualifiedFunctionForTypeId(QualType T, SourceLocation Loc);
 
   bool CheckFunctionReturnType(QualType T, SourceLocation Loc);

diff  --git a/clang/lib/CodeGen/CGLoopInfo.cpp b/clang/lib/CodeGen/CGLoopInfo.cpp
index e5d9db273c2d336..0d4800b90a2f26c 100644
--- a/clang/lib/CodeGen/CGLoopInfo.cpp
+++ b/clang/lib/CodeGen/CGLoopInfo.cpp
@@ -440,6 +440,14 @@ MDNode *LoopInfo::createMetadata(
         Ctx, {MDString::get(Ctx, "llvm.loop.parallel_accesses"), AccGroup}));
   }
 
+  // Setting clang::code_align attribute.
+  if (Attrs.CodeAlign > 0) {
+    Metadata *Vals[] = {MDString::get(Ctx, "llvm.loop.align"),
+                        ConstantAsMetadata::get(ConstantInt::get(
+                            llvm::Type::getInt32Ty(Ctx), Attrs.CodeAlign))};
+    LoopProperties.push_back(MDNode::get(Ctx, Vals));
+  }
+
   LoopProperties.insert(LoopProperties.end(), AdditionalLoopProperties.begin(),
                         AdditionalLoopProperties.end());
   return createFullUnrollMetadata(Attrs, LoopProperties, HasUserTransforms);
@@ -453,7 +461,7 @@ LoopAttributes::LoopAttributes(bool IsParallel)
       VectorizeScalable(LoopAttributes::Unspecified), InterleaveCount(0),
       UnrollCount(0), UnrollAndJamCount(0),
       DistributeEnable(LoopAttributes::Unspecified), PipelineDisabled(false),
-      PipelineInitiationInterval(0), MustProgress(false) {}
+      PipelineInitiationInterval(0), CodeAlign(0), MustProgress(false) {}
 
 void LoopAttributes::clear() {
   IsParallel = false;
@@ -469,6 +477,7 @@ void LoopAttributes::clear() {
   DistributeEnable = LoopAttributes::Unspecified;
   PipelineDisabled = false;
   PipelineInitiationInterval = 0;
+  CodeAlign = 0;
   MustProgress = false;
 }
 
@@ -493,8 +502,8 @@ LoopInfo::LoopInfo(BasicBlock *Header, const LoopAttributes &Attrs,
       Attrs.VectorizeEnable == LoopAttributes::Unspecified &&
       Attrs.UnrollEnable == LoopAttributes::Unspecified &&
       Attrs.UnrollAndJamEnable == LoopAttributes::Unspecified &&
-      Attrs.DistributeEnable == LoopAttributes::Unspecified && !StartLoc &&
-      !EndLoc && !Attrs.MustProgress)
+      Attrs.DistributeEnable == LoopAttributes::Unspecified &&
+      Attrs.CodeAlign == 0 && !StartLoc && !EndLoc && !Attrs.MustProgress)
     return;
 
   TempLoopID = MDNode::getTemporary(Header->getContext(), std::nullopt);
@@ -788,6 +797,15 @@ void LoopInfoStack::push(BasicBlock *Header, clang::ASTContext &Ctx,
     }
   }
 
+  // Identify loop attribute 'code_align' from Attrs.
+  // For attribute code_align:
+  // n - 'llvm.loop.align i32 n' metadata will be emitted.
+  if (const auto *CodeAlign = getSpecificAttr<const CodeAlignAttr>(Attrs)) {
+    const auto *CE = cast<ConstantExpr>(CodeAlign->getAlignment());
+    llvm::APSInt ArgVal = CE->getResultAsAPSInt();
+    setCodeAlign(ArgVal.getSExtValue());
+  }
+
   setMustProgress(MustProgress);
 
   if (CGOpts.OptimizationLevel > 0)

diff  --git a/clang/lib/CodeGen/CGLoopInfo.h b/clang/lib/CodeGen/CGLoopInfo.h
index 856e892f712e31f..a1c8c7e5307fd9c 100644
--- a/clang/lib/CodeGen/CGLoopInfo.h
+++ b/clang/lib/CodeGen/CGLoopInfo.h
@@ -79,6 +79,9 @@ struct LoopAttributes {
   /// Value for llvm.loop.pipeline.iicount metadata.
   unsigned PipelineInitiationInterval;
 
+  /// Value for 'llvm.loop.align' metadata.
+  unsigned CodeAlign;
+
   /// Value for whether the loop is required to make progress.
   bool MustProgress;
 };
@@ -282,6 +285,9 @@ class LoopInfoStack {
     StagedAttrs.PipelineInitiationInterval = C;
   }
 
+  /// Set value of code align for the next loop pushed.
+  void setCodeAlign(unsigned C) { StagedAttrs.CodeAlign = C; }
+
   /// Set no progress for the next loop pushed.
   void setMustProgress(bool P) { StagedAttrs.MustProgress = P; }
 

diff  --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp
index ad20bc8871f103a..eae1eaa2f9563d5 100644
--- a/clang/lib/Sema/SemaStmtAttr.cpp
+++ b/clang/lib/Sema/SemaStmtAttr.cpp
@@ -322,6 +322,81 @@ static Attr *handleUnlikely(Sema &S, Stmt *St, const ParsedAttr &A,
   return ::new (S.Context) UnlikelyAttr(S.Context, A);
 }
 
+CodeAlignAttr *Sema::BuildCodeAlignAttr(const AttributeCommonInfo &CI,
+                                        Expr *E) {
+  if (!E->isValueDependent()) {
+    llvm::APSInt ArgVal;
+    ExprResult Res = VerifyIntegerConstantExpression(E, &ArgVal);
+    if (Res.isInvalid())
+      return nullptr;
+    E = Res.get();
+
+    // This attribute requires an integer argument which is a constant power of
+    // two between 1 and 4096 inclusive.
+    if (ArgVal < CodeAlignAttr::MinimumAlignment ||
+        ArgVal > CodeAlignAttr::MaximumAlignment || !ArgVal.isPowerOf2()) {
+      if (std::optional<int64_t> Value = ArgVal.trySExtValue())
+        Diag(CI.getLoc(), diag::err_attribute_power_of_two_in_range)
+            << CI << CodeAlignAttr::MinimumAlignment
+            << CodeAlignAttr::MaximumAlignment << Value.value();
+      else
+        Diag(CI.getLoc(), diag::err_attribute_power_of_two_in_range)
+            << CI << CodeAlignAttr::MinimumAlignment
+            << CodeAlignAttr::MaximumAlignment << E;
+      return nullptr;
+    }
+  }
+  return new (Context) CodeAlignAttr(Context, CI, E);
+}
+
+static Attr *handleCodeAlignAttr(Sema &S, Stmt *St, const ParsedAttr &A) {
+
+  Expr *E = A.getArgAsExpr(0);
+  return S.BuildCodeAlignAttr(A, E);
+}
+
+// Diagnose non-identical duplicates as a 'conflicting' loop attributes
+// and suppress duplicate errors in cases where the two match for
+// [[clang::code_align()]] attribute.
+static void CheckForDuplicateCodeAlignAttrs(Sema &S,
+                                            ArrayRef<const Attr *> Attrs) {
+  auto FindFunc = [](const Attr *A) { return isa<const CodeAlignAttr>(A); };
+  const auto *FirstItr = std::find_if(Attrs.begin(), Attrs.end(), FindFunc);
+
+  if (FirstItr == Attrs.end()) // no attributes found
+    return;
+
+  const auto *LastFoundItr = FirstItr;
+  std::optional<llvm::APSInt> FirstValue;
+
+  const auto *CAFA =
+      dyn_cast<ConstantExpr>(cast<CodeAlignAttr>(*FirstItr)->getAlignment());
+  // Return early if first alignment expression is dependent (since we don't
+  // know what the effective size will be), and skip the loop entirely.
+  if (!CAFA)
+    return;
+
+  while (Attrs.end() != (LastFoundItr = std::find_if(LastFoundItr + 1,
+                                                     Attrs.end(), FindFunc))) {
+    const auto *CASA = dyn_cast<ConstantExpr>(
+        cast<CodeAlignAttr>(*LastFoundItr)->getAlignment());
+    // If the value is dependent, we can not test anything.
+    if (!CASA)
+      return;
+    // Test the attribute values.
+    llvm::APSInt SecondValue = CASA->getResultAsAPSInt();
+    if (!FirstValue)
+      FirstValue = CAFA->getResultAsAPSInt();
+
+    if (FirstValue != SecondValue) {
+      S.Diag((*LastFoundItr)->getLocation(), diag::err_loop_attr_conflict)
+          << *FirstItr;
+      S.Diag((*FirstItr)->getLocation(), diag::note_previous_attribute);
+    }
+    return;
+  }
+}
+
 #define WANT_STMT_MERGE_LOGIC
 #include "clang/Sema/AttrParsedAttrImpl.inc"
 #undef WANT_STMT_MERGE_LOGIC
@@ -523,6 +598,8 @@ static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const ParsedAttr &A,
     return handleLikely(S, St, A, Range);
   case ParsedAttr::AT_Unlikely:
     return handleUnlikely(S, St, A, Range);
+  case ParsedAttr::AT_CodeAlign:
+    return handleCodeAlignAttr(S, St, A);
   default:
     // N.B., ClangAttrEmitter.cpp emits a diagnostic helper that ensures a
     // declaration attribute is not written on a statement, but this code is
@@ -541,4 +618,10 @@ void Sema::ProcessStmtAttributes(Stmt *S, const ParsedAttributes &InAttrs,
   }
 
   CheckForIncompatibleAttributes(*this, OutAttrs);
+  CheckForDuplicateCodeAlignAttrs(*this, OutAttrs);
+}
+
+bool Sema::CheckRebuiltCodeAlignStmtAttributes(ArrayRef<const Attr *> Attrs) {
+  CheckForDuplicateCodeAlignAttrs(*this, Attrs);
+  return false;
 }

diff  --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 6ad70109c8cee95..4ae027bd1bb6735 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1374,7 +1374,7 @@ namespace {
     const AlwaysInlineAttr *
     TransformStmtAlwaysInlineAttr(const Stmt *OrigS, const Stmt *InstS,
                                   const AlwaysInlineAttr *A);
-
+    const CodeAlignAttr *TransformCodeAlignAttr(const CodeAlignAttr *CA);
     ExprResult TransformPredefinedExpr(PredefinedExpr *E);
     ExprResult TransformDeclRefExpr(DeclRefExpr *E);
     ExprResult TransformCXXDefaultArgExpr(CXXDefaultArgExpr *E);
@@ -1906,6 +1906,12 @@ const AlwaysInlineAttr *TemplateInstantiator::TransformStmtAlwaysInlineAttr(
   return A;
 }
 
+const CodeAlignAttr *
+TemplateInstantiator::TransformCodeAlignAttr(const CodeAlignAttr *CA) {
+  Expr *TransformedExpr = getDerived().TransformExpr(CA->getAlignment()).get();
+  return getSema().BuildCodeAlignAttr(*CA, TransformedExpr);
+}
+
 ExprResult TemplateInstantiator::transformNonTypeTemplateParmRef(
     Decl *AssociatedDecl, const NonTypeTemplateParmDecl *parm,
     SourceLocation loc, TemplateArgument arg,

diff  --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index e24f710fdedd4e2..645a4988f26d34c 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -1378,6 +1378,8 @@ class TreeTransform {
   StmtResult RebuildAttributedStmt(SourceLocation AttrLoc,
                                    ArrayRef<const Attr *> Attrs,
                                    Stmt *SubStmt) {
+    if (SemaRef.CheckRebuiltCodeAlignStmtAttributes(Attrs))
+      return StmtError();
     return SemaRef.BuildAttributedStmt(AttrLoc, Attrs, SubStmt);
   }
 

diff  --git a/clang/test/CodeGen/code_align.c b/clang/test/CodeGen/code_align.c
new file mode 100644
index 000000000000000..f6d86ec969ae5f6
--- /dev/null
+++ b/clang/test/CodeGen/code_align.c
@@ -0,0 +1,59 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -x c %s %s -o - | FileCheck -check-prefix=CHECK-C %s
+// RUN: %clang_cc1 -fsyntax-only -emit-llvm -x c++ -std=c++11 %s -o - | FileCheck %s --check-prefixes CHECK-C,CHECK-CPP
+
+// CHECK-C: br label %for.cond, !llvm.loop ![[MD_FP:[0-9]+]]
+// CHECK-C: br label %while.cond, !llvm.loop ![[MD_FP_1:[0-9]+]]
+// CHECK-C: br i1 %cmp3, label %do.body, label %do.end, !llvm.loop ![[MD_FP_2:[0-9]+]]
+// CHECK-C: br label %for.cond5, !llvm.loop ![[MD_FP_3:[0-9]+]]
+
+// CHECK-CPP: br label %for.cond, !llvm.loop ![[MD_FP_4:[0-9]+]]
+// CHECK-CPP: br label %for.cond2, !llvm.loop ![[MD_FP_5:[0-9]+]]
+
+void bar(int);
+void code_align() {
+  int a[10];
+  // CHECK-C: ![[MD_FP]] = distinct !{![[MD_FP]], ![[MP:[0-9]+]], ![[MD_code_align:[0-9]+]]}
+  // CHECK-C-NEXT: ![[MP]] = !{!"llvm.loop.mustprogress"}
+  // CHECK-C-NEXT: ![[MD_code_align]] = !{!"llvm.loop.align", i32 4}
+  [[clang::code_align(4)]]
+  for(int I=0; I<128; ++I) { bar(I); }
+
+  // CHECK-C: ![[MD_FP_1]] = distinct !{![[MD_FP_1]], ![[MP]], ![[MD_code_align_1:[0-9]+]]}
+  // CHECK-C-NEXT: ![[MD_code_align_1]] = !{!"llvm.loop.align", i32 16}
+  int i = 0;
+  [[clang::code_align(16)]] while (i < 60) {
+    a[i] += 3;
+  }
+
+  // CHECK-C: ![[MD_FP_2]] = distinct !{![[MD_FP_2]], ![[MP]], ![[MD_code_align_2:[0-9]+]]}
+  // CHECK-C-NEXT: ![[MD_code_align_2]] = !{!"llvm.loop.align", i32 8}
+  int b = 10;
+  [[clang::code_align(8)]] do {
+    b = b + 1;
+  } while (b < 20);
+
+  // CHECK-C: ![[MD_FP_3]] = distinct !{![[MD_FP_3]], ![[MP]], ![[MD_code_align_3:[0-9]+]]}
+  // CHECK-C-NEXT: ![[MD_code_align_3]] = !{!"llvm.loop.align", i32 64}
+  [[clang::code_align(64)]]
+  for(int I=0; I<128; ++I) { bar(I); }
+}
+
+#if __cplusplus >= 201103L
+template <int A, int B>
+void code_align_cpp() {
+  int a[10];	
+  // CHECK-CPP: ![[MD_FP_4]] = distinct !{![[MD_FP_4]], ![[MP]], ![[MD_code_align_4:[0-9]+]]}
+  // CHECK-CPP-NEXT: ![[MD_code_align_4]] = !{!"llvm.loop.align", i32 32}
+  [[clang::code_align(A)]] for (int i = 0; i != 10; ++i)
+    a[i] = 0;
+
+  // CHECK-CPP: ![[MD_FP_5]] = distinct !{![[MD_FP_5]], ![[MD_code_align]]}
+  int c[] = {0, 1, 2, 3, 4, 5};
+  [[clang::code_align(B)]] for (int n : c) { n *= 2; }
+}
+
+int main() {
+  code_align_cpp<32, 4>();
+  return 0;
+}
+#endif

diff  --git a/clang/test/Sema/code_align.c b/clang/test/Sema/code_align.c
new file mode 100644
index 000000000000000..539c0c0b0f1ccfb
--- /dev/null
+++ b/clang/test/Sema/code_align.c
@@ -0,0 +1,166 @@
+// RUN: %clang_cc1 -fsyntax-only -verify=expected,c-local -x c %s
+// RUN: %clang_cc1 -fsyntax-only -verify=expected,cpp-local -pedantic -x c++ -std=c++11 %s
+
+void foo() {
+  int i;
+  int a[10], b[10];
+
+  [[clang::code_align(8)]]
+  for (i = 0; i < 10; ++i) {  // this is OK
+    a[i] = b[i] = 0;
+  }
+  // expected-error at +1{{'code_align' attribute only applies to 'for', 'while', and 'do' statements}}
+  [[clang::code_align(4)]]
+  i = 7;
+  for (i = 0; i < 10; ++i) {
+    a[i] = b[i] = 0;
+  }
+
+  // expected-error at +1{{'code_align' attribute cannot be applied to a declaration}}
+  [[clang::code_align(12)]] int n[10];
+}
+
+void bar(int);
+// cpp-local-note at +1{{declared here}}
+void foo1(int A)
+{
+  // expected-error at +1{{'code_align' attribute requires an integer argument which is a constant power of two between 1 and 4096 inclusive; provided argument was 0}}
+  [[clang::code_align(0)]]
+  for(int I=0; I<128; ++I) { bar(I); }
+
+  // expected-error at +1{{'code_align' attribute requires an integer argument which is a constant power of two between 1 and 4096 inclusive; provided argument was -4}}
+  [[clang::code_align(-4)]]
+  for(int I=0; I<128; ++I) { bar(I); }
+
+    // cpp-local-error at +2{{integral constant expression must have integral or unscoped enumeration type, not 'double'}}
+    // c-local-error at +1{{integer constant expression must have integer type, not 'double'}}
+  [[clang::code_align(64.0)]]
+  for(int I=0; I<128; ++I) { bar(I); }
+
+  // expected-error at +1{{'code_align' attribute takes one argument}}
+  [[clang::code_align()]]
+  for(int I=0; I<128; ++I) { bar(I); }
+
+  // expected-error at +1{{'code_align' attribute takes one argument}}
+  [[clang::code_align(4,8)]]
+  for(int I=0; I<128; ++I) { bar(I); }
+
+  // no diagnostic is expected
+  [[clang::code_align(32)]]
+  for(int I=0; I<128; ++I) { bar(I); }
+
+  // cpp-local-error at +2{{integral constant expression must have integral or unscoped enumeration type, not 'const char[4]'}}
+  // c-local-error at +1{{integer constant expression must have integer type, not 'char[4]'}}
+  [[clang::code_align("abc")]]
+  for(int I=0; I<128; ++I) { bar(I); }
+
+  [[clang::code_align(64)]] // OK
+  [[clang::code_align(64)]] // OK
+  for(int I=0; I<128; ++I) { bar(I); }
+
+  [[clang::code_align(8)]]  // expected-note{{previous attribute is here}}
+  [[clang::code_align(64)]] // expected-error{{conflicting loop attribute 'code_align'}}
+  for(int I=0; I<128; ++I) { bar(I); }
+
+  // expected-error at +1{{'code_align' attribute requires an integer argument which is a constant power of two between 1 and 4096 inclusive; provided argument was 7}}
+  [[clang::code_align(7)]]
+  for(int I=0; I<128; ++I) { bar(I); }
+
+  // expected-error at +1{{'code_align' attribute requires an integer argument which is a constant power of two between 1 and 4096 inclusive; provided argument was 5000}}
+  [[clang::code_align(5000)]]
+  for(int I=0; I<128; ++I) { bar(I); }
+
+  // expected-warning at +2{{integer literal is too large to be represented in a signed integer type, interpreting as unsigned}}
+  // expected-error at +1{{'code_align' attribute requires an integer argument which is a constant power of two between 1 and 4096 inclusive; provided argument was -9223372036854775808}}
+  [[clang::code_align(9223372036854775808)]]
+  for(int I=0; I<256; ++I) { bar(I); }
+
+  // expected-error at +1{{'code_align' attribute requires an integer argument which is a constant power of two between 1 and 4096 inclusive; provided argument was (__int128_t)1311768467294899680ULL << 64}}
+  [[clang::code_align((__int128_t)0x1234567890abcde0ULL << 64)]]
+  for(int I=0; I<256; ++I) { bar(I); }
+
+  // expected-error at +1 {{'code_align' attribute requires an integer argument which is a constant power of two between 1 and 4096 inclusive; provided argument was -922337203685477}}
+  [[clang::code_align(-922337203685477)]]
+  for(int I=0; I<256; ++I) { bar(I); }
+
+  // cpp-local-error at +3{{expression is not an integral constant expression}}
+  // cpp-local-note at +2{{left shift of negative value -1311768467294899680}}
+  // c-local-error at +1{{'code_align' attribute requires an integer argument which is a constant power of two between 1 and 4096 inclusive; provided argument was -(__int128_t)1311768467294899680ULL << 64}}
+  [[clang::code_align(-(__int128_t)0x1234567890abcde0ULL << 64)]]
+  for(int I=0; I<256; ++I) { bar(I); }
+
+  // cpp-local-error at +3{{expression is not an integral constant expression}}
+  // cpp-local-note at +2{{function parameter 'A' with unknown value cannot be used in a constant expression}}
+  // c-local-error at +1{{expression is not an integer constant expression}}
+  [[clang::code_align(A)]]
+  for(int I=0; I<128; ++I) { bar(I); }
+}
+
+void check_code_align_expression() {
+  int a[10];
+
+  // Test that checks expression is not a constant expression.
+  int foo2; // cpp-local-note {{declared here}}
+  // c-local-error at +3{{expression is not an integer constant expression}}
+  // cpp-local-error at +2{{expression is not an integral constant expression}}
+  // cpp-local-note at +1{{read of non-const variable 'foo2' is not allowed in a constant expression}}
+  [[clang::code_align(foo2 + 1)]]
+  for (int i = 0; i != 10; ++i)
+    a[i] = 0;
+
+#if __cplusplus >= 201103L
+  // Test that checks expression is a constant expression.
+  constexpr int bars = 0;
+  [[clang::code_align(bars + 1)]]
+  for (int i = 0; i != 10; ++i)
+    a[i] = 0;
+#endif
+}
+
+#if __cplusplus >= 201103L
+template <int A, int B, int C, int D, int E>
+void code_align_dependent() {
+  [[clang::code_align(C)]]
+  for(int I=0; I<128; ++I) { bar(I); }
+
+  [[clang::code_align(A)]] // OK
+  [[clang::code_align(A)]] // OK
+  for(int I=0; I<128; ++I) { bar(I); }
+
+  [[clang::code_align(A)]] // expected-note{{previous attribute is here}}
+  [[clang::code_align(E)]] // cpp-local-error{{conflicting loop attribute 'code_align'}}
+  for(int I=0; I<128; ++I) { bar(I); }
+
+  // cpp-local-error at +1{{'code_align' attribute requires an integer argument which is a constant power of two between 1 and 4096 inclusive; provided argument was 23}}
+  [[clang::code_align(B)]]
+  for(int I=0; I<128; ++I) { bar(I); }
+
+  // cpp-local-error at +2{{'code_align' attribute requires an integer argument which is a constant power of two between 1 and 4096 inclusive; provided argument was -10}}
+  // cpp-local-note@#neg-instantiation{{in instantiation of function template specialization 'code_align_dependent<8, 23, 32, -10, 64>' requested here}}
+  [[clang::code_align(D)]]
+  for(int I=0; I<128; ++I) { bar(I); }
+}
+
+template<int ITMPL>
+void bar3() {
+  [[clang::code_align(8)]]      // expected-note{{previous attribute is here}}
+  [[clang::code_align(ITMPL)]] // cpp-local-error{{conflicting loop attribute 'code_align'}} \
+	                       // cpp-local-note@#temp-instantiation{{in instantiation of function template specialization 'bar3<4>' requested here}}
+  for(int I=0; I<128; ++I) { bar(I); }
+}
+
+template<int ITMPL1>
+void bar4() {
+  [[clang::code_align(ITMPL1)]] // expected-note{{previous attribute is here}}
+  [[clang::code_align(32)]]    // cpp-local-error{{conflicting loop attribute 'code_align'}} \
+	                       // cpp-local-note@#temp-instantiation1{{in instantiation of function template specialization 'bar4<64>' requested here}}
+  for(int I=0; I<128; ++I) { bar(I); }
+}
+
+int main() {
+  code_align_dependent<8, 23, 32, -10, 64>(); // #neg-instantiation
+  bar3<4>();  // #temp-instantiation
+  bar4<64>(); // #temp-instantiation1
+  return 0;
+}
+#endif

diff  --git a/clang/test/Sema/code_align_ast.c b/clang/test/Sema/code_align_ast.c
new file mode 100644
index 000000000000000..2cfbf11f1fd0eab
--- /dev/null
+++ b/clang/test/Sema/code_align_ast.c
@@ -0,0 +1,89 @@
+// RUN: %clang_cc1 -fsyntax-only -ast-dump -verify -x c %s | FileCheck -check-prefix=CHECK-C %s
+// RUN: %clang_cc1 -fsyntax-only -ast-dump -x c++ -std=c++11 %s | FileCheck %s --check-prefixes CHECK-C,CHECK-CPP
+
+// expected-no-diagnostics
+
+void bar(int);
+// CHECK-C: FunctionDecl{{.*}}code_align 'void ()'
+void code_align() {
+  int a1[10], a2[10];
+  // CHECK-C: AttributedStmt
+  // CHECK-C-NEXT: CodeAlignAttr
+  // CHECK-C-NEXT:  ConstantExpr{{.*}}'int'
+  // CHECK-C-NEXT:  value: Int 16
+  // CHECK-C-NEXT:  IntegerLiteral{{.*}}16{{$}}
+  [[clang::code_align(16)]] for (int p = 0; p < 128; ++p) {
+    a1[p] = a2[p] = 0;
+  }
+
+  // CHECK-C: AttributedStmt
+  // CHECK-C-NEXT:  CodeAlignAttr
+  // CHECK-C-NEXT:  ConstantExpr{{.*}}'int'
+  // CHECK-C-NEXT:  value: Int 4
+  // CHECK-C-NEXT:  IntegerLiteral{{.*}}4{{$}}
+  int i = 0;
+  [[clang::code_align(4)]] while (i < 30) {
+    a1[i] += 3;
+  }
+
+  // CHECK-C: AttributedStmt
+  // CHECK-C-NEXT:  CodeAlignAttr
+  // CHECK-C-NEXT:  ConstantExpr{{.*}}'int'
+  // CHECK-C-NEXT:  value: Int 32
+  // CHECK-C-NEXT:  IntegerLiteral{{.*}}32{{$}}
+  for (int i = 0; i < 128; ++i) {
+    [[clang::code_align(32)]]  for (int j = 0; j < 128; ++j) {
+      a1[i] += a1[j];
+    }
+  }
+
+  // CHECK-C: AttributedStmt
+  // CHECK-C-NEXT:  CodeAlignAttr
+  // CHECK-C-NEXT:  ConstantExpr{{.*}}'int'
+  // CHECK-C-NEXT:  value: Int 64
+  // CHECK-C-NEXT:  IntegerLiteral{{.*}}64{{$}}
+  [[clang::code_align(64)]]
+  for(int I=0; I<128; ++I) { bar(I); }
+
+  // CHECK-C: AttributedStmt
+  // CHECK-C-NEXT: CodeAlignAttr
+  // CHECK-C-NEXT:  ConstantExpr{{.*}}'int'
+  // CHECK-C-NEXT:  value: Int 4
+  // CHECK-C-NEXT:  IntegerLiteral{{.*}}4{{$}}
+  int b = 10;
+  [[clang::code_align(4)]] do {
+    b = b + 1;
+  } while (b < 20);
+}
+
+#if __cplusplus >= 201103L
+//CHECK-CPP: FunctionDecl{{.*}}used code_align_cpp 'void ()' implicit_instantiation
+template <int A, int B>
+void code_align_cpp() {
+  int a[10];
+  // CHECK-CPP: AttributedStmt
+  // CHECK-CPP-NEXT:  CodeAlignAttr
+  // CHECK-CPP-NEXT:  ConstantExpr{{.*}}'int'
+  // CHECK-CPP-NEXT:  value: Int 32
+  // CHECK-CPP-NEXT:  SubstNonTypeTemplateParmExpr{{.*}}'int'
+  // CHECK-CPP-NEXT:  NonTypeTemplateParmDecl{{.*}}referenced 'int' depth 0 index 0 A
+  // CHECK-CPP-NEXT:  IntegerLiteral{{.*}}32{{$}}
+  [[clang::code_align(A)]] for (int i = 0; i != 10; ++i)
+    a[i] = 0;
+
+  // CHECK-CPP: AttributedStmt
+  // CHECK-CPP-NEXT:  CodeAlignAttr
+  // CHECK-CPP-NEXT:  ConstantExpr{{.*}}'int'
+  // CHECK-CPP-NEXT:  value: Int 4
+  // CHECK-CPP-NEXT:  SubstNonTypeTemplateParmExpr{{.*}}'int'
+  // CHECK-CPP-NEXT:  NonTypeTemplateParmDecl{{.*}}referenced 'int' depth 0 index 1 B
+  // CHECK-CPP-NEXT:  IntegerLiteral{{.*}}4{{$}}
+  int c[] = {0, 1, 2, 3, 4, 5};
+  [[clang::code_align(B)]] for (int n : c) { n *= 2; }
+}
+
+int main() {
+  code_align_cpp<32, 4>();
+  return 0;
+}
+#endif


        


More information about the cfe-commits mailing list