Skip to content

Commit 5e92bfe

Browse files
authored
[OpenACC] Check Loop counts for 'collapse' clause. (#110851)
OpenACC Spec requires that each loop associated with a 'collapse' have exactly 1 loop/nest. This is implemented in 2 parts: 1- diagnosing when we see a 2nd loop at any level with an applicable 'collapse' 2- Diagnosing if we didn't see enough 'depth' of loops. Other loops (non-for) are diagnosed by a previous patch, and other intervening code will be diagnosed in a followup.
1 parent ec72666 commit 5e92bfe

File tree

4 files changed

+158
-18
lines changed

4 files changed

+158
-18
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12622,6 +12622,12 @@ def err_acc_invalid_in_collapse_loop
1262212622
"in intervening code of a 'loop' with a 'collapse' clause">;
1262312623
def note_acc_collapse_clause_here
1262412624
: Note<"active 'collapse' clause defined here">;
12625+
def err_acc_collapse_multiple_loops
12626+
: Error<"more than one for-loop in a loop associated with OpenACC 'loop' "
12627+
"construct with a 'collapse' clause">;
12628+
def err_acc_collapse_insufficient_loops
12629+
: Error<"'collapse' clause specifies a loop count greater than the number "
12630+
"of available loops">;
1262512631

1262612632
// AMDGCN builtins diagnostics
1262712633
def err_amdgcn_global_load_lds_size_invalid_value : Error<"invalid size value">;

clang/include/clang/Sema/SemaOpenACC.h

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,20 @@ class SemaOpenACC : public SemaBase {
6464
/// 'collapse inner loop not a 'for' loop' diagnostic.
6565
LLVM_PREFERRED_TYPE(bool)
6666
unsigned TopLevelLoopSeen : 1;
67-
} CollapseInfo{nullptr, std::nullopt, false};
67+
68+
/// Records whether this 'tier' of the loop has already seen a 'for' loop,
69+
/// used to diagnose if there are multiple 'for' loops at any one level.
70+
LLVM_PREFERRED_TYPE(bool)
71+
unsigned CurLevelHasLoopAlready : 1;
72+
73+
/// Records whether we've hit a CurCollapseCount of '0' on the way down,
74+
/// which allows us to diagnose if the value of 'N' is too large for the
75+
/// current number of 'for' loops.
76+
LLVM_PREFERRED_TYPE(bool)
77+
unsigned CollapseDepthSatisfied : 1;
78+
} CollapseInfo{nullptr, std::nullopt, /*TopLevelLoopSeen=*/false,
79+
/*CurLevelHasLoopAlready=*/false,
80+
/*CollapseDepthSatisfied=*/true};
6881

