Skip to content

Commit bb86d40

Browse files
erichkeaneSterling-Augustine
authored andcommitted
[OpenACC] Enforce all 'collapse' intervening rules (llvm#110684)
'collapse' makes the 'loop' construct apply to the next N nested loops, and 'loop' requires all associated loops be 'for' loops (or range-for). This patch adds this restriction, plus adds a number of infrastructure changes to permit some of the other restrictions in the future to be implemented. 'collapse' also requires that there is not any calls to other directives inside of the collapse region, so this also implements that limitation.
1 parent c9ecd43 commit bb86d40

File tree

7 files changed

+384
-10
lines changed

7 files changed

+384
-10
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12617,6 +12617,11 @@ def err_acc_loop_spec_conflict
1261712617
def err_acc_collapse_loop_count
1261812618
: Error<"OpenACC 'collapse' clause loop count must be a %select{constant "
1261912619
"expression|positive integer value, evaluated to %1}0">;
12620+
def err_acc_invalid_in_collapse_loop
12621+
: Error<"%select{OpenACC '%1' construct|while loop|do loop}0 cannot appear "
12622+
"in intervening code of a 'loop' with a 'collapse' clause">;
12623+
def note_acc_collapse_clause_here
12624+
: Note<"active 'collapse' clause defined here">;
1262012625

1262112626
// AMDGCN builtins diagnostics
1262212627
def err_amdgcn_global_load_lds_size_invalid_value : Error<"invalid size value">;

clang/include/clang/Sema/SemaOpenACC.h

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "clang/Sema/Ownership.h"
2323
#include "clang/Sema/SemaBase.h"
2424
#include "llvm/ADT/SmallVector.h"
25+
#include "llvm/Support/Compiler.h"
2526
#include <cassert>
2627
#include <optional>
2728
#include <utility>
@@ -41,6 +42,30 @@ class SemaOpenACC : public SemaBase {
4142
/// above collection.
4243
bool InsideComputeConstruct = false;
4344

45+
/// The 'collapse' clause requires quite a bit of checking while
46+
/// parsing/instantiating its body, so this structure/object keeps all of the
47+
/// necessary information as we do checking. This should rarely be directly
48+
/// modified, and typically should be controlled by the RAII objects.
49+
///
50+
/// Collapse has an 'N' count that makes it apply to a number of loops 'below'
51+
/// it.
52+
struct CollapseCheckingInfo {
53+
OpenACCCollapseClause *ActiveCollapse = nullptr;
54+
55+
/// This is a value that maintains the current value of the 'N' on the
56+
/// current collapse, minus the depth that has already been traversed. When
57+
/// there is not an active collapse, or a collapse whose depth we don't know
58+
/// (for example, if it is a dependent value), this should be `nullopt`,
59+
/// else it should be 'N' minus the current depth traversed.
60+
std::optional<llvm::APSInt> CurCollapseCount;
61+
62+
/// Records whether we've seen the top level 'for'. We already diagnose
63+
/// later that the 'top level' is a for loop, so we use this to suppress the
64+
/// 'collapse inner loop not a 'for' loop' diagnostic.
65+
LLVM_PREFERRED_TYPE(bool)
66+
unsigned TopLevelLoopSeen : 1;
67+
} CollapseInfo{nullptr, std::nullopt, false};
68+
4469
public:
4570
// Redeclaration of the version in OpenACCClause.h.
4671
using DeviceTypeArgument = std::pair<IdentifierInfo *, SourceLocation>;
@@ -411,6 +436,17 @@ class SemaOpenACC : public SemaBase {
411436

412437
SemaOpenACC(Sema &S);
413438

439+
// Called when we encounter a 'while' statement, before looking at its 'body'.
440+
void ActOnWhileStmt(SourceLocation WhileLoc);
441+
// Called when we encounter a 'do' statement, before looking at its 'body'.
442+
void ActOnDoStmt(SourceLocation DoLoc);
443+
// Called when we encounter a 'for' statement, before looking at its 'body'.
444+
void ActOnForStmtBegin(SourceLocation ForLoc);
445+
// Called when we encounter a 'for' statement, after we've consumed/checked
446+
// the body. This is necessary for a number of checks on the contents of the
447+
// 'for' statement.
448+
void ActOnForStmtEnd(SourceLocation ForLoc, StmtResult Body);
449+
414450
/// Called after parsing an OpenACC Clause so that it can be checked.
415451
OpenACCClause *ActOnClause(ArrayRef<const OpenACCClause *> ExistingClauses,
416452
OpenACCParsedClause &Clause);
@@ -474,6 +510,18 @@ class SemaOpenACC : public SemaBase {
474510
/// Checks the loop depth value for a collapse clause.
475511
ExprResult CheckCollapseLoopCount(Expr *LoopCount);
476512

513+
/// Helper type to restore the state of various 'loop' constructs when we run
514+
/// into a loop (for, etc) inside the construct.
515+
class LoopInConstructRAII {
516+
SemaOpenACC &SemaRef;
517+
CollapseCheckingInfo OldCollapseInfo;
518+
519+
public:
520+
LoopInConstructRAII(SemaOpenACC &SemaRef)
521+
: SemaRef(SemaRef), OldCollapseInfo(SemaRef.CollapseInfo) {}
522+
~LoopInConstructRAII() { SemaRef.CollapseInfo = OldCollapseInfo; }
523+
};
524+
477525
/// Helper type for the registration/assignment of constructs that need to
478526
/// 'know' about their parent constructs and hold a reference to them, such as
479527
/// Loop needing its parent construct.
@@ -482,9 +530,15 @@ class SemaOpenACC : public SemaBase {
482530
bool WasInsideComputeConstruct;
483531
OpenACCDirectiveKind DirKind;
484532
llvm::SmallVector<OpenACCLoopConstruct *> ParentlessLoopConstructs;
533+
LoopInConstructRAII LoopRAII;
485534

486535
public:
487-
AssociatedStmtRAII(SemaOpenACC &, OpenACCDirectiveKind);
536+
AssociatedStmtRAII(SemaOpenACC &, OpenACCDirectiveKind,
537+
ArrayRef<const OpenACCClause *>,
538+
ArrayRef<OpenACCClause *>);
539+
void SetCollapseInfoBeforeAssociatedStmt(
540+
ArrayRef<const OpenACCClause *> UnInstClauses,
541+
ArrayRef<OpenACCClause *> Clauses);
488542
~AssociatedStmtRAII();
489543
};
490544
};

clang/lib/Parse/ParseOpenACC.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1461,8 +1461,8 @@ StmtResult Parser::ParseOpenACCDirectiveStmt() {
14611461
return StmtError();
14621462

14631463
StmtResult AssocStmt;
1464-
SemaOpenACC::AssociatedStmtRAII AssocStmtRAII(getActions().OpenACC(),
1465-
DirInfo.DirKind);
1464+
SemaOpenACC::AssociatedStmtRAII AssocStmtRAII(
1465+
getActions().OpenACC(), DirInfo.DirKind, {}, DirInfo.Clauses);
14661466
if (doesDirectiveHaveAssociatedStmt(DirInfo.DirKind)) {
14671467
ParsingOpenACCDirectiveRAII DirScope(*this, /*Value=*/false);
14681468
ParseScope ACCScope(this, getOpenACCScopeFlags(DirInfo.DirKind));

clang/lib/Parse/ParseStmt.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "clang/Sema/Scope.h"
2525
#include "clang/Sema/SemaCodeCompletion.h"
2626
#include "clang/Sema/SemaObjC.h"
27+
#include "clang/Sema/SemaOpenACC.h"
2728
#include "clang/Sema/SemaOpenMP.h"
2829
#include "clang/Sema/TypoCorrection.h"
2930
#include "llvm/ADT/STLExtras.h"
@@ -1854,6 +1855,11 @@ StmtResult Parser::ParseWhileStatement(SourceLocation *TrailingElseLoc) {
18541855
Sema::ConditionKind::Boolean, LParen, RParen))
18551856
return StmtError();
18561857

1858+
// OpenACC Restricts a while-loop inside of certain construct/clause
1859+
// combinations, so diagnose that here in OpenACC mode.
1860+
SemaOpenACC::LoopInConstructRAII LCR{getActions().OpenACC()};
1861+
getActions().OpenACC().ActOnWhileStmt(WhileLoc);
1862+
18571863
// C99 6.8.5p5 - In C99, the body of the while statement is a scope, even if
18581864
// there is no compound stmt. C90 does not have this clause. We only do this
18591865
// if the body isn't a compound statement to avoid push/pop in common cases.
@@ -1902,6 +1908,11 @@ StmtResult Parser::ParseDoStatement() {
19021908

19031909
ParseScope DoScope(this, ScopeFlags);
19041910

1911+
// OpenACC Restricts a do-while-loop inside of certain construct/clause
1912+
// combinations, so diagnose that here in OpenACC mode.
1913+
SemaOpenACC::LoopInConstructRAII LCR{getActions().OpenACC()};
1914+
getActions().OpenACC().ActOnDoStmt(DoLoc);
1915+
19051916
// C99 6.8.5p5 - In C99, the body of the do statement is a scope, even if
19061917
// there is no compound stmt. C90 does not have this clause. We only do this
19071918
// if the body isn't a compound statement to avoid push/pop in common cases.
@@ -2326,6 +2337,11 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) {
23262337
}
23272338
}
23282339

2340+
// OpenACC Restricts a for-loop inside of certain construct/clause
2341+
// combinations, so diagnose that here in OpenACC mode.
2342+
SemaOpenACC::LoopInConstructRAII LCR{getActions().OpenACC()};
2343+
getActions().OpenACC().ActOnForStmtBegin(ForLoc);
2344+
23292345
// C99 6.8.5p5 - In C99, the body of the for statement is a scope, even if
23302346
// there is no compound stmt. C90 does not have this clause. We only do this
23312347
// if the body isn't a compound statement to avoid push/pop in common cases.
@@ -2358,6 +2374,8 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) {
23582374
// Pop the body scope if needed.
23592375
InnerScope.Exit();
23602376

2377+
getActions().OpenACC().ActOnForStmtEnd(ForLoc, Body);
2378+
23612379
// Leave the for-scope.
23622380
ForScope.Exit();
23632381

clang/lib/Sema/SemaOpenACC.cpp

Lines changed: 117 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,19 +1073,62 @@ OpenACCClause *SemaOpenACCClauseVisitor::VisitCollapseClause(
10731073

10741074
SemaOpenACC::SemaOpenACC(Sema &S) : SemaBase(S) {}
10751075

1076-
SemaOpenACC::AssociatedStmtRAII::AssociatedStmtRAII(SemaOpenACC &S,
1077-
OpenACCDirectiveKind DK)
1076+
SemaOpenACC::AssociatedStmtRAII::AssociatedStmtRAII(
1077+
SemaOpenACC &S, OpenACCDirectiveKind DK,
1078+
ArrayRef<const OpenACCClause *> UnInstClauses,
1079+
ArrayRef<OpenACCClause *> Clauses)
10781080
: SemaRef(S), WasInsideComputeConstruct(S.InsideComputeConstruct),
1079-
DirKind(DK) {
1081+
DirKind(DK), LoopRAII(SemaRef) {
10801082
// Compute constructs end up taking their 'loop'.
10811083
if (DirKind == OpenACCDirectiveKind::Parallel ||
10821084
DirKind == OpenACCDirectiveKind::Serial ||
10831085
DirKind == OpenACCDirectiveKind::Kernels) {
10841086
SemaRef.InsideComputeConstruct = true;
10851087
SemaRef.ParentlessLoopConstructs.swap(ParentlessLoopConstructs);
1088+
} else if (DirKind == OpenACCDirectiveKind::Loop) {
1089+
SetCollapseInfoBeforeAssociatedStmt(UnInstClauses, Clauses);
10861090
}
10871091
}
10881092

1093+
void SemaOpenACC::AssociatedStmtRAII::SetCollapseInfoBeforeAssociatedStmt(
1094+
ArrayRef<const OpenACCClause *> UnInstClauses,
1095+
ArrayRef<OpenACCClause *> Clauses) {
1096+
// We make sure to take an optional list of uninstantiated clauses, so that
1097+
// we can check to make sure we don't 'double diagnose' in the event that
1098+
// the value of 'N' was not dependent in a template. We also ensure during
1099+
// Sema that there is only 1 collapse on each construct, so we can count on
1100+
// the fact that if both find a 'collapse', that they are the same one.
1101+
auto *CollapseClauseItr =
1102+
llvm::find_if(Clauses, llvm::IsaPred<OpenACCCollapseClause>);
1103+
auto *UnInstCollapseClauseItr =
1104+
llvm::find_if(UnInstClauses, llvm::IsaPred<OpenACCCollapseClause>);
1105+
1106+
if (Clauses.end() == CollapseClauseItr)
1107+
return;
1108+
1109+
OpenACCCollapseClause *CollapseClause =
1110+
cast<OpenACCCollapseClause>(*CollapseClauseItr);
1111+
1112+
SemaRef.CollapseInfo.ActiveCollapse = CollapseClause;
1113+
Expr *LoopCount = CollapseClause->getLoopCount();
1114+
1115+
// If the loop count is still instantiation dependent, setting the depth
1116+
// counter isn't necessary, so return here.
1117+
if (!LoopCount || LoopCount->isInstantiationDependent())
1118+
return;
1119+
1120+
// Suppress diagnostics if we've done a 'transform' where the previous version
1121+
// wasn't dependent, meaning we already diagnosed it.
1122+
if (UnInstCollapseClauseItr != UnInstClauses.end() &&
1123+
!cast<OpenACCCollapseClause>(*UnInstCollapseClauseItr)
1124+
->getLoopCount()
1125+
->isInstantiationDependent())
1126+
return;
1127+
1128+
SemaRef.CollapseInfo.CurCollapseCount =
1129+
cast<ConstantExpr>(LoopCount)->getResultAsAPSInt();
1130+
}
1131+
10891132
SemaOpenACC::AssociatedStmtRAII::~AssociatedStmtRAII() {
10901133
SemaRef.InsideComputeConstruct = WasInsideComputeConstruct;
10911134
if (DirKind == OpenACCDirectiveKind::Parallel ||
@@ -1094,6 +1137,9 @@ SemaOpenACC::AssociatedStmtRAII::~AssociatedStmtRAII() {
10941137
assert(SemaRef.ParentlessLoopConstructs.empty() &&
10951138
"Didn't consume loop construct list?");
10961139
SemaRef.ParentlessLoopConstructs.swap(ParentlessLoopConstructs);
1140+
} else if (DirKind == OpenACCDirectiveKind::Loop) {
1141+
// Nothing really to do here, the LoopInConstruct should handle restorations
1142+
// correctly.
10971143
}
10981144
}
10991145

@@ -1646,10 +1692,78 @@ ExprResult SemaOpenACC::CheckCollapseLoopCount(Expr *LoopCount) {
16461692
ConstantExpr::Create(getASTContext(), LoopCount, APValue{*ICE})};
16471693
}
16481694

1695+
void SemaOpenACC::ActOnWhileStmt(SourceLocation WhileLoc) {
1696+
if (!getLangOpts().OpenACC)
1697+
return;
1698+
1699+
if (!CollapseInfo.TopLevelLoopSeen)
1700+
return;
1701+
1702+
if (CollapseInfo.CurCollapseCount && *CollapseInfo.CurCollapseCount > 0) {
1703+
Diag(WhileLoc, diag::err_acc_invalid_in_collapse_loop) << /*while loop*/ 1;
1704+
assert(CollapseInfo.ActiveCollapse && "Collapse count without object?");
1705+
Diag(CollapseInfo.ActiveCollapse->getBeginLoc(),
1706+
diag::note_acc_collapse_clause_here);
1707+
1708+
// Remove the value so that we don't get cascading errors in the body. The
1709+
// caller RAII object will restore this.
1710+
CollapseInfo.CurCollapseCount = std::nullopt;
1711+
}
1712+
}
1713+
1714+
void SemaOpenACC::ActOnDoStmt(SourceLocation DoLoc) {
1715+
if (!getLangOpts().OpenACC)
1716+
return;
1717+
1718+
if (!CollapseInfo.TopLevelLoopSeen)
1719+
return;
1720+
1721+
if (CollapseInfo.CurCollapseCount && *CollapseInfo.CurCollapseCount > 0) {
1722+
Diag(DoLoc, diag::err_acc_invalid_in_collapse_loop) << /*do loop*/ 2;
1723+
assert(CollapseInfo.ActiveCollapse && "Collapse count without object?");
1724+
Diag(CollapseInfo.ActiveCollapse->getBeginLoc(),
1725+
diag::note_acc_collapse_clause_here);
1726+
1727+
// Remove the value so that we don't get cascading errors in the body. The
1728+
// caller RAII object will restore this.
1729+
CollapseInfo.CurCollapseCount = std::nullopt;
1730+
}
1731+
}
1732+
1733+
void SemaOpenACC::ActOnForStmtBegin(SourceLocation ForLoc) {
1734+
if (!getLangOpts().OpenACC)
1735+
return;
1736+
1737+
// Enable the while/do-while checking.
1738+
CollapseInfo.TopLevelLoopSeen = true;
1739+
1740+
if (CollapseInfo.CurCollapseCount && *CollapseInfo.CurCollapseCount > 0)
1741+
--(*CollapseInfo.CurCollapseCount);
1742+
}
1743+
1744+
void SemaOpenACC::ActOnForStmtEnd(SourceLocation ForLoc, StmtResult Body) {
1745+
if (!getLangOpts().OpenACC)
1746+
return;
1747+
}
1748+
16491749
bool SemaOpenACC::ActOnStartStmtDirective(OpenACCDirectiveKind K,
16501750
SourceLocation StartLoc) {
16511751
SemaRef.DiscardCleanupsInEvaluationContext();
16521752
SemaRef.PopExpressionEvaluationContext();
1753+
1754+
// OpenACC 3.3 2.9.1:
1755+
// Intervening code must not contain other OpenACC directives or calls to API
1756+
// routines.
1757+
//
1758+
// ALL constructs are ill-formed if there is an active 'collapse'
1759+
if (CollapseInfo.CurCollapseCount && *CollapseInfo.CurCollapseCount > 0) {
1760+
Diag(StartLoc, diag::err_acc_invalid_in_collapse_loop)
1761+
<< /*OpenACC Construct*/ 0 << K;
1762+
assert(CollapseInfo.ActiveCollapse && "Collapse count without object?");
1763+
Diag(CollapseInfo.ActiveCollapse->getBeginLoc(),
1764+
diag::note_acc_collapse_clause_here);
1765+
}
1766+
16531767
return diagnoseConstructAppertainment(*this, K, StartLoc, /*IsStmt=*/true);
16541768
}
16551769

0 commit comments

Comments
 (0)