Skip to content

Commit 48ff354

Browse files
authored
[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).
1 parent 0e24179 commit 48ff354

File tree

13 files changed

+511
-4
lines changed

13 files changed

+511
-4
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,23 @@ Attribute Changes in Clang
317317
should be a coroutine. A non-coroutine function marked with ``[[clang::coro_wrapper]]``
318318
is still allowed to return the such a type. This is helpful for analyzers to recognize coroutines from the function signatures.
319319

320+
- Clang now supports ``[[clang::code_align(N)]]`` as an attribute which can be
321+
applied to a loop and specifies the byte alignment for a loop. This attribute
322+
accepts a positive integer constant initialization expression indicating the
323+
number of bytes for the minimum alignment boundary. Its value must be a power
324+
of 2, between 1 and 4096(inclusive).
325+
326+
.. code-block:: c++
327+
328+
void Array(int *array, size_t n) {
329+
[[clang::code_align(64)]] for (int i = 0; i < n; ++i) array[i] = 0;
330+
}
331+
332+
template<int A>
333+
void func() {
334+
[[clang::code_align(A)]] for(;;) { }
335+
}
336+
320337
Improvements to Clang's diagnostics
321338
-----------------------------------
322339
- Clang constexpr evaluator now prints template arguments when displaying

clang/include/clang/Basic/Attr.td

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4313,3 +4313,15 @@ def PreferredType: InheritableAttr {
43134313
let Args = [TypeArgument<"Type", 1>];
43144314
let Documentation = [PreferredTypeDocumentation];
43154315
}
4316+
4317+
def CodeAlign: StmtAttr {
4318+
let Spellings = [Clang<"code_align">];
4319+
let Subjects = SubjectList<[ForStmt, CXXForRangeStmt, WhileStmt, DoStmt],
4320+
ErrorDiag, "'for', 'while', and 'do' statements">;
4321+
let Args = [ExprArgument<"Alignment">];
4322+
let Documentation = [CodeAlignAttrDocs];
4323+
let AdditionalMembers = [{
4324+
static constexpr int MinimumAlignment = 1;
4325+
static constexpr int MaximumAlignment = 4096;
4326+
}];
4327+
}

clang/include/clang/Basic/AttrDocs.td

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7540,3 +7540,44 @@ Note: ``a_promise_type::get_return_object`` is exempted from this analysis as it
75407540
implementation detail of any coroutine library.
75417541
}];
75427542
}
7543+
7544+
def CodeAlignAttrDocs : Documentation {
7545+
let Category = DocCatVariable;
7546+
let Heading = "clang::code_align";
7547+
let Content = [{
7548+
The ``clang::code_align(N)`` attribute applies to a loop and specifies the byte
7549+
alignment for a loop. The attribute accepts a positive integer constant
7550+
initialization expression indicating the number of bytes for the minimum
7551+
alignment boundary. Its value must be a power of 2, between 1 and 4096
7552+
(inclusive).
7553+
7554+
.. code-block:: c++
7555+
7556+
void foo() {
7557+
int var = 0;
7558+
[[clang::code_align(16)]] for (int i = 0; i < 10; ++i) var++;
7559+
}
7560+
7561+
void Array(int *array, size_t n) {
7562+
[[clang::code_align(64)]] for (int i = 0; i < n; ++i) array[i] = 0;
7563+
}
7564+
7565+
void count () {
7566+
int a1[10], int i = 0;
7567+
[[clang::code_align(32)]] while (i < 10) { a1[i] += 3; }
7568+
}
7569+
7570+
void check() {
7571+
int a = 10;
7572+
[[clang::code_align(8)]] do {
7573+
a = a + 1;
7574+
} while (a < 20);
7575+
}
7576+
7577+
template<int A>
7578+
void func() {
7579+
[[clang::code_align(A)]] for(;;) { }
7580+
}
7581+
7582+
}];
7583+
}

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10025,6 +10025,11 @@ def err_duplicate_case_differing_expr : Error<
1002510025
def warn_case_empty_range : Warning<"empty case range specified">;
1002610026
def warn_missing_case_for_condition :
1002710027
Warning<"no case matching constant switch condition '%0'">;
10028+
def err_loop_attr_conflict : Error<
10029+
"conflicting loop attribute %0">;
10030+
def err_attribute_power_of_two_in_range : Error<
10031+
"%0 attribute requires an integer argument which is a constant power of two "
10032+
"between %1 and %2 inclusive; provided argument was %3">;
1002810033

