summaryrefslogtreecommitdiff
path: root/flang/lib/Semantics/check-omp-structure.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'flang/lib/Semantics/check-omp-structure.cpp')
-rw-r--r--flang/lib/Semantics/check-omp-structure.cpp680
1 files changed, 482 insertions, 198 deletions
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index c39daef6b0ea..9c1c614654b5 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -15,6 +15,7 @@
#include "flang/Common/idioms.h"
#include "flang/Common/indirection.h"
#include "flang/Common/visit.h"
+#include "flang/Evaluate/fold.h"
#include "flang/Evaluate/tools.h"
#include "flang/Evaluate/type.h"
#include "flang/Parser/char-block.h"
@@ -269,7 +270,8 @@ bool OmpStructureChecker::CheckAllowedClause(llvmOmpClause clause) {
}
void OmpStructureChecker::AnalyzeObject(const parser::OmpObject &object) {
- if (std::holds_alternative<parser::Name>(object.u)) {
+ if (std::holds_alternative<parser::Name>(object.u) ||
+ std::holds_alternative<parser::OmpObject::Invalid>(object.u)) {
// Do not analyze common block names. The analyzer will flag an error
// on those.
return;
@@ -294,7 +296,12 @@ void OmpStructureChecker::AnalyzeObject(const parser::OmpObject &object) {
}
evaluate::ExpressionAnalyzer ea{context_};
auto restore{ea.AllowWholeAssumedSizeArray(true)};
- common::visit([&](auto &&s) { ea.Analyze(s); }, object.u);
+ common::visit( //
+ common::visitors{
+ [&](auto &&s) { ea.Analyze(s); },
+ [&](const parser::OmpObject::Invalid &invalid) {},
+ },
+ object.u);
}
void OmpStructureChecker::AnalyzeObjects(const parser::OmpObjectList &objects) {
@@ -538,6 +545,7 @@ void OmpStructureChecker::CheckPredefinedAllocatorRestriction(
[&](const parser::Name &name) {
CheckPredefinedAllocatorRestriction(source, name);
},
+ [&](const parser::OmpObject::Invalid &invalid) {},
},
ompObject.u);
}
@@ -604,14 +612,6 @@ template <typename Checker> struct DirectiveSpellingVisitor {
checker_(GetDirName(x.t).source, Directive::OMPD_dispatch);
return false;
}
- bool Pre(const parser::OmpErrorDirective &x) {
- checker_(std::get<parser::Verbatim>(x.t).source, Directive::OMPD_error);
- return false;
- }
- bool Pre(const parser::OmpNothingDirective &x) {
- checker_(x.source, Directive::OMPD_nothing);
- return false;
- }
bool Pre(const parser::OpenMPExecutableAllocate &x) {
checker_(std::get<parser::Verbatim>(x.t).source, Directive::OMPD_allocate);
return false;
@@ -620,49 +620,14 @@ template <typename Checker> struct DirectiveSpellingVisitor {
checker_(GetDirName(x.t).source, Directive::OMPD_allocators);
return false;
}
- bool Pre(const parser::OmpMetadirectiveDirective &x) {
- checker_(
- std::get<parser::Verbatim>(x.t).source, Directive::OMPD_metadirective);
- return false;
- }
bool Pre(const parser::OpenMPDeclarativeAssumes &x) {
checker_(std::get<parser::Verbatim>(x.t).source, Directive::OMPD_assumes);
return false;
}
- bool Pre(const parser::OpenMPDeclareMapperConstruct &x) {
- checker_(
- std::get<parser::Verbatim>(x.t).source, Directive::OMPD_declare_mapper);
- return false;
- }
- bool Pre(const parser::OpenMPDeclareReductionConstruct &x) {
- checker_(std::get<parser::Verbatim>(x.t).source,
- Directive::OMPD_declare_reduction);
- return false;
- }
- bool Pre(const parser::OpenMPDeclareSimdConstruct &x) {
- checker_(
- std::get<parser::Verbatim>(x.t).source, Directive::OMPD_declare_simd);
- return false;
- }
- bool Pre(const parser::OpenMPDeclareTargetConstruct &x) {
- checker_(
- std::get<parser::Verbatim>(x.t).source, Directive::OMPD_declare_target);
- return false;
- }
- bool Pre(const parser::OmpDeclareVariantDirective &x) {
- checker_(std::get<parser::Verbatim>(x.t).source,
- Directive::OMPD_declare_variant);
- return false;
- }
bool Pre(const parser::OpenMPGroupprivate &x) {
checker_(x.v.DirName().source, Directive::OMPD_groupprivate);
return false;
}
- bool Pre(const parser::OpenMPThreadprivate &x) {
- checker_(
- std::get<parser::Verbatim>(x.t).source, Directive::OMPD_threadprivate);
- return false;
- }
bool Pre(const parser::OpenMPRequiresConstruct &x) {
checker_(std::get<parser::Verbatim>(x.t).source, Directive::OMPD_requires);
return false;
@@ -1057,7 +1022,10 @@ void OmpStructureChecker::Leave(const parser::OpenMPDeclarativeAssumes &) {
dirContext_.pop_back();
}
-void OmpStructureChecker::Leave(const parser::OmpBlockConstruct &) {
+void OmpStructureChecker::Leave(const parser::OmpBlockConstruct &x) {
+ if (GetContext().directive == llvm::omp::Directive::OMPD_taskgraph) {
+ CheckTaskgraph(x);
+ }
if (GetDirectiveNest(TargetBlockOnlyTeams)) {
ExitDirectiveNest(TargetBlockOnlyTeams);
}
@@ -1288,10 +1256,19 @@ void OmpStructureChecker::CheckThreadprivateOrDeclareTargetVar(
}
void OmpStructureChecker::CheckThreadprivateOrDeclareTargetVar(
+ const parser::OmpObject &object) {
+ common::visit( //
+ common::visitors{
+ [&](auto &&s) { CheckThreadprivateOrDeclareTargetVar(s); },
+ [&](const parser::OmpObject::Invalid &invalid) {},
+ },
+ object.u);
+}
+
+void OmpStructureChecker::CheckThreadprivateOrDeclareTargetVar(
const parser::OmpObjectList &objList) {
for (const auto &ompObject : objList.v) {
- common::visit([&](auto &&s) { CheckThreadprivateOrDeclareTargetVar(s); },
- ompObject.u);
+ CheckThreadprivateOrDeclareTargetVar(ompObject);
}
}
@@ -1351,24 +1328,50 @@ void OmpStructureChecker::Leave(const parser::OpenMPGroupprivate &x) {
dirContext_.pop_back();
}
-void OmpStructureChecker::Enter(const parser::OpenMPThreadprivate &c) {
- const auto &dir{std::get<parser::Verbatim>(c.t)};
- PushContextAndClauseSets(
- dir.source, llvm::omp::Directive::OMPD_threadprivate);
+void OmpStructureChecker::Enter(const parser::OpenMPThreadprivate &x) {
+ const parser::OmpDirectiveName &dirName{x.v.DirName()};
+ PushContextAndClauseSets(dirName.source, dirName.v);
}
-void OmpStructureChecker::Leave(const parser::OpenMPThreadprivate &c) {
- const auto &dir{std::get<parser::Verbatim>(c.t)};
- const auto &objectList{std::get<parser::OmpObjectList>(c.t)};
- CheckSymbolNames(dir.source, objectList);
- CheckVarIsNotPartOfAnotherVar(dir.source, objectList);
- CheckThreadprivateOrDeclareTargetVar(objectList);
+void OmpStructureChecker::Leave(const parser::OpenMPThreadprivate &x) {
+ const parser::OmpDirectiveSpecification &dirSpec{x.v};
+ for (const parser::OmpArgument &arg : x.v.Arguments().v) {
+ if (auto *object{GetArgumentObject(arg)}) {
+ CheckSymbolName(dirSpec.source, *object);
+ CheckVarIsNotPartOfAnotherVar(dirSpec.source, *object);
+ CheckThreadprivateOrDeclareTargetVar(*object);
+ }
+ }
dirContext_.pop_back();
}
void OmpStructureChecker::Enter(const parser::OpenMPDeclareSimdConstruct &x) {
- const auto &dir{std::get<parser::Verbatim>(x.t)};
- PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_declare_simd);
+ const parser::OmpDirectiveName &dirName{x.v.DirName()};
+ PushContextAndClauseSets(dirName.source, dirName.v);
+
+ const parser::OmpArgumentList &args{x.v.Arguments()};
+ if (args.v.empty()) {
+ return;
+ } else if (args.v.size() > 1) {
+ context_.Say(args.source,
+ "DECLARE_SIMD directive should have at most one argument"_err_en_US);
+ return;
+ }
+
+ const parser::OmpArgument &arg{args.v.front()};
+ if (auto *sym{GetArgumentSymbol(arg)}) {
+ if (!IsProcedure(*sym) && !IsFunction(*sym)) {
+ auto &msg{context_.Say(arg.source,
+ "The name '%s' should refer to a procedure"_err_en_US, sym->name())};
+ if (sym->test(Symbol::Flag::Implicit)) {
+ msg.Attach(arg.source,
+ "The name '%s' has been implicitly declared"_en_US, sym->name());
+ }
+ }
+ } else {
+ context_.Say(arg.source,
+ "The argument to the DECLARE_SIMD directive should be a procedure name"_err_en_US);
+ }
}
void OmpStructureChecker::Leave(const parser::OpenMPDeclareSimdConstruct &) {
@@ -1376,9 +1379,50 @@ void OmpStructureChecker::Leave(const parser::OpenMPDeclareSimdConstruct &) {
}
void OmpStructureChecker::Enter(const parser::OmpDeclareVariantDirective &x) {
- const auto &dir{std::get<parser::Verbatim>(x.t)};
- PushContextAndClauseSets(
- dir.source, llvm::omp::Directive::OMPD_declare_variant);
+ const parser::OmpDirectiveName &dirName{x.v.DirName()};
+ PushContextAndClauseSets(dirName.source, dirName.v);
+
+ const parser::OmpArgumentList &args{x.v.Arguments()};
+ if (args.v.size() != 1) {
+ context_.Say(args.source,
+ "DECLARE_VARIANT directive should have a single argument"_err_en_US);
+ return;
+ }
+
+ auto InvalidArgument{[&](parser::CharBlock source) {
+ context_.Say(source,
+ "The argument to the DECLARE_VARIANT directive should be [base-name:]variant-name"_err_en_US);
+ }};
+
+ auto CheckSymbol{[&](const Symbol *sym, parser::CharBlock source) {
+ if (sym) {
+ if (!IsProcedure(*sym) && !IsFunction(*sym)) {
+ auto &msg{context_.Say(source,
+ "The name '%s' should refer to a procedure"_err_en_US,
+ sym->name())};
+ if (sym->test(Symbol::Flag::Implicit)) {
+ msg.Attach(source, "The name '%s' has been implicitly declared"_en_US,
+ sym->name());
+ }
+ }
+ } else {
+ InvalidArgument(source);
+ }
+ }};
+
+ const parser::OmpArgument &arg{args.v.front()};
+ common::visit( //
+ common::visitors{
+ [&](const parser::OmpBaseVariantNames &y) {
+ CheckSymbol(GetObjectSymbol(std::get<0>(y.t)), arg.source);
+ CheckSymbol(GetObjectSymbol(std::get<1>(y.t)), arg.source);
+ },
+ [&](const parser::OmpLocator &y) {
+ CheckSymbol(GetArgumentSymbol(arg), arg.source);
+ },
+ [&](auto &&y) { InvalidArgument(arg.source); },
+ },
+ arg.u);
}
void OmpStructureChecker::Leave(const parser::OmpDeclareVariantDirective &) {
@@ -1422,8 +1466,14 @@ void OmpStructureChecker::Enter(const parser::OpenMPDepobjConstruct &x) {
// refer to the same depend object as the depobj argument of the construct.
if (clause.Id() == llvm::omp::Clause::OMPC_destroy) {
auto getObjSymbol{[&](const parser::OmpObject &obj) {
- return common::visit(
- [&](auto &&s) { return GetLastName(s).symbol; }, obj.u);
+ return common::visit( //
+ common::visitors{
+ [&](auto &&s) { return GetLastName(s).symbol; },
+ [&](const parser::OmpObject::Invalid &invalid) {
+ return static_cast<Symbol *>(nullptr);
+ },
+ },
+ obj.u);
}};
auto getArgSymbol{[&](const parser::OmpArgument &arg) {
if (auto *locator{std::get_if<parser::OmpLocator>(&arg.u)}) {
@@ -1438,9 +1488,7 @@ void OmpStructureChecker::Enter(const parser::OpenMPDepobjConstruct &x) {
if (const std::optional<parser::OmpDestroyClause> &destroy{wrapper.v}) {
const Symbol *constrSym{getArgSymbol(arguments.v.front())};
const Symbol *clauseSym{getObjSymbol(destroy->v)};
- assert(constrSym && "Unresolved depobj construct symbol");
- assert(clauseSym && "Unresolved destroy symbol on depobj construct");
- if (constrSym != clauseSym) {
+ if (constrSym && clauseSym && constrSym != clauseSym) {
context_.Say(x.source,
"The DESTROY clause must refer to the same object as the "
"DEPOBJ construct"_err_en_US);
@@ -1562,46 +1610,26 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Allocate &x) {
}
}
-void OmpStructureChecker::Enter(const parser::OmpDeclareTargetWithClause &x) {
- SetClauseSets(llvm::omp::Directive::OMPD_declare_target);
-}
+void OmpStructureChecker::Enter(const parser::OpenMPDeclareMapperConstruct &x) {
+ const parser::OmpDirectiveName &dirName{x.v.DirName()};
+ PushContextAndClauseSets(dirName.source, dirName.v);
-void OmpStructureChecker::Leave(const parser::OmpDeclareTargetWithClause &x) {
- if (x.v.v.size() > 0) {
- const parser::OmpClause *enterClause =
- FindClause(llvm::omp::Clause::OMPC_enter);
- const parser::OmpClause *toClause = FindClause(llvm::omp::Clause::OMPC_to);
- const parser::OmpClause *linkClause =
- FindClause(llvm::omp::Clause::OMPC_link);
- const parser::OmpClause *indirectClause =
- FindClause(llvm::omp::Clause::OMPC_indirect);
- if (!enterClause && !toClause && !linkClause) {
- context_.Say(x.source,
- "If the DECLARE TARGET directive has a clause, it must contain at least one ENTER clause or LINK clause"_err_en_US);
- }
- if (indirectClause && !enterClause) {
- context_.Say(x.source,
- "The INDIRECT clause cannot be used without the ENTER clause with the DECLARE TARGET directive."_err_en_US);
- }
- unsigned version{context_.langOptions().OpenMPVersion};
- if (toClause && version >= 52) {
- context_.Warn(common::UsageWarning::OpenMPUsage, toClause->source,
- "The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead."_warn_en_US);
- }
- if (indirectClause) {
- CheckAllowedClause(llvm::omp::Clause::OMPC_indirect);
- }
+ const parser::OmpArgumentList &args{x.v.Arguments()};
+ if (args.v.size() != 1) {
+ context_.Say(args.source,
+ "DECLARE_MAPPER directive should have a single argument"_err_en_US);
+ return;
}
-}
-void OmpStructureChecker::Enter(const parser::OpenMPDeclareMapperConstruct &x) {
- const auto &dir{std::get<parser::Verbatim>(x.t)};
- PushContextAndClauseSets(
- dir.source, llvm::omp::Directive::OMPD_declare_mapper);
- const auto &spec{std::get<parser::OmpMapperSpecifier>(x.t)};
- const auto &type = std::get<parser::TypeSpec>(spec.t);
- if (!std::get_if<parser::DerivedTypeSpec>(&type.u)) {
- context_.Say(dir.source, "Type is not a derived type"_err_en_US);
+ const parser::OmpArgument &arg{args.v.front()};
+ if (auto *spec{std::get_if<parser::OmpMapperSpecifier>(&arg.u)}) {
+ const auto &type = std::get<parser::TypeSpec>(spec->t);
+ if (!std::get_if<parser::DerivedTypeSpec>(&type.u)) {
+ context_.Say(arg.source, "Type is not a derived type"_err_en_US);
+ }
+ } else {
+ context_.Say(arg.source,
+ "The argument to the DECLARE_MAPPER directive should be a mapper-specifier"_err_en_US);
}
}
@@ -1611,9 +1639,21 @@ void OmpStructureChecker::Leave(const parser::OpenMPDeclareMapperConstruct &) {
void OmpStructureChecker::Enter(
const parser::OpenMPDeclareReductionConstruct &x) {
- const auto &dir{std::get<parser::Verbatim>(x.t)};
- PushContextAndClauseSets(
- dir.source, llvm::omp::Directive::OMPD_declare_reduction);
+ const parser::OmpDirectiveName &dirName{x.v.DirName()};
+ PushContextAndClauseSets(dirName.source, dirName.v);
+
+ const parser::OmpArgumentList &args{x.v.Arguments()};
+ if (args.v.size() != 1) {
+ context_.Say(args.source,
+ "DECLARE_REDUCTION directive should have a single argument"_err_en_US);
+ return;
+ }
+
+ const parser::OmpArgument &arg{args.v.front()};
+ if (!std::holds_alternative<parser::OmpReductionSpecifier>(arg.u)) {
+ context_.Say(arg.source,
+ "The argument to the DECLARE_REDUCTION directive should be a reduction-specifier"_err_en_US);
+ }
}
void OmpStructureChecker::Leave(
@@ -1621,130 +1661,185 @@ void OmpStructureChecker::Leave(
dirContext_.pop_back();
}
-void OmpStructureChecker::Enter(const parser::OpenMPDeclareTargetConstruct &x) {
- const auto &dir{std::get<parser::Verbatim>(x.t)};
- PushContext(dir.source, llvm::omp::Directive::OMPD_declare_target);
+void OmpStructureChecker::CheckSymbolName(
+ const parser::CharBlock &source, const parser::OmpObject &object) {
+ common::visit(
+ common::visitors{
+ [&](const parser::Designator &designator) {
+ if (const auto *name{parser::Unwrap<parser::Name>(object)}) {
+ if (!name->symbol) {
+ context_.Say(source,
+ "The given %s directive clause has an invalid argument"_err_en_US,
+ ContextDirectiveAsFortran());
+ }
+ }
+ },
+ [&](const parser::Name &name) {
+ if (!name.symbol) {
+ context_.Say(source,
+ "The given %s directive clause has an invalid argument"_err_en_US,
+ ContextDirectiveAsFortran());
+ }
+ },
+ [&](const parser::OmpObject::Invalid &invalid) {},
+ },
+ object.u);
}
-void OmpStructureChecker::Enter(const parser::OmpDeclareTargetWithList &x) {
- SymbolSourceMap symbols;
- GetSymbolsInObjectList(x.v, symbols);
- for (auto &[symbol, source] : symbols) {
+void OmpStructureChecker::CheckSymbolNames(
+ const parser::CharBlock &source, const parser::OmpObjectList &objList) {
+ for (const auto &ompObject : objList.v) {
+ CheckSymbolName(source, ompObject);
+ }
+}
+
+void OmpStructureChecker::Enter(const parser::OpenMPDeclareTargetConstruct &x) {
+ const parser::OmpDirectiveName &dirName{x.v.DirName()};
+ PushContext(dirName.source, dirName.v);
+
+ // Check if arguments are extended-list-items.
+ for (const parser::OmpArgument &arg : x.v.Arguments().v) {
+ const Symbol *symbol{GetArgumentSymbol(arg)};
+ if (!symbol) {
+ context_.Say(arg.source,
+ "An argument to the DECLARE TARGET directive should be an extended-list-item"_err_en_US);
+ continue;
+ }
const GenericDetails *genericDetails = symbol->detailsIf<GenericDetails>();
if (genericDetails) {
- context_.Say(source,
+ context_.Say(arg.source,
"The procedure '%s' in DECLARE TARGET construct cannot be a generic name."_err_en_US,
symbol->name());
genericDetails->specific();
}
if (IsProcedurePointer(*symbol)) {
- context_.Say(source,
+ context_.Say(arg.source,
"The procedure '%s' in DECLARE TARGET construct cannot be a procedure pointer."_err_en_US,
symbol->name());
}
const SubprogramDetails *entryDetails =
symbol->detailsIf<SubprogramDetails>();
if (entryDetails && entryDetails->entryScope()) {
- context_.Say(source,
+ context_.Say(arg.source,
"The procedure '%s' in DECLARE TARGET construct cannot be an entry name."_err_en_US,
symbol->name());
}
if (IsStmtFunction(*symbol)) {
- context_.Say(source,
+ context_.Say(arg.source,
"The procedure '%s' in DECLARE TARGET construct cannot be a statement function."_err_en_US,
symbol->name());
}
}
-}
-void OmpStructureChecker::CheckSymbolNames(
- const parser::CharBlock &source, const parser::OmpObjectList &objList) {
- for (const auto &ompObject : objList.v) {
- common::visit(
- common::visitors{
- [&](const parser::Designator &designator) {
- if (const auto *name{parser::Unwrap<parser::Name>(ompObject)}) {
- if (!name->symbol) {
- context_.Say(source,
- "The given %s directive clause has an invalid argument"_err_en_US,
- ContextDirectiveAsFortran());
- }
- }
- },
- [&](const parser::Name &name) {
- if (!name.symbol) {
- context_.Say(source,
- "The given %s directive clause has an invalid argument"_err_en_US,
- ContextDirectiveAsFortran());
- }
- },
- },
- ompObject.u);
+ // Check if there are arguments or clauses, but not both.
+ if (!x.v.Clauses().v.empty()) {
+ if (!x.v.Arguments().v.empty()) {
+ context_.Say(x.source,
+ "DECLARE TARGET directive can have argument or clauses, but not both"_err_en_US);
+ }
+ SetClauseSets(llvm::omp::Directive::OMPD_declare_target);
}
}
void OmpStructureChecker::Leave(const parser::OpenMPDeclareTargetConstruct &x) {
- const auto &dir{std::get<parser::Verbatim>(x.t)};
- const auto &spec{std::get<parser::OmpDeclareTargetSpecifier>(x.t)};
+ const parser::OmpDirectiveName &dirName{x.v.DirName()};
+
// Handle both forms of DECLARE TARGET.
// - Extended list: It behaves as if there was an ENTER/TO clause with the
// list of objects as argument. It accepts no explicit clauses.
// - With clauses.
- if (const auto *objectList{parser::Unwrap<parser::OmpObjectList>(spec.u)}) {
- deviceConstructFound_ = true;
- CheckSymbolNames(dir.source, *objectList);
- CheckVarIsNotPartOfAnotherVar(dir.source, *objectList);
- CheckThreadprivateOrDeclareTargetVar(*objectList);
- } else if (const auto *clauseList{
- parser::Unwrap<parser::OmpClauseList>(spec.u)}) {
- bool toClauseFound{false}, deviceTypeClauseFound{false},
- enterClauseFound{false};
- for (const auto &clause : clauseList->v) {
- common::visit(
- common::visitors{
- [&](const parser::OmpClause::To &toClause) {
- toClauseFound = true;
- auto &objList{std::get<parser::OmpObjectList>(toClause.v.t)};
- CheckSymbolNames(dir.source, objList);
- CheckVarIsNotPartOfAnotherVar(dir.source, objList);
- CheckThreadprivateOrDeclareTargetVar(objList);
- },
- [&](const parser::OmpClause::Link &linkClause) {
- CheckSymbolNames(dir.source, linkClause.v);
- CheckVarIsNotPartOfAnotherVar(dir.source, linkClause.v);
- CheckThreadprivateOrDeclareTargetVar(linkClause.v);
- },
- [&](const parser::OmpClause::Enter &enterClause) {
- enterClauseFound = true;
- auto &objList{std::get<parser::OmpObjectList>(enterClause.v.t)};
- CheckSymbolNames(dir.source, objList);
- CheckVarIsNotPartOfAnotherVar(dir.source, objList);
- CheckThreadprivateOrDeclareTargetVar(objList);
- },
- [&](const parser::OmpClause::DeviceType &deviceTypeClause) {
- deviceTypeClauseFound = true;
- if (deviceTypeClause.v.v !=
- parser::OmpDeviceTypeClause::DeviceTypeDescription::Host) {
- // Function / subroutine explicitly marked as runnable by the
- // target device.
- deviceConstructFound_ = true;
- }
- },
- [&](const auto &) {},
- },
- clause.u);
+ for (const parser::OmpArgument &arg : x.v.Arguments().v) {
+ if (auto *object{GetArgumentObject(arg)}) {
+ deviceConstructFound_ = true;
+ CheckSymbolName(dirName.source, *object);
+ CheckVarIsNotPartOfAnotherVar(dirName.source, *object);
+ CheckThreadprivateOrDeclareTargetVar(*object);
+ }
+ }
- if ((toClauseFound || enterClauseFound) && !deviceTypeClauseFound) {
- deviceConstructFound_ = true;
- }
+ if (!x.v.Clauses().v.empty()) {
+ const parser::OmpClause *enterClause =
+ FindClause(llvm::omp::Clause::OMPC_enter);
+ const parser::OmpClause *toClause = FindClause(llvm::omp::Clause::OMPC_to);
+ const parser::OmpClause *linkClause =
+ FindClause(llvm::omp::Clause::OMPC_link);
+ const parser::OmpClause *indirectClause =
+ FindClause(llvm::omp::Clause::OMPC_indirect);
+ if (!enterClause && !toClause && !linkClause) {
+ context_.Say(x.source,
+ "If the DECLARE TARGET directive has a clause, it must contain at least one ENTER clause or LINK clause"_err_en_US);
+ }
+ if (indirectClause && !enterClause) {
+ context_.Say(x.source,
+ "The INDIRECT clause cannot be used without the ENTER clause with the DECLARE TARGET directive."_err_en_US);
+ }
+ unsigned version{context_.langOptions().OpenMPVersion};
+ if (toClause && version >= 52) {
+ context_.Warn(common::UsageWarning::OpenMPUsage, toClause->source,
+ "The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead."_warn_en_US);
+ }
+ if (indirectClause) {
+ CheckAllowedClause(llvm::omp::Clause::OMPC_indirect);
+ }
+ }
+
+ bool toClauseFound{false}, deviceTypeClauseFound{false},
+ enterClauseFound{false};
+ for (const parser::OmpClause &clause : x.v.Clauses().v) {
+ common::visit(
+ common::visitors{
+ [&](const parser::OmpClause::To &toClause) {
+ toClauseFound = true;
+ auto &objList{std::get<parser::OmpObjectList>(toClause.v.t)};
+ CheckSymbolNames(dirName.source, objList);
+ CheckVarIsNotPartOfAnotherVar(dirName.source, objList);
+ CheckThreadprivateOrDeclareTargetVar(objList);
+ },
+ [&](const parser::OmpClause::Link &linkClause) {
+ CheckSymbolNames(dirName.source, linkClause.v);
+ CheckVarIsNotPartOfAnotherVar(dirName.source, linkClause.v);
+ CheckThreadprivateOrDeclareTargetVar(linkClause.v);
+ },
+ [&](const parser::OmpClause::Enter &enterClause) {
+ enterClauseFound = true;
+ auto &objList{std::get<parser::OmpObjectList>(enterClause.v.t)};
+ CheckSymbolNames(dirName.source, objList);
+ CheckVarIsNotPartOfAnotherVar(dirName.source, objList);
+ CheckThreadprivateOrDeclareTargetVar(objList);
+ },
+ [&](const parser::OmpClause::DeviceType &deviceTypeClause) {
+ deviceTypeClauseFound = true;
+ if (deviceTypeClause.v.v !=
+ parser::OmpDeviceTypeClause::DeviceTypeDescription::Host) {
+ // Function / subroutine explicitly marked as runnable by the
+ // target device.
+ deviceConstructFound_ = true;
+ }
+ },
+ [&](const auto &) {},
+ },
+ clause.u);
+
+ if ((toClauseFound || enterClauseFound) && !deviceTypeClauseFound) {
+ deviceConstructFound_ = true;
}
}
+
dirContext_.pop_back();
}
void OmpStructureChecker::Enter(const parser::OmpErrorDirective &x) {
- const auto &dir{std::get<parser::Verbatim>(x.t)};
- PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_error);
+ const parser::OmpDirectiveName &dirName{x.v.DirName()};
+ PushContextAndClauseSets(dirName.source, dirName.v);
+}
+
+void OmpStructureChecker::Enter(const parser::OmpNothingDirective &x) {
+ const parser::OmpDirectiveName &dirName{x.v.DirName()};
+ PushContextAndClauseSets(dirName.source, dirName.v);
+}
+
+void OmpStructureChecker::Leave(const parser::OmpNothingDirective &x) {
+ dirContext_.pop_back();
}
void OmpStructureChecker::Enter(const parser::OpenMPDispatchConstruct &x) {
@@ -2017,6 +2112,193 @@ void OmpStructureChecker::CheckTargetUpdate() {
}
}
+namespace {
+struct TaskgraphVisitor {
+ TaskgraphVisitor(SemanticsContext &context) : context_(context) {}
+
+ template <typename T> bool Pre(const T &) { return true; }
+ template <typename T> void Post(const T &) {}
+
+ bool Pre(const parser::OpenMPConstruct &x) {
+ parser::OmpDirectiveName name{GetOmpDirectiveName(x)};
+ llvm::ArrayRef<llvm::omp::Directive> leafs{getLeafConstructsOrSelf(name.v)};
+
+ if (!IsTaskGenerating(leafs)) {
+ context_.Say(name.source,
+ "Only task-generating constructs are allowed inside TASKGRAPH region"_err_en_US);
+ // Only visit top-level constructs.
+ return false;
+ }
+
+ const parser::OmpDirectiveSpecification &dirSpec{GetDirSpec(x)};
+
+ // Most restrictions apply to replayable constructs. All constructs are
+ // replayable unless REPLAYABLE(false) is present.
+ bool isReplayable{IsReplayable(dirSpec)};
+ const parser::OmpClause *nogroup{nullptr};
+
+ for (const parser::OmpClause &clause : dirSpec.Clauses().v) {
+ switch (clause.Id()) {
+ case llvm::omp::Clause::OMPC_transparent:
+ if (isReplayable) {
+ CheckTransparent(clause);
+ }
+ break;
+ case llvm::omp::Clause::OMPC_detach:
+ if (isReplayable) {
+ context_.Say(clause.source,
+ "Detachable replayable tasks are not allowed in a TASKGRAPH region"_err_en_US);
+ }
+ break;
+ case llvm::omp::Clause::OMPC_if:
+ if (isReplayable) {
+ CheckIf(clause, leafs);
+ }
+ break;
+ case llvm::omp::Clause::OMPC_nogroup:
+ nogroup = &clause;
+ break;
+ default:
+ break;
+ }
+ }
+
+ unsigned version{context_.langOptions().OpenMPVersion};
+ bool allowsNogroup{llvm::omp::isAllowedClauseForDirective(
+ leafs[0], llvm::omp::Clause::OMPC_nogroup, version)};
+
+ if (allowsNogroup) {
+ if (!nogroup) {
+ context_.Say(dirSpec.source,
+ "The NOGROUP clause must be specified on every construct in a TASKGRAPH region that could be enclosed in an implicit TASKGROUP"_err_en_US);
+ }
+ }
+
+ // Only visit top-level constructs.
+ return false;
+ }
+
+private:
+ const parser::OmpDirectiveSpecification &GetDirSpec(
+ const parser::OpenMPConstruct &x) const {
+ return common::visit(
+ common::visitors{
+ [&](const parser::OmpBlockConstruct &y)
+ -> const parser::OmpDirectiveSpecification & {
+ return y.BeginDir();
+ },
+ [&](const parser::OpenMPLoopConstruct &y)
+ -> const parser::OmpDirectiveSpecification & {
+ return y.BeginDir();
+ },
+ [&](const parser::OpenMPStandaloneConstruct &y)
+ -> const parser::OmpDirectiveSpecification & {
+ return std::get<parser::OpenMPSimpleStandaloneConstruct>(y.u).v;
+ },
+ [&](const auto &) -> const parser::OmpDirectiveSpecification & {
+ llvm_unreachable("Invalid construct");
+ },
+ },
+ x.u);
+ }
+
+ bool IsTaskGenerating(llvm::ArrayRef<llvm::omp::Directive> leafs) const {
+ const static llvm::omp::Directive taskGen[] = {
+ llvm::omp::Directive::OMPD_target,
+ llvm::omp::Directive::OMPD_target_data,
+ llvm::omp::Directive::OMPD_target_enter_data,
+ llvm::omp::Directive::OMPD_target_exit_data,
+ llvm::omp::Directive::OMPD_target_update,
+ llvm::omp::Directive::OMPD_task,
+ llvm::omp::Directive::OMPD_taskloop,
+ };
+ return llvm::all_of(leafs,
+ [](llvm::omp::Directive d) { return llvm::is_contained(taskGen, d); });
+ }
+
+ bool IsReplayable(const parser::OmpDirectiveSpecification &dirSpec) const {
+ for (const parser::OmpClause &clause : dirSpec.Clauses().v) {
+ if (clause.Id() != llvm::omp::Clause::OMPC_replayable) {
+ continue;
+ }
+ if (auto &repl{std::get<parser::OmpClause::Replayable>(clause.u).v}) {
+ // Scalar<Logical<Constant<indirection<Expr>>>>
+ const parser::Expr &parserExpr{repl->v.thing.thing.thing.value()};
+ if (auto &&expr{GetEvaluateExpr(parserExpr)}) {
+ return GetLogicalValue(*expr).value_or(true);
+ }
+ }
+ break;
+ }
+ return true;
+ }
+
+ void CheckTransparent(const parser::OmpClause &clause) const {
+ bool isTransparent{true};
+ if (auto &transp{std::get<parser::OmpClause::Transparent>(clause.u).v}) {
+ // Scalar<Integer<indirection<Expr>>>
+ const parser::Expr &parserExpr{transp->v.thing.thing.value()};
+ if (auto &&expr{GetEvaluateExpr(parserExpr)}) {
+ // If the argument is omp_not_impex (defined as 0), then
+ // the task is not transparent, otherwise it is.
+ const int64_t omp_not_impex{0};
+ if (auto &&val{evaluate::ToInt64(*expr)}) {
+ isTransparent = *val != omp_not_impex;
+ }
+ }
+ }
+ if (isTransparent) {
+ context_.Say(clause.source,
+ "Transparent replayable tasks are not allowed in a TASKGRAPH region"_err_en_US);
+ }
+ }
+
+ void CheckIf(const parser::OmpClause &clause,
+ llvm::ArrayRef<llvm::omp::Directive> leafs) const {
+ // The only constructs that can generate undeferred tasks (via IF clause)
+ // are TASK and TASKLOOP.
+ if (leafs[0] != llvm::omp::Directive::OMPD_task &&
+ leafs[0] != llvm::omp::Directive::OMPD_taskloop) {
+ return;
+ }
+
+ auto &&ifc{std::get<parser::OmpClause::If>(clause.u)};
+ // Check if there is a directive-name-modifier first.
+ auto &modifiers{OmpGetModifiers(ifc.v)};
+ if (auto *dnm{OmpGetUniqueModifier<parser::OmpDirectiveNameModifier>(
+ modifiers)}) {
+ llvm::omp::Directive sub{dnm->v};
+ auto subLeafs{llvm::omp::getLeafConstructsOrSelf(sub)};
+ // Only interested in the outermost constructs. The body of the created
+ // task is not a part of the TASKGRAPH region.
+ if (subLeafs[0] != leafs[0]) {
+ return;
+ }
+ }
+ // Scalar<Logical<indirection<Expr>>>
+ auto &parserExpr{
+ std::get<parser::ScalarLogicalExpr>(ifc.v.t).thing.thing.value()};
+ if (auto &&expr{GetEvaluateExpr(parserExpr)}) {
+ // If the value is known to be false, an undeferred task will be
+ // generated.
+ if (!GetLogicalValue(*expr).value_or(true)) {
+ context_.Say(clause.source,
+ "Undeferred replayable tasks are not allowed in a TASKGRAPH region"_err_en_US);
+ }
+ }
+ }
+
+ SemanticsContext &context_;
+};
+} // namespace
+
+void OmpStructureChecker::CheckTaskgraph(const parser::OmpBlockConstruct &x) {
+ const parser::Block &block{std::get<parser::Block>(x.t)};
+
+ TaskgraphVisitor visitor{context_};
+ parser::Walk(block, visitor);
+}
+
void OmpStructureChecker::CheckTaskDependenceType(
const parser::OmpTaskDependenceType::Value &x) {
// Common checks for task-dependence-type (DEPEND and UPDATE clauses).
@@ -2698,6 +2980,7 @@ void OmpStructureChecker::Leave(const parser::OmpClauseList &) {
}
}
},
+ [&](const parser::OmpObject::Invalid &invalid) {},
},
ompObject.u);
}
@@ -3405,6 +3688,7 @@ void OmpStructureChecker::CheckVarIsNotPartOfAnotherVar(
}
},
[&](const parser::Name &name) {},
+ [&](const parser::OmpObject::Invalid &invalid) {},
},
ompObject.u);
}
@@ -4090,11 +4374,11 @@ void OmpStructureChecker::CheckStructureComponent(
}};
for (const auto &object : objects.v) {
- common::visit(
- common::visitors{
- CheckComponent,
- [&](const parser::Name &name) {},
- },
+ common::visit(common::visitors{
+ CheckComponent,
+ [&](const parser::Name &name) {},
+ [&](const parser::OmpObject::Invalid &invalid) {},
+ },
object.u);
}
}