diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 5fdc061c32cb9..324dd3410beb5 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 + 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 f7a2b83b15ef5..c2fbdfc66c540 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 438d846c39eaa..b5ceb47b6b8ad 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 + void func() { + [[clang::code_align(A)]] for(;;) { } + } + + }]; +} diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 3321ccaa8dab6..990692c06d7d3 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10025,6 +10025,11 @@ def err_duplicate_case_differing_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 e6a9d8da6d911..8a4fe2cc6dbe0 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 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 e5d9db273c2d3..0d4800b90a2f2 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(Attrs)) { + const auto *CE = cast(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 856e892f712e3..a1c8c7e5307fd 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 ad20bc8871f10..eae1eaa2f9563 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 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 Attrs) { + auto FindFunc = [](const Attr *A) { return isa(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 FirstValue; + + const auto *CAFA = + dyn_cast(cast(*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( + cast(*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 Attrs) { + CheckForDuplicateCodeAlignAttrs(*this, Attrs); + return false; } diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 6ad70109c8cee..4ae027bd1bb67 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 e24f710fdedd4..645a4988f26d3 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -1378,6 +1378,8 @@ class TreeTransform { StmtResult RebuildAttributedStmt(SourceLocation AttrLoc, ArrayRef 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 0000000000000..f6d86ec969ae5 --- /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 +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 0000000000000..539c0c0b0f1cc --- /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@+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@+1{{'code_align' attribute cannot be applied to a declaration}} + [[clang::code_align(12)]] int n[10]; +} + +void bar(int); +// cpp-local-note@+1{{declared here}} +void foo1(int A) +{ + // expected-error@+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@+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@+2{{integral constant expression must have integral or unscoped enumeration type, not 'double'}} + // c-local-error@+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@+1{{'code_align' attribute takes one argument}} + [[clang::code_align()]] + for(int I=0; I<128; ++I) { bar(I); } + + // expected-error@+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@+2{{integral constant expression must have integral or unscoped enumeration type, not 'const char[4]'}} + // c-local-error@+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@+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@+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@+2{{integer literal is too large to be represented in a signed integer type, interpreting as unsigned}} + // expected-error@+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@+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@+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@+3{{expression is not an integral constant expression}} + // cpp-local-note@+2{{left shift of negative value -1311768467294899680}} + // c-local-error@+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@+3{{expression is not an integral constant expression}} + // cpp-local-note@+2{{function parameter 'A' with unknown value cannot be used in a constant expression}} + // c-local-error@+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@+3{{expression is not an integer constant expression}} + // cpp-local-error@+2{{expression is not an integral constant expression}} + // cpp-local-note@+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 +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@+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@+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 +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 +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 0000000000000..2cfbf11f1fd0e --- /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 +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