forked from OSchip/llvm-project
[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:
parent
ebf5b43e9d
commit
1b0be3e0c9
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue