diff options
Diffstat (limited to 'flang/lib/Semantics/check-omp-structure.cpp')
| -rw-r--r-- | flang/lib/Semantics/check-omp-structure.cpp | 680 |
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); } } |
