Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 119 additions & 1 deletion flang/lib/Semantics/check-acc-structure.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,9 @@ void AccStructureChecker::Enter(const parser::AccClause &x) {
SetContextClause(x);
}

void AccStructureChecker::Leave(const parser::AccClauseList &) {}
void AccStructureChecker::Leave(const parser::AccClauseList &) {
CheckLoopLevelClauseKernelsConflicts();
}

void AccStructureChecker::Enter(const parser::OpenACCBlockConstruct &x) {
const auto &beginBlockDir{std::get<parser::AccBeginBlockDirective>(x.t)};
Expand Down Expand Up @@ -293,6 +295,105 @@ std::optional<std::int64_t> AccStructureChecker::getGangDimensionSize(
return std::nullopt;
}

void AccStructureChecker::CheckLoopLevelClauseValue(
llvm::StringRef clauseName) {
if (GetContext().directive == llvm::acc::ACCD_kernels_loop ||
IsInsideKernelsConstruct())
return;

if (GetContext().directive == llvm::acc::ACCD_routine ||
HasOpenACCRoutineDirective(
&context_.FindScope(GetContext().clauseSource))) {
context_.Say(GetContext().clauseSource,
"'%s(value)' not allowed in subprogram compiled with ROUTINE directive"_err_en_US,
clauseName.str());
return;
}

llvm::acc::Directive dir{GetContext().directive};
if (dir == llvm::acc::ACCD_loop) {
if (std::optional<llvm::acc::Directive> parent{
getParentComputeConstruct()}) {
if (*parent == llvm::acc::ACCD_parallel)
dir = llvm::acc::ACCD_parallel_loop;
else if (*parent == llvm::acc::ACCD_serial)
dir = llvm::acc::ACCD_serial_loop;
}
}
context_.Say(GetContext().clauseSource,
"'%s(value)' not allowed in %s directive"_err_en_US, clauseName.str(),
parser::ToUpperCaseLetters(getDirectiveName(dir).str()));
}

static bool AccClauseHasVectorValue(const parser::AccClause &clause) {
const auto *vectorClause{std::get_if<parser::AccClause::Vector>(&clause.u)};
return vectorClause && vectorClause->v.has_value();
}

static bool AccClauseHasWorkerValue(const parser::AccClause &clause) {
const auto *workerClause{std::get_if<parser::AccClause::Worker>(&clause.u)};
return workerClause && workerClause->v.has_value();
}

static bool AccClauseHasGangNum(const parser::AccClause &clause) {
const auto *gangClause{std::get_if<parser::AccClause::Gang>(&clause.u)};
if (!gangClause || !gangClause->v)
return false;
for (const parser::AccGangArg &gangArg : gangClause->v->v)
if (std::get_if<parser::AccGangArg::Num>(&gangArg.u))
return true;
return false;
}

void AccStructureChecker::CheckLoopLevelClauseKernelsConflicts() {
if (dirContext_.empty())
return;
if (GetContext().directive != llvm::acc::ACCD_kernels_loop &&
!IsInsideKernelsConstruct())
return;

auto hasKernelsSizeClause{[&](llvm::acc::Clause sizeClause) {
for (DirectiveContext &ctx : dirContext_) {
if ((ctx.directive == llvm::acc::ACCD_kernels ||
ctx.directive == llvm::acc::ACCD_kernels_loop) &&
FindClause(ctx, sizeClause))
return true;
}
return false;
}};

llvm::acc::Directive kernelsDir{
GetContext().directive == llvm::acc::ACCD_kernels_loop
? llvm::acc::ACCD_kernels_loop
: llvm::acc::ACCD_kernels};
std::string dirName{
parser::ToUpperCaseLetters(getDirectiveName(kernelsDir).str())};

auto emitConflicts{[&](llvm::acc::Clause loopClause,
llvm::acc::Clause sizeClause,
bool (*isValued)(const parser::AccClause &)) {
if (!hasKernelsSizeClause(sizeClause))
return;
for (const auto &entry : FindClauses(loopClause)) {
const parser::AccClause *clause{entry.second};
if (clause && isValued(*clause)) {
context_.Say(clause->source,
"'%s(value)' not allowed in %s region that has a %s clause"_err_en_US,
parser::ToUpperCaseLetters(getClauseName(loopClause).str()),
dirName,
parser::ToUpperCaseLetters(getClauseName(sizeClause).str()));
}
}
}};

emitConflicts(llvm::acc::Clause::ACCC_vector,
llvm::acc::Clause::ACCC_vector_length, AccClauseHasVectorValue);
emitConflicts(llvm::acc::Clause::ACCC_worker,
llvm::acc::Clause::ACCC_num_workers, AccClauseHasWorkerValue);
emitConflicts(llvm::acc::Clause::ACCC_gang, llvm::acc::Clause::ACCC_num_gangs,
AccClauseHasGangNum);
}

void AccStructureChecker::CheckNotInSameOrSubLevelLoopConstruct() {
for (std::size_t i = dirContext_.size() - 1; i > 0; --i) {
auto &parent{dirContext_[i - 1]};
Expand Down Expand Up @@ -1030,6 +1131,10 @@ void AccStructureChecker::Enter(const parser::AccClause::Vector &g) {
if (GetContext().directive != llvm::acc::Directive::ACCD_routine) {
CheckAllowedOncePerGroup(crtClause, llvm::acc::Clause::ACCC_device_type);
}
if (g.v) {
CheckLoopLevelClauseValue(
parser::ToUpperCaseLetters(getClauseName(crtClause).str()));
}
}

void AccStructureChecker::Enter(const parser::AccClause::Worker &g) {
Expand All @@ -1042,6 +1147,10 @@ void AccStructureChecker::Enter(const parser::AccClause::Worker &g) {
if (GetContext().directive != llvm::acc::Directive::ACCD_routine) {
CheckAllowedOncePerGroup(crtClause, llvm::acc::Clause::ACCC_device_type);
}
if (g.v) {
CheckLoopLevelClauseValue(
parser::ToUpperCaseLetters(getClauseName(crtClause).str()));
}
}

void AccStructureChecker::Enter(const parser::AccClause::Tile &g) {
Expand Down Expand Up @@ -1090,6 +1199,15 @@ void AccStructureChecker::Enter(const parser::AccClause::Gang &g) {
context_.Say(GetContext().clauseSource,
"The num argument is not allowed when dim is specified"_err_en_US);
}

// Only the num argument is restricted to kernels. The static and dim
// arguments are allowed on any loop. On ROUTINE, num is already diagnosed
// above as only dim being allowed.
if (hasNum &&
GetContext().directive != llvm::acc::Directive::ACCD_routine) {
CheckLoopLevelClauseValue(
parser::ToUpperCaseLetters(getClauseName(crtClause).str()));
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions flang/lib/Semantics/check-acc-structure.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ class AccStructureChecker
std::optional<std::int64_t> getGangDimensionSize(
DirectiveContext &dirContext);
void CheckNotInSameOrSubLevelLoopConstruct();
void CheckLoopLevelClauseValue(llvm::StringRef clauseName);
void CheckLoopLevelClauseKernelsConflicts();
void CheckRoutineCallInLoop(const Symbol &);
void CheckMultipleOccurrenceInDeclare(
const parser::AccObjectList &, llvm::acc::Clause);
Expand Down
28 changes: 22 additions & 6 deletions flang/test/Lower/OpenACC/acc-loop.f90
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ program acc_loop
! CHECK: acc.yield
! CHECK-NEXT: } inclusiveUpperbound(array<i1: true>) independent

! A GANG clause with a num argument requires the loop to be associated with a
! kernels construct, which also makes the loop default to auto.
!$acc kernels
!$acc loop gang(num: 8)
DO i = 1, n
a(i) = b(i)
Expand All @@ -81,7 +84,7 @@ program acc_loop
! CHECK: %[[PRIVATE_I:.*]] = acc.private varPtr(%{{.*}} : !fir.ref<i32>) recipe(@privatization_ref_i32) implicit(true) name("i") -> !fir.ref<i32>
! CHECK: acc.loop gang({num=[[GANGNUM1]] : i32}) private(%[[PRIVATE_I]] : !fir.ref<i32>) control(%arg0 : i32) = (%{{.*}} : i32) to (%{{.*}} : i32) step (%{{.*}} : i32) {
! CHECK: acc.yield
! CHECK-NEXT: } inclusiveUpperbound(array<i1: true>) independent
! CHECK-NEXT: } inclusiveUpperbound(array<i1: true>) auto_

!$acc loop gang(num: gangNum)
DO i = 1, n
Expand All @@ -92,17 +95,18 @@ program acc_loop
! CHECK: %[[PRIVATE_I:.*]] = acc.private varPtr(%{{.*}} : !fir.ref<i32>) recipe(@privatization_ref_i32) implicit(true) name("i") -> !fir.ref<i32>
! CHECK: acc.loop gang({num=[[GANGNUM2]] : i32}) private(%[[PRIVATE_I]] : !fir.ref<i32>) control(%arg0 : i32) = (%{{.*}} : i32) to (%{{.*}} : i32) step (%{{.*}} : i32) {
! CHECK: acc.yield
! CHECK-NEXT: } inclusiveUpperbound(array<i1: true>) independent
! CHECK-NEXT: } inclusiveUpperbound(array<i1: true>) auto_

!$acc loop gang(num: gangNum, static: gangStatic)
DO i = 1, n
a(i) = b(i)
END DO
!$acc end kernels

! CHECK: %[[PRIVATE_I:.*]] = acc.private varPtr(%{{.*}} : !fir.ref<i32>) recipe(@privatization_ref_i32) implicit(true) name("i") -> !fir.ref<i32>
! CHECK: acc.loop gang({num=%{{.*}} : i32, static=%{{.*}} : i32}) private(%[[PRIVATE_I]] : !fir.ref<i32>) control(%arg0 : i32) = (%{{.*}} : i32) to (%{{.*}} : i32) step (%{{.*}} : i32) {
! CHECK: acc.yield
! CHECK-NEXT: } inclusiveUpperbound(array<i1: true>) independent
! CHECK-NEXT: } inclusiveUpperbound(array<i1: true>) auto_

!$acc loop vector
DO i = 1, n
Expand All @@ -114,6 +118,9 @@ program acc_loop
! CHECK: acc.yield
! CHECK-NEXT: } inclusiveUpperbound(array<i1: true>) independent

! A VECTOR clause with a value requires the loop to be associated with a
! kernels construct, which also makes the loop default to auto.
!$acc kernels
!$acc loop vector(128)
DO i = 1, n
a(i) = b(i)
Expand All @@ -123,18 +130,19 @@ program acc_loop
! CHECK: %[[PRIVATE_I:.*]] = acc.private varPtr(%{{.*}} : !fir.ref<i32>) recipe(@privatization_ref_i32) implicit(true) name("i") -> !fir.ref<i32>
! CHECK: acc.loop vector([[CONSTANT128]] : i32) private(%[[PRIVATE_I]] : !fir.ref<i32>) control(%arg0 : i32) = (%{{.*}} : i32) to (%{{.*}} : i32) step (%{{.*}} : i32) {
! CHECK: acc.yield
! CHECK-NEXT: } inclusiveUpperbound(array<i1: true>) independent
! CHECK-NEXT: } inclusiveUpperbound(array<i1: true>) auto_

