diff options
| author | Krzysztof Parzyszek <Krzysztof.Parzyszek@amd.com> | 2025-11-22 12:28:58 -0600 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-11-22 12:28:58 -0600 |
| commit | c2d659b9b8efac9f80b8ebcb2b38b61295d82bdc (patch) | |
| tree | 5b03136dfe47e5301733fd117ac5d71b3ab94fcb | |
| parent | 216e85bdda22ae7eda3f3e04c51d9d6d82e2b617 (diff) | |
[flang][OpenMP] Implement loop nest parser (#168884)
Previously, loop constructs were parsed in a piece-wise manner: the
begin directive, the body, and the end directive were parsed separately.
Later on in canonicalization they were all coalesced into a loop
construct. To facilitate that end-loop directives were given a special
treatment, namely they were parsed as OpenMP constructs. As a result
syntax errors caused by misplaced end-loop directives were handled
differently from those cause by misplaced non-loop end directives.
The new loop nest parser constructs the complete loop construct,
removing the need for the canonicalization step. Additionally, it is the
basis for parsing loop-sequence-associated constructs in the future.
It also removes the need for the special treatment of end-loop
directives. While this patch temporarily degrades the error messaging
for misplaced end-loop directives, it enables uniform handling of any
misplaced end-directives in the future.
20 files changed, 356 insertions, 272 deletions
diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h index 60d2ad0b764b..9795a0d2ae25 100644 --- a/flang/include/flang/Parser/parse-tree.h +++ b/flang/include/flang/Parser/parse-tree.h @@ -271,7 +271,6 @@ struct OpenACCRoutineConstruct; struct OpenMPConstruct; struct OpenMPLoopConstruct; struct OpenMPDeclarativeConstruct; -struct OmpEndLoopDirective; struct CUFKernelDoConstruct; // Cooked character stream locations @@ -539,7 +538,6 @@ struct ExecutableConstruct { common::Indirection<OpenACCConstruct>, common::Indirection<AccEndCombinedDirective>, common::Indirection<OpenMPConstruct>, - common::Indirection<OmpEndLoopDirective>, common::Indirection<CUFKernelDoConstruct>> u; }; @@ -5359,6 +5357,7 @@ struct OpenMPLoopConstruct { const DoConstruct *GetNestedLoop() const; const OpenMPLoopConstruct *GetNestedConstruct() const; + CharBlock source; std::tuple<OmpBeginLoopDirective, Block, std::optional<OmpEndLoopDirective>> t; }; diff --git a/flang/lib/Parser/executable-parsers.cpp b/flang/lib/Parser/executable-parsers.cpp index fadec1f11d1d..8d777a667149 100644 --- a/flang/lib/Parser/executable-parsers.cpp +++ b/flang/lib/Parser/executable-parsers.cpp @@ -49,7 +49,6 @@ constexpr auto executableConstruct{first( construct<ExecutableConstruct>(indirect(Parser<SelectTypeConstruct>{})), construct<ExecutableConstruct>(indirect(whereConstruct)), construct<ExecutableConstruct>(indirect(forallConstruct)), - construct<ExecutableConstruct>(indirect(ompEndLoopDirective)), construct<ExecutableConstruct>(indirect(openmpConstruct)), construct<ExecutableConstruct>(indirect(Parser<OpenACCConstruct>{})), construct<ExecutableConstruct>(indirect(compilerDirective)), diff --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp index 231eea8841d4..a0c94296de5e 100644 --- a/flang/lib/Parser/openmp-parsers.cpp +++ b/flang/lib/Parser/openmp-parsers.cpp @@ -17,6 +17,7 @@ #include "type-parser-implementation.h" #include "flang/Parser/openmp-utils.h" #include "flang/Parser/parse-tree.h" +#include "flang/Parser/tools.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Bitset.h" #include "llvm/ADT/STLExtras.h" @@ -30,6 +31,7 @@ #include <iterator> #include <list> #include <optional> +#include <set> #include <string> #include <tuple> #include <type_traits> @@ -56,6 +58,35 @@ constexpr auto endOmpLine = space >> endOfLine; constexpr auto logicalConstantExpr{logical(constantExpr)}; constexpr auto scalarLogicalConstantExpr{scalar(logicalConstantExpr)}; +// Parser that wraps the result of another parser into a Block. If the given +// parser succeeds, the result is a block containing the ExecutionPartConstruct +// result of the argument parser. Otherwise the parser fails. +template <typename ExecParser> struct AsBlockParser { + using resultType = Block; + static_assert( + std::is_same_v<typename ExecParser::resultType, ExecutionPartConstruct>); + + constexpr AsBlockParser(ExecParser epc) : epc_(epc) {} + std::optional<resultType> Parse(ParseState &state) const { + if (auto &&exec{attempt(epc_).Parse(state)}) { + Block body; + body.push_back(std::move(*exec)); + return body; + } + return std::nullopt; + } + +private: + const ExecParser epc_; +}; + +template <typename ExecParser, + typename = std::enable_if<std::is_same_v<typename ExecParser::resultType, + ExecutionPartConstruct>>> +constexpr auto asBlock(ExecParser epc) { + return AsBlockParser<ExecParser>(epc); +} + // Given a parser for a single element, and a parser for a list of elements // of the same type, create a parser that constructs the entire list by having // the single element be the head of the list, and the rest be the tail. @@ -1656,6 +1687,110 @@ struct LooselyStructuredBlockParser { } }; +struct NonBlockDoConstructParser { + using resultType = Block; + + std::optional<resultType> Parse(ParseState &state) const { + std::set<Label> labels; + Block body; + + // Parse nests like + // do 20 i = 1, n LabelDoStmt.t<Label> = 20 + // do 10 j = 1, m + // ... + // 10 continue Statement<...>.label = 10 + // 20 continue + + // Keep parsing ExecutionPartConstructs until the set of open label-do + // statements becomes empty, or until the EPC parser fails. + auto processEpc{[&](ExecutionPartConstruct &&epc) { + if (auto &&label{GetStatementLabel(epc)}) { + labels.erase(*label); + } + if (auto *labelDo{Unwrap<LabelDoStmt>(epc)}) { + labels.insert(std::get<Label>(labelDo->t)); + } + body.push_back(std::move(epc)); + }}; + + auto nonBlockDo{predicated(executionPartConstruct, + [](auto &epc) { return Unwrap<LabelDoStmt>(epc); })}; + + if (auto &&nbd{nonBlockDo.Parse(state)}) { + processEpc(std::move(*nbd)); + while (auto &&epc{attempt(executionPartConstruct).Parse(state)}) { + processEpc(std::move(*epc)); + if (labels.empty()) { + break; + } + } + } + + if (!body.empty()) { + return std::move(body); + } + return std::nullopt; + } + +private: + // Is the template argument "Statement<T>" for some T? + template <typename T> struct IsStatement { + static constexpr bool value{false}; + }; + template <typename T> struct IsStatement<Statement<T>> { + static constexpr bool value{true}; + }; + + // Get the Label from a Statement<...> contained in an ExecutionPartConstruct, + // or std::nullopt, if there is no Statement<...> contained in there. + template <typename T> + static std::optional<Label> GetStatementLabel(const T &stmt) { + if constexpr (IsStatement<T>::value) { + return stmt.label; + } else if constexpr (WrapperTrait<T>) { + return GetStatementLabel(stmt.v); + } else if constexpr (UnionTrait<T>) { + return common::visit( + [&](auto &&s) { return GetStatementLabel(s); }, stmt.u); + } + return std::nullopt; + } +}; + +struct LoopNestParser { + using resultType = Block; + + std::optional<resultType> Parse(ParseState &state) const { + // Parse !$DIR as an ExecutionPartConstruct + auto fortranDirective{predicated(executionPartConstruct, + [](auto &epc) { return Unwrap<CompilerDirective>(epc); })}; + // Parse DO loop as an ExecutionPartConstruct + auto fortranDoConstruct{predicated(executionPartConstruct, + [&](auto &epc) { return Unwrap<DoConstruct>(epc); })}; + ParseState backtrack{state}; + + Block body; + llvm::move(*many(fortranDirective).Parse(state), std::back_inserter(body)); + + if (auto &&doLoop{attempt(fortranDoConstruct).Parse(state)}) { + body.push_back(std::move(*doLoop)); + return std::move(body); + } + if (auto &&labelDo{attempt(NonBlockDoConstructParser{}).Parse(state)}) { + llvm::move(*labelDo, std::back_inserter(body)); + return std::move(body); + } + if (auto &&sblock{attempt(StrictlyStructuredBlockParser{}).Parse(state)}) { + llvm::move(*sblock, std::back_inserter(body)); + return std::move(body); + } + // If it's neither a DO-loop, nor a BLOCK, undo the parsing of the + // directives and fail. + state = backtrack; + return std::nullopt; + } +}; + TYPE_PARSER(construct<OmpErrorDirective>( predicated(Parser<OmpDirectiveName>{}, IsDirective(llvm::omp::Directive::OMPD_error)) >= @@ -1783,6 +1918,54 @@ private: llvm::omp::Directive dir_; }; +struct OmpLoopConstructParser { + using resultType = OpenMPLoopConstruct; + + constexpr OmpLoopConstructParser(DirectiveSet dirs) : dirs_(dirs) {} + + std::optional<resultType> Parse(ParseState &state) const { + auto ompLoopConstruct{asBlock(predicated(executionPartConstruct, + [](auto &epc) { return Unwrap<OpenMPLoopConstruct>(epc); }))}; + auto loopItem{LoopNestParser{} || ompLoopConstruct}; + + if (auto &&begin{OmpBeginDirectiveParser(dirs_).Parse(state)}) { + auto loopDir{begin->DirName().v}; + auto assoc{llvm::omp::getDirectiveAssociation(loopDir)}; + if (assoc == llvm::omp::Association::LoopNest) { + if (auto &&item{attempt(loopItem).Parse(state)}) { + auto end{maybe(OmpEndDirectiveParser{loopDir}).Parse(state)}; + return OpenMPLoopConstruct{OmpBeginLoopDirective(std::move(*begin)), + std::move(*item), + llvm::transformOptional(std::move(*end), + [](auto &&s) { return OmpEndLoopDirective(std::move(s)); })}; + } else if (auto &&empty{pure<Block>().Parse(state)}) { + // Allow empty body. + auto end{maybe(OmpEndDirectiveParser{loopDir}).Parse(state)}; + return OpenMPLoopConstruct{OmpBeginLoopDirective(std::move(*begin)), + std::move(*empty), + llvm::transformOptional(std::move(*end), + [](auto &&s) { return OmpEndLoopDirective(std::move(s)); })}; + } + } else if (assoc == llvm::omp::Association::LoopSeq) { + // Parse loop sequence as a block. + if (auto &&body{block.Parse(state)}) { + auto end{maybe(OmpEndDirectiveParser{loopDir}).Parse(state)}; + return OpenMPLoopConstruct{OmpBeginLoopDirective(std::move(*begin)), + std::move(*body), + llvm::transformOptional(std::move(*end), + [](auto &&s) { return OmpEndLoopDirective(std::move(s)); })}; + } + } else { + llvm_unreachable("Unexpected association"); + } + } + return std::nullopt; + } + +private: + DirectiveSet dirs_; +}; + struct OmpDeclarativeAllocateParser { using resultType = OmpAllocateDirective; @@ -2267,13 +2450,7 @@ static constexpr DirectiveSet GetLoopDirectives() { return loopDirectives; } -TYPE_PARSER(sourced(construct<OmpBeginLoopDirective>( - sourced(OmpBeginDirectiveParser(GetLoopDirectives()))))) - -// END OMP Loop directives -TYPE_PARSER(sourced(construct<OmpEndLoopDirective>( - sourced(OmpEndDirectiveParser(GetLoopDirectives()))))) +TYPE_PARSER(sourced(construct<OpenMPLoopConstruct>( + OmpLoopConstructParser(GetLoopDirectives())))) -TYPE_PARSER(construct<OpenMPLoopConstruct>( - Parser<OmpBeginLoopDirective>{} / endOmpLine)) } // namespace Fortran::parser diff --git a/flang/lib/Parser/parse-tree.cpp b/flang/lib/Parser/parse-tree.cpp index 60e51895cdce..53d4e4e680ca 100644 --- a/flang/lib/Parser/parse-tree.cpp +++ b/flang/lib/Parser/parse-tree.cpp @@ -435,17 +435,36 @@ const OmpClauseList &OmpDirectiveSpecification::Clauses() const { } const DoConstruct *OpenMPLoopConstruct::GetNestedLoop() const { - if (auto &body{std::get<Block>(t)}; !body.empty()) { - return Unwrap<DoConstruct>(body.front()); - } - return nullptr; + auto getFromBlock{[](const Block &body, auto self) -> const DoConstruct * { + for (auto &stmt : body) { + if (auto *block{Unwrap<BlockConstruct>(&stmt)}) { + return self(std::get<Block>(block->t), self); + } + if (auto *loop{Unwrap<DoConstruct>(&stmt)}) { + return loop; + } + } + return nullptr; + }}; + + return getFromBlock(std::get<Block>(t), getFromBlock); } const OpenMPLoopConstruct *OpenMPLoopConstruct::GetNestedConstruct() const { - if (auto &body{std::get<Block>(t)}; !body.empty()) { - return Unwrap<OpenMPLoopConstruct>(body.front()); - } - return nullptr; + auto getFromBlock{ + [](const Block &body, auto self) -> const OpenMPLoopConstruct * { + for (auto &stmt : body) { + if (auto *block{Unwrap<BlockConstruct>(&stmt)}) { + return self(std::get<Block>(block->t), self); + } + if (auto *omp{Unwrap<OpenMPLoopConstruct>(&stmt)}) { + return omp; + } + } + return nullptr; + }}; + + return getFromBlock(std::get<Block>(t), getFromBlock); } static bool InitCharBlocksFromStrings(llvm::MutableArrayRef<CharBlock> blocks, diff --git a/flang/lib/Semantics/canonicalize-omp.cpp b/flang/lib/Semantics/canonicalize-omp.cpp index f7c53d6d8f4c..802b2acd3c0d 100644 --- a/flang/lib/Semantics/canonicalize-omp.cpp +++ b/flang/lib/Semantics/canonicalize-omp.cpp @@ -32,26 +32,6 @@ public: CanonicalizationOfOmp(SemanticsContext &context) : context_{context}, messages_{context.messages()} {} - void Post(parser::Block &block) { - for (auto it{block.begin()}; it != block.end(); ++it) { - if (auto *ompCons{GetConstructIf<parser::OpenMPConstruct>(*it)}) { - // OpenMPLoopConstruct - if (auto *ompLoop{ - std::get_if<parser::OpenMPLoopConstruct>(&ompCons->u)}) { - RewriteOpenMPLoopConstruct(*ompLoop, block, it); - } - } else if (auto *endDir{ - GetConstructIf<parser::OmpEndLoopDirective>(*it)}) { - // Unmatched OmpEndLoopDirective - const parser::OmpDirectiveName &endName{endDir->DirName()}; - messages_.Say(endName.source, - "The %s directive must follow the DO loop associated with the " - "loop construct"_err_en_US, - parser::ToUpperCaseLetters(endName.source.ToString())); - } - } // Block list - } - // Pre-visit all constructs that have both a specification part and // an execution part, and store the connection between the two. bool Pre(parser::BlockConstruct &x) { @@ -93,175 +73,6 @@ public: void Post(parser::OmpMapClause &map) { CanonicalizeMapModifiers(map); } private: - template <typename T> T *GetConstructIf(parser::ExecutionPartConstruct &x) { - if (auto *y{std::get_if<parser::ExecutableConstruct>(&x.u)}) { - if (auto *z{std::get_if<common::Indirection<T>>(&y->u)}) { - return &z->value(); - } - } - return nullptr; - } - - template <typename T> T *GetOmpIf(parser::ExecutionPartConstruct &x) { - if (auto *construct{GetConstructIf<parser::OpenMPConstruct>(x)}) { - if (auto *omp{std::get_if<T>(&construct->u)}) { - return omp; - } - } - return nullptr; - } - - void RewriteOpenMPLoopConstruct(parser::OpenMPLoopConstruct &x, - parser::Block &block, parser::Block::iterator it) { - // Check the sequence of DoConstruct and OmpEndLoopDirective - // in the same iteration - // - // Original: - // ExecutableConstruct -> OpenMPConstruct -> OpenMPLoopConstruct - // OmpBeginLoopDirective - // ExecutableConstruct -> DoConstruct - // ExecutableConstruct -> OmpEndLoopDirective (if available) - // - // After rewriting: - // ExecutableConstruct -> OpenMPConstruct -> OpenMPLoopConstruct - // OmpBeginLoopDirective - // DoConstruct - // OmpEndLoopDirective (if available) - parser::Block::iterator nextIt; - const parser::OmpDirectiveSpecification &beginDir{x.BeginDir()}; - const parser::OmpDirectiveName &beginName{beginDir.DirName()}; - - auto missingDoConstruct = [](const parser::OmpDirectiveName &dirName, - parser::Messages &messages) { - messages.Say(dirName.source, - "A DO loop must follow the %s directive"_err_en_US, - parser::ToUpperCaseLetters(dirName.source.ToString())); - }; - auto transformUnrollError = [](const parser::OmpDirectiveName &dirName, - parser::Messages &messages) { - messages.Say(dirName.source, - "If a loop construct has been fully unrolled, it cannot then be further transformed"_err_en_US, - parser::ToUpperCaseLetters(dirName.source.ToString())); - }; - auto missingEndFuse = [](auto &dir, auto &messages) { - messages.Say(dir.source, - "The %s construct requires the END FUSE directive"_err_en_US, - parser::ToUpperCaseLetters(dir.source.ToString())); - }; - - bool endFuseNeeded = beginName.v == llvm::omp::Directive::OMPD_fuse; - - auto &body{std::get<parser::Block>(x.t)}; - - nextIt = it; - nextIt++; - while (nextIt != block.end()) { - // Ignore compiler directives. - if (GetConstructIf<parser::CompilerDirective>(*nextIt)) { - nextIt++; - continue; - } - - if (auto *doCons{GetConstructIf<parser::DoConstruct>(*nextIt)}) { - if (doCons->GetLoopControl()) { - // move DoConstruct - body.push_back(std::move(*nextIt)); - nextIt = block.erase(nextIt); - // try to match OmpEndLoopDirective - if (nextIt != block.end()) { - if (auto *endDir{ - GetConstructIf<parser::OmpEndLoopDirective>(*nextIt)}) { - auto &endDirName = endDir->DirName(); - if (endDirName.v != llvm::omp::Directive::OMPD_fuse) { - std::get<std::optional<parser::OmpEndLoopDirective>>(x.t) = - std::move(*endDir); - nextIt = block.erase(nextIt); - } - } - } - } else { - messages_.Say(beginName.source, - "DO loop after the %s directive must have loop control"_err_en_US, - parser::ToUpperCaseLetters(beginName.source.ToString())); - } - } else if (auto *ompLoopCons{ - GetOmpIf<parser::OpenMPLoopConstruct>(*nextIt)}) { - // We should allow loop transformation constructs to be inserted between - // an OpenMP Loop Construct and the DO loop itself - auto &nestedBeginDirective = ompLoopCons->BeginDir(); - auto &nestedBeginName = nestedBeginDirective.DirName(); - if (llvm::omp::loopTransformationSet.test(nestedBeginName.v)) { - if (nestedBeginName.v == llvm::omp::Directive::OMPD_unroll && - llvm::omp::loopTransformationSet.test(beginName.v)) { - // if a loop has been unrolled, the user can not then transform that - // loop as it has been unrolled - const parser::OmpClauseList &unrollClauseList{ - nestedBeginDirective.Clauses()}; - if (unrollClauseList.v.empty()) { - // if the clause list is empty for an unroll construct, we assume - // the loop is being fully unrolled - transformUnrollError(beginName, messages_); - } else { - // parse the clauses for the unroll directive to find the full - // clause - for (auto &clause : unrollClauseList.v) { - if (clause.Id() == llvm::omp::OMPC_full) { - transformUnrollError(beginName, messages_); - } - } - } - } - RewriteOpenMPLoopConstruct(*ompLoopCons, block, nextIt); - body.push_back(std::move(*nextIt)); - nextIt = block.erase(nextIt); - // check the following block item to find the end directive - // for the loop transform directive. - if (nextIt != block.end()) { - if (auto *endDir{ - GetConstructIf<parser::OmpEndLoopDirective>(*nextIt)}) { - auto &endDirName = endDir->DirName(); - if (endDirName.v == beginName.v && - endDirName.v != llvm::omp::Directive::OMPD_fuse) { - std::get<std::optional<parser::OmpEndLoopDirective>>(x.t) = - std::move(*endDir); - nextIt = block.erase(nextIt); - } - } - } - } else { - messages_.Say(nestedBeginName.source, - "Only Loop Transformation Constructs or Loop Nests can be nested within Loop Constructs"_err_en_US, - parser::ToUpperCaseLetters(nestedBeginName.source.ToString())); - } - } else { - missingDoConstruct(beginName, messages_); - } - - if (endFuseNeeded && nextIt != block.end()) { - if (auto *endDir{ - GetConstructIf<parser::OmpEndLoopDirective>(*nextIt)}) { - auto &endDirName = endDir->DirName(); - if (endDirName.v == llvm::omp::Directive::OMPD_fuse) { - endFuseNeeded = false; - std::get<std::optional<parser::OmpEndLoopDirective>>(x.t) = - std::move(*endDir); - nextIt = block.erase(nextIt); - } - } - } - if (endFuseNeeded) - continue; - // If we get here, we either found a loop, or issued an error message. - return; - } - if (nextIt == block.end()) { - if (endFuseNeeded) - missingEndFuse(beginName, messages_); - else - missingDoConstruct(beginName, messages_); - } - } - // Canonicalization of allocate directives // // In OpenMP 5.0 and 5.1 the allocate directive could either be a declarative diff --git a/flang/lib/Semantics/check-omp-loop.cpp b/flang/lib/Semantics/check-omp-loop.cpp index 13581008433a..6d83ee62c3b7 100644 --- a/flang/lib/Semantics/check-omp-loop.cpp +++ b/flang/lib/Semantics/check-omp-loop.cpp @@ -245,6 +245,79 @@ void OmpStructureChecker::CheckSIMDNest(const parser::OpenMPConstruct &c) { } } +static bool IsLoopTransforming(llvm::omp::Directive dir) { + switch (dir) { + // TODO case llvm::omp::Directive::OMPD_flatten: + case llvm::omp::Directive::OMPD_fuse: + case llvm::omp::Directive::OMPD_interchange: + case llvm::omp::Directive::OMPD_nothing: + case llvm::omp::Directive::OMPD_reverse: + // TODO case llvm::omp::Directive::OMPD_split: + case llvm::omp::Directive::OMPD_stripe: + case llvm::omp::Directive::OMPD_tile: + case llvm::omp::Directive::OMPD_unroll: + return true; + default: + return false; + } +} + +void OmpStructureChecker::CheckNestedBlock(const parser::OpenMPLoopConstruct &x, + const parser::Block &body, size_t &nestedCount) { + for (auto &stmt : body) { + if (auto *dir{parser::Unwrap<parser::CompilerDirective>(stmt)}) { + context_.Say(dir->source, + "Compiler directives are not allowed inside OpenMP loop constructs"_err_en_US); + } else if (parser::Unwrap<parser::DoConstruct>(stmt)) { + ++nestedCount; + } else if (auto *omp{parser::Unwrap<parser::OpenMPLoopConstruct>(stmt)}) { + if (!IsLoopTransforming(omp->BeginDir().DirName().v)) { + context_.Say(omp->source, + "Only loop-transforming OpenMP constructs are allowed inside OpenMP loop constructs"_err_en_US); + } + ++nestedCount; + } else if (auto *block{parser::Unwrap<parser::BlockConstruct>(stmt)}) { + CheckNestedBlock(x, std::get<parser::Block>(block->t), nestedCount); + } else { + parser::CharBlock source{parser::GetSource(stmt).value_or(x.source)}; + context_.Say(source, + "OpenMP loop construct can only contain DO loops or loop-nest-generating OpenMP constructs"_err_en_US); + } + } +} + +void OmpStructureChecker::CheckNestedConstruct( + const parser::OpenMPLoopConstruct &x) { + size_t nestedCount{0}; + + auto &body{std::get<parser::Block>(x.t)}; + if (body.empty()) { + context_.Say(x.source, + "OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct"_err_en_US); + } else { + CheckNestedBlock(x, body, nestedCount); + } +} + +void OmpStructureChecker::CheckFullUnroll( + const parser::OpenMPLoopConstruct &x) { + // If the nested construct is a full unroll, then this construct is invalid + // since it won't contain a loop. + if (const parser::OpenMPLoopConstruct *nested{x.GetNestedConstruct()}) { + auto &nestedSpec{nested->BeginDir()}; + if (nestedSpec.DirName().v == llvm::omp::Directive::OMPD_unroll) { + bool isPartial{ + llvm::any_of(nestedSpec.Clauses().v, [](const parser::OmpClause &c) { + return c.Id() == llvm::omp::Clause::OMPC_partial; + })}; + if (!isPartial) { + context_.Say(x.source, + "OpenMP loop construct cannot apply to a fully unrolled loop"_err_en_US); + } + } + } +} + void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) { loopStack_.push_back(&x); @@ -292,6 +365,8 @@ void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) { } } CheckLoopItrVariableIsInt(x); + CheckNestedConstruct(x); + CheckFullUnroll(x); CheckAssociatedLoopConstraints(x); HasInvalidDistributeNesting(x); HasInvalidLoopBinding(x); @@ -475,7 +550,9 @@ void OmpStructureChecker::CheckLooprangeBounds( void OmpStructureChecker::CheckNestedFuse( const parser::OpenMPLoopConstruct &x) { auto &loopConsList{std::get<parser::Block>(x.t)}; - assert(loopConsList.size() == 1 && "Not Expecting a loop sequence"); + if (loopConsList.empty()) { + return; + } const auto *ompConstruct{parser::omp::GetOmpLoop(loopConsList.front())}; if (!ompConstruct) { return; diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h index a4d74398378d..3e5de0071aca 100644 --- a/flang/lib/Semantics/check-omp-structure.h +++ b/flang/lib/Semantics/check-omp-structure.h @@ -320,6 +320,10 @@ private: void CheckNestedFuse(const parser::OpenMPLoopConstruct &x); void CheckDistLinear(const parser::OpenMPLoopConstruct &x); void CheckSIMDNest(const parser::OpenMPConstruct &x); + void CheckNestedBlock(const parser::OpenMPLoopConstruct &x, + const parser::Block &body, size_t &nestedCount); + void CheckNestedConstruct(const parser::OpenMPLoopConstruct &x); + void CheckFullUnroll(const parser::OpenMPLoopConstruct &x); void CheckTargetNest(const parser::OpenMPConstruct &x); void CheckTargetUpdate(); void CheckTaskgraph(const parser::OmpBlockConstruct &x); diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp index 48b23ad07762..bd6c1f8e4c77 100644 --- a/flang/lib/Semantics/resolve-directives.cpp +++ b/flang/lib/Semantics/resolve-directives.cpp @@ -2359,7 +2359,6 @@ void OmpAttributeVisitor::CheckPerfectNestAndRectangularLoop( // construct with multiple associated do-loops are lastprivate. void OmpAttributeVisitor::PrivatizeAssociatedLoopIndexAndCheckLoopLevel( const parser::OpenMPLoopConstruct &x) { - unsigned version{context_.langOptions().OpenMPVersion}; std::int64_t level{GetContext().associatedLoopLevel}; if (level <= 0) { return; @@ -2418,6 +2417,7 @@ void OmpAttributeVisitor::PrivatizeAssociatedLoopIndexAndCheckLoopLevel( } CheckAssocLoopLevel(level, GetAssociatedClause()); } else { + unsigned version{context_.langOptions().OpenMPVersion}; context_.Say(GetContext().directiveSource, "A DO loop must follow the %s directive"_err_en_US, parser::ToUpperCaseLetters( diff --git a/flang/test/Lower/OpenMP/nested-loop-transformation-construct02.f90 b/flang/test/Lower/OpenMP/nested-loop-transformation-construct02.f90 index cdc628a3b2e6..5200777e54cd 100644 --- a/flang/test/Lower/OpenMP/nested-loop-transformation-construct02.f90 +++ b/flang/test/Lower/OpenMP/nested-loop-transformation-construct02.f90 @@ -9,7 +9,7 @@ program loop_transformation_construct integer :: y(I) !$omp do - !$omp unroll + !$omp unroll partial(2) do x = 1, I y(x) = y(x) * 5 end do diff --git a/flang/test/Parser/OpenMP/fail-looprange.f90 b/flang/test/Parser/OpenMP/fail-looprange.f90 index ebe3480b44f1..1c8a08087138 100644 --- a/flang/test/Parser/OpenMP/fail-looprange.f90 +++ b/flang/test/Parser/OpenMP/fail-looprange.f90 @@ -1,11 +1,11 @@ ! RUN: not %flang_fc1 -fsyntax-only -fopenmp %s 2>&1 | FileCheck %s -! CHECK: error: expected end of line +! CHECK: error: !$omp fuse looprange -! CHECK: error: expected end of line +! CHECK: error: !$omp fuse looprange(1) -! CHECK: error: expected end of line +! CHECK: error: !$omp fuse looprange(1,2,3) end diff --git a/flang/test/Parser/OpenMP/loop-transformation-construct01.f90 b/flang/test/Parser/OpenMP/loop-transformation-construct01.f90 index 8b314d8d823d..979dd0c57e8b 100644 --- a/flang/test/Parser/OpenMP/loop-transformation-construct01.f90 +++ b/flang/test/Parser/OpenMP/loop-transformation-construct01.f90 @@ -10,7 +10,7 @@ subroutine loop_transformation_construct integer :: y(I) !$omp do - !$omp unroll + !$omp unroll partial(1) do i = 1, I y(i) = y(i) * 5 end do @@ -28,7 +28,8 @@ end subroutine !CHECK-PARSE-NEXT: | | | | ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> OpenMPLoopConstruct !CHECK-PARSE-NEXT: | | | | | OmpBeginLoopDirective !CHECK-PARSE-NEXT: | | | | | | OmpDirectiveName -> llvm::omp::Directive = unroll -!CHECK-PARSE-NEXT: | | | | | | OmpClauseList -> +!CHECK-PARSE-NEXT: | | | | | | OmpClauseList -> OmpClause -> Partial -> Scalar -> Integer -> Constant -> Expr = '1_4' +!CHECK-PARSE-NEXT: | | | | | | | LiteralConstant -> IntLiteralConstant = '1' !CHECK-PARSE-NEXT: | | | | | | Flags = None !CHECK-PARSE-NEXT: | | | | | Block !CHECK-PARSE-NEXT: | | | | | | ExecutionPartConstruct -> ExecutableConstruct -> DoConstruct @@ -71,7 +72,7 @@ end subroutine !CHECK-UNPARSE-NEXT: INTEGER x !CHECK-UNPARSE-NEXT: INTEGER y(i) !CHECK-UNPARSE-NEXT: !$OMP DO -!CHECK-UNPARSE-NEXT: !$OMP UNROLL +!CHECK-UNPARSE-NEXT: !$OMP UNROLL PARTIAL(1_4) !CHECK-UNPARSE-NEXT: DO i=1_4,i !CHECK-UNPARSE-NEXT: y(int(i,kind=8))=5_4*y(int(i,kind=8)) !CHECK-UNPARSE-NEXT: END DO diff --git a/flang/test/Parser/OpenMP/loop-transformation-construct02.f90 b/flang/test/Parser/OpenMP/loop-transformation-construct02.f90 index 5b5b591b35f8..814a885f14a1 100644 --- a/flang/test/Parser/OpenMP/loop-transformation-construct02.f90 +++ b/flang/test/Parser/OpenMP/loop-transformation-construct02.f90 @@ -10,7 +10,7 @@ subroutine loop_transformation_construct integer :: y(I) !$omp do - !$omp unroll + !$omp unroll partial(1) !$omp tile sizes(2) do i = 1, I y(i) = y(i) * 5 @@ -30,7 +30,8 @@ end subroutine !CHECK-PARSE-NEXT: | | | | ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> OpenMPLoopConstruct !CHECK-PARSE-NEXT: | | | | | OmpBeginLoopDirective !CHECK-PARSE-NEXT: | | | | | | OmpDirectiveName -> llvm::omp::Directive = unroll -!CHECK-PARSE-NEXT: | | | | | | OmpClauseList -> +!CHECK-PARSE-NEXT: | | | | | | OmpClauseList -> OmpClause -> Partial -> Scalar -> Integer -> Constant -> Expr = '1_4' +!CHECK-PARSE-NEXT: | | | | | | | LiteralConstant -> IntLiteralConstant = '1' !CHECK-PARSE-NEXT: | | | | | | Flags = None !CHECK-PARSE-NEXT: | | | | | Block !CHECK-PARSE-NEXT: | | | | | | ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> OpenMPLoopConstruct @@ -84,7 +85,7 @@ end subroutine !CHECK-UNPARSE-NEXT: INTEGER x !CHECK-UNPARSE-NEXT: INTEGER y(i) !CHECK-UNPARSE-NEXT: !$OMP DO -!CHECK-UNPARSE-NEXT: !$OMP UNROLL +!CHECK-UNPARSE-NEXT: !$OMP UNROLL PARTIAL(1_4) !CHECK-UNPARSE-NEXT: !$OMP TILE !CHECK-UNPARSE-NEXT: DO i=1_4,i !CHECK-UNPARSE-NEXT: y(int(i,kind=8))=5_4*y(int(i,kind=8)) diff --git a/flang/test/Parser/OpenMP/tile-fail.f90 b/flang/test/Parser/OpenMP/tile-fail.f90 index 0a92e5bcb657..3cb0ea96975c 100644 --- a/flang/test/Parser/OpenMP/tile-fail.f90 +++ b/flang/test/Parser/OpenMP/tile-fail.f90 @@ -14,11 +14,10 @@ end subroutine !--- stray_end2.f90 -! Semantic error subroutine stray_end2 print * - !CHECK: error: The END TILE directive must follow the DO loop associated with the loop construct + !CHECK: error: expected 'END' !$omp end tile end subroutine @@ -26,7 +25,7 @@ end subroutine !--- stray_begin.f90 subroutine stray_begin - !CHECK: error: A DO loop must follow the TILE directive + !CHECK: error: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct !$omp tile sizes(2) end subroutine diff --git a/flang/test/Semantics/OpenMP/clause-validity01.f90 b/flang/test/Semantics/OpenMP/clause-validity01.f90 index 5f74978f3827..fda15eb9876e 100644 --- a/flang/test/Semantics/OpenMP/clause-validity01.f90 +++ b/flang/test/Semantics/OpenMP/clause-validity01.f90 @@ -252,8 +252,6 @@ use omp_lib !$omp parallel do if(target:a>1.) do i = 1, N enddo - !ERROR: Unmatched END SIMD directive - !$omp end simd ! 2.7.2 sections-clause -> private-clause | ! firstprivate-clause | @@ -574,8 +572,7 @@ use omp_lib do i = 1, N a = a + 3.14 enddo - !ERROR: Unmatched END TASKLOOP directive - !$omp end taskloop + !$omp end taskloop simd !ERROR: GRAINSIZE and NUM_TASKS clauses are mutually exclusive and may not appear on the same TASKLOOP SIMD directive !$omp taskloop simd num_tasks(3) grainsize(2) diff --git a/flang/test/Semantics/OpenMP/do21.f90 b/flang/test/Semantics/OpenMP/do21.f90 index 2f5815c10c11..e6fe7dd39dd3 100644 --- a/flang/test/Semantics/OpenMP/do21.f90 +++ b/flang/test/Semantics/OpenMP/do21.f90 @@ -2,26 +2,26 @@ ! Check for existence of loop following a DO directive subroutine do1 - !ERROR: A DO loop must follow the DO directive + !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct !$omp do end subroutine subroutine do2 - !ERROR: A DO loop must follow the PARALLEL DO directive + !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct !$omp parallel do end subroutine subroutine do3 - !ERROR: A DO loop must follow the SIMD directive + !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct !$omp simd end subroutine subroutine do4 - !ERROR: A DO loop must follow the DO SIMD directive + !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct !$omp do simd end subroutine subroutine do5 - !ERROR: A DO loop must follow the LOOP directive + !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct !$omp loop end subroutine diff --git a/flang/test/Semantics/OpenMP/loop-association.f90 b/flang/test/Semantics/OpenMP/loop-association.f90 index 9fac508e6128..b2898d3967a2 100644 --- a/flang/test/Semantics/OpenMP/loop-association.f90 +++ b/flang/test/Semantics/OpenMP/loop-association.f90 @@ -17,11 +17,13 @@ !$omp end parallel !$omp parallel do + !ERROR: DO CONCURRENT loops cannot form part of a loop nest. DO CONCURRENT (i = 1:N) a = 3.14 END DO !$omp parallel do simd + !ERROR: The associated loop of a loop-associated directive cannot be a DO WHILE. outer: DO WHILE (c > 1) inner: do while (b > 100) a = 3.14 @@ -31,7 +33,10 @@ END DO outer ! Accept directives between parallel do and actual loop. + !ERROR: A DO loop must follow the PARALLEL DO directive !$OMP PARALLEL DO + !WARNING: Unrecognized compiler directive was ignored [-Wignored-directive] + !ERROR: Compiler directives are not allowed inside OpenMP loop constructs !DIR$ VECTOR ALIGNED DO 20 i=1,N a = a + 0.5 @@ -39,11 +44,14 @@ !$OMP END PARALLEL DO c = 16 - !ERROR: DO loop after the PARALLEL DO directive must have loop control !$omp parallel do + !ERROR: Loop control is not present in the DO LOOP + !ERROR: The associated loop of a loop-associated directive cannot be a DO without control. do a = 3.14 c = c - 1 + !ERROR: EXIT to construct outside of PARALLEL DO construct is not allowed + !ERROR: EXIT statement terminates associated loop of an OpenMP DO construct if (c < 1) exit enddo @@ -57,9 +65,9 @@ do 100 j=1, N a = 3.14 100 continue - !ERROR: The ENDDO directive must follow the DO loop associated with the loop construct !$omp enddo + !ERROR: Non-THREADPRIVATE object 'a' in COPYIN clause !$omp parallel do copyin(a) do i = 1, N !$omp parallel do @@ -74,8 +82,6 @@ do i = 1, N enddo !$omp end parallel do - !ERROR: The END PARALLEL DO directive must follow the DO loop associated with the loop construct - !$omp end parallel do !$omp parallel a = 3.0 @@ -84,27 +90,22 @@ enddo !$omp end do simd + !ERROR: Non-THREADPRIVATE object 'a' in COPYIN clause !$omp parallel do copyin(a) do i = 1, N enddo !$omp end parallel a = 0.0 - !ERROR: The END PARALLEL DO directive must follow the DO loop associated with the loop construct - !$omp end parallel do !$omp parallel do private(c) do i = 1, N do j = 1, N - !ERROR: A DO loop must follow the PARALLEL DO directive + !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct !$omp parallel do shared(b) a = 3.14 enddo - !ERROR: The END PARALLEL DO directive must follow the DO loop associated with the loop construct - !$omp end parallel do enddo a = 1.414 - !ERROR: The END PARALLEL DO directive must follow the DO loop associated with the loop construct - !$omp end parallel do do i = 1, N !$omp parallel do @@ -112,29 +113,22 @@ a = 3.14 enddo enddo - !ERROR: The END PARALLEL DO directive must follow the DO loop associated with the loop construct - !$omp end parallel do - !ERROR: A DO loop must follow the PARALLEL DO directive + !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct !$omp parallel do private(c) 5 FORMAT (1PE12.4, I10) do i=1, N a = 3.14 enddo - !ERROR: The END PARALLEL DO directive must follow the DO loop associated with the loop construct - !$omp end parallel do !$omp parallel do simd do i = 1, N a = 3.14 enddo !$omp end parallel do simd - !ERROR: The END PARALLEL DO SIMD directive must follow the DO loop associated with the loop construct - !$omp end parallel do simd - !ERROR: A DO loop must follow the SIMD directive + !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct !$omp simd - a = i + 1 - !ERROR: The END SIMD directive must follow the DO loop associated with the loop construct !$omp end simd + a = i + 1 end diff --git a/flang/test/Semantics/OpenMP/loop-transformation-construct01.f90 b/flang/test/Semantics/OpenMP/loop-transformation-construct01.f90 index ceada35cf9ec..caa8f3f216fe 100644 --- a/flang/test/Semantics/OpenMP/loop-transformation-construct01.f90 +++ b/flang/test/Semantics/OpenMP/loop-transformation-construct01.f90 @@ -5,45 +5,45 @@ subroutine loop_transformation_construct1 implicit none + !ERROR: OpenMP loop construct cannot apply to a fully unrolled loop !$omp do - !ERROR: A DO loop must follow the UNROLL directive + !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct !$omp unroll end subroutine subroutine loop_transformation_construct2 implicit none - integer :: i = 5 + integer, parameter :: i = 5 integer :: x integer :: v(i) !$omp do + !ERROR: At least one of SIZES clause must appear on the TILE directive !$omp tile do x = 1, i v(x) = v(x) * 2 end do !$omp end tile !$omp end do - !ERROR: The END TILE directive must follow the DO loop associated with the loop construct - !$omp end tile end subroutine -subroutine loop_transformation_construct2 +subroutine loop_transformation_construct3 implicit none - integer :: i = 5 + integer, parameter :: i = 5 integer :: x integer :: v(i) !$omp do - !ERROR: Only Loop Transformation Constructs or Loop Nests can be nested within Loop Constructs + !ERROR: Only loop-transforming OpenMP constructs are allowed inside OpenMP loop constructs !$omp parallel do do x = 1, i v(x) = v(x) * 2 end do end subroutine -subroutine loop_transformation_construct3 +subroutine loop_transformation_construct4 implicit none - integer :: i = 5 + integer, parameter :: i = 5 integer :: x integer :: v(i) @@ -51,18 +51,20 @@ subroutine loop_transformation_construct3 do x = 1, i v(x) = v(x) * 2 end do - !ERROR: A DO loop must follow the TILE directive + !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct + !ERROR: At least one of SIZES clause must appear on the TILE directive !$omp tile end subroutine -subroutine loop_transformation_construct4 +subroutine loop_transformation_construct5 implicit none - integer :: i = 5 + integer, parameter :: i = 5 integer :: x integer :: v(i) !$omp do - !ERROR: If a loop construct has been fully unrolled, it cannot then be further transformed + !ERROR: OpenMP loop construct cannot apply to a fully unrolled loop + !ERROR: At least one of SIZES clause must appear on the TILE directive !$omp tile !$omp unroll full do x = 1, i @@ -70,14 +72,15 @@ subroutine loop_transformation_construct4 end do end subroutine -subroutine loop_transformation_construct5 +subroutine loop_transformation_construct6 implicit none - integer :: i = 5 + integer, parameter :: i = 5 integer :: x integer :: v(i) !$omp do - !ERROR: If a loop construct has been fully unrolled, it cannot then be further transformed + !ERROR: OpenMP loop construct cannot apply to a fully unrolled loop + !ERROR: At least one of SIZES clause must appear on the TILE directive !$omp tile !$omp unroll do x = 1, i @@ -85,13 +88,14 @@ subroutine loop_transformation_construct5 end do end subroutine -subroutine loop_transformation_construct6 +subroutine loop_transformation_construct7 implicit none - integer :: i = 5 + integer, parameter :: i = 5 integer :: x integer :: v(i) !$omp do + !ERROR: At least one of SIZES clause must appear on the TILE directive !$omp tile !$omp unroll partial(2) do x = 1, i diff --git a/flang/test/Semantics/OpenMP/loop-transformation-construct02.f90 b/flang/test/Semantics/OpenMP/loop-transformation-construct02.f90 index 7cf7b15c41a6..3c07d6d08508 100644 --- a/flang/test/Semantics/OpenMP/loop-transformation-construct02.f90 +++ b/flang/test/Semantics/OpenMP/loop-transformation-construct02.f90 @@ -2,6 +2,7 @@ ! nested Loop Transformation Constructs !RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=60 +!XFAIL: * subroutine loop_transformation_construct1 implicit none diff --git a/flang/test/Semantics/OpenMP/loop-transformation-construct03.f90 b/flang/test/Semantics/OpenMP/loop-transformation-construct03.f90 index 88c3bd2bae4e..ab6b1bd7d67f 100644 --- a/flang/test/Semantics/OpenMP/loop-transformation-construct03.f90 +++ b/flang/test/Semantics/OpenMP/loop-transformation-construct03.f90 @@ -1,6 +1,7 @@ ! Testing the Semantic failure of forming loop sequences under regular OpenMP directives !RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=60 +!XFAIL: * subroutine loop_transformation_construct1 implicit none diff --git a/flang/test/Semantics/OpenMP/tile02.f90 b/flang/test/Semantics/OpenMP/tile02.f90 index 096a0f349932..5b70f94afb09 100644 --- a/flang/test/Semantics/OpenMP/tile02.f90 +++ b/flang/test/Semantics/OpenMP/tile02.f90 @@ -6,7 +6,7 @@ subroutine on_unroll implicit none integer i - !ERROR: If a loop construct has been fully unrolled, it cannot then be further transformed + !ERROR: OpenMP loop construct cannot apply to a fully unrolled loop !$omp tile sizes(2) !$omp unroll do i = 1, 5 |
