Skip to content

Commit 3787fd9

Browse files
committed
[Flang][OpenMP][Sema] Support propagation of REQUIRES information across program units
This patch adds support for storing OpenMP REQUIRES information in the semantics symbols for programs/subprograms and modules/submodules, and populates them during directive resolution. A pass is added to name resolution that makes sure this information is also propagated across top-level programs, functions and subprograms. Storing REQUIRES information inside of semantics symbols will also allow supporting the propagation of this information across Fortran modules. This will come as a separate patch. The `bool DirectiveAttributeVisitor::Pre(const parser::SpecificationPart &x)` method is removed since it resulted in specification parts being visited twice. This is patch 3/5 of a series splitting D149337 to simplify review. Differential Revision: https://reviews.llvm.org/D157983
1 parent 7d574ff commit 3787fd9

File tree

11 files changed

+217
-27
lines changed

11 files changed

+217
-27
lines changed

flang/examples/FeatureList/FeatureList.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <utility>
2424
#include <vector>
2525

26+
using namespace Fortran::common;
2627
using namespace Fortran::frontend;
2728
using namespace Fortran::parser;
2829
using namespace Fortran;
@@ -553,7 +554,7 @@ struct NodeVisitor {
553554
READ_FEATURE(OmpAtomicClause)
554555
READ_FEATURE(OmpAtomicClauseList)
555556
READ_FEATURE(OmpAtomicDefaultMemOrderClause)
556-
READ_FEATURE(OmpAtomicDefaultMemOrderClause::Type)
557+
READ_FEATURE(OmpAtomicDefaultMemOrderType)
557558
READ_FEATURE(OpenMPFlushConstruct)
558559
READ_FEATURE(OpenMPLoopConstruct)
559560
READ_FEATURE(OpenMPExecutableAllocate)

flang/include/flang/Common/Fortran.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ ENUM_CLASS(CUDASubprogramAttrs, Host, Device, HostDevice, Global, Grid_Global)
8787
// CUDA data attributes; mutually exclusive
8888
ENUM_CLASS(CUDADataAttr, Constant, Device, Managed, Pinned, Shared, Texture)
8989

90+
// OpenMP atomic_default_mem_order clause allowed values
91+
ENUM_CLASS(OmpAtomicDefaultMemOrderType, SeqCst, AcqRel, Relaxed)
92+
9093
// Fortran names may have up to 63 characters (See Fortran 2018 C601).
9194
static constexpr int maxNameLen{63};
9295

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -589,7 +589,7 @@ class ParseTreeDumper {
589589
NODE(parser, OmpAtomicClause)
590590
NODE(parser, OmpAtomicClauseList)
591591
NODE(parser, OmpAtomicDefaultMemOrderClause)
592-
NODE_ENUM(OmpAtomicDefaultMemOrderClause, Type)
592+
NODE_ENUM(common, OmpAtomicDefaultMemOrderType)
593593
NODE(parser, OpenMPFlushConstruct)
594594
NODE(parser, OpenMPLoopConstruct)
595595
NODE(parser, OpenMPExecutableAllocate)

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3593,8 +3593,8 @@ struct OmpDependClause {
35933593
// ATOMIC_DEFAULT_MEM_ORDER (SEQ_CST | ACQ_REL |
35943594
// RELAXED)
35953595
struct OmpAtomicDefaultMemOrderClause {
3596-
ENUM_CLASS(Type, SeqCst, AcqRel, Relaxed)
3597-
WRAPPER_CLASS_BOILERPLATE(OmpAtomicDefaultMemOrderClause, Type);
3596+
WRAPPER_CLASS_BOILERPLATE(
3597+
OmpAtomicDefaultMemOrderClause, common::OmpAtomicDefaultMemOrderType);
35983598
};
35993599

36003600
// OpenMP Clauses

flang/include/flang/Semantics/symbol.h

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,38 @@ using SymbolVector = std::vector<SymbolRef>;
4545
using MutableSymbolRef = common::Reference<Symbol>;
4646
using MutableSymbolVector = std::vector<MutableSymbolRef>;
4747

48+
// Mixin for details with OpenMP declarative constructs.
49+
class WithOmpDeclarative {
50+
using OmpAtomicOrderType = common::OmpAtomicDefaultMemOrderType;
51+
52+
public:
53+
ENUM_CLASS(RequiresFlag, ReverseOffload, UnifiedAddress, UnifiedSharedMemory,
54+
DynamicAllocators);
55+
using RequiresFlags = common::EnumSet<RequiresFlag, RequiresFlag_enumSize>;
56+
57+
bool has_ompRequires() const { return ompRequires_.has_value(); }
58+
const RequiresFlags *ompRequires() const {
59+
return ompRequires_ ? &*ompRequires_ : nullptr;
60+
}
61+
void set_ompRequires(RequiresFlags flags) { ompRequires_ = flags; }
62+
63+
bool has_ompAtomicDefaultMemOrder() const {
64+
return ompAtomicDefaultMemOrder_.has_value();
65+
}
66+
const OmpAtomicOrderType *ompAtomicDefaultMemOrder() const {
67+
return ompAtomicDefaultMemOrder_ ? &*ompAtomicDefaultMemOrder_ : nullptr;
68+
}
69+
void set_ompAtomicDefaultMemOrder(OmpAtomicOrderType flags) {
70+
ompAtomicDefaultMemOrder_ = flags;
71+
}
72+
73+
private:
74+
std::optional<RequiresFlags> ompRequires_;
75+
std::optional<OmpAtomicOrderType> ompAtomicDefaultMemOrder_;
76+
};
77+
4878
// A module or submodule.
49-
class ModuleDetails {
79+
class ModuleDetails : public WithOmpDeclarative {
5080
public:
5181
ModuleDetails(bool isSubmodule = false) : isSubmodule_{isSubmodule} {}
5282
bool isSubmodule() const { return isSubmodule_; }
@@ -63,7 +93,7 @@ class ModuleDetails {
6393
const Scope *scope_{nullptr};
6494
};
6595

66-
class MainProgramDetails {
96+
class MainProgramDetails : public WithOmpDeclarative {
6797
public:
6898
private:
6999
};
@@ -114,7 +144,7 @@ class OpenACCRoutineInfo {
114144
// A subroutine or function definition, or a subprogram interface defined
115145
// in an INTERFACE block as part of the definition of a dummy procedure
116146
// or a procedure pointer (with just POINTER).
117-
class SubprogramDetails : public WithBindName {
147+
class SubprogramDetails : public WithBindName, public WithOmpDeclarative {
118148
public:
119149
bool isFunction() const { return result_ != nullptr; }
120150
bool isInterface() const { return isInterface_; }

flang/lib/Parser/openmp-parsers.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -432,9 +432,9 @@ TYPE_PARSER(sourced(construct<OmpMemoryOrderClause>(
432432
// acq_rel
433433
// relaxed
434434
TYPE_PARSER(construct<OmpAtomicDefaultMemOrderClause>(
435-
"SEQ_CST" >> pure(OmpAtomicDefaultMemOrderClause::Type::SeqCst) ||
436-
"ACQ_REL" >> pure(OmpAtomicDefaultMemOrderClause::Type::AcqRel) ||
437-
"RELAXED" >> pure(OmpAtomicDefaultMemOrderClause::Type::Relaxed)))
435+
"SEQ_CST" >> pure(common::OmpAtomicDefaultMemOrderType::SeqCst) ||
436+
"ACQ_REL" >> pure(common::OmpAtomicDefaultMemOrderType::AcqRel) ||
437+
"RELAXED" >> pure(common::OmpAtomicDefaultMemOrderType::Relaxed)))
438438

439439
// 2.17.7 Atomic construct
440440
// atomic-clause -> memory-order-clause | HINT(hint-expression)

flang/lib/Parser/unparse.cpp

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2307,17 +2307,7 @@ class UnparseVisitor {
23072307
}
23082308

23092309
void Unparse(const OmpAtomicDefaultMemOrderClause &x) {
2310-
switch (x.v) {
2311-
case OmpAtomicDefaultMemOrderClause::Type::SeqCst:
2312-
Word("SEQ_CST");
2313-
break;
2314-
case OmpAtomicDefaultMemOrderClause::Type::AcqRel:
2315-
Word("ACQ_REL");
2316-
break;
2317-
case OmpAtomicDefaultMemOrderClause::Type::Relaxed:
2318-
Word("RELAXED");
2319-
break;
2320-
}
2310+
Word(ToUpperCaseLetters(common::EnumToString(x.v)));
23212311
}
23222312

23232313
void Unparse(const OmpAtomicClauseList &x) { Walk(" ", x.v, " "); }

flang/lib/Semantics/resolve-directives.cpp

Lines changed: 152 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@
2222
#include <map>
2323
#include <sstream>
2424

25+
template <typename T>
26+
static Fortran::semantics::Scope *GetScope(
27+
Fortran::semantics::SemanticsContext &context, const T &x) {
28+
std::optional<Fortran::parser::CharBlock> source{GetSource(x)};
29+
return source ? &context.FindScope(*source) : nullptr;
30+
}
31+
2532
namespace Fortran::semantics {
2633

2734
template <typename T> class DirectiveAttributeVisitor {
@@ -324,11 +331,6 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> {
324331
return true;
325332
}
326333

327-
bool Pre(const parser::SpecificationPart &x) {
328-
Walk(std::get<std::list<parser::OpenMPDeclarativeConstruct>>(x.t));
329-
return true;
330-
}
331-
332334
bool Pre(const parser::StmtFunctionStmt &x) {
333335
const auto &parsedExpr{std::get<parser::Scalar<parser::Expr>>(x.t)};
334336
if (const auto *expr{GetExpr(context_, parsedExpr)}) {
@@ -375,7 +377,38 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> {
375377
void Post(const parser::OpenMPDeclareSimdConstruct &) { PopContext(); }
376378

377379
bool Pre(const parser::OpenMPRequiresConstruct &x) {
380+
using Flags = WithOmpDeclarative::RequiresFlags;
381+
using Requires = WithOmpDeclarative::RequiresFlag;
378382
PushContext(x.source, llvm::omp::Directive::OMPD_requires);
383+
384+
// Gather information from the clauses.
385+
Flags flags;
386+
std::optional<common::OmpAtomicDefaultMemOrderType> memOrder;
387+
for (const auto &clause : std::get<parser::OmpClauseList>(x.t).v) {
388+
flags |= common::visit(
389+
common::visitors{
390+
[&memOrder](
391+
const parser::OmpClause::AtomicDefaultMemOrder &atomic) {
392+
memOrder = atomic.v.v;
393+
return Flags{};
394+
},
395+
[](const parser::OmpClause::ReverseOffload &) {
396+
return Flags{Requires::ReverseOffload};
397+
},
398+
[](const parser::OmpClause::UnifiedAddress &) {
399+
return Flags{Requires::UnifiedAddress};
400+
},
401+
[](const parser::OmpClause::UnifiedSharedMemory &) {
402+
return Flags{Requires::UnifiedSharedMemory};
403+
},
404+
[](const parser::OmpClause::DynamicAllocators &) {
405+
return Flags{Requires::DynamicAllocators};
406+
},
407+
[](const auto &) { return Flags{}; }},
408+
clause.u);
409+
}
410+
// Merge clauses into parents' symbols details.
411+
AddOmpRequiresToScope(currScope(), flags, memOrder);
379412
return true;
380413
}
381414
void Post(const parser::OpenMPRequiresConstruct &) { PopContext(); }
@@ -672,6 +705,9 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> {
672705

673706
bool HasSymbolInEnclosingScope(const Symbol &, Scope &);
674707
std::int64_t ordCollapseLevel{0};
708+
709+
void AddOmpRequiresToScope(Scope &, WithOmpDeclarative::RequiresFlags,
710+
std::optional<common::OmpAtomicDefaultMemOrderType>);
675711
};
676712

677713
template <typename T>
@@ -2175,6 +2211,77 @@ void ResolveOmpParts(
21752211
}
21762212
}
21772213

2214+
void ResolveOmpTopLevelParts(
2215+
SemanticsContext &context, const parser::Program &program) {
2216+
if (!context.IsEnabled(common::LanguageFeature::OpenMP)) {
2217+
return;
2218+
}
2219+
2220+
// Gather REQUIRES clauses from all non-module top-level program unit symbols,
2221+
// combine them together ensuring compatibility and apply them to all these
2222+
// program units. Modules are skipped because their REQUIRES clauses should be
2223+
// propagated via USE statements instead.
2224+
WithOmpDeclarative::RequiresFlags combinedFlags;
2225+
std::optional<common::OmpAtomicDefaultMemOrderType> combinedMemOrder;
2226+
2227+
// Function to go through non-module top level program units and extract
2228+
// REQUIRES information to be processed by a function-like argument.
2229+
auto processProgramUnits{[&](auto processFn) {
2230+
for (const parser::ProgramUnit &unit : program.v) {
2231+
if (!std::holds_alternative<common::Indirection<parser::Module>>(
2232+
unit.u) &&
2233+
!std::holds_alternative<common::Indirection<parser::Submodule>>(
2234+
unit.u)) {
2235+
Symbol *symbol{common::visit(
2236+
[&context](
2237+
auto &x) { return GetScope(context, x.value())->symbol(); },
2238+
unit.u)};
2239+
2240+
common::visit(
2241+
[&](auto &details) {
2242+
if constexpr (std::is_convertible_v<decltype(&details),
2243+
WithOmpDeclarative *>) {
2244+
processFn(*symbol, details);
2245+
}
2246+
},
2247+
symbol->details());
2248+
}
2249+
}
2250+
}};
2251+
2252+
// Combine global REQUIRES information from all program units except modules
2253+
// and submodules.
2254+
processProgramUnits([&](Symbol &symbol, WithOmpDeclarative &details) {
2255+
if (const WithOmpDeclarative::RequiresFlags *
2256+
flags{details.ompRequires()}) {
2257+
combinedFlags |= *flags;
2258+
}
2259+
if (const common::OmpAtomicDefaultMemOrderType *
2260+
memOrder{details.ompAtomicDefaultMemOrder()}) {
2261+
if (combinedMemOrder && *combinedMemOrder != *memOrder) {
2262+
context.Say(symbol.scope()->sourceRange(),
2263+
"Conflicting '%s' REQUIRES clauses found in compilation "
2264+
"unit"_err_en_US,
2265+
parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(
2266+
llvm::omp::Clause::OMPC_atomic_default_mem_order)
2267+
.str()));
2268+
}
2269+
combinedMemOrder = *memOrder;
2270+
}
2271+
});
2272+
2273+
// Update all program units except modules and submodules with the combined
2274+
// global REQUIRES information.
2275+
processProgramUnits([&](Symbol &, WithOmpDeclarative &details) {
2276+
if (combinedFlags.any()) {
2277+
details.set_ompRequires(combinedFlags);
2278+
}
2279+
if (combinedMemOrder) {
2280+
details.set_ompAtomicDefaultMemOrder(*combinedMemOrder);
2281+
}
2282+
});
2283+
}
2284+
21782285
void OmpAttributeVisitor::CheckDataCopyingClause(
21792286
const parser::Name &name, const Symbol &symbol, Symbol::Flag ompFlag) {
21802287
const auto *checkSymbol{&symbol};
@@ -2322,4 +2429,44 @@ void OmpAttributeVisitor::CheckNameInAllocateStmt(
23222429
parser::ToUpperCaseLetters(
23232430
llvm::omp::getOpenMPDirectiveName(GetContext().directive).str()));
23242431
}
2432+
2433+
void OmpAttributeVisitor::AddOmpRequiresToScope(Scope &scope,
2434+
WithOmpDeclarative::RequiresFlags flags,
2435+
std::optional<common::OmpAtomicDefaultMemOrderType> memOrder) {
2436+
Scope *scopeIter = &scope;
2437+
do {
2438+
if (Symbol * symbol{scopeIter->symbol()}) {
2439+
common::visit(
2440+
[&](auto &details) {
2441+
// Store clauses information into the symbol for the parent and
2442+
// enclosing modules, programs, functions and subroutines.
2443+
if constexpr (std::is_convertible_v<decltype(&details),
2444+
WithOmpDeclarative *>) {
2445+
if (flags.any()) {
2446+
if (const WithOmpDeclarative::RequiresFlags *
2447+
otherFlags{details.ompRequires()}) {
2448+
flags |= *otherFlags;
2449+
}
2450+
details.set_ompRequires(flags);
2451+
}
2452+
if (memOrder) {
2453+
if (details.has_ompAtomicDefaultMemOrder() &&
2454+
*details.ompAtomicDefaultMemOrder() != *memOrder) {
2455+
context_.Say(scopeIter->sourceRange(),
2456+
"Conflicting '%s' REQUIRES clauses found in compilation "
2457+
"unit"_err_en_US,
2458+
parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(
2459+
llvm::omp::Clause::OMPC_atomic_default_mem_order)
2460+
.str()));
2461+
}
2462+
details.set_ompAtomicDefaultMemOrder(*memOrder);
2463+
}
2464+
}
2465+
},
2466+
symbol->details());
2467+
}
2468+
scopeIter = &scopeIter->parent();
2469+
} while (!scopeIter->IsGlobal());
2470+
}
2471+
23252472
} // namespace Fortran::semantics

flang/lib/Semantics/resolve-directives.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Fortran::parser {
1313
struct Name;
14+
struct Program;
1415
struct ProgramUnit;
1516
} // namespace Fortran::parser
1617

@@ -21,6 +22,7 @@ class SemanticsContext;
2122
// Name resolution for OpenACC and OpenMP directives
2223
void ResolveAccParts(SemanticsContext &, const parser::ProgramUnit &);
2324
void ResolveOmpParts(SemanticsContext &, const parser::ProgramUnit &);
25+
void ResolveOmpTopLevelParts(SemanticsContext &, const parser::Program &);
2426

2527
} // namespace Fortran::semantics
2628
#endif

flang/lib/Semantics/resolve-names.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8699,11 +8699,14 @@ void ResolveNamesVisitor::ResolveExecutionParts(const ProgramTree &node) {
86998699
}
87008700
}
87018701

8702-
void ResolveNamesVisitor::Post(const parser::Program &) {
8702+
void ResolveNamesVisitor::Post(const parser::Program &x) {
87038703
// ensure that all temps were deallocated
87048704
CHECK(!attrs_);
87058705
CHECK(!cudaDataAttr_);
87068706
CHECK(!GetDeclTypeSpec());
8707+
// Top-level resolution to propagate information across program units after
8708+
// each of them has been resolved separately.
8709+
ResolveOmpTopLevelParts(context(), x);
87078710
}
87088711

87098712
// A singleton instance of the scope -> IMPLICIT rules mapping is
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
! RUN: %python %S/../test_errors.py %s %flang -fopenmp
2+
! OpenMP Version 5.0
3+
! 2.4 Requires directive
4+
! All atomic_default_mem_order clauses in 'requires' directives found within a
5+
! compilation unit must specify the same ordering.
6+
7+
subroutine f
8+
!$omp requires atomic_default_mem_order(seq_cst)
9+
end subroutine f
10+
11+
!ERROR: Conflicting 'ATOMIC_DEFAULT_MEM_ORDER' REQUIRES clauses found in compilation unit
12+
subroutine g
13+
!$omp requires atomic_default_mem_order(relaxed)
14+
end subroutine g

0 commit comments

Comments
 (0)