!$acc loop vector(vectorLength)
DO i = 1, n
a(i) = b(i)
END DO
!$acc end kernels

! CHECK: [[VECTORLENGTH:%.*]] = fir.load %{{.*}} : !fir.ref<i32>
! CHECK: %[[PRIVATE_I:.*]] = acc.private varPtr(%{{.*}} : !fir.ref<i32>) recipe(@privatization_ref_i32) implicit(true) name("i") -> !fir.ref<i32>
! CHECK: acc.loop vector([[VECTORLENGTH]] : i32) private(%[[PRIVATE_I]] : !fir.ref<i32>) control(%arg0 : i32) = (%{{.*}} : i32) to (%{{.*}} : i32) step (%{{.*}} : i32) {
! CHECK: acc.yield
! CHECK-NEXT: } inclusiveUpperbound(array<i1: true>) independent
! CHECK-NEXT: } inclusiveUpperbound(array<i1: true>) auto_

!$acc loop worker
DO i = 1, n
Expand All @@ -146,16 +154,20 @@ program acc_loop
! CHECK: acc.yield
! CHECK-NEXT: } inclusiveUpperbound(array<i1: true>) independent

! A WORKER clause with a value requires the loop to be associated with a
! kernels construct, which also makes the loop default to auto.
!$acc kernels
!$acc loop worker(128)
DO i = 1, n
a(i) = b(i)
END DO
!$acc end kernels

