Skip to content

Commit

Permalink
[OpenACC] Implement loop restrictions on for loops. (llvm#115370)
Browse files Browse the repository at this point in the history
OpenACC restricts the contents of a 'for' loop affected by a 'loop'
construct without a 'seq'. The loop variable must be integer, pointer,
or random-access-iterator, it must monotonically increase/decrease, and
the trip count must be computable at runtime before the function.

This patch tries to implement some of these limitations to the best of
our ability, though it causes us to be perhaps overly restrictive at the
moment. I expect we'll revisit some of these rules/add additional
supported forms of loop-variable and 'monotonically increasing' here,
but the currently enforced rules are heavily inspired by the OMP
implementation here.
  • Loading branch information
erichkeane authored Nov 8, 2024
1 parent 32c744a commit b0cfbfd
Show file tree
Hide file tree
Showing 50 changed files with 3,553 additions and 1,821 deletions.
13 changes: 13 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -12747,6 +12747,19 @@ def err_acc_gang_reduction_numgangs_conflict
def err_reduction_op_mismatch
: Error<"OpenACC 'reduction' variable must have the same operator in all "
"nested constructs (%0 vs %1)">;
def err_acc_loop_variable_type
: Error<"loop variable of loop associated with an OpenACC 'loop' construct "
"must be of integer, pointer, or random-access-iterator type (is "
"%0)">;
def err_acc_loop_variable
: Error<"OpenACC 'loop' construct must have initialization clause in "
"canonical form ('var = init' or 'T var = init')">;
def err_acc_loop_terminating_condition
: Error<"OpenACC 'loop' construct must have a terminating condition">;
def err_acc_loop_not_monotonic
: Error<"OpenACC 'loop' variable must monotonically increase or decrease "
"('++', '--', or compound assignment)">;

// AMDGCN builtins diagnostics
def err_amdgcn_global_load_lds_size_invalid_value : Error<"invalid size value">;
def note_amdgcn_global_load_lds_size_valid_value : Note<"size must be 1, 2, or 4">;
Expand Down
62 changes: 59 additions & 3 deletions clang/include/clang/Sema/SemaOpenACC.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,43 @@ class SemaOpenACC : public SemaBase {
/// 'loop' clause enforcement, where this is 'blocked' by a compute construct.
llvm::SmallVector<OpenACCReductionClause *> ActiveReductionClauses;

// Type to check the info about the 'for stmt'.
struct ForStmtBeginChecker {
SemaOpenACC &SemaRef;
SourceLocation ForLoc;
bool IsRangeFor = false;
std::optional<const CXXForRangeStmt *> RangeFor = nullptr;
const Stmt *Init = nullptr;
bool InitChanged = false;
std::optional<const Stmt *> Cond = nullptr;
std::optional<const Stmt *> Inc = nullptr;
// Prevent us from checking 2x, which can happen with collapse & tile.
bool AlreadyChecked = false;

ForStmtBeginChecker(SemaOpenACC &SemaRef, SourceLocation ForLoc,
std::optional<const CXXForRangeStmt *> S)
: SemaRef(SemaRef), ForLoc(ForLoc), IsRangeFor(true), RangeFor(S) {}

ForStmtBeginChecker(SemaOpenACC &SemaRef, SourceLocation ForLoc,
const Stmt *I, bool InitChanged,
std::optional<const Stmt *> C,
std::optional<const Stmt *> Inc)
: SemaRef(SemaRef), ForLoc(ForLoc), IsRangeFor(false), Init(I),
InitChanged(InitChanged), Cond(C), Inc(Inc) {}
// Do the checking for the For/Range-For. Currently this implements the 'not
// seq' restrictions only, and should be called either if we know we are a
// top-level 'for' (the one associated via associated-stmt), or extended via
// 'collapse'.
void check();

const ValueDecl *checkInit();
void checkCond();
void checkInc(const ValueDecl *Init);
};

/// Helper function for checking the 'for' and 'range for' stmts.
void ForStmtBeginHelper(SourceLocation ForLoc, ForStmtBeginChecker &C);

public:
ComputeConstructInfo &getActiveComputeConstructInfo() {
return ActiveComputeConstructInfo;
Expand All @@ -137,6 +174,11 @@ class SemaOpenACC : public SemaBase {
/// permits us to implement the restriction of no further 'gang', 'vector', or
/// 'worker' clauses.
SourceLocation LoopVectorClauseLoc;
/// If there is a current 'active' loop construct that does NOT have a 'seq'
/// clause on it, this has that source location. This permits us to implement
/// the 'loop' restrictions on the loop variable. This can be extended via
/// 'collapse', so we need to keep this around for a while.
SourceLocation LoopWithoutSeqLoc;

// Redeclaration of the version in OpenACCClause.h.
using DeviceTypeArgument = std::pair<IdentifierInfo *, SourceLocation>;
Expand Down Expand Up @@ -568,8 +610,19 @@ class SemaOpenACC : public SemaBase {
void ActOnWhileStmt(SourceLocation WhileLoc);
// Called when we encounter a 'do' statement, before looking at its 'body'.
void ActOnDoStmt(SourceLocation DoLoc);
// Called when we encounter a 'for' statement, before looking at its 'body',
// for the 'range-for'. 'ActOnForStmtEnd' is used after the body.
void ActOnRangeForStmtBegin(SourceLocation ForLoc, const Stmt *OldRangeFor,
const Stmt *RangeFor);
void ActOnRangeForStmtBegin(SourceLocation ForLoc, const Stmt *RangeFor);
// Called when we encounter a 'for' statement, before looking at its 'body'.
void ActOnForStmtBegin(SourceLocation ForLoc);
// 'ActOnForStmtEnd' is used after the body.
void ActOnForStmtBegin(SourceLocation ForLoc, const Stmt *First,
const Stmt *Second, const Stmt *Third);
void ActOnForStmtBegin(SourceLocation ForLoc, const Stmt *OldFirst,
const Stmt *First, const Stmt *OldSecond,
const Stmt *Second, const Stmt *OldThird,
const Stmt *Third);
// Called when we encounter a 'for' statement, after we've consumed/checked
// the body. This is necessary for a number of checks on the contents of the
// 'for' statement.
Expand Down Expand Up @@ -598,7 +651,9 @@ class SemaOpenACC : public SemaBase {
/// Called when we encounter an associated statement for our construct, this
/// should check legality of the statement as it appertains to this Construct.
StmtResult ActOnAssociatedStmt(SourceLocation DirectiveLoc,
OpenACCDirectiveKind K, StmtResult AssocStmt);
OpenACCDirectiveKind K,
ArrayRef<const OpenACCClause *> Clauses,
StmtResult AssocStmt);

/// Called after the directive has been completely parsed, including the
/// declaration group or associated statement.
Expand Down Expand Up @@ -712,12 +767,13 @@ class SemaOpenACC : public SemaBase {
SourceLocation OldLoopGangClauseOnKernelLoc;
SourceLocation OldLoopWorkerClauseLoc;
SourceLocation OldLoopVectorClauseLoc;
SourceLocation OldLoopWithoutSeqLoc;
llvm::SmallVector<OpenACCLoopConstruct *> ParentlessLoopConstructs;
llvm::SmallVector<OpenACCReductionClause *> ActiveReductionClauses;
LoopInConstructRAII LoopRAII;

public:
AssociatedStmtRAII(SemaOpenACC &, OpenACCDirectiveKind,
AssociatedStmtRAII(SemaOpenACC &, OpenACCDirectiveKind, SourceLocation,
ArrayRef<const OpenACCClause *>,
ArrayRef<OpenACCClause *>);
void SetCollapseInfoBeforeAssociatedStmt(
Expand Down
7 changes: 4 additions & 3 deletions clang/lib/Parse/ParseOpenACC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1498,14 +1498,15 @@ StmtResult Parser::ParseOpenACCDirectiveStmt() {
return StmtError();

StmtResult AssocStmt;
SemaOpenACC::AssociatedStmtRAII AssocStmtRAII(
getActions().OpenACC(), DirInfo.DirKind, {}, DirInfo.Clauses);
SemaOpenACC::AssociatedStmtRAII AssocStmtRAII(getActions().OpenACC(),
DirInfo.DirKind, DirInfo.DirLoc,
{}, DirInfo.Clauses);
if (doesDirectiveHaveAssociatedStmt(DirInfo.DirKind)) {
ParsingOpenACCDirectiveRAII DirScope(*this, /*Value=*/false);
ParseScope ACCScope(this, getOpenACCScopeFlags(DirInfo.DirKind));

AssocStmt = getActions().OpenACC().ActOnAssociatedStmt(
DirInfo.StartLoc, DirInfo.DirKind, ParseStatement());
DirInfo.StartLoc, DirInfo.DirKind, DirInfo.Clauses, ParseStatement());
}

return getActions().OpenACC().ActOnEndStmtDirective(
Expand Down
6 changes: 5 additions & 1 deletion clang/lib/Parse/ParseStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2360,7 +2360,11 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc) {
// OpenACC Restricts a for-loop inside of certain construct/clause
// combinations, so diagnose that here in OpenACC mode.
SemaOpenACC::LoopInConstructRAII LCR{getActions().OpenACC()};
getActions().OpenACC().ActOnForStmtBegin(ForLoc);
if (ForRangeInfo.ParsedForRangeDecl())
getActions().OpenACC().ActOnRangeForStmtBegin(ForLoc, ForRangeStmt.get());
else
getActions().OpenACC().ActOnForStmtBegin(
ForLoc, FirstPart.get(), SecondPart.get().second, ThirdPart.get());

// C99 6.8.5p5 - In C99, the body of the for statement is a scope, even if
// there is no compound stmt. C90 does not have this clause. We only do this
Expand Down
Loading

0 comments on commit b0cfbfd

Please sign in to comment.