1002910034
def warn_def_missing_case : Warning<"%plural{"
1003010035
"1:enumeration value %1 not explicitly handled in switch|"

clang/include/clang/Sema/Sema.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2099,6 +2099,9 @@ class Sema final {
20992099
QualType BuildAddressSpaceAttr(QualType &T, Expr *AddrSpace,
21002100
SourceLocation AttrLoc);
21012101

2102+
CodeAlignAttr *BuildCodeAlignAttr(const AttributeCommonInfo &CI, Expr *E);
2103+
bool CheckRebuiltCodeAlignStmtAttributes(ArrayRef<const Attr *> Attrs);
2104+
21022105
bool CheckQualifiedFunctionForTypeId(QualType T, SourceLocation Loc);
21032106

21042107
bool CheckFunctionReturnType(QualType T, SourceLocation Loc);

clang/lib/CodeGen/CGLoopInfo.cpp

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,14 @@ MDNode *LoopInfo::createMetadata(
440440
Ctx, {MDString::get(Ctx, "llvm.loop.parallel_accesses"), AccGroup}));
441441
}
442442

443+
// Setting clang::code_align attribute.
444+
if (Attrs.CodeAlign > 0) {
445+
Metadata *Vals[] = {MDString::get(Ctx, "llvm.loop.align"),
446+
ConstantAsMetadata::get(ConstantInt::get(
447+
llvm::Type::getInt32Ty(Ctx), Attrs.CodeAlign))};
448+
LoopProperties.push_back(MDNode::get(Ctx, Vals));
449+
}
450+
443451
LoopProperties.insert(LoopProperties.end(), AdditionalLoopProperties.begin(),
444452
AdditionalLoopProperties.end());
445453
return createFullUnrollMetadata(Attrs, LoopProperties, HasUserTransforms);
@@ -453,7 +461,7 @@ LoopAttributes::LoopAttributes(bool IsParallel)
453461
VectorizeScalable(LoopAttributes::Unspecified), InterleaveCount(0),
454462
UnrollCount(0), UnrollAndJamCount(0),
455463
DistributeEnable(LoopAttributes::Unspecified), PipelineDisabled(false),
456-
PipelineInitiationInterval(0), MustProgress(false) {}
464+
PipelineInitiationInterval(0), CodeAlign(0), MustProgress(false) {}
457465

458466
void LoopAttributes::clear() {
459467
IsParallel = false;
@@ -469,6 +477,7 @@ void LoopAttributes::clear() {
469477
DistributeEnable = LoopAttributes::Unspecified;
470478
PipelineDisabled = false;
471479
PipelineInitiationInterval = 0;
480+
CodeAlign = 0;
472481
MustProgress = false;
473482
}
474483

@@ -493,8 +502,8 @@ LoopInfo::LoopInfo(BasicBlock *Header, const LoopAttributes &Attrs,
493502
Attrs.VectorizeEnable == LoopAttributes::Unspecified &&
494503
Attrs.UnrollEnable == LoopAttributes::Unspecified &&
495504
Attrs.UnrollAndJamEnable == LoopAttributes::Unspecified &&
496-
Attrs.DistributeEnable == LoopAttributes::Unspecified && !StartLoc &&
497-
!EndLoc && !Attrs.MustProgress)
505+
Attrs.DistributeEnable == LoopAttributes::Unspecified &&
506+
Attrs.CodeAlign == 0 && !StartLoc && !EndLoc && !Attrs.MustProgress)
498507
return;
499508

500509
TempLoopID = MDNode::getTemporary(Header->getContext(), std::nullopt);
@@ -788,6 +797,15 @@ void LoopInfoStack::push(BasicBlock *Header, clang::ASTContext &Ctx,
788797
}
789798
}
790799

