Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 43 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
93d46d4
[clang] Add support for new loop attribute [[clang::code_align()]]
smanna12 Oct 31, 2023
d049dc9
Address review comments
smanna12 Nov 2, 2023
6c44cc1
Fix clang format errors
smanna12 Nov 2, 2023
f35c4be
Fix Lit test failure
smanna12 Nov 2, 2023
4c40ce2
Address review comments
smanna12 Nov 6, 2023
9144e44
Fix clang format errors
smanna12 Nov 6, 2023
22f66bd
Address review comments
smanna12 Nov 6, 2023
66f6269
Fix lit test failure
smanna12 Nov 7, 2023
3ac064b
Fix DuplicateCodeAlignAttrs check
smanna12 Nov 7, 2023
172e238
Fix Clang format errors
smanna12 Nov 7, 2023
c8d5270
Remove wrong function
smanna12 Nov 7, 2023
d16a0f9
Merge remote-tracking branch 'my_remote/main' into AddCodeAlignAttr
smanna12 Nov 7, 2023
b4b3c10
Merge remote-tracking branch 'my_remote/main' into AddCodeAlignAttr
smanna12 Nov 10, 2023
2e6ee33
Update diagnostic message and test
smanna12 Nov 13, 2023
82c2e70
Merge remote-tracking branch 'my_remote/main' into AddCodeAlignAttr
smanna12 Nov 13, 2023
8474860
Update min and max aligment check
smanna12 Nov 13, 2023
aa85ed1
Add additional members as static-constexpr
smanna12 Nov 13, 2023
0887674
Address review comments
smanna12 Nov 14, 2023
48e00f1
Fix test
smanna12 Nov 14, 2023
8fb7d22
Fix lit test failures
smanna12 Nov 14, 2023
8bec3c4
Fix bug for alignment value with ((__int128_t)0x1234567890abcde0ULL <…
smanna12 Nov 15, 2023
5c4ce9e
Fix clang format errors
smanna12 Nov 15, 2023
974ebf2
update patch
smanna12 Nov 15, 2023
06eadd9
Fix return nullptr check
smanna12 Nov 15, 2023
5ebb42f
Update patch with trySExtValue() and add test case for big negative
smanna12 Nov 15, 2023
ebcefed
Check an expression resulting in a negative that takes more than 64 bits
smanna12 Nov 15, 2023
72a1a69
Fix lit test
smanna12 Nov 15, 2023
845b687
Fix assertion and remove duplicate codes
smanna12 Nov 15, 2023
fca4436
Fix Clang format errors
smanna12 Nov 15, 2023
a8b13eb
Use Simple algorithm to handle duplicate attribute values.
smanna12 Nov 16, 2023
488b810
Fix clang format errors
smanna12 Nov 16, 2023
09b00c7
Fix Duplicate Attribute Values diagnostic using find_if()
smanna12 Nov 16, 2023
5fadd6a
Apply review comments
smanna12 Nov 16, 2023
87bb3f2
Fix clang format errors
smanna12 Nov 16, 2023
8ad4686
Diagnose non-identical duplicates as a 'conflicting' loop attr and
smanna12 Nov 17, 2023
e8ce33e
Merge remote-tracking branch 'my_remote/main' into AddCodeAlignAttr
smanna12 Nov 17, 2023
ac63c62
Address review comments
smanna12 Nov 17, 2023
8fd7bc1
Fix clang format errors
smanna12 Nov 17, 2023
6a986a1
Fix crash and add dependence tests
smanna12 Nov 17, 2023
f32df3f
Address review comments
smanna12 Nov 17, 2023
d5ee5c1
Fix clang format errors
smanna12 Nov 17, 2023
6778154
Fix diagnostic issues with conflicting attributes for template cases
smanna12 Nov 20, 2023
886c03a
Fix clang format errors
smanna12 Nov 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,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
Expand Down
12 changes: 12 additions & 0 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}];
}
41 changes: 41 additions & 0 deletions clang/include/clang/Basic/AttrDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -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(;;) { }
}

}];
}
5 changes: 5 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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|"
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
24 changes: 21 additions & 3 deletions clang/lib/CodeGen/CGLoopInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
Expand All @@ -469,6 +477,7 @@ void LoopAttributes::clear() {
DistributeEnable = LoopAttributes::Unspecified;
PipelineDisabled = false;
PipelineInitiationInterval = 0;
CodeAlign = 0;
MustProgress = false;
}

Expand All @@ -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);
Expand Down Expand Up @@ -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)
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/CodeGen/CGLoopInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down Expand Up @@ -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; }

Expand Down
83 changes: 83 additions & 0 deletions clang/lib/Sema/SemaStmtAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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;
}
8 changes: 7 additions & 1 deletion clang/lib/Sema/SemaTemplateInstantiate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Sema/TreeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
59 changes: 59 additions & 0 deletions clang/test/CodeGen/code_align.c
Original file line number Diff line number Diff line change
@@ -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
Loading