Skip to content

Commit 8b6808c

Browse files
authored
[SYCL] Correct the tablegen for checking mutually exclusive stmt attrs (#3519)
The work that was done upstream turned out to be insufficient for checking statement attribute mutual exclusion because attributed statements do not collect their attributes one-at-a-time in the same way that declarations do. So the design that was attempting to check for mutual exclusion as each attribute was processed would not ever catch a mutual exclusion. The new approach is to check all of attributes that are to be applied to the attributed statement in a group. This required generating another DiagnoseMutualExclusions() function into AttrParsedAttrImpl.inc.
1 parent b61c5f2 commit 8b6808c

File tree

8 files changed

+114
-100
lines changed

8 files changed

+114
-100
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1952,6 +1952,10 @@ def SYCLIntelFPGADisableLoopPipelining : DeclOrStmtAttr {
19521952
}
19531953
def : MutualExclusions<[SYCLIntelFPGAInitiationInterval,
19541954
SYCLIntelFPGADisableLoopPipelining]>;
1955+
def : MutualExclusions<[SYCLIntelFPGAIVDep,
1956+
SYCLIntelFPGADisableLoopPipelining]>;
1957+
def : MutualExclusions<[SYCLIntelFPGAMaxConcurrency,
1958+
SYCLIntelFPGADisableLoopPipelining]>;
19551959

19561960
def SYCLIntelFPGAMaxInterleaving : StmtAttr {
19571961
let Spellings = [CXX11<"intelfpga","max_interleaving">,
@@ -1963,6 +1967,8 @@ def SYCLIntelFPGAMaxInterleaving : StmtAttr {
19631967
let HasCustomTypeTransform = 1;
19641968
let Documentation = [SYCLIntelFPGAMaxInterleavingAttrDocs];
19651969
}
1970+
def : MutualExclusions<[SYCLIntelFPGADisableLoopPipelining,
1971+
SYCLIntelFPGAMaxInterleaving]>;
19661972

19671973
def SYCLIntelFPGASpeculatedIterations : StmtAttr {
19681974
let Spellings = [CXX11<"intelfpga","speculated_iterations">,
@@ -1974,6 +1980,8 @@ def SYCLIntelFPGASpeculatedIterations : StmtAttr {
19741980
let HasCustomTypeTransform = 1;
19751981
let Documentation = [SYCLIntelFPGASpeculatedIterationsAttrDocs];
19761982
}
1983+
def : MutualExclusions<[SYCLIntelFPGADisableLoopPipelining,
1984+
SYCLIntelFPGASpeculatedIterations]>;
19771985

19781986
def SYCLIntelFPGANofusion : StmtAttr {
19791987
let Spellings = [CXX11<"intel","nofusion">];

clang/include/clang/Sema/ParsedAttr.h

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,6 @@ struct ParsedAttrInfo {
9595
const Decl *D) const {
9696
return true;
9797
}
98-
/// Check if the given attribute is mutually exclusive with other attributes
99-
/// already applied to the given statement.
100-
virtual bool diagMutualExclusion(Sema &S, const ParsedAttr &A,
101-
const Stmt *St) const {
102-
return true;
103-
}
10498
/// Check if this attribute is allowed by the language we are compiling, and
10599
/// issue a diagnostic if not.
106100
virtual bool diagLangOpts(Sema &S, const ParsedAttr &Attr) const {
@@ -615,7 +609,12 @@ class ParsedAttr final
615609
bool diagnoseAppertainsTo(class Sema &S, const Decl *D) const;
616610
bool diagnoseAppertainsTo(class Sema &S, const Stmt *St) const;
617611
bool diagnoseMutualExclusion(class Sema &S, const Decl *D) const;
618-
bool diagnoseMutualExclusion(class Sema &S, const Stmt *St) const;
612+
// This function stub exists for parity with the declaration checking code so
613+
// that checkCommonAttributeFeatures() can work generically on declarations
614+
// or statements.
615+
bool diagnoseMutualExclusion(class Sema &S, const Stmt *St) const {
616+
return true;
617+
}
619618
bool appliesToDecl(const Decl *D, attr::SubjectMatchRule MatchRule) const;
620619
void getMatchRules(const LangOptions &LangOpts,
621620
SmallVectorImpl<std::pair<attr::SubjectMatchRule, bool>>

clang/lib/Sema/ParsedAttr.cpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -167,10 +167,6 @@ bool ParsedAttr::diagnoseMutualExclusion(Sema &S, const Decl *D) const {
167167
return getInfo().diagMutualExclusion(S, *this, D);
168168
}
169169

170-
bool ParsedAttr::diagnoseMutualExclusion(Sema &S, const Stmt *St) const {
171-
return getInfo().diagMutualExclusion(S, *this, St);
172-
}
173-
174170
bool ParsedAttr::appliesToDecl(const Decl *D,
175171
attr::SubjectMatchRule MatchRule) const {
176172
return checkAttributeMatchRuleAppliesTo(D, MatchRule);

clang/lib/Sema/SemaDecl.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2540,9 +2540,9 @@ static bool mergeAlignedAttrs(Sema &S, NamedDecl *New, Decl *Old) {
25402540
return AnyAdded;
25412541
}
25422542

2543-
#define WANT_MERGE_LOGIC
2543+
#define WANT_DECL_MERGE_LOGIC
25442544
#include "clang/Sema/AttrParsedAttrImpl.inc"
2545-
#undef WANT_MERGE_LOGIC
2545+
#undef WANT_DECL_MERGE_LOGIC
25462546

25472547
static bool mergeDeclAttribute(Sema &S, NamedDecl *D,
25482548
const InheritableAttr *Attr,

clang/lib/Sema/SemaStmtAttr.cpp

Lines changed: 13 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -407,9 +407,22 @@ static Attr *handleUnlikely(Sema &S, Stmt *St, const ParsedAttr &A,
407407
return ::new (S.Context) UnlikelyAttr(S.Context, A);
408408
}
409409

410+
#define WANT_STMT_MERGE_LOGIC
411+
#include "clang/Sema/AttrParsedAttrImpl.inc"
412+
#undef WANT_STMT_MERGE_LOGIC
413+
410414
static void
411415
CheckForIncompatibleAttributes(Sema &S,
412416
const SmallVectorImpl<const Attr *> &Attrs) {
417+
// The vast majority of attributed statements will only have one attribute
418+
// on them, so skip all of the checking in the common case.
419+
if (Attrs.size() < 2)
420+
return;
421+
422+
// First, check for the easy cases that are table-generated for us.
423+
if (!DiagnoseMutualExclusions(S, Attrs))
424+
return;
425+
413426
// There are 6 categories of loop hints attributes: vectorize, interleave,
414427
// unroll, unroll_and_jam, pipeline and distribute. Except for distribute they
415428
// come in two variants: a state form and a numeric form. The state form
@@ -532,33 +545,6 @@ CheckForDuplicationSYCLLoopAttribute(Sema &S,
532545
}
533546
}
534547

535-
/// Diagnose mutually exclusive attributes when present on a given
536-
/// declaration. Returns true if diagnosed.
537-
template <typename LoopAttrT, typename LoopAttrT2>
538-
static void CheckMutualExclusionSYCLLoopAttribute(
539-
Sema &S, const SmallVectorImpl<const Attr *> &Attrs) {
540-
std::pair<bool, bool> SeenAttrs;
541-
const Attr *FirstSeen = nullptr;
542-
543-
for (const auto *I : Attrs) {
544-
// Remember the first attribute of the problematic type so that we can
545-
// potentially diagnose it later.
546-
if (!FirstSeen && isa<LoopAttrT, LoopAttrT2>(I))
547-
FirstSeen = I;
548-
549-
// Remember if we've seen either of the attribute types.
550-
if (isa<LoopAttrT>(I))
551-
SeenAttrs.first = true;
552-
else if (isa<LoopAttrT2>(I))
553-
SeenAttrs.second = true;
554-
555-
// If we've seen both of the attribute types, then diagnose them both.
556-
if (SeenAttrs.first && SeenAttrs.second)
557-
S.Diag(I->getLocation(), diag::err_attributes_are_not_compatible)
558-
<< FirstSeen << I;
559-
}
560-
}
561-
562548
static void CheckForIncompatibleSYCLLoopAttributes(
563549
Sema &S, const SmallVectorImpl<const Attr *> &Attrs) {
564550
CheckForDuplicationSYCLLoopAttribute<SYCLIntelFPGAInitiationIntervalAttr>(
@@ -573,21 +559,6 @@ static void CheckForIncompatibleSYCLLoopAttributes(
573559
CheckForDuplicationSYCLLoopAttribute<SYCLIntelFPGASpeculatedIterationsAttr>(
574560
S, Attrs);
575561
CheckForDuplicationSYCLLoopAttribute<LoopUnrollHintAttr>(S, Attrs, false);
576-
CheckMutualExclusionSYCLLoopAttribute<SYCLIntelFPGADisableLoopPipeliningAttr,
577-
SYCLIntelFPGAMaxInterleavingAttr>(
578-
S, Attrs);
579-
CheckMutualExclusionSYCLLoopAttribute<SYCLIntelFPGADisableLoopPipeliningAttr,
580-
SYCLIntelFPGASpeculatedIterationsAttr>(
581-
S, Attrs);
582-
CheckMutualExclusionSYCLLoopAttribute<SYCLIntelFPGADisableLoopPipeliningAttr,
583-
SYCLIntelFPGAInitiationIntervalAttr>(
584-
S, Attrs);
585-
CheckMutualExclusionSYCLLoopAttribute<SYCLIntelFPGADisableLoopPipeliningAttr,
586-
SYCLIntelFPGAIVDepAttr>(S, Attrs);
587-
CheckMutualExclusionSYCLLoopAttribute<SYCLIntelFPGADisableLoopPipeliningAttr,
588-
SYCLIntelFPGAMaxConcurrencyAttr>(S,
589-
Attrs);
590-
591562
CheckRedundantSYCLIntelFPGAIVDepAttrs(S, Attrs);
592563
CheckForDuplicationSYCLLoopAttribute<SYCLIntelFPGANofusionAttr>(S, Attrs);
593564
}

clang/test/SemaCXX/attr-likelihood.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,5 +147,11 @@ void o()
147147
if constexpr (true) [[likely]] {
148148
// expected-warning@+1 {{attribute 'likely' has no effect when annotating an 'if constexpr' statement}}
149149
} else [[likely]];
150+
151+
if (1) [[likely, unlikely]] { // expected-error {{'unlikely' and 'likely' attributes are not compatible}} \
152+
// expected-note {{conflicting attribute is here}}
153+
} else [[unlikely]][[likely]] { // expected-error {{'likely' and 'unlikely' attributes are not compatible}} \
154+
// expected-note {{conflicting attribute is here}}
155+
}
150156
}
151157
#endif

clang/test/SemaSYCL/intel-fpga-loops.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -313,23 +313,28 @@ void loop_attrs_compatibility() {
313313
[[intel::disable_loop_pipelining]]
314314
[[intel::loop_coalesce]] for (int i = 0; i != 10; ++i)
315315
a[i] = 0;
316-
// expected-error@+2 {{'disable_loop_pipelining' and 'max_interleaving' attributes are not compatible}}
316+
// expected-error@+3 {{'max_interleaving' and 'disable_loop_pipelining' attributes are not compatible}}
317+
// expected-note@+1 {{conflicting attribute is here}}
317318
[[intel::disable_loop_pipelining]]
318319
[[intel::max_interleaving(0)]] for (int i = 0; i != 10; ++i)
319320
a[i] = 0;
320-
// expected-error@+2 {{'speculated_iterations' and 'disable_loop_pipelining' attributes are not compatible}}
321+
// expected-error@+3 {{'disable_loop_pipelining' and 'speculated_iterations' attributes are not compatible}}
322+
// expected-note@+1 {{conflicting attribute is here}}
321323
[[intel::speculated_iterations(0)]]
322324
[[intel::disable_loop_pipelining]] for (int i = 0; i != 10; ++i)
323325
a[i] = 0;
324-
// expected-error@+2 {{'disable_loop_pipelining' and 'max_concurrency' attributes are not compatible}}
326+
// expected-error@+3 {{'max_concurrency' and 'disable_loop_pipelining' attributes are not compatible}}
327+
// expected-note@+1 {{conflicting attribute is here}}
325328
[[intel::disable_loop_pipelining]]
326329
[[intel::max_concurrency(0)]] for (int i = 0; i != 10; ++i)
327330
a[i] = 0;
328-
// expected-error@+2 {{'initiation_interval' and 'disable_loop_pipelining' attributes are not compatible}}
331+
// expected-error@+3 {{'disable_loop_pipelining' and 'initiation_interval' attributes are not compatible}}
332+
// expected-note@+1 {{conflicting attribute is here}}
329333
[[intel::initiation_interval(10)]]
330334
[[intel::disable_loop_pipelining]] for (int i = 0; i != 10; ++i)
331335
a[i] = 0;
332-
// expected-error@+2 {{'disable_loop_pipelining' and 'ivdep' attributes are not compatible}}
336+
// expected-error@+3 {{'ivdep' and 'disable_loop_pipelining' attributes are not compatible}}
337+
// expected-note@+1 {{conflicting attribute is here}}
333338
[[intel::disable_loop_pipelining]]
334339
[[intel::ivdep]] for (int i = 0; i != 10; ++i)
335340
a[i] = 0;

clang/utils/TableGen/ClangAttrEmitter.cpp

Lines changed: 69 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3653,7 +3653,8 @@ static void GenerateAppertainsTo(const Record &Attr, raw_ostream &OS) {
36533653
static void GenerateMutualExclusionsChecks(const Record &Attr,
36543654
const RecordKeeper &Records,
36553655
raw_ostream &OS,
3656-
raw_ostream &MergeOS) {
3656+
raw_ostream &MergeDeclOS,
3657+
raw_ostream &MergeStmtOS) {
36573658
// Find all of the definitions that inherit from MutualExclusions and include
36583659
// the given attribute in the list of exclusions to generate the
36593660
// diagMutualExclusion() check.
@@ -3717,43 +3718,59 @@ static void GenerateMutualExclusionsChecks(const Record &Attr,
37173718
// Sema &S, Decl *D, Attr *A and that returns a bool (false on diagnostic,
37183719
// true on success).
37193720
if (Attr.isSubClassOf("InheritableAttr")) {
3720-
MergeOS << " if (const auto *Second = dyn_cast<"
3721-
<< (Attr.getName() + "Attr").str() << ">(A)) {\n";
3721+
MergeDeclOS << " if (const auto *Second = dyn_cast<"
3722+
<< (Attr.getName() + "Attr").str() << ">(A)) {\n";
37223723
for (const std::string &A : DeclAttrs) {
3723-
MergeOS << " if (const auto *First = D->getAttr<" << A << ">()) {\n";
3724-
MergeOS << " S.Diag(First->getLocation(), "
3725-
<< "diag::err_attributes_are_not_compatible) << First << "
3726-
<< "Second;\n";
3727-
MergeOS << " S.Diag(Second->getLocation(), "
3728-
<< "diag::note_conflicting_attribute);\n";
3729-
MergeOS << " return false;\n";
3730-
MergeOS << " }\n";
3724+
MergeDeclOS << " if (const auto *First = D->getAttr<" << A
3725+
<< ">()) {\n";
3726+
MergeDeclOS << " S.Diag(First->getLocation(), "
3727+
<< "diag::err_attributes_are_not_compatible) << First << "
3728+
<< "Second;\n";
3729+
MergeDeclOS << " S.Diag(Second->getLocation(), "
3730+
<< "diag::note_conflicting_attribute);\n";
3731+
MergeDeclOS << " return false;\n";
3732+
MergeDeclOS << " }\n";
37313733
}
3732-
MergeOS << " return true;\n";
3733-
MergeOS << " }\n";
3734+
MergeDeclOS << " return true;\n";
3735+
MergeDeclOS << " }\n";
37343736
}
37353737
}
3738+
3739+
// Statement attributes are a bit different from declarations. With
3740+
// declarations, each attribute is added to the declaration as it is
3741+
// processed, and so you can look on the Decl * itself to see if there is a
3742+
// conflicting attribute. Statement attributes are processed as a group
3743+
// because AttributedStmt needs to tail-allocate all of the attribute nodes
3744+
// at once. This means we cannot check whether the statement already contains
3745+
// an attribute to check for the conflict. Instead, we need to check whether
3746+
// the given list of semantic attributes contain any conflicts. It is assumed
3747+
// this code will be executed in the context of a function with parameters:
3748+
// Sema &S, const SmallVectorImpl<const Attr *> &C. The code will be within a
3749+
// loop which loops over the container C with a loop variable named A to
3750+
// represent the current attribute to check for conflicts.
3751+
//
3752+
// FIXME: it would be nice not to walk over the list of potential attributes
3753+
// to apply to the statement more than once, but statements typically don't
3754+
// have long lists of attributes on them, so re-walking the list should not
3755+
// be an expensive operation.
37363756
if (!StmtAttrs.empty()) {
3737-
// Generate the ParsedAttrInfo subclass logic for statements.
3738-
OS << " bool diagMutualExclusion(Sema &S, const ParsedAttr &AL, "
3739-
<< "const Stmt *St) const override {\n";
3740-
OS << " if (const auto *AS = dyn_cast<AttributedStmt>(St)) {\n";
3741-
OS << " const ArrayRef<const Attr *> &Attrs = AS->getAttrs();\n";
3742-
for (const std::string &A : StmtAttrs) {
3743-
OS << " auto Iter" << A << " = llvm::find_if(Attrs, [](const Attr "
3744-
<< "*A) { return isa<" << A << ">(A); });\n";
3745-
OS << " if (Iter" << A << " != Attrs.end()) {\n";
3746-
OS << " S.Diag(AL.getLoc(), "
3747-
<< "diag::err_attributes_are_not_compatible) << AL << *Iter" << A
3748-
<< ";\n";
3749-
OS << " S.Diag((*Iter" << A << ")->getLocation(), "
3750-
<< "diag::note_conflicting_attribute);\n";
3751-
OS << " return false;\n";
3752-
OS << " }\n";
3753-
}
3754-
OS << " }\n";
3755-
OS << " return true;\n";
3756-
OS << " }\n\n";
3757+
MergeStmtOS << " if (const auto *Second = dyn_cast<"
3758+
<< (Attr.getName() + "Attr").str() << ">(A)) {\n";
3759+
MergeStmtOS << " auto Iter = llvm::find_if(C, [](const Attr *Check) "
3760+
<< "{ return isa<";
3761+
interleave(
3762+
StmtAttrs, [&](const std::string &Name) { MergeStmtOS << Name; },
3763+
[&] { MergeStmtOS << ", "; });
3764+
MergeStmtOS << ">(Check); });\n";
3765+
MergeStmtOS << " if (Iter != C.end()) {\n";
3766+
MergeStmtOS << " S.Diag((*Iter)->getLocation(), "
3767+
<< "diag::err_attributes_are_not_compatible) << *Iter << "
3768+
<< "Second;\n";
3769+
MergeStmtOS << " S.Diag(Second->getLocation(), "
3770+
<< "diag::note_conflicting_attribute);\n";
3771+
MergeStmtOS << " return false;\n";
3772+
MergeStmtOS << " }\n";
3773+
MergeStmtOS << " }\n";
37573774
}
37583775
}
37593776

@@ -3905,7 +3922,8 @@ static bool IsKnownToGCC(const Record &Attr) {
39053922
void EmitClangAttrParsedAttrImpl(RecordKeeper &Records, raw_ostream &OS) {
39063923
emitSourceFileHeader("Parsed attribute helpers", OS);
39073924

3908-
OS << "#if !defined(WANT_MERGE_LOGIC)\n";
3925+
OS << "#if !defined(WANT_DECL_MERGE_LOGIC) && "
3926+
<< "!defined(WANT_STMT_MERGE_LOGIC)\n";
39093927
PragmaClangAttributeSupport &PragmaAttributeSupport =
39103928
getPragmaAttributeSupport(Records);
39113929

@@ -3929,8 +3947,8 @@ void EmitClangAttrParsedAttrImpl(RecordKeeper &Records, raw_ostream &OS) {
39293947
// This stream is used to collect all of the declaration attribute merging
39303948
// logic for performing mutual exclusion checks. This gets emitted at the
39313949
// end of the file in a helper function of its own.
3932-
std::string DeclMergeChecks;
3933-
raw_string_ostream MergeOS(DeclMergeChecks);
3950+
std::string DeclMergeChecks, StmtMergeChecks;
3951+
raw_string_ostream MergeDeclOS(DeclMergeChecks), MergeStmtOS(StmtMergeChecks);
39343952

39353953
// Generate a ParsedAttrInfo struct for each of the attributes.
39363954
for (auto I = Attrs.begin(), E = Attrs.end(); I != E; ++I) {
@@ -3987,7 +4005,7 @@ void EmitClangAttrParsedAttrImpl(RecordKeeper &Records, raw_ostream &OS) {
39874005
OS << " Spellings = " << I->first << "Spellings;\n";
39884006
OS << " }\n";
39894007
GenerateAppertainsTo(Attr, OS);
3990-
GenerateMutualExclusionsChecks(Attr, Records, OS, MergeOS);
4008+
GenerateMutualExclusionsChecks(Attr, Records, OS, MergeDeclOS, MergeStmtOS);
39914009
GenerateLangOptRequirements(Attr, OS);
39924010
GenerateTargetRequirements(Attr, Dupes, OS);
39934011
GenerateSpellingIndexToSemanticSpelling(Attr, OS);
@@ -4008,16 +4026,27 @@ void EmitClangAttrParsedAttrImpl(RecordKeeper &Records, raw_ostream &OS) {
40084026
// Generate the attribute match rules.
40094027
emitAttributeMatchRules(PragmaAttributeSupport, OS);
40104028

4011-
OS << "#else // WANT_MERGE_LOGIC\n\n";
4029+
OS << "#elif defined(WANT_DECL_MERGE_LOGIC)\n\n";
40124030

40134031
// Write out the declaration merging check logic.
40144032
OS << "static bool DiagnoseMutualExclusions(Sema &S, const NamedDecl *D, "
40154033
<< "const Attr *A) {\n";
4016-
OS << MergeOS.str();
4034+
OS << MergeDeclOS.str();
40174035
OS << " return true;\n";
40184036
OS << "}\n\n";
40194037

4020-
OS << "#endif // WANT_MERGE_LOGIC\n";
4038+
OS << "#elif defined(WANT_STMT_MERGE_LOGIC)\n\n";
4039+
4040+
// Write out the statement merging check logic.
4041+
OS << "static bool DiagnoseMutualExclusions(Sema &S, "
4042+
<< "const SmallVectorImpl<const Attr *> &C) {\n";
4043+
OS << " for (const Attr *A : C) {\n";
4044+
OS << MergeStmtOS.str();
4045+
OS << " }\n";
4046+
OS << " return true;\n";
4047+
OS << "}\n\n";
4048+
4049+
OS << "#endif\n";
40214050
}
40224051

40234052
// Emits the kind list of parsed attributes

0 commit comments

Comments
 (0)