[flang][OpenMP] Add semantic checks for ordered construct

This patch implements the following semantic checks according to
OpenMP Version 5.1 Ordered construct restriction:

```
At most one threads clause can appear on an ordered construct; At most
one simd clause can appear on an ordered construct; At most one
depend(source) clause can appear on an ordered construct; Either
depend(sink:vec) clauses or depend(source) clauses may appear on an
ordered construct, but not both.
```

This patch also implements the following semantic checks according to
the syntax and descriptions in OpenMP Version 5.1 Ordered construct:

```
The dependence types of sink or source are only allowed on an ordered
construct. The depend(*) clauses are not allowed when ordered construct
is a block construct with an ordered region. The threads or simd clauses
are not allowed when the ordered construct is a standalone construct
with no ordered region.
```

Co-authored-by: Sameeran Joshi <sameeranjayant.joshi@amd.com>

Reviewed By: kiranchandramohan

Differential Revision: https://reviews.llvm.org/D108512
This commit is contained in:
PeixinQiao 2021-09-17 21:53:07 +08:00
parent ff6b074674
commit 6fb01a9470
6 changed files with 192 additions and 4 deletions

View File

@ -771,6 +771,25 @@ void OmpStructureChecker::Leave(const parser::OpenMPBlockConstruct &) {
dirContext_.pop_back();
}
void OmpStructureChecker::ChecksOnOrderedAsBlock() {
if (FindClause(llvm::omp::Clause::OMPC_depend)) {
context_.Say(GetContext().clauseSource,
"DEPEND(*) clauses are not allowed when ORDERED construct is a block"
" construct with an ORDERED region"_err_en_US);
}
}
void OmpStructureChecker::Leave(const parser::OmpBeginBlockDirective &) {
switch (GetContext().directive) {
case llvm::omp::Directive::OMPD_ordered:
// [5.1] 2.19.9 Ordered Construct Restriction
ChecksOnOrderedAsBlock();
break;
default:
break;
}
}
void OmpStructureChecker::Enter(const parser::OpenMPSectionsConstruct &x) {
const auto &beginSectionsDir{
std::get<parser::OmpBeginSectionsDirective>(x.t)};
@ -911,6 +930,48 @@ void OmpStructureChecker::CheckBarrierNesting(
}
}
void OmpStructureChecker::ChecksOnOrderedAsStandalone() {
if (FindClause(llvm::omp::Clause::OMPC_threads) ||
FindClause(llvm::omp::Clause::OMPC_simd)) {
context_.Say(GetContext().clauseSource,
"THREADS, SIMD clauses are not allowed when ORDERED construct is a "
"standalone construct with no ORDERED region"_err_en_US);
}
bool isSinkPresent{false};
int dependSourceCount{0};
auto clauseAll = FindClauses(llvm::omp::Clause::OMPC_depend);
for (auto itr = clauseAll.first; itr != clauseAll.second; ++itr) {
const auto &dependClause{
std::get<parser::OmpClause::Depend>(itr->second->u)};
if (std::get_if<parser::OmpDependClause::Source>(&dependClause.v.u)) {
dependSourceCount++;
if (isSinkPresent) {
context_.Say(itr->second->source,
"DEPEND(SOURCE) is not allowed when DEPEND(SINK: vec) is present "
"on ORDERED directive"_err_en_US);
}
if (dependSourceCount > 1) {
context_.Say(itr->second->source,
"At most one DEPEND(SOURCE) clause can appear on the ORDERED "
"directive"_err_en_US);
}
} else if (std::get_if<parser::OmpDependClause::Sink>(&dependClause.v.u)) {
isSinkPresent = true;
if (dependSourceCount > 0) {
context_.Say(itr->second->source,
"DEPEND(SINK: vec) is not allowed when DEPEND(SOURCE) is present "
"on ORDERED directive"_err_en_US);
}
} else {
context_.Say(itr->second->source,
"Only DEPEND(SOURCE) or DEPEND(SINK: vec) are allowed when ORDERED "
"construct is a standalone construct with no ORDERED "
"region"_err_en_US);
}
}
}
void OmpStructureChecker::Enter(
const parser::OpenMPSimpleStandaloneConstruct &x) {
const auto &dir{std::get<parser::OmpSimpleStandaloneDirective>(x.t)};
@ -920,6 +981,14 @@ void OmpStructureChecker::Enter(
void OmpStructureChecker::Leave(
const parser::OpenMPSimpleStandaloneConstruct &) {
switch (GetContext().directive) {
case llvm::omp::Directive::OMPD_ordered:
// [5.1] 2.19.9 Ordered Construct Restriction
ChecksOnOrderedAsStandalone();
break;
default:
break;
}
dirContext_.pop_back();
}

View File

@ -135,6 +135,7 @@ public:
void Enter(const parser::OpenMPBlockConstruct &);
void Leave(const parser::OpenMPBlockConstruct &);
void Leave(const parser::OmpBeginBlockDirective &);
void Enter(const parser::OmpEndBlockDirective &);
void Leave(const parser::OmpEndBlockDirective &);
@ -238,7 +239,9 @@ private:
const parser::DefinedOperator::IntrinsicOperator &);
void CheckReductionTypeList(const parser::OmpClause::Reduction &);
void CheckMasterNesting(const parser::OpenMPBlockConstruct &x);
void ChecksOnOrderedAsBlock();
void CheckBarrierNesting(const parser::OpenMPSimpleStandaloneConstruct &x);
void ChecksOnOrderedAsStandalone();
void CheckReductionArraySection(const parser::OmpObjectList &ompObjectList);
void CheckIntentInPointerAndDefinable(
const parser::OmpObjectList &, const llvm::omp::Clause);

View File

@ -298,6 +298,9 @@ public:
GetContext().withinConstruct = true;
}
bool Pre(const parser::OpenMPSimpleStandaloneConstruct &);
void Post(const parser::OpenMPSimpleStandaloneConstruct &) { PopContext(); }
bool Pre(const parser::OpenMPLoopConstruct &);
void Post(const parser::OpenMPLoopConstruct &) { PopContext(); }
void Post(const parser::OmpBeginLoopDirective &) {
@ -414,6 +417,18 @@ public:
return false;
}
bool Pre(const parser::OmpDependClause &x) {
if (const auto *dependSink{
std::get_if<parser::OmpDependClause::Sink>(&x.u)}) {
const auto &dependSinkVec{dependSink->v};
for (const auto &dependSinkElement : dependSinkVec) {
const auto &name{std::get<parser::Name>(dependSinkElement.t)};
ResolveName(&name);
}
}
return false;
}
void Post(const parser::Name &);
// Keep track of labels in the statements that causes jumps to target labels
@ -1132,6 +1147,27 @@ void OmpAttributeVisitor::Post(const parser::OpenMPBlockConstruct &x) {
PopContext();
}
bool OmpAttributeVisitor::Pre(
const parser::OpenMPSimpleStandaloneConstruct &x) {
const auto &standaloneDir{
std::get<parser::OmpSimpleStandaloneDirective>(x.t)};
switch (standaloneDir.v) {
case llvm::omp::Directive::OMPD_barrier:
case llvm::omp::Directive::OMPD_ordered:
case llvm::omp::Directive::OMPD_target_enter_data:
case llvm::omp::Directive::OMPD_target_exit_data:
case llvm::omp::Directive::OMPD_target_update:
case llvm::omp::Directive::OMPD_taskwait:
case llvm::omp::Directive::OMPD_taskyield:
PushContext(standaloneDir.source, standaloneDir.v);
break;
default:
break;
}
ClearDataSharingAttributeObjects();
return true;
}
bool OmpAttributeVisitor::Pre(const parser::OpenMPLoopConstruct &x) {
const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)};
const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)};

