Skip to content

Commit c2d659b

Browse files
authored
[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.
1 parent 216e85b commit c2d659b

20 files changed

+356
-272
lines changed

flang/include/flang/Parser/parse-tree.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,6 @@ struct OpenACCRoutineConstruct;
271271
struct OpenMPConstruct;
272272
struct OpenMPLoopConstruct;
273273
struct OpenMPDeclarativeConstruct;
274-
struct OmpEndLoopDirective;
275274
struct CUFKernelDoConstruct;
276275

277276
// Cooked character stream locations
@@ -539,7 +538,6 @@ struct ExecutableConstruct {
539538
common::Indirection<OpenACCConstruct>,
540539
common::Indirection<AccEndCombinedDirective>,
541540
common::Indirection<OpenMPConstruct>,
542-
common::Indirection<OmpEndLoopDirective>,
543541
common::Indirection<CUFKernelDoConstruct>>
544542
u;
545543
};
@@ -5359,6 +5357,7 @@ struct OpenMPLoopConstruct {
53595357
const DoConstruct *GetNestedLoop() const;
53605358
const OpenMPLoopConstruct *GetNestedConstruct() const;
53615359

5360+
CharBlock source;
53625361
std::tuple<OmpBeginLoopDirective, Block, std::optional<OmpEndLoopDirective>>
53635362
t;
53645363
};

flang/lib/Parser/executable-parsers.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ constexpr auto executableConstruct{first(
4949
construct<ExecutableConstruct>(indirect(Parser<SelectTypeConstruct>{})),
5050
construct<ExecutableConstruct>(indirect(whereConstruct)),
5151
construct<ExecutableConstruct>(indirect(forallConstruct)),
52-
construct<ExecutableConstruct>(indirect(ompEndLoopDirective)),
5352
construct<ExecutableConstruct>(indirect(openmpConstruct)),
5453
construct<ExecutableConstruct>(indirect(Parser<OpenACCConstruct>{})),
5554
construct<ExecutableConstruct>(indirect(compilerDirective)),

flang/lib/Parser/openmp-parsers.cpp

Lines changed: 185 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "type-parser-implementation.h"
1818
#include "flang/Parser/openmp-utils.h"
1919
#include "flang/Parser/parse-tree.h"
20+
#include "flang/Parser/tools.h"
2021
#include "llvm/ADT/ArrayRef.h"
2122
#include "llvm/ADT/Bitset.h"
2223
#include "llvm/ADT/STLExtras.h"
@@ -30,6 +31,7 @@
3031
#include <iterator>
3132
#include <list>
3233
#include <optional>
34+
#include <set>
3335
#include <string>
3436
#include <tuple>
3537
#include <type_traits>
@@ -56,6 +58,35 @@ constexpr auto endOmpLine = space >> endOfLine;
5658
constexpr auto logicalConstantExpr{logical(constantExpr)};
5759
constexpr auto scalarLogicalConstantExpr{scalar(logicalConstantExpr)};
5860

61+
// Parser that wraps the result of another parser into a Block. If the given
62+
// parser succeeds, the result is a block containing the ExecutionPartConstruct
63+
// result of the argument parser. Otherwise the parser fails.
64+
template <typename ExecParser> struct AsBlockParser {
65+
using resultType = Block;
66+
static_assert(
67+
std::is_same_v<typename ExecParser::resultType, ExecutionPartConstruct>);
68+
69+
constexpr AsBlockParser(ExecParser epc) : epc_(epc) {}
70+
std::optional<resultType> Parse(ParseState &state) const {
71+
if (auto &&exec{attempt(epc_).Parse(state)}) {
72+
Block body;
73+
body.push_back(std::move(*exec));
74+
return body;
75+
}
76+
return std::nullopt;
77+
}
78+
79+
private:
80+
const ExecParser epc_;
81+
};
82+
83+
template <typename ExecParser,
84+
typename = std::enable_if<std::is_same_v<typename ExecParser::resultType,
85+
ExecutionPartConstruct>>>
86+
constexpr auto asBlock(ExecParser epc) {
87+
return AsBlockParser<ExecParser>(epc);
88+
}
89+
5990
// Given a parser for a single element, and a parser for a list of elements
6091
// of the same type, create a parser that constructs the entire list by having
6192
// the single element be the head of the list, and the rest be the tail.
@@ -1656,6 +1687,110 @@ struct LooselyStructuredBlockParser {
16561687
}
16571688
};
16581689

1690+
struct NonBlockDoConstructParser {
1691+
using resultType = Block;
1692+
1693+
std::optional<resultType> Parse(ParseState &state) const {
1694+
std::set<Label> labels;
1695+
Block body;
1696+
1697+
// Parse nests like
1698+
// do 20 i = 1, n LabelDoStmt.t<Label> = 20
1699+
// do 10 j = 1, m
1700+
// ...
1701+
// 10 continue Statement<...>.label = 10
1702+
// 20 continue
1703+
1704+
// Keep parsing ExecutionPartConstructs until the set of open label-do
1705+
// statements becomes empty, or until the EPC parser fails.
1706+
auto processEpc{[&](ExecutionPartConstruct &&epc) {
1707+
if (auto &&label{GetStatementLabel(epc)}) {
1708+
labels.erase(*label);
1709+
}
1710+
if (auto *labelDo{Unwrap<LabelDoStmt>(epc)}) {
1711+
labels.insert(std::get<Label>(labelDo->t));
1712+
}
1713+
body.push_back(std::move(epc));
1714+
}};
1715+
1716+
auto nonBlockDo{predicated(executionPartConstruct,
1717+
[](auto &epc) { return Unwrap<LabelDoStmt>(epc); })};
1718+
1719+
if (auto &&nbd{nonBlockDo.Parse(state)}) {
1720+
processEpc(std::move(*nbd));
1721+
while (auto &&epc{attempt(executionPartConstruct).Parse(state)}) {
1722+
processEpc(std::move(*epc));
1723+
if (labels.empty()) {
1724+
break;
1725+
}
1726+
}
1727+
}
1728+
1729+
if (!body.empty()) {
1730+
return std::move(body);
1731+
}
1732+
return std::nullopt;
1733+
}
1734+
1735+
private:
1736+
// Is the template argument "Statement<T>" for some T?
1737+
template <typename T> struct IsStatement {
1738+
static constexpr bool value{false};
1739+
};
1740+
template <typename T> struct IsStatement<Statement<T>> {
1741+
static constexpr bool value{true};
1742+
};
1743+
1744+
// Get the Label from a Statement<...> contained in an ExecutionPartConstruct,
1745+
// or std::nullopt, if there is no Statement<...> contained in there.
1746+
template <typename T>
1747+
static std::optional<Label> GetStatementLabel(const T &stmt) {
1748+
if constexpr (IsStatement<T>::value) {
1749+
return stmt.label;
1750+
} else if constexpr (WrapperTrait<T>) {
1751+
return GetStatementLabel(stmt.v);
1752+
} else if constexpr (UnionTrait<T>) {
1753+
return common::visit(
1754+
[&](auto &&s) { return GetStatementLabel(s); }, stmt.u);
1755+
}
1756+
return std::nullopt;
1757+
}
1758+
};
1759+
1760+
struct LoopNestParser {
1761+
using resultType = Block;
1762+
1763+
std::optional<resultType> Parse(ParseState &state) const {
1764+
// Parse !$DIR as an ExecutionPartConstruct
1765+
auto fortranDirective{predicated(executionPartConstruct,
1766+
[](auto &epc) { return Unwrap<CompilerDirective>(epc); })};
1767+
// Parse DO loop as an ExecutionPartConstruct
1768+
auto fortranDoConstruct{predicated(executionPartConstruct,
1769+
[&](auto &epc) { return Unwrap<DoConstruct>(epc); })};
1770+
ParseState backtrack{state};
1771+
1772+
Block body;
1773+
llvm::move(*many(fortranDirective).Parse(state), std::back_inserter(body));
1774+
1775+
if (auto &&doLoop{attempt(fortranDoConstruct).Parse(state)}) {
1776+
body.push_back(std::move(*doLoop));
1777+
return std::move(body);
1778+
}
1779+
if (auto &&labelDo{attempt(NonBlockDoConstructParser{}).Parse(state)}) {
1780+
llvm::move(*labelDo, std::back_inserter(body));
1781+
return std::move(body);
1782+
}
1783+
if (auto &&sblock{attempt(StrictlyStructuredBlockParser{}).Parse(state)}) {
1784+
llvm::move(*sblock, std::back_inserter(body));
1785+
return std::move(body);
1786+
}
1787+
// If it's neither a DO-loop, nor a BLOCK, undo the parsing of the
1788+
// directives and fail.
1789+
state = backtrack;
1790+
return std::nullopt;
1791+
}
1792+
};
1793+
16591794
TYPE_PARSER(construct<OmpErrorDirective>(
16601795
predicated(Parser<OmpDirectiveName>{},
16611796
IsDirective(llvm::omp::Directive::OMPD_error)) >=
@@ -1783,6 +1918,54 @@ struct OmpBlockConstructParser {
17831918
llvm::omp::Directive dir_;
17841919
};
17851920

1921+
struct OmpLoopConstructParser {
1922+
using resultType = OpenMPLoopConstruct;
1923+
1924+
constexpr OmpLoopConstructParser(DirectiveSet dirs) : dirs_(dirs) {}
1925+
1926+
std::optional<resultType> Parse(ParseState &state) const {
1927+
auto ompLoopConstruct{asBlock(predicated(executionPartConstruct,
1928+
[](auto &epc) { return Unwrap<OpenMPLoopConstruct>(epc); }))};
1929+
auto loopItem{LoopNestParser{} || ompLoopConstruct};
1930+
1931+
if (auto &&begin{OmpBeginDirectiveParser(dirs_).Parse(state)}) {
1932+
auto loopDir{begin->DirName().v};
1933+
auto assoc{llvm::omp::getDirectiveAssociation(loopDir)};
1934+
if (assoc == llvm::omp::Association::LoopNest) {
1935+
if (auto &&item{attempt(loopItem).Parse(state)}) {
1936+
auto end{maybe(OmpEndDirectiveParser{loopDir}).Parse(state)};
1937+
return OpenMPLoopConstruct{OmpBeginLoopDirective(std::move(*begin)),
1938+
std::move(*item),
1939+
llvm::transformOptional(std::move(*end),
1940+
[](auto &&s) { return OmpEndLoopDirective(std::move(s)); })};
1941+
} else if (auto &&empty{pure<Block>().Parse(state)}) {
1942+
// Allow empty body.
1943+
auto end{maybe(OmpEndDirectiveParser{loopDir}).Parse(state)};
1944+
return OpenMPLoopConstruct{OmpBeginLoopDirective(std::move(*begin)),
1945+
std::move(*empty),
1946+
llvm::transformOptional(std::move(*end),
1947+
[](auto &&s) { return OmpEndLoopDirective(std::move(s)); })};
1948+
}
1949+
} else if (assoc == llvm::omp::Association::LoopSeq) {
1950+
// Parse loop sequence as a block.
1951+
if (auto &&body{block.Parse(state)}) {
1952+
auto end{maybe(OmpEndDirectiveParser{loopDir}).Parse(state)};
1953+
return OpenMPLoopConstruct{OmpBeginLoopDirective(std::move(*begin)),
1954+
std::move(*body),
1955+
llvm::transformOptional(std::move(*end),
1956+
[](auto &&s) { return OmpEndLoopDirective(std::move(s)); })};
1957+
}
1958+
} else {
1959+
llvm_unreachable("Unexpected association");
1960+
}
1961+
}
1962+
return std::nullopt;
1963+
}
1964+
1965+
private:
1966+
DirectiveSet dirs_;
1967+
};
1968+
17861969
struct OmpDeclarativeAllocateParser {
17871970
using resultType = OmpAllocateDirective;
17881971

@@ -2267,13 +2450,7 @@ static constexpr DirectiveSet GetLoopDirectives() {
22672450
return loopDirectives;
22682451
}
22692452

2270-
TYPE_PARSER(sourced(construct<OmpBeginLoopDirective>(
2271-
sourced(OmpBeginDirectiveParser(GetLoopDirectives())))))
2272-
2273-
// END OMP Loop directives
2274-
TYPE_PARSER(sourced(construct<OmpEndLoopDirective>(
2275-
sourced(OmpEndDirectiveParser(GetLoopDirectives())))))
2453+
TYPE_PARSER(sourced(construct<OpenMPLoopConstruct>(
2454+
OmpLoopConstructParser(GetLoopDirectives()))))
22762455

2277-
TYPE_PARSER(construct<OpenMPLoopConstruct>(
2278-
Parser<OmpBeginLoopDirective>{} / endOmpLine))
22792456
} // namespace Fortran::parser

flang/lib/Parser/parse-tree.cpp

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -435,17 +435,36 @@ const OmpClauseList &OmpDirectiveSpecification::Clauses() const {
435435
}
436436

437437
const DoConstruct *OpenMPLoopConstruct::GetNestedLoop() const {
438-
if (auto &body{std::get<Block>(t)}; !body.empty()) {
439-
return Unwrap<DoConstruct>(body.front());
440-
}
441-
return nullptr;
438+
auto getFromBlock{[](const Block &body, auto self) -> const DoConstruct * {
439+
for (auto &stmt : body) {
440+
if (auto *block{Unwrap<BlockConstruct>(&stmt)}) {
441+
return self(std::get<Block>(block->t), self);
442+
}
443+
if (auto *loop{Unwrap<DoConstruct>(&stmt)}) {
444+
return loop;
445+
}
446+
}
447+
return nullptr;
448+
}};
449+
450+
return getFromBlock(std::get<Block>(t), getFromBlock);
442451
}
443452

444453
const OpenMPLoopConstruct *OpenMPLoopConstruct::GetNestedConstruct() const {
445-
if (auto &body{std::get<Block>(t)}; !body.empty()) {
446-
return Unwrap<OpenMPLoopConstruct>(body.front());
447-
}
448-
return nullptr;
454+
auto getFromBlock{
455+
[](const Block &body, auto self) -> const OpenMPLoopConstruct * {
456+
for (auto &stmt : body) {
457+
if (auto *block{Unwrap<BlockConstruct>(&stmt)}) {
458+
return self(std::get<Block>(block->t), self);
459+
}
460+
if (auto *omp{Unwrap<OpenMPLoopConstruct>(&stmt)}) {
461+
return omp;
462+
}
463+
}
464+
return nullptr;
465+
}};
466+
467+
return getFromBlock(std::get<Block>(t), getFromBlock);
449468
}
450469

451470
static bool InitCharBlocksFromStrings(llvm::MutableArrayRef<CharBlock> blocks,

0 commit comments

Comments
 (0)