[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