6982
public:
7083
// Redeclaration of the version in OpenACCClause.h.
@@ -515,11 +528,26 @@ class SemaOpenACC : public SemaBase {
515528
class LoopInConstructRAII {
516529
SemaOpenACC &SemaRef;
517530
CollapseCheckingInfo OldCollapseInfo;
531+
bool PreserveDepth;
518532

519533
public:
520-
LoopInConstructRAII(SemaOpenACC &SemaRef)
521-
: SemaRef(SemaRef), OldCollapseInfo(SemaRef.CollapseInfo) {}
522-
~LoopInConstructRAII() { SemaRef.CollapseInfo = OldCollapseInfo; }
534+
LoopInConstructRAII(SemaOpenACC &SemaRef, bool PreserveDepth = true)
535+
: SemaRef(SemaRef), OldCollapseInfo(SemaRef.CollapseInfo),
536+
PreserveDepth(PreserveDepth) {}
537+
~LoopInConstructRAII() {
538+
// The associated-statement level of this should NOT preserve this, as it
539+
// is a new construct, but other loop uses need to preserve the depth so
540+
// it makes it to the 'top level' for diagnostics.
541+
bool CollapseDepthSatisified =
542+
PreserveDepth ? SemaRef.CollapseInfo.CollapseDepthSatisfied
543+
: OldCollapseInfo.CollapseDepthSatisfied;
544+
bool CurLevelHasLoopAlready =
545+
PreserveDepth ? SemaRef.CollapseInfo.CurLevelHasLoopAlready
546+
: OldCollapseInfo.CurLevelHasLoopAlready;
547+
SemaRef.CollapseInfo = OldCollapseInfo;
548+
SemaRef.CollapseInfo.CollapseDepthSatisfied = CollapseDepthSatisified;
549+
SemaRef.CollapseInfo.CurLevelHasLoopAlready = CurLevelHasLoopAlready;
550+
}
523551
};
524552

525553
/// Helper type for the registration/assignment of constructs that need to

clang/lib/Sema/SemaOpenACC.cpp

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,7 +1078,7 @@ SemaOpenACC::AssociatedStmtRAII::AssociatedStmtRAII(
10781078
ArrayRef<const OpenACCClause *> UnInstClauses,
10791079
ArrayRef<OpenACCClause *> Clauses)
10801080
: SemaRef(S), WasInsideComputeConstruct(S.InsideComputeConstruct),
1081-
DirKind(DK), LoopRAII(SemaRef) {
1081+
DirKind(DK), LoopRAII(SemaRef, /*PreserveDepth=*/false) {
10821082
// Compute constructs end up taking their 'loop'.
10831083
if (DirKind == OpenACCDirectiveKind::Parallel ||
10841084
DirKind == OpenACCDirectiveKind::Serial ||
@@ -1093,6 +1093,11 @@ SemaOpenACC::AssociatedStmtRAII::AssociatedStmtRAII(
10931093
void SemaOpenACC::AssociatedStmtRAII::SetCollapseInfoBeforeAssociatedStmt(
10941094
ArrayRef<const OpenACCClause *> UnInstClauses,
10951095
ArrayRef<OpenACCClause *> Clauses) {
1096+
1097+
// Reset this checking for loops that aren't covered in a RAII object.
1098+
SemaRef.CollapseInfo.CurLevelHasLoopAlready = false;
1099+
SemaRef.CollapseInfo.CollapseDepthSatisfied = true;
1100+
10961101
// We make sure to take an optional list of uninstantiated clauses, so that
10971102
// we can check to make sure we don't 'double diagnose' in the event that
10981103
// the value of 'N' was not dependent in a template. We also ensure during
@@ -1125,6 +1130,7 @@ void SemaOpenACC::AssociatedStmtRAII::SetCollapseInfoBeforeAssociatedStmt(
11251130
->isInstantiationDependent())
11261131
return;
11271132

1133+
SemaRef.CollapseInfo.CollapseDepthSatisfied = false;
11281134
SemaRef.CollapseInfo.CurCollapseCount =
11291135
cast<ConstantExpr>(LoopCount)->getResultAsAPSInt();
11301136
}
@@ -1737,13 +1743,38 @@ void SemaOpenACC::ActOnForStmtBegin(SourceLocation ForLoc) {
17371743
// Enable the while/do-while checking.
17381744
CollapseInfo.TopLevelLoopSeen = true;
17391745

1740-
if (CollapseInfo.CurCollapseCount && *CollapseInfo.CurCollapseCount > 0)
1741-
--(*CollapseInfo.CurCollapseCount);
1746+
if (CollapseInfo.CurCollapseCount && *CollapseInfo.CurCollapseCount > 0) {
1747+
1748+
// OpenACC 3.3 2.9.1:
1749+
// Each associated loop, except the innermost, must contain exactly one loop
1750+
// or loop nest.
1751+
// This checks for more than 1 loop at the current level, the
1752+
// 'depth'-satisifed checking manages the 'not zero' case.
1753+
if (CollapseInfo.CurLevelHasLoopAlready) {
1754+
Diag(ForLoc, diag::err_acc_collapse_multiple_loops);
1755+
assert(CollapseInfo.ActiveCollapse && "No collapse object?");
1756+
Diag(CollapseInfo.ActiveCollapse->getBeginLoc(),
1757+
diag::note_acc_collapse_clause_here);
1758+
} else {
1759+
--(*CollapseInfo.CurCollapseCount);
1760+
1761+
// Once we've hit zero here, we know we have deep enough 'for' loops to
1762+
// get to the bottom.
1763+
if (*CollapseInfo.CurCollapseCount == 0)
1764+
CollapseInfo.CollapseDepthSatisfied = true;
1765+
}
1766+
}
1767+
1768+
// Set this to 'false' for the body of this loop, so that the next level
1769+
// checks independently.
1770+
CollapseInfo.CurLevelHasLoopAlready = false;
17421771
}
17431772

17441773
void SemaOpenACC::ActOnForStmtEnd(SourceLocation ForLoc, StmtResult Body) {
17451774
if (!getLangOpts().OpenACC)
17461775
return;
1776+
// Set this to 'true' so if we find another one at this level we can diagnose.
1777+
CollapseInfo.CurLevelHasLoopAlready = true;
17471778
}
17481779

17491780
bool SemaOpenACC::ActOnStartStmtDirective(OpenACCDirectiveKind K,
@@ -1827,12 +1858,23 @@ StmtResult SemaOpenACC::ActOnAssociatedStmt(SourceLocation DirectiveLoc,
18271858
// the 'structured block'.
18281859
return AssocStmt;
18291860
case OpenACCDirectiveKind::Loop:
1830-
if (AssocStmt.isUsable() &&
1831-
!isa<CXXForRangeStmt, ForStmt>(AssocStmt.get())) {
1861+
if (!AssocStmt.isUsable())
1862+
return StmtError();
1863+
1864+
if (!isa<CXXForRangeStmt, ForStmt>(AssocStmt.get())) {
18321865
Diag(AssocStmt.get()->getBeginLoc(), diag::err_acc_loop_not_for_loop);
18331866
Diag(DirectiveLoc, diag::note_acc_construct_here) << K;
18341867
return StmtError();
18351868
}
1869+
1870+
if (!CollapseInfo.CollapseDepthSatisfied) {
1871+
Diag(DirectiveLoc, diag::err_acc_collapse_insufficient_loops);
1872+
assert(CollapseInfo.ActiveCollapse && "Collapse count without object?");
1873+
Diag(CollapseInfo.ActiveCollapse->getBeginLoc(),
1874+
diag::note_acc_collapse_clause_here);
1875+
return StmtError();
1876+
}
1877+
18361878
// TODO OpenACC: 2.9 ~ line 2010 specifies that the associated loop has some
18371879
// restrictions when there is a 'seq' clause in place. We probably need to
18381880
// implement that, including piping in the clauses here.

clang/test/SemaOpenACC/loop-construct-collapse-clause.cpp

Lines changed: 73 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -115,18 +115,51 @@ void negative_constexpr(int i) {
115115
negative_constexpr_templ<int, 1>(); // #NCET1
116116
}
117117

118+
template<unsigned Val>
119+
void depth_too_high_templ() {
120+
// expected-error@+2{{'collapse' clause specifies a loop count greater than the number of available loops}}
121+
// expected-note@+1{{active 'collapse' clause defined here}}
122+
#pragma acc loop collapse(Val)
123+
for(;;)
124+
for(;;);
125+
}
126+
127+
void depth_too_high() {
128+
depth_too_high_templ<3>(); // expected-note{{in instantiation of function template specialization}}
129+
130+
// expected-error@+2{{'collapse' clause specifies a loop count greater than the number of available loops}}
131+
// expected-note@+1{{active 'collapse' clause defined here}}
132+
#pragma acc loop collapse(3)
133+
for(;;)
134+
for(;;);
135+
136+
// expected-error@+2{{'collapse' clause specifies a loop count greater than the number of available loops}}
137+
// expected-note@+1{{active 'collapse' clause defined here}}
138+
#pragma acc loop collapse(three())
139+
for(;;)
140+
for(;;);
141+
142+
// expected-error@+2{{'collapse' clause specifies a loop count greater than the number of available loops}}
143+
// expected-note@+1{{active 'collapse' clause defined here}}
144+
#pragma acc loop collapse(ConvertsThree{})
145+
for(;;)
146+
for(;;);
147+
}
148+
118149
template<typename T, unsigned Three>
119150
void not_single_loop_templ() {
120151
T Arr[5];
121-
// expected-note@+1{{active 'collapse' clause defined here}}
152+
// expected-error@+2{{'collapse' clause specifies a loop count greater than the number of available loops}}
153+
// expected-note@+1 2{{active 'collapse' clause defined here}}
122154
#pragma acc loop collapse(3)
123155
for(auto x : Arr) {
124156
for(auto y : Arr){
125157
do{}while(true); // expected-error{{do loop cannot appear in intervening code of a 'loop' with a 'collapse' clause}}
126158
}
127159
}
128160

129-
// expected-note@+1{{active 'collapse' clause defined here}}
161+
// expected-error@+2{{'collapse' clause specifies a loop count greater than the number of available loops}}
162+
// expected-note@+1 2{{active 'collapse' clause defined here}}
130163
#pragma acc loop collapse(Three)
131164
for(;;) {
132165
for(;;){
@@ -142,7 +175,8 @@ void not_single_loop_templ() {
142175
}
143176
}
144177
}
145-
// expected-note@+1{{active 'collapse' clause defined here}}
178+
// expected-error@+2{{'collapse' clause specifies a loop count greater than the number of available loops}}
179+
// expected-note@+1 2{{active 'collapse' clause defined here}}
146180
#pragma acc loop collapse(Three)
147181
for(auto x : Arr) {
148182
for(auto y: Arr) {
@@ -181,15 +215,16 @@ void not_single_loop() {
181215
do{}while(true); // expected-error{{do loop cannot appear in intervening code of a 'loop' with a 'collapse' clause}}
182216
}
183217

184-
// expected-note@+1{{active 'collapse' clause defined here}}
218+
// expected-error@+2{{'collapse' clause specifies a loop count greater than the number of available loops}}
219+
// expected-note@+1 2{{active 'collapse' clause defined here}}
185220
#pragma acc loop collapse(3)
186221
for(;;) {
187222
for(;;){
188223
while(true); // expected-error{{while loop cannot appear in intervening code of a 'loop' with a 'collapse' clause}}
189224
}
190225
}
191-
192-
// expected-note@+1{{active 'collapse' clause defined here}}
226+
// expected-error@+2{{'collapse' clause specifies a loop count greater than the number of available loops}}
227+
// expected-note@+1 2{{active 'collapse' clause defined here}}
193228
#pragma acc loop collapse(3)
194229
for(;;) {
195230
for(;;){
@@ -211,23 +246,51 @@ void not_single_loop() {
211246
}
212247

213248
int Arr[5];
214-
215-
// expected-note@+1{{active 'collapse' clause defined here}}
249+
// expected-error@+2{{'collapse' clause specifies a loop count greater than the number of available loops}}
250+
// expected-note@+1 2{{active 'collapse' clause defined here}}
216251
#pragma acc loop collapse(3)
217252
for(auto x : Arr) {
218253
for(auto y : Arr){
219254
do{}while(true); // expected-error{{do loop cannot appear in intervening code of a 'loop' with a 'collapse' clause}}
220255
}
221256
}
222257

258+
// expected-note@+1 {{active 'collapse' clause defined here}}
259+
#pragma acc loop collapse(3)
260+
for (;;) {
261+
for (;;) {
262+
for(;;);
263+
}
264+
// expected-error@+1{{more than one for-loop in a loop associated with OpenACC 'loop' construct with a 'collapse' clause}}
265+
for(;;);
266+
}
267+
268+
// expected-note@+1 {{active 'collapse' clause defined here}}
269+
#pragma acc loop collapse(3)
270+
for (;;) {
271+
for (;;) {
272+
for(;;);
273+
// expected-error@+1{{more than one for-loop in a loop associated with OpenACC 'loop' construct with a 'collapse' clause}}
274+
for(;;);
275+
}
276+
}
277+
278+
for(;;);
279+
#pragma acc loop collapse(3)
280+
for (;;) {
281+
for (;;) {
282+
for (;;);
283+
}
284+
}
223285
}
224286

225287
template<unsigned Two, unsigned Three>
226288
void no_other_directives() {
227289
#pragma acc loop collapse(Two)
228290
for(;;) {
229291
for (;;) { // last loop associated with the top level.
230-
#pragma acc loop collapse(Three) // expected-note{{active 'collapse' clause defined here}}
292+
// expected-error@+1{{'collapse' clause specifies a loop count greater than the number of available loops}}
293+
#pragma acc loop collapse(Three) // expected-note 2{{active 'collapse' clause defined here}}
231294
for(;;) {
232295
for(;;) {
233296
// expected-error@+1{{OpenACC 'serial' construct cannot appear in intervening code of a 'loop' with a 'collapse' clause}}
@@ -243,6 +306,7 @@ void no_other_directives() {
243306
#pragma acc loop collapse(Three)
244307
for(;;) {
245308
for(;;) {
309+
for(;;);
246310
}
247311
}
248312
}

0 commit comments

Comments
 (0)