View File

@ -480,8 +480,6 @@ use omp_lib
! !$omp target enter data map(to:arrayA) map(alloc:arrayB)
! !$omp target update from(arrayA) to(arrayB)
! !$omp target exit data map(from:arrayA) map(delete:arrayB)
!$omp ordered depend(source)
! !$omp ordered depend(sink:i-1)
!$omp flush (c)
!$omp flush acq_rel
!$omp flush release

View File

@ -0,0 +1,80 @@
! RUN: %python %S/test_errors.py %s %flang -fopenmp
! OpenMP Version 5.1
! Check OpenMP construct validity for the following directives:
! 2.19.9 Ordered Construct
program main
integer :: i, N = 10
real :: a, arrayA(10), arrayB(10), arrayC(10)
real, external :: foo, bar, baz
!$omp do ordered
do i = 1, N
!ERROR: At most one THREADS clause can appear on the ORDERED directive
!$omp ordered threads threads
arrayA(i) = i
!$omp end ordered
end do
!$omp end do
!$omp simd
do i = 1, N
!ERROR: At most one SIMD clause can appear on the ORDERED directive
!$omp ordered simd simd
arrayA(i) = i
!$omp end ordered
end do
!$omp end simd
!$omp do simd ordered
do i = 1, N
!ERROR: At most one SIMD clause can appear on the ORDERED directive
!$omp ordered simd simd
arrayA(i) = i
!$omp end ordered
end do
!$omp end do simd
!$omp do ordered(1)
do i = 2, N
!ERROR: Only DEPEND(SOURCE) or DEPEND(SINK: vec) are allowed when ORDERED construct is a standalone construct with no ORDERED region
!ERROR: At most one DEPEND(SOURCE) clause can appear on the ORDERED directive
!$omp ordered depend(source) depend(inout: arrayA) depend(source)
arrayA(i) = foo(i)
!ERROR: DEPEND(SOURCE) is not allowed when DEPEND(SINK: vec) is present on ORDERED directive
!ERROR: DEPEND(SOURCE) is not allowed when DEPEND(SINK: vec) is present on ORDERED directive
!ERROR: At most one DEPEND(SOURCE) clause can appear on the ORDERED directive
!$omp ordered depend(sink: i - 1) depend(source) depend(source)
arrayB(i) = bar(arrayA(i), arrayB(i-1))
!ERROR: Only DEPEND(SOURCE) or DEPEND(SINK: vec) are allowed when ORDERED construct is a standalone construct with no ORDERED region
!ERROR: Only DEPEND(SOURCE) or DEPEND(SINK: vec) are allowed when ORDERED construct is a standalone construct with no ORDERED region
!$omp ordered depend(out: arrayC) depend(in: arrayB)
arrayC(i) = baz(arrayB(i-1))
end do
!$omp end do
!$omp do ordered(1)
do i = 2, N
!ERROR: DEPEND(*) clauses are not allowed when ORDERED construct is a block construct with an ORDERED region
!$omp ordered depend(source)
arrayA(i) = foo(i)
!$omp end ordered
!ERROR: DEPEND(*) clauses are not allowed when ORDERED construct is a block construct with an ORDERED region
!$omp ordered depend(sink: i - 1)
arrayB(i) = bar(arrayA(i), arrayB(i-1))
!$omp end ordered
end do
!$omp end do
contains
subroutine work1()
!ERROR: THREADS, SIMD clauses are not allowed when ORDERED construct is a standalone construct with no ORDERED region
!$omp ordered simd
end subroutine work1
subroutine work2()
!ERROR: THREADS, SIMD clauses are not allowed when ORDERED construct is a standalone construct with no ORDERED region
!$omp ordered threads
end subroutine work2
end program main

View File

@ -491,10 +491,12 @@ def OMP_Flush : Directive<"flush"> {
}
def OMP_Ordered : Directive<"ordered"> {
let allowedClauses = [
VersionedClause<OMPC_Threads>,
VersionedClause<OMPC_Simd>,
VersionedClause<OMPC_Depend>
];
let allowedOnceClauses = [
VersionedClause<OMPC_Threads>,
VersionedClause<OMPC_Simd>
];
}
def OMP_Atomic : Directive<"atomic"> {
let allowedClauses = [