Skip to content

Commit adeff9f

Browse files
authored
[flang][OpenMP] Allow utility constructs in specification part (#121509)
Allow utility constructs (error and nothing) to appear in the specification part as well as the execution part. The exception is "ERROR AT(EXECUTION)" which should only be in the execution part. In case of ambiguity (the boundary between the specification and the execution part), utility constructs will be parsed as belonging to the specification part. In such cases move them to the execution part in the OpenMP canonicalization code.
1 parent a4d9240 commit adeff9f

File tree

11 files changed

+365
-75
lines changed

11 files changed

+365
-75
lines changed

flang/examples/FlangOmpReport/FlangOmpReportVisitor.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,16 @@ std::string OpenMPCounterVisitor::getName(const OmpWrapperType &w) {
106106
return getName(*std::get<const OpenMPDeclarativeConstruct *>(w));
107107
}
108108
std::string OpenMPCounterVisitor::getName(const OpenMPDeclarativeConstruct &c) {
109-
return std::visit(
110-
[&](const auto &o) -> std::string {
111-
const CharBlock &source{std::get<Verbatim>(o.t).source};
112-
return normalize_construct_name(source.ToString());
109+
return std::visit( //
110+
Fortran::common::visitors{
111+
[&](const OpenMPUtilityConstruct &o) -> std::string {
112+
const CharBlock &source{o.source};
113+
return normalize_construct_name(source.ToString());
114+
},
115+
[&](const auto &o) -> std::string {
116+
const CharBlock &source{std::get<Verbatim>(o.t).source};
117+
return normalize_construct_name(source.ToString());
118+
},
113119
},
114120
c.u);
115121
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4342,7 +4342,7 @@ struct OpenMPDeclarativeConstruct {
43424342
std::variant<OpenMPDeclarativeAllocate, OpenMPDeclareMapperConstruct,
43434343
OpenMPDeclareReductionConstruct, OpenMPDeclareSimdConstruct,
43444344
OpenMPDeclareTargetConstruct, OpenMPThreadprivate,
4345-
OpenMPRequiresConstruct>
4345+
OpenMPRequiresConstruct, OpenMPUtilityConstruct>
43464346
u;
43474347
};
43484348

flang/lib/Lower/OpenMP/OpenMP.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2586,6 +2586,10 @@ static void genOMPDispatch(lower::AbstractConverter &converter,
25862586
//===----------------------------------------------------------------------===//
25872587
// OpenMPDeclarativeConstruct visitors
25882588
//===----------------------------------------------------------------------===//
2589+
static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
2590+
semantics::SemanticsContext &semaCtx,
2591+
lower::pft::Evaluation &eval,
2592+
const parser::OpenMPUtilityConstruct &);
25892593

25902594
static void
25912595
genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,

flang/lib/Parser/openmp-parsers.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1090,7 +1090,9 @@ TYPE_PARSER(startOmpLine >>
10901090
construct<OpenMPDeclarativeConstruct>(
10911091
Parser<OpenMPRequiresConstruct>{}) ||
10921092
construct<OpenMPDeclarativeConstruct>(
1093-
Parser<OpenMPThreadprivate>{})) /
1093+
Parser<OpenMPThreadprivate>{}) ||
1094+
construct<OpenMPDeclarativeConstruct>(
1095+
Parser<OpenMPUtilityConstruct>{})) /
10941096
endOmpLine))
10951097

10961098
// Block Construct

flang/lib/Parser/unparse.cpp