800+
// Identify loop attribute 'code_align' from Attrs.
801+
// For attribute code_align:
802+
// n - 'llvm.loop.align i32 n' metadata will be emitted.
803+
if (const auto *CodeAlign = getSpecificAttr<const CodeAlignAttr>(Attrs)) {
804+
const auto *CE = cast<ConstantExpr>(CodeAlign->getAlignment());
805+
llvm::APSInt ArgVal = CE->getResultAsAPSInt();
806+
setCodeAlign(ArgVal.getSExtValue());
807+
}
808+
791809
setMustProgress(MustProgress);
792810

793811
if (CGOpts.OptimizationLevel > 0)

clang/lib/CodeGen/CGLoopInfo.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ struct LoopAttributes {
7979
/// Value for llvm.loop.pipeline.iicount metadata.
8080
unsigned PipelineInitiationInterval;
8181

82+
/// Value for 'llvm.loop.align' metadata.
83+
unsigned CodeAlign;
84+
8285
/// Value for whether the loop is required to make progress.
8386
bool MustProgress;
8487
};
@@ -282,6 +285,9 @@ class LoopInfoStack {
282285
StagedAttrs.PipelineInitiationInterval = C;
283286
}
284287

288+
/// Set value of code align for the next loop pushed.
289+
void setCodeAlign(unsigned C) { StagedAttrs.CodeAlign = C; }
290+
285291
/// Set no progress for the next loop pushed.
286292
void setMustProgress(bool P) { StagedAttrs.MustProgress = P; }
287293

clang/lib/Sema/SemaStmtAttr.cpp

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,81 @@ static Attr *handleUnlikely(Sema &S, Stmt *St, const ParsedAttr &A,
322322
return ::new (S.Context) UnlikelyAttr(S.Context, A);
323323
}
324324

325+
CodeAlignAttr *Sema::BuildCodeAlignAttr(const AttributeCommonInfo &CI,
326+
Expr *E) {
327+
if (!E->isValueDependent()) {
328+
llvm::APSInt ArgVal;
329+
ExprResult Res = VerifyIntegerConstantExpression(E, &ArgVal);
330+
if (Res.isInvalid())
331+
return nullptr;
332+
E = Res.get();
333+
334+
// This attribute requires an integer argument which is a constant power of
335+
// two between 1 and 4096 inclusive.
336+
if (ArgVal < CodeAlignAttr::MinimumAlignment ||
337+
ArgVal > CodeAlignAttr::MaximumAlignment || !ArgVal.isPowerOf2()) {
338+
if (std::optional<int64_t> Value = ArgVal.trySExtValue())
339+
Diag(CI.getLoc(), diag::err_attribute_power_of_two_in_range)
340+
<< CI << CodeAlignAttr::MinimumAlignment
341+
<< CodeAlignAttr::MaximumAlignment << Value.value();
342+
else
343+
Diag(CI.getLoc(), diag::err_attribute_power_of_two_in_range)
344+
<< CI << CodeAlignAttr::MinimumAlignment
345+
<< CodeAlignAttr::MaximumAlignment << E;
346+
return nullptr;
347+
}
348+
}
349+
return new (Context) CodeAlignAttr(Context, CI, E);
350+
}
351+
352+
static Attr *handleCodeAlignAttr(Sema &S, Stmt *St, const ParsedAttr &A) {
353+
354+
Expr *E = A.getArgAsExpr(0);
355+
return S.BuildCodeAlignAttr(A, E);
356+
}
357+
358+
// Diagnose non-identical duplicates as a 'conflicting' loop attributes
359+
// and suppress duplicate errors in cases where the two match for
360+
// [[clang::code_align()]] attribute.
361+
static void CheckForDuplicateCodeAlignAttrs(Sema &S,
362+
ArrayRef<const Attr *> Attrs) {
363+
auto FindFunc = [](const Attr *A) { return isa<const CodeAlignAttr>(A); };
364+
const auto *FirstItr = std::find_if(Attrs.begin(), Attrs.end(), FindFunc);
365+
366+
if (FirstItr == Attrs.end()) // no attributes found
367+
return;
368+
369+
const auto *LastFoundItr = FirstItr;
370+
std::optional<llvm::APSInt> FirstValue;
371+
372+
const auto *CAFA =
373+
dyn_cast<ConstantExpr>(cast<CodeAlignAttr>(*FirstItr)->getAlignment());
374+
// Return early if first alignment expression is dependent (since we don't
375+
// know what the effective size will be), and skip the loop entirely.
376+
if (!CAFA)
377+
return;
378+
379+
while (Attrs.end() != (LastFoundItr = std::find_if(LastFoundItr + 1,
380+
Attrs.end(), FindFunc))) {
381+
const auto *CASA = dyn_cast<ConstantExpr>(
382+
cast<CodeAlignAttr>(*LastFoundItr)->getAlignment());
383+
// If the value is dependent, we can not test anything.
384+
if (!CASA)
385+
return;
386+
// Test the attribute values.
387+
llvm::APSInt SecondValue = CASA->getResultAsAPSInt();
388+
if (!FirstValue)
389+
FirstValue = CAFA->getResultAsAPSInt();
390+
391+
if (FirstValue != SecondValue) {
392+
S.Diag((*LastFoundItr)->getLocation(), diag::err_loop_attr_conflict)
393+
<< *FirstItr;
394+
S.Diag((*FirstItr)->getLocation(), diag::note_previous_attribute);
395+
}
396+
return;
397+
}
398+
}
399+
325400
#define WANT_STMT_MERGE_LOGIC
326401
#include "clang/Sema/AttrParsedAttrImpl.inc"
327402
#undef WANT_STMT_MERGE_LOGIC
@@ -523,6 +598,8 @@ static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const ParsedAttr &A,
523598
return handleLikely(S, St, A, Range);
524599
case ParsedAttr::AT_Unlikely:
525600
return handleUnlikely(S, St, A, Range);
601+
case ParsedAttr::AT_CodeAlign:
602+
return handleCodeAlignAttr(S, St, A);
526603
default:
527604
// N.B., ClangAttrEmitter.cpp emits a diagnostic helper that ensures a
528605
// declaration attribute is not written on a statement, but this code is
@@ -541,4 +618,10 @@ void Sema::ProcessStmtAttributes(Stmt *S, const ParsedAttributes &InAttrs,
541618
}
542619

