Skip to content

Commit 8470027

Browse files
authored
[flang][acc] Add a semantic check for the validity of nested parallelism (#152225)
This PR implements a semantic checker to ensure the legality of nested OpenACC parallelism. The following are quotes from Spec 3.3. We need to disallow loops from having parallelism at the same level as or at a sub-level of child loops. >**2.9.2 gang clause** >[2064] <ins>When the parent compute construct is a parallel construct</ins>, or on an orphaned loop construct, the gang clause behaves as follows. (...) The associated dimension is the value of the dim argument, if it appears, or is dimension one. The dim argument must be a constant positive integer with value 1, 2, or 3. >[2112] The region of a loop with a gang(dim:d) clause may not contain a loop construct with a gang(dim:e) clause where e >= d unless it appears within a nested compute region. >[2074] <ins>When the parent compute construct is a kernels construct</ins>, the gang clause behaves as follows. (...) >[2148] The region of a loop with the gang clause may not contain another loop with a gang clause unless within a nested compute region. >**2.9.3 worker clause** >[2122]/[2129] The region of a loop with the worker clause may not contain a loop with the gang or worker clause unless within a nested compute region. >**2.9.4 vector clause** >[2141]/[2148] The region of a loop with the vector clause may not contain a loop with a gang, worker, or vector clause unless within a nested compute region. https://openacc.org/sites/default/files/inline-images/Specification/OpenACC-3.3-final.pdf
1 parent b05e26b commit 8470027

File tree

3 files changed

+206
-11
lines changed

3 files changed

+206
-11
lines changed

flang/lib/Semantics/check-acc-structure.cpp

Lines changed: 107 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
//
77
//===----------------------------------------------------------------------===//
88
#include "check-acc-structure.h"
9+
#include "resolve-names-utils.h"
910
#include "flang/Common/enum-set.h"
1011
#include "flang/Evaluate/tools.h"
1112
#include "flang/Parser/parse-tree.h"
@@ -106,18 +107,25 @@ bool AccStructureChecker::IsComputeConstruct(
106107
directive == llvm::acc::ACCD_kernels_loop;
107108
}
108109

109-
bool AccStructureChecker::IsInsideComputeConstruct() const {
110-
if (dirContext_.size() <= 1) {
111-
return false;
112-
}
110+
bool AccStructureChecker::IsLoopConstruct(
111+
llvm::acc::Directive directive) const {
112+
return directive == llvm::acc::Directive::ACCD_loop ||
113+
directive == llvm::acc::ACCD_parallel_loop ||
114+
directive == llvm::acc::ACCD_serial_loop ||
115+
directive == llvm::acc::ACCD_kernels_loop;
116+
}
113117

118+
std::optional<llvm::acc::Directive>
119+
AccStructureChecker::getParentComputeConstruct() const {
114120
// Check all nested context skipping the first one.
115-
for (std::size_t i = dirContext_.size() - 1; i > 0; --i) {
116-
if (IsComputeConstruct(dirContext_[i - 1].directive)) {
117-
return true;
118-
}
119-
}
120-
return false;
121+
for (std::size_t i = dirContext_.size() - 1; i > 0; --i)
122+
if (IsComputeConstruct(dirContext_[i - 1].directive))
123+
return dirContext_[i - 1].directive;
124+
return std::nullopt;
125+
}
126+
127+
bool AccStructureChecker::IsInsideComputeConstruct() const {
128+
return getParentComputeConstruct().has_value();
121129
}
122130

123131
void AccStructureChecker::CheckNotInComputeConstruct() {
@@ -128,6 +136,14 @@ void AccStructureChecker::CheckNotInComputeConstruct() {
128136
}
129137
}
130138

139+
bool AccStructureChecker::IsInsideParallelConstruct() const {
140+
if (auto directive = getParentComputeConstruct())
141+
if (*directive == llvm::acc::ACCD_parallel ||
142+
*directive == llvm::acc::ACCD_parallel_loop)
143+
return true;
144+
return false;
145+
}
146+
131147
void AccStructureChecker::Enter(const parser::AccClause &x) {
132148
SetContextClause(x);
133149
}
@@ -250,6 +266,85 @@ void AccStructureChecker::Leave(const parser::OpenACCCombinedConstruct &x) {
250266
dirContext_.pop_back();
251267
}
252268

269+
std::optional<std::int64_t> AccStructureChecker::getGangDimensionSize(
270+
DirectiveContext &dirContext) {
271+
for (auto it : dirContext.clauseInfo) {
272+
const auto *clause{it.second};
273+
if (const auto *gangClause{
274+
std::get_if<parser::AccClause::Gang>(&clause->u)})
275+
if (gangClause->v) {
276+
const Fortran::parser::AccGangArgList &x{*gangClause->v};
277+
for (const Fortran::parser::AccGangArg &gangArg : x.v)
278+
if (const auto *dim{
279+
std::get_if<Fortran::parser::AccGangArg::Dim>(&gangArg.u)})
280+
if (const auto v{EvaluateInt64(context_, dim->v)})
281+
return *v;
282+
}
283+
}
284+
return std::nullopt;
285+
}
286+
287+
void AccStructureChecker::CheckNotInSameOrSubLevelLoopConstruct() {
288+
for (std::size_t i = dirContext_.size() - 1; i > 0; --i) {
289+
auto &parent{dirContext_[i - 1]};
290+
if (IsLoopConstruct(parent.directive)) {
291+
for (auto parentClause : parent.actualClauses) {
292+
for (auto cl : GetContext().actualClauses) {
293+
bool invalid{false};
294+
if (parentClause == llvm::acc::Clause::ACCC_gang &&
295+
cl == llvm::acc::Clause::ACCC_gang) {
296+
if (IsInsideParallelConstruct()) {
297+
auto parentDim = getGangDimensionSize(parent);
298+
auto currentDim = getGangDimensionSize(GetContext());
299+
std::int64_t parentDimNum = 1, currentDimNum = 1;
300+
if (parentDim)
301+
parentDimNum = *parentDim;
302+
if (currentDim)
303+
currentDimNum = *currentDim;
304+
if (parentDimNum <= currentDimNum) {
305+
std::string parentDimStr, currentDimStr;
306+
if (parentDim)
307+
parentDimStr = "(dim:" + std::to_string(parentDimNum) + ")";
308+
if (currentDim)
309+
currentDimStr = "(dim:" + std::to_string(currentDimNum) + ")";
310+
context_.Say(GetContext().clauseSource,
311+
"%s%s clause is not allowed in the region of a loop with the %s%s clause"_err_en_US,
312+
parser::ToUpperCaseLetters(
313+
llvm::acc::getOpenACCClauseName(cl).str()),
314+
currentDimStr,
315+
parser::ToUpperCaseLetters(
316+
llvm::acc::getOpenACCClauseName(parentClause).str()),
317+
parentDimStr);
318+
continue;
319+
}
320+
} else {
321+
invalid = true;
322+
}
323+
} else if (parentClause == llvm::acc::Clause::ACCC_worker &&
324+
(cl == llvm::acc::Clause::ACCC_gang ||
325+
cl == llvm::acc::Clause::ACCC_worker)) {
326+
invalid = true;
327+
} else if (parentClause == llvm::acc::Clause::ACCC_vector &&
328+
(cl == llvm::acc::Clause::ACCC_gang ||
329+
cl == llvm::acc::Clause::ACCC_worker ||
330+
cl == llvm::acc::Clause::ACCC_vector)) {
331+
invalid = true;
332+
}
333+
if (invalid)
334+
context_.Say(GetContext().clauseSource,
335+
"%s clause is not allowed in the region of a loop with the %s clause"_err_en_US,
336+
parser::ToUpperCaseLetters(
337+
llvm::acc::getOpenACCClauseName(cl).str()),
338+
parser::ToUpperCaseLetters(
339+
llvm::acc::getOpenACCClauseName(parentClause).str()));
340+
}
341+
}
342+
}
343+
if (IsComputeConstruct(parent.directive))
344+
break;
345+
}
346+
}
347+
253348
void AccStructureChecker::Enter(const parser::OpenACCLoopConstruct &x) {
254349
const auto &beginDir{std::get<parser::AccBeginLoopDirective>(x.t)};
255350
const auto &loopDir{std::get<parser::AccLoopDirective>(beginDir.t)};
@@ -267,6 +362,8 @@ void AccStructureChecker::Leave(const parser::OpenACCLoopConstruct &x) {
267362
CheckNotAllowedIfClause(llvm::acc::Clause::ACCC_seq,
268363
{llvm::acc::Clause::ACCC_gang, llvm::acc::Clause::ACCC_vector,
269364
llvm::acc::Clause::ACCC_worker});
365+
// Restriction - 2.9.2, 2.9.3, 2.9.4
366+
CheckNotInSameOrSubLevelLoopConstruct();
270367
}
271368
dirContext_.pop_back();
272369
}

flang/lib/Semantics/check-acc-structure.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,14 @@ class AccStructureChecker
9898

9999
bool CheckAllowedModifier(llvm::acc::Clause clause);
100100
bool IsComputeConstruct(llvm::acc::Directive directive) const;
101+
bool IsLoopConstruct(llvm::acc::Directive directive) const;
102+
std::optional<llvm::acc::Directive> getParentComputeConstruct() const;
101103
bool IsInsideComputeConstruct() const;
104+
bool IsInsideParallelConstruct() const;
102105
void CheckNotInComputeConstruct();
106+
std::optional<std::int64_t> getGangDimensionSize(
107+
DirectiveContext &dirContext);
108+
void CheckNotInSameOrSubLevelLoopConstruct();
103109
void CheckMultipleOccurrenceInDeclare(
104110
const parser::AccObjectList &, llvm::acc::Clause);
105111
void CheckMultipleOccurrenceInDeclare(

flang/test/Semantics/OpenACC/acc-loop.f90

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ program openacc_loop_validity
1313
integer :: n
1414
end type atype
1515

16-
integer :: i, j, k, b, gang_size, vector_size, worker_size
16+
integer :: i, j, k, l, m, b, gang_size, vector_size, worker_size
1717
integer, parameter :: N = 256
1818
integer, dimension(N) :: c
1919
logical, dimension(N) :: d, e
@@ -259,6 +259,98 @@ program openacc_loop_validity
259259
end do
260260
!$acc end parallel
261261

262+
!$acc parallel
263+
!$acc loop gang
264+
do i = 1, n
265+
!$acc loop worker
266+
do j = 1, n
267+
!ERROR: GANG clause is not allowed in the region of a loop with the WORKER clause
268+
!ERROR: GANG clause is not allowed in the region of a loop with the GANG clause
269+
!$acc loop gang vector
270+
do k = 1, i
271+
end do
272+
end do
273+
end do
274+
!$acc end parallel
275+
276+
!$acc parallel loop vector
277+
do i = 1, n
278+
!ERROR: GANG clause is not allowed in the region of a loop with the VECTOR clause
279+
!$acc loop gang
280+
do j = 1, n
281+
!ERROR: WORKER clause is not allowed in the region of a loop with the VECTOR clause
282+
!$acc loop worker
283+
do k = 1, i
284+
!ERROR: VECTOR clause is not allowed in the region of a loop with the VECTOR clause
285+
!$acc loop vector
286+
do l = 1, 1
287+
end do
288+
end do
289+
end do
290+
end do
291+
!$acc end parallel loop
292+
293+
!$acc kernels
294+
do i = 1, n
295+
!$acc loop gang worker
296+
do j = 1, n
297+
!ERROR: WORKER clause is not allowed in the region of a loop with the WORKER clause
298+
!$acc loop worker vector
299+
do k = 1, i
300+
end do
301+
end do
302+
end do
303+
!$acc end kernels
304+
305+
!$acc parallel
306+
!$acc loop gang(dim:1)
307+
do i = 1, n
308+
!ERROR: GANG(dim:1) clause is not allowed in the region of a loop with the GANG(dim:1) clause
309+
!$acc loop gang(dim:1)
310+
do j = 1, n
311+
!ERROR: GANG(dim:2) clause is not allowed in the region of a loop with the GANG(dim:1) clause
312+
!$acc loop gang(dim:2)
313+
do k = 1, i
314+
!ERROR: GANG(dim:3) clause is not allowed in the region of a loop with the GANG(dim:2) clause
315+
!ERROR: GANG(dim:3) clause is not allowed in the region of a loop with the GANG(dim:1) clause
316+
!$acc loop gang(dim:3)
317+
do l = 1, 1
318+
!ERROR: GANG(dim:3) clause is not allowed in the region of a loop with the GANG(dim:3) clause
319+
!ERROR: GANG(dim:3) clause is not allowed in the region of a loop with the GANG(dim:2) clause
320+
!ERROR: GANG(dim:3) clause is not allowed in the region of a loop with the GANG(dim:1) clause
321+
!$acc loop gang(dim:3)
322+
do m = 1, 1
323+
end do
324+
end do
325+
end do
326+
end do
327+
end do
328+
!$acc end parallel
329+
330+
!$acc parallel loop gang(dim:3)
331+
do i = 1, n
332+
!$acc loop gang(dim:2)
333+
do j = 1, n
334+
!$acc loop gang(dim:1) worker vector
335+
do k = 1, i
336+
end do
337+
end do
338+
end do
339+
!$acc end parallel loop
340+
341+
!$acc kernels loop gang(dim:3)
342+
do i = 1, n
343+
!ERROR: GANG clause is not allowed in the region of a loop with the GANG clause
344+
!$acc loop gang(dim:2)
345+
do j = 1, n
346+
!ERROR: GANG clause is not allowed in the region of a loop with the GANG clause
347+
!$acc loop gang(dim:1) worker vector
348+
do k = 1, i
349+
end do
350+
end do
351+
end do
352+
!$acc end kernels loop
353+
262354
!ERROR: Clause IF is not allowed after clause DEVICE_TYPE on the PARALLEL directive
263355
!$acc parallel device_type(*) if(.TRUE.)
264356
!$acc loop

0 commit comments

Comments
 (0)