[flang] Structural checks for DO construct (flang-compiler/f18#536)

Most restrictions in 2.7.1 Loop construct, nesting checks are still on TODO list.

seenClauses (EnumSet) is dropped. A multimap clauseInfo is added to save the encountering clause and its parser tree node pointer (parser::OmpClause) and to do the checks after walking through all the clauses.

Original-commit: flang-compiler/f18@f797357682
Reviewed-on: https://github.com/flang-compiler/f18/pull/536
This commit is contained in:
Jinxin (Brian) Yang 2019-07-01 13:55:06 -07:00 committed by GitHub
parent ebf5b43e9d
commit 1b0be3e0c9
3 changed files with 173 additions and 17 deletions

View File

@ -40,15 +40,14 @@ void OmpStructureChecker::CheckAllowed(const OmpClause &type) {
parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()));
return;
}
if (GetContext().allowedOnceClauses.test(type) &&
GetContext().seenClauses.test(type)) {
if (GetContext().allowedOnceClauses.test(type) && FindClause(type)) {
context_.Say(GetContext().clauseSource,
"At most one %s clause can appear on the %s directive"_err_en_US,
EnumToString(type),
parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()));
return;
}
SetContextSeen(type);
SetContextClauseInfo(type);
}
void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) {
@ -104,6 +103,7 @@ void OmpStructureChecker::Enter(const parser::OmpBlockDirective::Parallel &) {
}
// 2.7.1 do-clause -> private-clause |
// firstprivate-clause |
// lastprivate-clause |
// linear-clause |
// reduction-clause |
@ -114,9 +114,8 @@ void OmpStructureChecker::Enter(const parser::OmpLoopDirective::Do &) {
// reserve for nesting check
SetContextDirectiveEnum(OmpDirective::DO);
OmpClauseSet allowed{OmpClause::PRIVATE, OmpClause::LASTPRIVATE,
OmpClause::LINEAR, OmpClause::REDUCTION, OmpClause::SCHEDULE,
OmpClause::COLLAPSE, OmpClause::ORDERED};
OmpClauseSet allowed{OmpClause::PRIVATE, OmpClause::FIRSTPRIVATE,
OmpClause::LASTPRIVATE, OmpClause::LINEAR, OmpClause::REDUCTION};
SetContextAllowed(allowed);
OmpClauseSet allowedOnce{
@ -124,8 +123,47 @@ void OmpStructureChecker::Enter(const parser::OmpLoopDirective::Do &) {
SetContextAllowedOnce(allowedOnce);
}
void OmpStructureChecker::Leave(const parser::OmpClauseList &) {
// 2.7 Loop Construct Restriction
if (GetContext().directive == OmpDirective::DO) {
if (auto *clause{FindClause(OmpClause::SCHEDULE)}) {
// only one schedule clause is allowed
const auto &schedClause{std::get<parser::OmpScheduleClause>(clause->u)};
if (ScheduleModifierHasType(schedClause,
parser::OmpScheduleModifierType::ModType::Nonmonotonic)) {
if (FindClause(OmpClause::ORDERED)) {
context_.Say(clause->source,
"The NONMONOTONIC modifier cannot be specified "
"if an ORDERED clause is specified"_err_en_US);
}
if (ScheduleModifierHasType(schedClause,
parser::OmpScheduleModifierType::ModType::Monotonic)) {
context_.Say(clause->source,
"The MONOTONIC and NONMONOTONIC modifiers "
"cannot be both specified"_err_en_US);
}
}
}
if (auto *clause{FindClause(OmpClause::ORDERED)}) {
if (FindClause(OmpClause::LINEAR)) {
// only one ordered clause is allowed
const auto &orderedClause{
std::get<parser::OmpClause::Ordered>(clause->u)};
if (orderedClause.v.has_value()) {
context_.Say(clause->source,
"A loop directive may not have both a LINEAR clause and "
"an ORDERED clause with a parameter"_err_en_US);
}
}
// TODO: ordered region binding check (requires nesting implementation)
}
}
}
void OmpStructureChecker::Enter(const parser::OmpClause &x) {
SetContextClauseSource(x.source);
SetContextClause(x);
}
void OmpStructureChecker::Enter(const parser::OmpClause::Defaultmap &) {
@ -234,8 +272,18 @@ void OmpStructureChecker::Enter(const parser::OmpDependClause &) {
void OmpStructureChecker::Enter(const parser::OmpIfClause &) {
CheckAllowed(OmpClause::IF);
}
void OmpStructureChecker::Enter(const parser::OmpLinearClause &) {
void OmpStructureChecker::Enter(const parser::OmpLinearClause &x) {
CheckAllowed(OmpClause::LINEAR);
// 2.7 Loop Construct Restriction
if (GetContext().directive == OmpDirective::DO ||
GetContext().directive == OmpDirective::SIMD) {
if (std::holds_alternative<parser::OmpLinearClause::WithModifier>(x.u)) {
context_.Say(GetContext().clauseSource,
"A modifier may not be specified in a LINEAR clause "
"on the DO or SIMD directive"_err_en_US);
}
}
}
void OmpStructureChecker::Enter(const parser::OmpMapClause &) {
CheckAllowed(OmpClause::MAP);
@ -246,7 +294,52 @@ void OmpStructureChecker::Enter(const parser::OmpProcBindClause &) {
void OmpStructureChecker::Enter(const parser::OmpReductionClause &) {
CheckAllowed(OmpClause::REDUCTION);
}
void OmpStructureChecker::Enter(const parser::OmpScheduleClause &) {
bool OmpStructureChecker::ScheduleModifierHasType(
const parser::OmpScheduleClause &x,
const parser::OmpScheduleModifierType::ModType &type) {
const auto &modifier{
std::get<std::optional<parser::OmpScheduleModifier>>(x.t)};
if (modifier.has_value()) {
const auto &modType1{
std::get<parser::OmpScheduleModifier::Modifier1>(modifier->t)};
const auto &modType2{
std::get<std::optional<parser::OmpScheduleModifier::Modifier2>>(
modifier->t)};
if (modType1.v.v == type ||
(modType2.has_value() && modType2->v.v == type)) {
return true;
}
}
return false;
}
void OmpStructureChecker::Enter(const parser::OmpScheduleClause &x) {
CheckAllowed(OmpClause::SCHEDULE);
// 2.7 Loop Construct Restriction
if (GetContext().directive == OmpDirective::DO) {
const auto &kind{std::get<1>(x.t)};
const auto &chunk{std::get<2>(x.t)};
if (chunk.has_value()) {
if (kind == parser::OmpScheduleClause::ScheduleType::Runtime ||
kind == parser::OmpScheduleClause::ScheduleType::Auto) {
context_.Say(GetContext().clauseSource,
"When SCHEDULE clause has %s specified, "
"it must not have chunk size specified"_err_en_US,
parser::ToUpperCaseLetters(
parser::OmpScheduleClause::EnumToString(kind)));
}
}
if (ScheduleModifierHasType(
x, parser::OmpScheduleModifierType::ModType::Nonmonotonic)) {
if (kind != parser::OmpScheduleClause::ScheduleType::Dynamic &&
kind != parser::OmpScheduleClause::ScheduleType::Guided) {
context_.Say(GetContext().clauseSource,
"The NONMONOTONIC modifier can only be specified with "
"SCHEDULE(DYNAMIC) or SCHEDULE(GUIDED)"_err_en_US);
}
}
}
}
}

View File

@ -63,6 +63,7 @@ public:
void Leave(const parser::OpenMPBlockConstruct &);
void Enter(const parser::OmpBlockDirective::Parallel &);
void Leave(const parser::OmpClauseList &);
void Enter(const parser::OmpClause &);
void Enter(const parser::OmpClause::Defaultmap &);
void Enter(const parser::OmpClause::Inbranch &);
@ -113,15 +114,18 @@ private:
OmpDirective directive;
OmpClauseSet allowedClauses;
OmpClauseSet allowedOnceClauses;
OmpClauseSet seenClauses;
const parser::OmpClause *clause{nullptr};
std::multimap<OmpClause, const parser::OmpClause *> clauseInfo;
};
// back() is the top of the stack
const OmpContext &GetContext() const { return ompContext_.back(); }
void SetContextDirectiveSource(const parser::CharBlock &directive) {
ompContext_.back().directiveSource = directive;
}
void SetContextClauseSource(const parser::CharBlock &clause) {
ompContext_.back().clauseSource = clause;
void SetContextClause(const parser::OmpClause &clause) {
ompContext_.back().clauseSource = clause.source;
ompContext_.back().clause = &clause;
}
void SetContextDirectiveEnum(const OmpDirective &dir) {
ompContext_.back().directive = dir;
@ -132,8 +136,15 @@ private:
void SetContextAllowedOnce(const OmpClauseSet &allowedOnce) {
ompContext_.back().allowedOnceClauses = allowedOnce;
}
void SetContextSeen(const OmpClause &seenType) {
ompContext_.back().seenClauses.set(seenType);
void SetContextClauseInfo(const OmpClause &type) {
ompContext_.back().clauseInfo.emplace(type, ompContext_.back().clause);
}
const parser::OmpClause *FindClause(const OmpClause &type) {
auto it{GetContext().clauseInfo.find(type)};
if (it != GetContext().clauseInfo.end()) {
return it->second;
}
return nullptr;
}
bool CurrentDirectiveIsNested() { return ompContext_.size() > 0; };
@ -141,6 +152,10 @@ private:
const parser::CharBlock &, const OmpDirectiveSet &);
void CheckAllowed(const OmpClause &);
// specific clause related
bool ScheduleModifierHasType(const parser::OmpScheduleClause &,
const parser::OmpScheduleModifierType::ModType &);
SemanticsContext &context_;
std::vector<OmpContext> ompContext_; // used as a stack
};

View File

@ -17,10 +17,13 @@
! Check OpenMP clause validity for the following directives:
!
! 2.5 PARALLEL construct
! 2.7.1 Loop construct
! ...
! 2.5 parallel -> PARALLEL [parallel-clause[ [,] parallel-clause]...]
! parallel-clause -> if-clause |
integer :: b = 128
N = 1024
! 2.5 parallel-clause -> if-clause |
! num-threads-clause |
! default-clause |
! private-clause |
@ -30,7 +33,6 @@
! reduction-clause |
! proc-bind-clause
N = 1024
!$omp parallel
do i = 1, N
a = 3.14
@ -73,4 +75,50 @@
a = 3.14
enddo
!$omp end parallel
! 2.7.1 do-clause -> private-clause |
! firstprivate-clause |
! lastprivate-clause |
! linear-clause |
! reduction-clause |
! schedule-clause |
! collapse-clause |
! ordered-clause
! TODO: all the internal errors
!ERROR: When SCHEDULE clause has AUTO specified, it must not have chunk size specified
!ERROR: At most one SCHEDULE clause can appear on the DO directive
!ERROR: When SCHEDULE clause has RUNTIME specified, it must not have chunk size specified
!$omp do schedule(auto, 2) schedule(runtime, 2)
do i = 1, N
a = 3.14
enddo
!ERROR: A modifier may not be specified in a LINEAR clause on the DO or SIMD directive
!ERROR: Internal: no symbol found for 'b'
!$omp do linear(ref(b))
do i = 1, N
a = 3.14
enddo
!ERROR: The NONMONOTONIC modifier can only be specified with SCHEDULE(DYNAMIC) or SCHEDULE(GUIDED)
!ERROR: The NONMONOTONIC modifier cannot be specified if an ORDERED clause is specified
!$omp do schedule(NONMONOTONIC:static) ordered
do i = 1, N
a = 3.14
enddo
!$omp do schedule(simd, monotonic:dynamic)
do i = 1, N
a = 3.14
enddo
!ERROR: A loop directive may not have both a LINEAR clause and an ORDERED clause with a parameter
!ERROR: Internal: no symbol found for 'b'
!ERROR: Internal: no symbol found for 'a'
!$omp do ordered(1) private(b) linear(b) linear(a)
do i = 1, N
a = 3.14
enddo
end