1 //===-- lib/Semantics/rewrite-directives.cpp ------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "rewrite-directives.h"
10 #include "flang/Parser/parse-tree-visitor.h"
11 #include "flang/Parser/parse-tree.h"
12 #include "flang/Semantics/semantics.h"
13 #include "flang/Semantics/symbol.h"
14 #include "llvm/Frontend/OpenMP/OMP.h"
17 namespace Fortran::semantics
{
19 using namespace parser::literals
;
21 class DirectiveRewriteMutator
{
23 explicit DirectiveRewriteMutator(SemanticsContext
&context
)
24 : context_
{context
} {}
26 // Default action for a parse tree node is to visit children.
27 template <typename T
> bool Pre(T
&) { return true; }
28 template <typename T
> void Post(T
&) {}
31 SemanticsContext
&context_
;
34 // Rewrite atomic constructs to add an explicit memory ordering to all that do
35 // not specify it, honoring in this way the `atomic_default_mem_order` clause of
36 // the REQUIRES directive.
37 class OmpRewriteMutator
: public DirectiveRewriteMutator
{
39 explicit OmpRewriteMutator(SemanticsContext
&context
)
40 : DirectiveRewriteMutator(context
) {}
42 template <typename T
> bool Pre(T
&) { return true; }
43 template <typename T
> void Post(T
&) {}
45 bool Pre(parser::OpenMPAtomicConstruct
&);
46 bool Pre(parser::OpenMPRequiresConstruct
&);
49 bool atomicDirectiveDefaultOrderFound_
{false};
52 bool OmpRewriteMutator::Pre(parser::OpenMPAtomicConstruct
&x
) {
53 // Find top-level parent of the operation.
54 Symbol
*topLevelParent
{common::visit(
56 Symbol
*symbol
{nullptr};
58 &context_
.FindScope(std::get
<parser::Verbatim
>(atomic
.t
).source
)};
60 if (Symbol
* parent
{scope
->symbol()}) {
63 scope
= &scope
->parent();
64 } while (!scope
->IsGlobal());
67 "Atomic construct must be within a scope associated with a symbol");
72 // Get the `atomic_default_mem_order` clause from the top-level parent.
73 std::optional
<common::OmpAtomicDefaultMemOrderType
> defaultMemOrder
;
76 if constexpr (std::is_convertible_v
<decltype(&details
),
77 WithOmpDeclarative
*>) {
78 if (details
.has_ompAtomicDefaultMemOrder()) {
79 defaultMemOrder
= *details
.ompAtomicDefaultMemOrder();
83 topLevelParent
->details());
85 if (!defaultMemOrder
) {
89 auto findMemOrderClause
=
90 [](const std::list
<parser::OmpAtomicClause
> &clauses
) {
91 return llvm::any_of(clauses
, [](const auto &clause
) {
92 return std::get_if
<parser::OmpMemoryOrderClause
>(&clause
.u
);
96 // Get the clause list to which the new memory order clause must be added,
97 // only if there are no other memory order clauses present for this atomic
99 std::list
<parser::OmpAtomicClause
> *clauseList
= common::visit(
100 common::visitors
{[&](parser::OmpAtomic
&atomicConstruct
) {
101 // OmpAtomic only has a single list of clauses.
102 auto &clauses
{std::get
<parser::OmpAtomicClauseList
>(
104 return !findMemOrderClause(clauses
.v
) ? &clauses
.v
107 [&](auto &atomicConstruct
) {
108 // All other atomic constructs have two lists of clauses.
109 auto &clausesLhs
{std::get
<0>(atomicConstruct
.t
)};
110 auto &clausesRhs
{std::get
<2>(atomicConstruct
.t
)};
111 return !findMemOrderClause(clausesLhs
.v
) &&
112 !findMemOrderClause(clausesRhs
.v
)
118 // Add a memory order clause to the atomic directive.
120 atomicDirectiveDefaultOrderFound_
= true;
121 switch (*defaultMemOrder
) {
122 case common::OmpAtomicDefaultMemOrderType::AcqRel
:
123 clauseList
->emplace_back
<parser::OmpMemoryOrderClause
>(common::visit(
124 common::visitors
{[](parser::OmpAtomicRead
&) -> parser::OmpClause
{
125 return parser::OmpClause::Acquire
{};
127 [](parser::OmpAtomicCapture
&) -> parser::OmpClause
{
128 return parser::OmpClause::AcqRel
{};
130 [](auto &) -> parser::OmpClause
{
131 // parser::{OmpAtomic, OmpAtomicUpdate, OmpAtomicWrite}
132 return parser::OmpClause::Release
{};
136 case common::OmpAtomicDefaultMemOrderType::Relaxed
:
137 clauseList
->emplace_back
<parser::OmpMemoryOrderClause
>(
138 parser::OmpClause
{parser::OmpClause::Relaxed
{}});
140 case common::OmpAtomicDefaultMemOrderType::SeqCst
:
141 clauseList
->emplace_back
<parser::OmpMemoryOrderClause
>(
142 parser::OmpClause
{parser::OmpClause::SeqCst
{}});
150 bool OmpRewriteMutator::Pre(parser::OpenMPRequiresConstruct
&x
) {
151 for (parser::OmpClause
&clause
: std::get
<parser::OmpClauseList
>(x
.t
).v
) {
152 if (std::holds_alternative
<parser::OmpClause::AtomicDefaultMemOrder
>(
154 atomicDirectiveDefaultOrderFound_
) {
155 context_
.Say(clause
.source
,
156 "REQUIRES directive with '%s' clause found lexically after atomic "
157 "operation without a memory order clause"_err_en_US
,
158 parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(
159 llvm::omp::OMPC_atomic_default_mem_order
)
166 bool RewriteOmpParts(SemanticsContext
&context
, parser::Program
&program
) {
167 if (!context
.IsEnabled(common::LanguageFeature::OpenMP
)) {
170 OmpRewriteMutator ompMutator
{context
};
171 parser::Walk(program
, ompMutator
);
172 return !context
.AnyFatalError();
175 } // namespace Fortran::semantics