-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[Flang][OpenMP][Sema] Adding parsing and semantic support for scan directive. #102792
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ee14b03
615a759
a0480ca
9b8dbd9
865ca2e
6409cd9
b10a198
9facbf7
8464253
4b644ff
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -12,6 +12,7 @@ | |||||||
#include "flang/Parser/parse-tree.h" | ||||||||
#include "flang/Semantics/expression.h" | ||||||||
#include "flang/Semantics/tools.h" | ||||||||
#include <variant> | ||||||||
|
||||||||
namespace Fortran::semantics { | ||||||||
|
||||||||
|
@@ -747,62 +748,69 @@ void OmpStructureChecker::CheckSIMDNest(const parser::OpenMPConstruct &c) { | |||||||
// current context yet. | ||||||||
// TODO: Check for declare simd regions. | ||||||||
bool eligibleSIMD{false}; | ||||||||
common::visit(Fortran::common::visitors{ | ||||||||
// Allow `!$OMP ORDERED SIMD` | ||||||||
[&](const parser::OpenMPBlockConstruct &c) { | ||||||||
const auto &beginBlockDir{ | ||||||||
std::get<parser::OmpBeginBlockDirective>(c.t)}; | ||||||||
const auto &beginDir{ | ||||||||
std::get<parser::OmpBlockDirective>(beginBlockDir.t)}; | ||||||||
if (beginDir.v == llvm::omp::Directive::OMPD_ordered) { | ||||||||
const auto &clauses{ | ||||||||
std::get<parser::OmpClauseList>(beginBlockDir.t)}; | ||||||||
for (const auto &clause : clauses.v) { | ||||||||
if (std::get_if<parser::OmpClause::Simd>(&clause.u)) { | ||||||||
eligibleSIMD = true; | ||||||||
break; | ||||||||
} | ||||||||
} | ||||||||
} | ||||||||
}, | ||||||||
[&](const parser::OpenMPSimpleStandaloneConstruct &c) { | ||||||||
const auto &dir{ | ||||||||
std::get<parser::OmpSimpleStandaloneDirective>(c.t)}; | ||||||||
if (dir.v == llvm::omp::Directive::OMPD_ordered) { | ||||||||
const auto &clauses{ | ||||||||
std::get<parser::OmpClauseList>(c.t)}; | ||||||||
for (const auto &clause : clauses.v) { | ||||||||
if (std::get_if<parser::OmpClause::Simd>(&clause.u)) { | ||||||||
eligibleSIMD = true; | ||||||||
break; | ||||||||
} | ||||||||
} | ||||||||
} | ||||||||
}, | ||||||||
// Allowing SIMD construct | ||||||||
[&](const parser::OpenMPLoopConstruct &c) { | ||||||||
const auto &beginLoopDir{ | ||||||||
std::get<parser::OmpBeginLoopDirective>(c.t)}; | ||||||||
const auto &beginDir{ | ||||||||
std::get<parser::OmpLoopDirective>(beginLoopDir.t)}; | ||||||||
if ((beginDir.v == llvm::omp::Directive::OMPD_simd) || | ||||||||
(beginDir.v == llvm::omp::Directive::OMPD_do_simd)) { | ||||||||
eligibleSIMD = true; | ||||||||
} | ||||||||
}, | ||||||||
[&](const parser::OpenMPAtomicConstruct &c) { | ||||||||
// Allow `!$OMP ATOMIC` | ||||||||
eligibleSIMD = true; | ||||||||
}, | ||||||||
[&](const auto &c) {}, | ||||||||
}, | ||||||||
common::visit( | ||||||||
Fortran::common::visitors{ | ||||||||
// Allow `!$OMP ORDERED SIMD` | ||||||||
[&](const parser::OpenMPBlockConstruct &c) { | ||||||||
const auto &beginBlockDir{ | ||||||||
std::get<parser::OmpBeginBlockDirective>(c.t)}; | ||||||||
const auto &beginDir{ | ||||||||
std::get<parser::OmpBlockDirective>(beginBlockDir.t)}; | ||||||||
if (beginDir.v == llvm::omp::Directive::OMPD_ordered) { | ||||||||
const auto &clauses{ | ||||||||
std::get<parser::OmpClauseList>(beginBlockDir.t)}; | ||||||||
for (const auto &clause : clauses.v) { | ||||||||
if (std::get_if<parser::OmpClause::Simd>(&clause.u)) { | ||||||||
eligibleSIMD = true; | ||||||||
break; | ||||||||
} | ||||||||
} | ||||||||
} | ||||||||
}, | ||||||||
[&](const parser::OpenMPStandaloneConstruct &c) { | ||||||||
if (const auto &simpleConstruct = | ||||||||
std::get_if<parser::OpenMPSimpleStandaloneConstruct>( | ||||||||
&c.u)) { | ||||||||
const auto &dir{std::get<parser::OmpSimpleStandaloneDirective>( | ||||||||
simpleConstruct->t)}; | ||||||||
if (dir.v == llvm::omp::Directive::OMPD_ordered) { | ||||||||
const auto &clauses{ | ||||||||
std::get<parser::OmpClauseList>(simpleConstruct->t)}; | ||||||||
for (const auto &clause : clauses.v) { | ||||||||
if (std::get_if<parser::OmpClause::Simd>(&clause.u)) { | ||||||||
eligibleSIMD = true; | ||||||||
break; | ||||||||
} | ||||||||
} | ||||||||
} else if (dir.v == llvm::omp::Directive::OMPD_scan) { | ||||||||
eligibleSIMD = true; | ||||||||
} | ||||||||
} | ||||||||
}, | ||||||||
// Allowing SIMD construct | ||||||||
[&](const parser::OpenMPLoopConstruct &c) { | ||||||||
const auto &beginLoopDir{ | ||||||||
std::get<parser::OmpBeginLoopDirective>(c.t)}; | ||||||||
const auto &beginDir{ | ||||||||
std::get<parser::OmpLoopDirective>(beginLoopDir.t)}; | ||||||||
if ((beginDir.v == llvm::omp::Directive::OMPD_simd) || | ||||||||
(beginDir.v == llvm::omp::Directive::OMPD_do_simd)) { | ||||||||
eligibleSIMD = true; | ||||||||
} | ||||||||
}, | ||||||||
[&](const parser::OpenMPAtomicConstruct &c) { | ||||||||
// Allow `!$OMP ATOMIC` | ||||||||
eligibleSIMD = true; | ||||||||
}, | ||||||||
[&](const auto &c) {}, | ||||||||
}, | ||||||||
c.u); | ||||||||
if (!eligibleSIMD) { | ||||||||
context_.Say(parser::FindSourceLocation(c), | ||||||||
"The only OpenMP constructs that can be encountered during execution " | ||||||||
"of a 'SIMD' region are the `ATOMIC` construct, the `LOOP` construct, " | ||||||||
"the `SIMD` construct and the `ORDERED` construct with the `SIMD` " | ||||||||
"clause."_err_en_US); | ||||||||
"the `SIMD` construct, the `SCAN` construct and the `ORDERED` " | ||||||||
"construct with the `SIMD` clause."_err_en_US); | ||||||||
} | ||||||||
} | ||||||||
|
||||||||
|
@@ -966,6 +974,49 @@ void OmpStructureChecker::CheckDistLinear( | |||||||
} | ||||||||
|
||||||||
void OmpStructureChecker::Leave(const parser::OpenMPLoopConstruct &x) { | ||||||||
const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)}; | ||||||||
const auto &clauseList{std::get<parser::OmpClauseList>(beginLoopDir.t)}; | ||||||||
|
||||||||
// A few semantic checks for InScan reduction are performed below as SCAN | ||||||||
// constructs inside LOOP may add the relevant information. Scan reduction is | ||||||||
// supported only in loop constructs, so same checks are not applicable to | ||||||||
// other directives. | ||||||||
for (const auto &clause : clauseList.v) { | ||||||||
if (const auto *reductionClause{ | ||||||||
std::get_if<parser::OmpClause::Reduction>(&clause.u)}) { | ||||||||
const auto &maybeModifier{ | ||||||||
std::get<std::optional<ReductionModifier>>(reductionClause->v.t)}; | ||||||||
if (maybeModifier && *maybeModifier == ReductionModifier::Inscan) { | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: Reduce the nesting level to make code easier to follow.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Generally frontend code in flang is more nested. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I prefer to go with the existing approach as personally I feel it more readable. Please let me know if there are suggestions otherwise. |
||||||||
const auto &objectList{ | ||||||||
std::get<parser::OmpObjectList>(reductionClause->v.t)}; | ||||||||
auto checkReductionSymbolInScan = [&](const parser::Name *name) { | ||||||||
if (auto &symbol = name->symbol) { | ||||||||
if (!symbol->test(Symbol::Flag::OmpInclusiveScan) && | ||||||||
!symbol->test(Symbol::Flag::OmpExclusiveScan)) { | ||||||||
context_.Say(name->source, | ||||||||
"List item %s must appear in EXCLUSIVE or " | ||||||||
"INCLUSIVE clause of an " | ||||||||
"enclosed SCAN directive"_err_en_US, | ||||||||
name->ToString()); | ||||||||
} | ||||||||
} | ||||||||
}; | ||||||||
for (const auto &ompObj : objectList.v) { | ||||||||
common::visit( | ||||||||
common::visitors{ | ||||||||
[&](const parser::Designator &designator) { | ||||||||
if (const auto *name{semantics::getDesignatorNameIfDataRef( | ||||||||
designator)}) { | ||||||||
checkReductionSymbolInScan(name); | ||||||||
} | ||||||||
}, | ||||||||
[&](const auto &name) { checkReductionSymbolInScan(&name); }, | ||||||||
}, | ||||||||
ompObj.u); | ||||||||
} | ||||||||
} | ||||||||
} | ||||||||
} | ||||||||
if (llvm::omp::allSimdSet.test(GetContext().directive)) { | ||||||||
ExitDirectiveNest(SIMDNest); | ||||||||
} | ||||||||
|
@@ -1638,19 +1689,32 @@ void OmpStructureChecker::Leave(const parser::OpenMPAllocatorsConstruct &x) { | |||||||
dirContext_.pop_back(); | ||||||||
} | ||||||||
|
||||||||
void OmpStructureChecker::CheckScan( | ||||||||
const parser::OpenMPSimpleStandaloneConstruct &x) { | ||||||||
if (std::get<parser::OmpClauseList>(x.t).v.size() != 1) { | ||||||||
context_.Say(x.source, | ||||||||
"Exactly one of EXCLUSIVE or INCLUSIVE clause is expected"_err_en_US); | ||||||||
} | ||||||||
if (!CurrentDirectiveIsNested() || | ||||||||
!llvm::omp::scanParentAllowedSet.test(GetContextParent().directive)) { | ||||||||
context_.Say(x.source, | ||||||||
"Orphaned SCAN directives are prohibited; perhaps you forgot " | ||||||||
"to enclose the directive in to a WORKSHARING LOOP, a WORKSHARING " | ||||||||
"LOOP SIMD or a SIMD directive."_err_en_US); | ||||||||
} | ||||||||
} | ||||||||
|
||||||||
void OmpStructureChecker::CheckBarrierNesting( | ||||||||
const parser::OpenMPSimpleStandaloneConstruct &x) { | ||||||||
// A barrier region may not be `closely nested` inside a worksharing, loop, | ||||||||
// task, taskloop, critical, ordered, atomic, or master region. | ||||||||
// TODO: Expand the check to include `LOOP` construct as well when it is | ||||||||
// supported. | ||||||||
if (GetContext().directive == llvm::omp::Directive::OMPD_barrier) { | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Was this check for barrier not required? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||||
if (IsCloselyNestedRegion(llvm::omp::nestedBarrierErrSet)) { | ||||||||
context_.Say(parser::FindSourceLocation(x), | ||||||||
"`BARRIER` region may not be closely nested inside of `WORKSHARING`, " | ||||||||
"`LOOP`, `TASK`, `TASKLOOP`," | ||||||||
"`CRITICAL`, `ORDERED`, `ATOMIC` or `MASTER` region."_err_en_US); | ||||||||
} | ||||||||
if (IsCloselyNestedRegion(llvm::omp::nestedBarrierErrSet)) { | ||||||||
context_.Say(parser::FindSourceLocation(x), | ||||||||
"`BARRIER` region may not be closely nested inside of `WORKSHARING`, " | ||||||||
"`LOOP`, `TASK`, `TASKLOOP`," | ||||||||
"`CRITICAL`, `ORDERED`, `ATOMIC` or `MASTER` region."_err_en_US); | ||||||||
} | ||||||||
} | ||||||||
|
||||||||
|
@@ -1834,7 +1898,16 @@ void OmpStructureChecker::Enter( | |||||||
const parser::OpenMPSimpleStandaloneConstruct &x) { | ||||||||
const auto &dir{std::get<parser::OmpSimpleStandaloneDirective>(x.t)}; | ||||||||
PushContextAndClauseSets(dir.source, dir.v); | ||||||||
CheckBarrierNesting(x); | ||||||||
switch (dir.v) { | ||||||||
case llvm::omp::Directive::OMPD_barrier: | ||||||||
CheckBarrierNesting(x); | ||||||||
break; | ||||||||
case llvm::omp::Directive::OMPD_scan: | ||||||||
CheckScan(x); | ||||||||
break; | ||||||||
default: | ||||||||
break; | ||||||||
} | ||||||||
} | ||||||||
|
||||||||
void OmpStructureChecker::Leave( | ||||||||
|
@@ -2673,8 +2746,8 @@ CHECK_SIMPLE_CLAUSE(Full, OMPC_full) | |||||||
CHECK_SIMPLE_CLAUSE(Grainsize, OMPC_grainsize) | ||||||||
CHECK_SIMPLE_CLAUSE(Hint, OMPC_hint) | ||||||||
CHECK_SIMPLE_CLAUSE(Holds, OMPC_holds) | ||||||||
CHECK_SIMPLE_CLAUSE(InReduction, OMPC_in_reduction) | ||||||||
CHECK_SIMPLE_CLAUSE(Inclusive, OMPC_inclusive) | ||||||||
CHECK_SIMPLE_CLAUSE(InReduction, OMPC_in_reduction) | ||||||||
CHECK_SIMPLE_CLAUSE(Match, OMPC_match) | ||||||||
CHECK_SIMPLE_CLAUSE(Nontemporal, OMPC_nontemporal) | ||||||||
CHECK_SIMPLE_CLAUSE(NumTasks, OMPC_num_tasks) | ||||||||
|
@@ -2767,7 +2840,11 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Reduction &x) { | |||||||
if (CheckReductionOperators(x)) { | ||||||||
CheckReductionTypeList(x); | ||||||||
} | ||||||||
CheckReductionModifier(x); | ||||||||
if (const auto &maybeModifier{ | ||||||||
std::get<std::optional<ReductionModifier>>(x.v.t)}) { | ||||||||
const ReductionModifier modifier{*maybeModifier}; | ||||||||
CheckReductionModifier(modifier); | ||||||||
} | ||||||||
} | ||||||||
|
||||||||
bool OmpStructureChecker::CheckReductionOperators( | ||||||||
|
@@ -2810,6 +2887,7 @@ bool OmpStructureChecker::CheckReductionOperators( | |||||||
|
||||||||
return ok; | ||||||||
} | ||||||||
|
||||||||
bool OmpStructureChecker::CheckIntrinsicOperator( | ||||||||
const parser::DefinedOperator::IntrinsicOperator &op) { | ||||||||
|
||||||||
|
@@ -2944,14 +3022,11 @@ void OmpStructureChecker::CheckReductionTypeList( | |||||||
} | ||||||||
|
||||||||
void OmpStructureChecker::CheckReductionModifier( | ||||||||
const parser::OmpClause::Reduction &x) { | ||||||||
using ReductionModifier = parser::OmpReductionClause::ReductionModifier; | ||||||||
const auto &maybeModifier{std::get<std::optional<ReductionModifier>>(x.v.t)}; | ||||||||
if (!maybeModifier || *maybeModifier == ReductionModifier::Default) { | ||||||||
// No modifier, or the default one is always ok. | ||||||||
const ReductionModifier &modifier) { | ||||||||
if (modifier == ReductionModifier::Default) { | ||||||||
// The default one is always ok. | ||||||||
return; | ||||||||
} | ||||||||
ReductionModifier modifier{*maybeModifier}; | ||||||||
const DirectiveContext &dirCtx{GetContext()}; | ||||||||
if (dirCtx.directive == llvm::omp::Directive::OMPD_loop) { | ||||||||
// [5.2:257:33-34] | ||||||||
|
@@ -2982,15 +3057,10 @@ void OmpStructureChecker::CheckReductionModifier( | |||||||
// or "simd" directive. | ||||||||
// The worksharing-loop directives are OMPD_do and OMPD_for. Only the | ||||||||
// former is allowed in Fortran. | ||||||||
switch (dirCtx.directive) { | ||||||||
case llvm::omp::Directive::OMPD_do: // worksharing-loop | ||||||||
case llvm::omp::Directive::OMPD_do_simd: // worksharing-loop simd | ||||||||
case llvm::omp::Directive::OMPD_simd: // "simd" | ||||||||
break; | ||||||||
default: | ||||||||
if (!llvm::omp::scanParentAllowedSet.test(dirCtx.directive)) { | ||||||||
context_.Say(GetContext().clauseSource, | ||||||||
"Modifier 'INSCAN' on REDUCTION clause is only allowed with " | ||||||||
"worksharing-loop, worksharing-loop simd, " | ||||||||
"WORKSHARING LOOP, WORKSHARING LOOP SIMD, " | ||||||||
"or SIMD directive"_err_en_US); | ||||||||
} | ||||||||
} else { | ||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This an the lines below are pure formatting changes. Can you please do those after the review?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you! Replied here. I am unable to remove them at the moment. Please let me know your suggestion.