! CHECK: [[WORKER128:%.*]] = arith.constant 128 : i32
! CHECK: %[[PRIVATE_I:.*]] = acc.private varPtr(%{{.*}} : !fir.ref<i32>) recipe(@privatization_ref_i32) implicit(true) name("i") -> !fir.ref<i32>
! CHECK: acc.loop worker([[WORKER128]] : i32) private(%[[PRIVATE_I]] : !fir.ref<i32>) control(%arg0 : i32) = (%{{.*}} : i32) to (%{{.*}} : i32) step (%{{.*}} : i32) {
! CHECK: acc.yield
! CHECK-NEXT: } inclusiveUpperbound(array<i1: true>) independent
! CHECK-NEXT: } inclusiveUpperbound(array<i1: true>) auto_

!$acc loop private(c)
DO i = 1, n
Expand Down Expand Up @@ -364,9 +376,13 @@ program acc_loop
100 continue
! CHECK: acc.loop

! A GANG clause with a num argument requires the loop to be associated with a
! kernels construct.
!$acc kernels
!$acc loop gang device_type(nvidia) gang(8)
DO i = 1, n
END DO
!$acc end kernels

! CHECK: acc.loop gang([#acc.device_type<none>], {num=%c8{{.*}} : i32} [#acc.device_type<nvidia>])

Expand Down
82 changes: 6 additions & 76 deletions flang/test/Lower/OpenACC/acc-parallel-loop.f90
Original file line number Diff line number Diff line change
Expand Up @@ -509,43 +509,8 @@ subroutine acc_parallel_loop
! CHECK: acc.yield
! CHECK-NEXT: }{{.*}}

!$acc parallel loop gang(num: 8)
DO i = 1, n
a(i) = b(i)
END DO

! CHECK: acc.parallel {{.*}} {
! CHECK: [[GANGNUM1:%.*]] = arith.constant 8 : i32
! CHECK: acc.loop {{.*}} gang({num=[[GANGNUM1]] : i32})
! CHECK: acc.yield
! CHECK-NEXT: } inclusiveUpperbound(array<i1: true>) independent
! CHECK: acc.yield
! CHECK-NEXT: }{{.*}}