543620
CheckForIncompatibleAttributes(*this, OutAttrs);
621+
CheckForDuplicateCodeAlignAttrs(*this, OutAttrs);
622+
}
623+
624+
bool Sema::CheckRebuiltCodeAlignStmtAttributes(ArrayRef<const Attr *> Attrs) {
625+
CheckForDuplicateCodeAlignAttrs(*this, Attrs);
626+
return false;
544627
}

clang/lib/Sema/SemaTemplateInstantiate.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1374,7 +1374,7 @@ namespace {
13741374
const AlwaysInlineAttr *
13751375
TransformStmtAlwaysInlineAttr(const Stmt *OrigS, const Stmt *InstS,
13761376
const AlwaysInlineAttr *A);
1377-
1377+
const CodeAlignAttr *TransformCodeAlignAttr(const CodeAlignAttr *CA);
13781378
ExprResult TransformPredefinedExpr(PredefinedExpr *E);
13791379
ExprResult TransformDeclRefExpr(DeclRefExpr *E);
13801380
ExprResult TransformCXXDefaultArgExpr(CXXDefaultArgExpr *E);
@@ -1906,6 +1906,12 @@ const AlwaysInlineAttr *TemplateInstantiator::TransformStmtAlwaysInlineAttr(
19061906
return A;
19071907
}
19081908

1909+
const CodeAlignAttr *
1910+
TemplateInstantiator::TransformCodeAlignAttr(const CodeAlignAttr *CA) {
1911+
Expr *TransformedExpr = getDerived().TransformExpr(CA->getAlignment()).get();
1912+
return getSema().BuildCodeAlignAttr(*CA, TransformedExpr);
1913+
}
1914+
19091915
ExprResult TemplateInstantiator::transformNonTypeTemplateParmRef(
19101916
Decl *AssociatedDecl, const NonTypeTemplateParmDecl *parm,
19111917
SourceLocation loc, TemplateArgument arg,

clang/lib/Sema/TreeTransform.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1378,6 +1378,8 @@ class TreeTransform {
13781378
StmtResult RebuildAttributedStmt(SourceLocation AttrLoc,
13791379
ArrayRef<const Attr *> Attrs,
13801380
Stmt *SubStmt) {
1381+
if (SemaRef.CheckRebuiltCodeAlignStmtAttributes(Attrs))
1382+
return StmtError();
13811383
return SemaRef.BuildAttributedStmt(AttrLoc, Attrs, SubStmt);
13821384
}
13831385

clang/test/CodeGen/code_align.c

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -x c %s %s -o - | FileCheck -check-prefix=CHECK-C %s
2+
// RUN: %clang_cc1 -fsyntax-only -emit-llvm -x c++ -std=c++11 %s -o - | FileCheck %s --check-prefixes CHECK-C,CHECK-CPP
3+
4+
// CHECK-C: br label %for.cond, !llvm.loop ![[MD_FP:[0-9]+]]
5+
// CHECK-C: br label %while.cond, !llvm.loop ![[MD_FP_1:[0-9]+]]
6+
// CHECK-C: br i1 %cmp3, label %do.body, label %do.end, !llvm.loop ![[MD_FP_2:[0-9]+]]
7+
// CHECK-C: br label %for.cond5, !llvm.loop ![[MD_FP_3:[0-9]+]]
8+
9+
// CHECK-CPP: br label %for.cond, !llvm.loop ![[MD_FP_4:[0-9]+]]
10+
// CHECK-CPP: br label %for.cond2, !llvm.loop ![[MD_FP_5:[0-9]+]]
11+
12+
void bar(int);
13+
void code_align() {
14+
int a[10];
15+
// CHECK-C: ![[MD_FP]] = distinct !{![[MD_FP]], ![[MP:[0-9]+]], ![[MD_code_align:[0-9]+]]}
16+
// CHECK-C-NEXT: ![[MP]] = !{!"llvm.loop.mustprogress"}
17+
// CHECK-C-NEXT: ![[MD_code_align]] = !{!"llvm.loop.align", i32 4}
18+
[[clang::code_align(4)]]
19+
for(int I=0; I<128; ++I) { bar(I); }
20+
21+
// CHECK-C: ![[MD_FP_1]] = distinct !{![[MD_FP_1]], ![[MP]], ![[MD_code_align_1:[0-9]+]]}
22+
// CHECK-C-NEXT: ![[MD_code_align_1]] = !{!"llvm.loop.align", i32 16}
23+
int i = 0;
24+
[[clang::code_align(16)]] while (i < 60) {
25+
a[i] += 3;
26+
}
27+
28+
// CHECK-C: ![[MD_FP_2]] = distinct !{![[MD_FP_2]], ![[MP]], ![[MD_code_align_2:[0-9]+]]}
29+
// CHECK-C-NEXT: ![[MD_code_align_2]] = !{!"llvm.loop.align", i32 8}
30+
int b = 10;
31+
[[clang::code_align(8)]] do {
32+
b = b + 1;
33+
} while (b < 20);
34+
35+
// CHECK-C: ![[MD_FP_3]] = distinct !{![[MD_FP_3]], ![[MP]], ![[MD_code_align_3:[0-9]+]]}
36+
// CHECK-C-NEXT: ![[MD_code_align_3]] = !{!"llvm.loop.align", i32 64}
37+
[[clang::code_align(64)]]
38+
for(int I=0; I<128; ++I) { bar(I); }
39+
}
40+
41+
#if __cplusplus >= 201103L
42+
template <int A, int B>
43+
void code_align_cpp() {
44+
int a[10];
45+
// CHECK-CPP: ![[MD_FP_4]] = distinct !{![[MD_FP_4]], ![[MP]], ![[MD_code_align_4:[0-9]+]]}
46+
// CHECK-CPP-NEXT: ![[MD_code_align_4]] = !{!"llvm.loop.align", i32 32}
47+
[[clang::code_align(A)]] for (int i = 0; i != 10; ++i)
48+
a[i] = 0;
49+
50+
// CHECK-CPP: ![[MD_FP_5]] = distinct !{![[MD_FP_5]], ![[MD_code_align]]}
51+
int c[] = {0, 1, 2, 3, 4, 5};
52+
[[clang::code_align(B)]] for (int n : c) { n *= 2; }
53+
}
54+
55+
int main() {
56+
code_align_cpp<32, 4>();
57+
return 0;
58+
}
59+
#endif

0 commit comments

Comments
 (0)