Lines changed: 42 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -2631,81 +2631,64 @@ class UnparseVisitor {
26312631
}
26322632
}
26332633
void Unparse(const OpenMPDeclareReductionConstruct &x) {
2634+
BeginOpenMP();
2635+
Word("!$OMP DECLARE REDUCTION ");
26342636
Put("(");
26352637
Walk(std::get<OmpReductionIdentifier>(x.t)), Put(" : ");
26362638
Walk(std::get<std::list<DeclarationTypeSpec>>(x.t), ","), Put(" : ");
26372639
Walk(std::get<OmpReductionCombiner>(x.t));
26382640
Put(")");
26392641
Walk(std::get<std::optional<OmpReductionInitializerClause>>(x.t));
2642+
EndOpenMP();
26402643
}
2641-
bool Pre(const OpenMPDeclarativeConstruct &x) {
2644+
2645+
void Unparse(const OpenMPDeclareMapperConstruct &z) {
26422646
BeginOpenMP();
2643-
Word("!$OMP ");
2644-
return common::visit(
2645-
common::visitors{
2646-
[&](const OpenMPDeclarativeAllocate &z) {
2647-
Word("ALLOCATE (");
2648-
Walk(std::get<OmpObjectList>(z.t));
2649-
Put(")");
2650-
Walk(std::get<OmpClauseList>(z.t));
2651-
Put("\n");
2652-
EndOpenMP();
2653-
return false;
2654-
},
2655-
[&](const OpenMPDeclareMapperConstruct &z) {
2656-
Word("DECLARE MAPPER (");
2657-
const auto &spec{std::get<OmpDeclareMapperSpecifier>(z.t)};
2658-
if (auto mapname{std::get<std::optional<Name>>(spec.t)}) {
2659-
Walk(mapname);
2660-
Put(":");
2661-
}
2662-
Walk(std::get<TypeSpec>(spec.t));
2663-
Put("::");
2664-
Walk(std::get<Name>(spec.t));
2665-
Put(")");
2647+
Word("!$OMP DECLARE MAPPER (");
2648+
const auto &spec{std::get<OmpDeclareMapperSpecifier>(z.t)};
2649+
if (auto mapname{std::get<std::optional<Name>>(spec.t)}) {
2650+
Walk(mapname);
2651+
Put(":");
2652+
}
2653+
Walk(std::get<TypeSpec>(spec.t));
2654+
Put("::");
2655+
Walk(std::get<Name>(spec.t));
2656+
Put(")");
26662657

2667-
Walk(std::get<OmpClauseList>(z.t));
2668-
Put("\n");
2669-
return false;
2670-
},
2671-
[&](const OpenMPDeclareReductionConstruct &) {
2672-
Word("DECLARE REDUCTION ");
2673-
return true;
2674-
},
2675-
[&](const OpenMPDeclareSimdConstruct &y) {
2676-
Word("DECLARE SIMD ");
2677-
Walk("(", std::get<std::optional<Name>>(y.t), ")");
2678-
Walk(std::get<OmpClauseList>(y.t));
2679-
Put("\n");
2680-
EndOpenMP();
2681-
return false;
2682-
},
2683-
[&](const OpenMPDeclareTargetConstruct &) {
2684-
Word("DECLARE TARGET ");
2685-
return true;
2686-
},
2687-
[&](const OpenMPRequiresConstruct &y) {
2688-
Word("REQUIRES ");
2689-
Walk(std::get<OmpClauseList>(y.t));
2690-
Put("\n");
2691-
EndOpenMP();
2692-
return false;
2693-
},
2694-
[&](const OpenMPThreadprivate &) {
2695-
Word("THREADPRIVATE (");
2696-
return true;
2697-
},
2698-
},
2699-
x.u);
2658+
Walk(std::get<OmpClauseList>(z.t));
2659+
Put("\n");
2660+
EndOpenMP();
2661+
}
2662+
void Unparse(const OpenMPDeclareSimdConstruct &y) {
2663+
BeginOpenMP();
2664+
Word("!$OMP DECLARE SIMD ");
2665+
Walk("(", std::get<std::optional<Name>>(y.t), ")");
2666+
Walk(std::get<OmpClauseList>(y.t));
2667+
Put("\n");
2668+
EndOpenMP();
27002669
}
2701-
void Post(const OpenMPDeclarativeConstruct &) {
2670+
void Unparse(const OpenMPDeclareTargetConstruct &x) {
2671+
BeginOpenMP();
2672+
Word("!$OMP DECLARE TARGET ");
2673+
Walk(std::get<parser::OmpDeclareTargetSpecifier>(x.t));
27022674
Put("\n");
27032675
EndOpenMP();
27042676
}
2705-
void Post(const OpenMPThreadprivate &) {
2677+
void Unparse(const OpenMPRequiresConstruct &y) {
2678+
BeginOpenMP();
2679+
Word("!$OMP REQUIRES ");
2680+
Walk(std::get<OmpClauseList>(y.t));
2681+
Put("\n");
2682+
EndOpenMP();
2683+
}
2684+
void Unparse(const OpenMPThreadprivate &x) {
2685+
BeginOpenMP();
2686+
Word("!$OMP THREADPRIVATE (");
2687+
Walk(std::get<parser::OmpObjectList>(x.t));
27062688
Put(")\n");
27072689
EndOpenMP();
27082690
}
2691+
27092692
bool Pre(const OmpMessageClause &x) {
27102693
Walk(x.v);
27112694
return false;

flang/lib/Semantics/canonicalize-omp.cpp

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,43 @@ class CanonicalizationOfOmp {
5050

5151
void Post(parser::ExecutionPart &body) { RewriteOmpAllocations(body); }
5252

53+
// Pre-visit all constructs that have both a specification part and
54+
// an execution part, and store the connection between the two.
55+
bool Pre(parser::BlockConstruct &x) {
56+
auto *spec = &std::get<parser::BlockSpecificationPart>(x.t).v;
57+
auto *block = &std::get<parser::Block>(x.t);
58+
blockForSpec_.insert(std::make_pair(spec, block));
59+
return true;
60+
}
61+
bool Pre(parser::MainProgram &x) {
62+
auto *spec = &std::get<parser::SpecificationPart>(x.t);
63+
auto *block = &std::get<parser::ExecutionPart>(x.t).v;
64+
blockForSpec_.insert(std::make_pair(spec, block));
65+
return true;
66+
}
67+
bool Pre(parser::FunctionSubprogram &x) {
68+
auto *spec = &std::get<parser::SpecificationPart>(x.t);
69+
auto *block = &std::get<parser::ExecutionPart>(x.t).v;
70+
blockForSpec_.insert(std::make_pair(spec, block));
71+
return true;
72+
}
73+
bool Pre(parser::SubroutineSubprogram &x) {
74+
auto *spec = &std::get<parser::SpecificationPart>(x.t);
75+
auto *block = &std::get<parser::ExecutionPart>(x.t).v;
76+
blockForSpec_.insert(std::make_pair(spec, block));
77+
return true;
78+
}
79+
bool Pre(parser::SeparateModuleSubprogram &x) {
80+
auto *spec = &std::get<parser::SpecificationPart>(x.t);
81+
auto *block = &std::get<parser::ExecutionPart>(x.t).v;
82+
blockForSpec_.insert(std::make_pair(spec, block));
83+
return true;
84+
}
85+
86+
void Post(parser::SpecificationPart &spec) {
87+
CanonicalizeUtilityConstructs(spec);
88+
}
89+
5390
private:
5491
template <typename T> T *GetConstructIf(parser::ExecutionPartConstruct &x) {
5592
if (auto *y{std::get_if<parser::ExecutableConstruct>(&x.u)}) {
@@ -155,6 +192,131 @@ class CanonicalizationOfOmp {
155192
}
156193
}
157194

195+
// Canonicalization of utility constructs.
196+
//
197+
// This addresses the issue of utility constructs that appear at the
198+
// boundary between the specification and the execution parts, e.g.
199+
// subroutine foo
200+
// integer :: x ! Specification
201+
// !$omp nothing
202+
// x = 1 ! Execution
203+
// ...
204+
// end
205+
//
206+
// Utility constructs (error and nothing) can appear in both the
207+
// specification part and the execution part, except "error at(execution)",
208+
// which cannot be present in the specification part (whereas any utility
209+
// construct can be in the execution part).
210+
// When a utility construct is at the boundary, it should preferably be
211+
// parsed as an element of the execution part, but since the specification
212+
// part is parsed first, the utility construct ends up belonging to the
213+
// specification part.
214+
//
215+
// To allow the likes of the following code to compile, move all utility
216+
// construct that are at the end of the specification part to the beginning
217+
// of the execution part.
218+
//
219+
// subroutine foo
220+
// !$omp error at(execution) ! Initially parsed as declarative construct.
221+
// ! Move it to the execution part.
222+
// end
223+
224+
void CanonicalizeUtilityConstructs(parser::SpecificationPart &spec) {
225+
auto found = blockForSpec_.find(&spec);
226+
if (found == blockForSpec_.end()) {
227+
// There is no corresponding execution part, so there is nothing to do.
228+
return;
229+
}
230+
parser::Block &block = *found->second;
231+
232+
// There are two places where an OpenMP declarative construct can
233+
// show up in the tuple in specification part:
234+
// (1) in std::list<OpenMPDeclarativeConstruct>, or
235+
// (2) in std::list<DeclarationConstruct>.
236+
// The case (1) is only possible is the list (2) is empty.
237+
238+
auto &omps =
239+
std::get<std::list<parser::OpenMPDeclarativeConstruct>>(spec.t);
240+
auto &decls = std::get<std::list<parser::DeclarationConstruct>>(spec.t);
241+
242+
if (!decls.empty()) {
243+
MoveUtilityConstructsFromDecls(decls, block);
244+
} else {
245+
MoveUtilityConstructsFromOmps(omps, block);
246+
}
247+
}
248+
249+
void MoveUtilityConstructsFromDecls(
250+
std::list<parser::DeclarationConstruct> &decls, parser::Block &block) {
251+
// Find the trailing range of DeclarationConstructs that are OpenMP
252+
// utility construct, that are to be moved to the execution part.
253+
std::list<parser::DeclarationConstruct>::reverse_iterator rlast = [&]() {
254+
for (auto rit = decls.rbegin(), rend = decls.rend(); rit != rend; ++rit) {
255+
parser::DeclarationConstruct &dc = *rit;
256+
if (!std::holds_alternative<parser::SpecificationConstruct>(dc.u)) {
257+
return rit;
258+
}
259+
auto &sc = std::get<parser::SpecificationConstruct>(dc.u);
260+
using OpenMPDeclarativeConstruct =
261+
common::Indirection<parser::OpenMPDeclarativeConstruct>;
262+
if (!std::holds_alternative<OpenMPDeclarativeConstruct>(sc.u)) {
263+
return rit;
264+
}
265+
// Got OpenMPDeclarativeConstruct. If it's not a utility construct
266+
// then stop.
267+
auto &odc = std::get<OpenMPDeclarativeConstruct>(sc.u).value();
268+
if (!std::holds_alternative<parser::OpenMPUtilityConstruct>(odc.u)) {
269+
return rit;
270+
}
271+
}
272+
return decls.rend();
273+
}();
274+
275+
std::transform(decls.rbegin(), rlast, std::front_inserter(block),
276+
[](parser::DeclarationConstruct &dc) {
277+
auto &sc = std::get<parser::SpecificationConstruct>(dc.u);
278+
using OpenMPDeclarativeConstruct =
279+
common::Indirection<parser::OpenMPDeclarativeConstruct>;
280+
auto &oc = std::get<OpenMPDeclarativeConstruct>(sc.u).value();
281+
auto &ut = std::get<parser::OpenMPUtilityConstruct>(oc.u);
282+
283+
return parser::ExecutionPartConstruct(parser::ExecutableConstruct(
284+
common::Indirection(parser::OpenMPConstruct(std::move(ut)))));
285+
});
286+
287+
decls.erase(rlast.base(), decls.end());
288+
}
289+
290+
void MoveUtilityConstructsFromOmps(
291+
std::list<parser::OpenMPDeclarativeConstruct> &omps,
292+
parser::Block &block) {
293+
using OpenMPDeclarativeConstruct = parser::OpenMPDeclarativeConstruct;
294+
// Find the trailing range of OpenMPDeclarativeConstruct that are OpenMP
295+
// utility construct, that are to be moved to the execution part.
296+
std::list<OpenMPDeclarativeConstruct>::reverse_iterator rlast = [&]() {
297+
for (auto rit = omps.rbegin(), rend = omps.rend(); rit != rend; ++rit) {
298+
OpenMPDeclarativeConstruct &dc = *rit;
299+
if (!std::holds_alternative<parser::OpenMPUtilityConstruct>(dc.u)) {
300+
return rit;
301+
}
302+
}
303+
return omps.rend();
304+
}();
305+
306+
std::transform(omps.rbegin(), rlast, std::front_inserter(block),
307+
[](parser::OpenMPDeclarativeConstruct &dc) {
308+
auto &ut = std::get<parser::OpenMPUtilityConstruct>(dc.u);
309+
return parser::ExecutionPartConstruct(parser::ExecutableConstruct(
310+
common::Indirection(parser::OpenMPConstruct(std::move(ut)))));
311+
});
312+
313+
omps.erase(rlast.base(), omps.end());
314+
}
315+
316+
// Mapping from the specification parts to the blocks that follow in the
317+
// same construct. This is for converting utility constructs to executable
318+
// constructs.
319+
std::map<parser::SpecificationPart *, parser::Block *> blockForSpec_;
158320
parser::Messages &messages_;
159321
};
160322

flang/lib/Semantics/check-omp-structure.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,14 @@ void OmpStructureChecker::Leave(const parser::OpenMPConstruct &) {
614614
deferredNonVariables_.clear();
615615
}
616616

617+
void OmpStructureChecker::Enter(const parser::OpenMPDeclarativeConstruct &x) {
618+
EnterDirectiveNest(DeclarativeNest);
619+
}
620+
621+
void OmpStructureChecker::Leave(const parser::OpenMPDeclarativeConstruct &x) {
622+
ExitDirectiveNest(DeclarativeNest);
623+
}
624+
617625
void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) {
618626
loopStack_.push_back(&x);
619627
const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)};
@@ -1697,6 +1705,16 @@ void OmpStructureChecker::Leave(const parser::OmpErrorDirective &x) {
16971705
dirContext_.pop_back();
16981706
}
16991707

1708+
void OmpStructureChecker::Enter(const parser::OmpClause::At &x) {
1709+
CheckAllowedClause(llvm::omp::Clause::OMPC_at);
1710+
if (GetDirectiveNest(DeclarativeNest) > 0) {
1711+
if (x.v.v == parser::OmpAtClause::ActionTime::Execution) {
1712+
context_.Say(GetContext().clauseSource,
1713+
"The ERROR directive with AT(EXECUTION) cannot appear in the specification part"_err_en_US);
1714+
}
1715+
}
1716+
}
1717+
17001718
void OmpStructureChecker::Enter(const parser::OpenMPExecutableAllocate &x) {
17011719
isPredefinedAllocator = true;
17021720
const auto &dir{std::get<parser::Verbatim>(x.t)};
@@ -2856,7 +2874,6 @@ CHECK_SIMPLE_CLAUSE(Init, OMPC_init)
28562874
CHECK_SIMPLE_CLAUSE(Use, OMPC_use)
28572875
CHECK_SIMPLE_CLAUSE(Novariants, OMPC_novariants)
28582876
CHECK_SIMPLE_CLAUSE(Nocontext, OMPC_nocontext)
2859-
CHECK_SIMPLE_CLAUSE(At, OMPC_at)
28602877
CHECK_SIMPLE_CLAUSE(Severity, OMPC_severity)
28612878
CHECK_SIMPLE_CLAUSE(Message, OMPC_message)
28622879
CHECK_SIMPLE_CLAUSE(Filter, OMPC_filter)

0 commit comments

Comments
 (0)