!$acc parallel loop gang(num: gangNum)
DO i = 1, n
a(i) = b(i)
END DO

! CHECK: acc.parallel {{.*}} {
! CHECK: [[GANGNUM2:%.*]] = fir.load %{{.*}} : !fir.ref<i32>
! CHECK: acc.loop {{.*}} gang({num=[[GANGNUM2]] : i32})
! CHECK: acc.yield
! CHECK-NEXT: } inclusiveUpperbound(array<i1: true>) independent
! CHECK: acc.yield
! CHECK-NEXT: }{{.*}}

!$acc parallel loop gang(num: gangNum, static: gangStatic)
DO i = 1, n
a(i) = b(i)
END DO

! CHECK: acc.parallel {{.*}} {
! CHECK: acc.loop {{.*}} gang({num=%{{.*}} : i32, static=%{{.*}} : i32})
! CHECK: acc.yield
! CHECK-NEXT: } inclusiveUpperbound(array<i1: true>) independent
! CHECK: acc.yield
! CHECK-NEXT: }{{.*}}
! A GANG clause with a num argument is only allowed on a loop associated with
! a kernels construct, so it is covered by acc-kernels-loop.f90 instead.

!$acc parallel loop vector
DO i = 1, n
Expand All @@ -559,32 +524,8 @@ subroutine acc_parallel_loop
! CHECK: acc.yield
! CHECK-NEXT: }{{.*}}

!$acc parallel loop vector(128)
DO i = 1, n
a(i) = b(i)
END DO

! CHECK: acc.parallel {{.*}} {
! CHECK: [[CONSTANT128:%.*]] = arith.constant 128 : i32
! CHECK: acc.loop {{.*}} vector([[CONSTANT128]] : i32) {{.*}} {
! CHECK: acc.yield
! CHECK-NEXT: } inclusiveUpperbound(array<i1: true>) independent
! CHECK: acc.yield
! CHECK-NEXT: }{{.*}}

!$acc parallel loop vector(vectorLength)
DO i = 1, n
a(i) = b(i)
END DO

! CHECK: acc.parallel {{.*}} {
! CHECK: [[VECTORLENGTH:%.*]] = fir.load %{{.*}} : !fir.ref<i32>
! CHECK: acc.loop {{.*}} vector([[VECTORLENGTH]] : i32) {{.*}} {
! CHECK-NOT: fir.do_loop
! CHECK: acc.yield
! CHECK-NEXT: } inclusiveUpperbound(array<i1: true>) independent
! CHECK: acc.yield
! CHECK-NEXT: }{{.*}}
! A VECTOR clause with a value is only allowed on a loop associated with a
! kernels construct, so it is covered by acc-kernels-loop.f90 instead.

!$acc parallel loop worker
DO i = 1, n
Expand All @@ -599,19 +540,8 @@ subroutine acc_parallel_loop
! CHECK: acc.yield
! CHECK-NEXT: }{{.*}}

!$acc parallel loop worker(128)
DO i = 1, n
a(i) = b(i)
END DO

! CHECK: acc.parallel {{.*}}{
! CHECK: [[WORKER128:%.*]] = arith.constant 128 : i32
! CHECK: acc.loop {{.*}} worker([[WORKER128]] : i32) {{.*}} {
! CHECK-NOT: fir.do_loop
! CHECK: acc.yield
! CHECK-NEXT: } inclusiveUpperbound(array<i1: true>) independent
! CHECK: acc.yield
! CHECK-NEXT: }{{.*}}
! A WORKER clause with a value is only allowed on a loop associated with a
! kernels construct, so it is covered by acc-kernels-loop.f90 instead.

!$acc parallel loop collapse(2)
DO i = 1, n
Expand Down
Loading
Loading