Skip to content

Commit fb4ecad

Browse files
authored
[flang][OpenMP] Change clause modifier representation in parser (#116656)
The main issue to solve is that OpenMP modifiers can be specified in any order, so the parser cannot expect any specific modifier at a given position. To solve that, define modifier to be a union of all allowable specific modifiers for a given clause. Additionally, implement modifier descriptors: for each modifier the corresponding descriptor contains a set of properties of the modifier that allow a common set of semantic checks. Start with the syntactic properties defined in the spec: Required, Unique, Exclusive, Ultimate, and implement common checks to verify each of them. OpenMP modifier overhaul: #2/3
1 parent 4b3b74d commit fb4ecad

File tree

5 files changed

+545
-0
lines changed

5 files changed

+545
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,391 @@
1+
//===-- flang/lib/Semantics/openmp-modifiers.h ------------------*- C++ -*-===//
2+
//
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
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef FORTRAN_SEMANTICS_OPENMP_MODIFIERS_H_
10+
#define FORTRAN_SEMANTICS_OPENMP_MODIFIERS_H_
11+
12+
#include "flang/Common/enum-set.h"
13+
#include "flang/Parser/parse-tree.h"
14+
#include "flang/Semantics/semantics.h"
15+
#include "llvm/ADT/STLExtras.h"
16+
#include "llvm/ADT/StringRef.h"
17+
#include "llvm/Frontend/OpenMP/OMP.h"
18+
19+
#include <cassert>
20+
#include <map>
21+
#include <optional>
22+
#include <variant>
23+
24+
namespace Fortran::semantics {
25+
26+
// Ref: [5.2:58]
27+
//
28+
// Syntactic properties for Clauses, Arguments and Modifiers
29+
//
30+
// Inverse properties:
31+
// not Required -> Optional
32+
// not Unique -> Repeatable
33+
// not Exclusive -> Compatible
34+
// not Ultimate -> Free
35+
//
36+
// Clause defaults: Optional, Repeatable, Compatible, Free
37+
// Argument defaults: Required, Unique, Compatible, Free
38+
// Modifier defaults: Optional, Unique, Compatible, Free
39+
//
40+
// ---
41+
// Each modifier is used as either pre-modifier (i.e. modifier: item),
42+
// or post-modifier (i.e. item: modifier). The default is pre-.
43+
// Add an additional property that reflects the type of modifier.
44+
45+
ENUM_CLASS(OmpProperty, Required, Unique, Exclusive, Ultimate, Post);
46+
using OmpProperties = common::EnumSet<OmpProperty, OmpProperty_enumSize>;
47+
using OmpClauses =
48+
common::EnumSet<llvm::omp::Clause, llvm::omp::Clause_enumSize>;
49+
50+
struct OmpModifierDescriptor {
51+
// Modifier name for use in diagnostic messages.
52+
const OmpProperties &props(unsigned version) const;
53+
const OmpClauses &clauses(unsigned version) const;
54+
55+
const llvm::StringRef name;
56+
// Version-dependent properties of the modifier.
57+
const std::map<unsigned, OmpProperties> props_;
58+
// Version-dependent set of clauses to which the modifier can apply.
59+
const std::map<unsigned, OmpClauses> clauses_;
60+
};
61+
62+
template <typename SpecificTy> const OmpModifierDescriptor &OmpGetDescriptor();
63+
64+
template <>
65+
const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpDependenceType>();
66+
template <>
67+
const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpIterator>();
68+
template <>
69+
const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpLinearModifier>();
70+
template <>
71+
const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpReductionIdentifier>();
72+
template <>
73+
const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpTaskDependenceType>();
74+
75+
// Explanation of terminology:
76+
//
77+
// A typical clause with modifier[s] looks like this (with parts that are
78+
// not relevant here removed):
79+
// struct OmpSomeClause {
80+
// struct Modifier {
81+
// using Variant = std::variant<Specific1, Specific2...>;
82+
// Variant u;
83+
// };
84+
// std::tuple<std::optional<std::list<Modifier>>, ...> t;
85+
// };
86+
//
87+
// The Speficic1, etc. refer to parser classes that represent modifiers,
88+
// e.g. OmpIterator or OmpTaskDependenceType. The Variant type contains
89+
// all modifiers that are allowed for a given clause. The Modifier class
90+
// is there to wrap the variant into the form that the parse tree visitor
91+
// expects, i.e. with traits, member "u", etc.
92+
//
93+
// To avoid ambiguities with the word "modifier" (e.g. is it "any modifier",
94+
// or "this specific modifier"?), the following code uses different terms:
95+
//
96+
// - UnionTy: refers to the nested "Modifier" class, i.e.
97+
// "OmpSomeClause::Modifier" in the example above.
98+
// - SpecificTy: refers to any of the alternatives, i.e. "Specific1" or
99+
// "Specific2".
100+
101+
template <typename UnionTy>
102+
const OmpModifierDescriptor &OmpGetDescriptor(const UnionTy &modifier) {
103+
return common::visit(
104+
[](auto &&m) -> decltype(auto) {
105+
using SpecificTy = llvm::remove_cvref_t<decltype(m)>;
106+
return OmpGetDescriptor<SpecificTy>();
107+
},
108+
modifier.u);
109+
}
110+
111+
/// Return the optional list of modifiers for a given `Omp[...]Clause`.
112+
/// Specifically, the parameter type `ClauseTy` is the class that OmpClause::v
113+
/// holds.
114+
template <typename ClauseTy>
115+
const std::optional<std::list<typename ClauseTy::Modifier>> &OmpGetModifiers(
116+
const ClauseTy &clause) {
117+
using UnionTy = typename ClauseTy::Modifier;
118+
return std::get<std::optional<std::list<UnionTy>>>(clause.t);
119+
}
120+
121+
namespace detail {
122+
/// Finds the first entry in the iterator range that holds the `SpecificTy`
123+
/// alternative, or the end iterator if it does not exist.
124+
/// The `SpecificTy` should be provided, the `UnionTy` is expected to be
125+
/// auto-deduced, e.g.
126+
/// const std::optional<std::list<X>> &modifiers = ...
127+
/// ... = findInRange<OmpIterator>(modifiers->begin(), modifiers->end());
128+
template <typename SpecificTy, typename UnionTy>
129+
typename std::list<UnionTy>::const_iterator findInRange(
130+
typename std::list<UnionTy>::const_iterator begin,
131+
typename std::list<UnionTy>::const_iterator end) {
132+
for (auto it{begin}; it != end; ++it) {
133+
if (std::holds_alternative<SpecificTy>(it->u)) {
134+
return it;
135+
}
136+
}
137+
return end;
138+
}
139+
} // namespace detail
140+
141+
/// Finds the entry in the list that holds the `SpecificTy` alternative,
142+
/// and returns the pointer to that alternative. If such an entry does not
143+
/// exist, it returns nullptr.
144+
/// The list is assumed to contain at most one such item, with a check
145+
/// whether the condition is met.
146+
/// This function should only be called after the verification of modifier
147+
/// properties has been performed, since it will assert if multiple items
148+
/// are found.
149+
template <typename SpecificTy, typename UnionTy>
150+
const SpecificTy *OmpGetUniqueModifier(
151+
const std::optional<std::list<UnionTy>> &modifiers) {
152+
const SpecificTy *found{nullptr};
153+
if (modifiers) {
154+
auto end{modifiers->cend()};
155+
// typename std::list<UnionTy>::iterator end{modifiers->end()};
156+
auto at{detail::findInRange<SpecificTy, UnionTy>(modifiers->cbegin(), end)};
157+
if (at != end) {
158+
found = &std::get<SpecificTy>(at->u);
159+
#ifndef NDEBUG
160+
auto another{
161+
detail::findInRange<SpecificTy, UnionTy>(std::next(at), end)};
162+
assert(another == end && "repeated modifier");
163+
#endif
164+
}
165+
}
166+
return found;
167+
}
168+
169+
namespace detail {
170+
template <typename T> constexpr const T *make_nullptr() {
171+
return static_cast<const T *>(nullptr);
172+
}
173+
174+
/// Helper function for verifying the Required property:
175+
/// For a specific SpecificTy, if SpecificTy is has the Required property,
176+
/// check if the list has an item that holds SpecificTy as an alternative.
177+
/// If SpecificTy does not have the Required property, ignore it.
178+
template <typename SpecificTy, typename UnionTy>
179+
bool verifyIfRequired(const SpecificTy *,
180+
const std::optional<std::list<UnionTy>> &modifiers,
181+
parser::CharBlock clauseSource, SemanticsContext &semaCtx) {
182+
unsigned version{semaCtx.langOptions().OpenMPVersion};
183+
const OmpModifierDescriptor &desc{OmpGetDescriptor<SpecificTy>()};
184+
if (!desc.props(version).test(OmpProperty::Required)) {
185+
// If the modifier is not required, there is nothing to do.
186+
return true;
187+
}
188+
bool present{modifiers.has_value()};
189+
present = present && llvm::any_of(*modifiers, [](auto &&m) {
190+
return std::holds_alternative<SpecificTy>(m.u);
191+
});
192+
if (!present) {
193+
semaCtx.Say(
194+
clauseSource, "A %s modifier is required"_err_en_US, desc.name.str());
195+
}
196+
return present;
197+
}
198+
199+
/// Helper function for verifying the Required property:
200+
/// Visit all specific types in UnionTy, and verify the Required property
201+
/// for each one of them.
202+
template <typename UnionTy, size_t... Idxs>
203+
bool verifyRequiredPack(const std::optional<std::list<UnionTy>> &modifiers,
204+
parser::CharBlock clauseSource, SemanticsContext &semaCtx,
205+
std::integer_sequence<size_t, Idxs...>) {
206+
using VariantTy = typename UnionTy::Variant;
207+
return (verifyIfRequired(
208+
make_nullptr<std::variant_alternative_t<Idxs, VariantTy>>(),
209+
modifiers, clauseSource, semaCtx) &&
210+
...);
211+
}
212+
213+
/// Verify the Required property for the given list. Return true if the
214+
/// list is valid, or false otherwise.
215+
template <typename UnionTy>
216+
bool verifyRequired(const std::optional<std::list<UnionTy>> &modifiers,
217+
parser::CharBlock clauseSource, SemanticsContext &semaCtx) {
218+
using VariantTy = typename UnionTy::Variant;
219+
return verifyRequiredPack(modifiers, clauseSource, semaCtx,
220+
std::make_index_sequence<std::variant_size_v<VariantTy>>{});
221+
}
222+
223+
/// Helper function to verify the Unique property.
224+
/// If SpecificTy has the Unique property, and an item is found holding
225+
/// it as the alternative, verify that none of the elements that follow
226+
/// hold SpecificTy as the alternative.
227+
template <typename UnionTy, typename SpecificTy>
228+
bool verifyIfUnique(const SpecificTy *,
229+
typename std::list<UnionTy>::const_iterator specific,
230+
typename std::list<UnionTy>::const_iterator end,
231+
SemanticsContext &semaCtx) {
232+
// `specific` is the location of the modifier of type SpecificTy.
233+
assert(specific != end && "`specific` must be a valid location");
234+
235+
unsigned version{semaCtx.langOptions().OpenMPVersion};
236+
const OmpModifierDescriptor &desc{OmpGetDescriptor<SpecificTy>()};
237+
// Ultimate implies Unique.
238+
if (!desc.props(version).test(OmpProperty::Unique) &&
239+
!desc.props(version).test(OmpProperty::Ultimate)) {
240+
return true;
241+
}
242+
if (std::next(specific) != end) {
243+
auto next{
244+
detail::findInRange<SpecificTy, UnionTy>(std::next(specific), end)};
245+
if (next != end) {
246+
semaCtx.Say(next->source, "A %s cannot occur multiple times"_err_en_US,
247+
desc.name.str());
248+
}
249+
}
250+
return true;
251+
}
252+
253+
/// Verify the Unique property for the given list. Return true if the
254+
/// list is valid, or false otherwise.
255+
template <typename UnionTy>
256+
bool verifyUnique(const std::optional<std::list<UnionTy>> &modifiers,
257+
parser::CharBlock clauseSource, SemanticsContext &semaCtx) {
258+
if (!modifiers) {
259+
return true;
260+
}
261+
bool result{true};
262+
for (auto it{modifiers->cbegin()}, end{modifiers->cend()}; it != end; ++it) {
263+
result = common::visit(
264+
[&](auto &&m) {
265+
return verifyIfUnique<UnionTy>(&m, it, end, semaCtx);
266+
},
267+
it->u) &&
268+
result;
269+
}
270+
return result;
271+
}
272+
273+
/// Verify the Ultimate property for the given list. Return true if the
274+
/// list is valid, or false otherwise.
275+
template <typename UnionTy>
276+
bool verifyUltimate(const std::optional<std::list<UnionTy>> &modifiers,
277+
parser::CharBlock clauseSource, SemanticsContext &semaCtx) {
278+
if (!modifiers || modifiers->size() <= 1) {
279+
return true;
280+
}
281+
unsigned version{semaCtx.langOptions().OpenMPVersion};
282+
bool result{true};
283+
auto first{modifiers->cbegin()};
284+
auto last{std::prev(modifiers->cend())};
285+
286+
// Any item that has the Ultimate property has to be either at the back
287+
// or at the front of the list (depending on whether it's a pre- or a post-
288+
// modifier).
289+
// Walk over the list, and if a given item has the Ultimate property but is
290+
// not at the right position, mark it as an error.
291+
for (auto it{first}, end{modifiers->cend()}; it != end; ++it) {
292+
result =
293+
common::visit(
294+
[&](auto &&m) {
295+
using SpecificTy = llvm::remove_cvref_t<decltype(m)>;
296+
const OmpModifierDescriptor &desc{OmpGetDescriptor<SpecificTy>()};
297+
auto &props{desc.props(version)};
298+
299+
if (props.test(OmpProperty::Ultimate)) {
300+
bool isPre = !props.test(OmpProperty::Post);
301+
if (it == (isPre ? last : first)) {
302+
// Skip, since this is the correct place for this modifier.
303+
return true;
304+
}
305+
llvm::StringRef where{isPre ? "last" : "first"};
306+
semaCtx.Say(it->source,
307+
"The %s should be the %s modifier"_err_en_US,
308+
desc.name.str(), where.str());
309+
return false;
310+
}
311+
return true;
312+
},
313+
it->u) &&
314+
result;
315+
}
316+
return result;
317+
}
318+
319+
/// Verify the Exclusive property for the given list. Return true if the
320+
/// list is valid, or false otherwise.
321+
template <typename UnionTy>
322+
bool verifyExclusive(const std::optional<std::list<UnionTy>> &modifiers,
323+
parser::CharBlock clauseSource, SemanticsContext &semaCtx) {
324+
if (!modifiers || modifiers->size() <= 1) {
325+
return true;
326+
}
327+
unsigned version{semaCtx.langOptions().OpenMPVersion};
328+
const UnionTy &front{modifiers->front()};
329+
const OmpModifierDescriptor &frontDesc{OmpGetDescriptor(front)};
330+
331+
auto second{std::next(modifiers->cbegin())};
332+
auto end{modifiers->end()};
333+
334+
auto emitErrorMessage{[&](const UnionTy &excl, const UnionTy &other) {
335+
const OmpModifierDescriptor &descExcl{OmpGetDescriptor(excl)};
336+
const OmpModifierDescriptor &descOther{OmpGetDescriptor(other)};
337+
parser::MessageFormattedText txt(
338+
"An exclusive %s cannot be specified together with a modifier of a different type"_err_en_US,
339+
descExcl.name.str());
340+
parser::Message message(excl.source, txt);
341+
message.Attach(
342+
other.source, "%s provided here"_en_US, descOther.name.str());
343+
semaCtx.Say(std::move(message));
344+
}};
345+
346+
if (frontDesc.props(version).test(OmpProperty::Exclusive)) {
347+
// If the first item has the Exclusive property, then check if there is
348+
// another item in the rest of the list with a different SpecificTy as
349+
// the alternative, and mark it as an error. This allows multiple Exclusive
350+
// items to coexist as long as they hold the same SpecificTy.
351+
bool result{true};
352+
size_t frontIndex{front.u.index()};
353+
for (auto it{second}; it != end; ++it) {
354+
if (it->u.index() != frontIndex) {
355+
emitErrorMessage(front, *it);
356+
result = false;
357+
break;
358+
}
359+
}
360+
return result;
361+
} else {
362+
// If the first item does not have the Exclusive property, then check
363+
// if there is an item in the rest of the list that is Exclusive, and
364+
// mark it as an error if so.
365+
bool result{true};
366+
for (auto it{second}; it != end; ++it) {
367+
const OmpModifierDescriptor &desc{OmpGetDescriptor(*it)};
368+
if (desc.props(version).test(OmpProperty::Exclusive)) {
369+
emitErrorMessage(*it, front);
370+
result = false;
371+
break;
372+
}
373+
}
374+
return result;
375+
}
376+
}
377+
} // namespace detail
378+
379+
template <typename ClauseTy>
380+
bool OmpVerifyModifiers(const ClauseTy &clause, parser::CharBlock clauseSource,
381+
SemanticsContext &semaCtx) {
382+
auto &modifiers{OmpGetModifiers(clause)};
383+
bool result{detail::verifyRequired(modifiers, clauseSource, semaCtx)};
384+
result = detail::verifyUnique(modifiers, clauseSource, semaCtx) && result;
385+
result = detail::verifyUltimate(modifiers, clauseSource, semaCtx) && result;
386+
result = detail::verifyExclusive(modifiers, clauseSource, semaCtx) && result;
387+
return result;
388+
}
389+
} // namespace Fortran::semantics
390+
391+
#endif // FORTRAN_SEMANTICS_OPENMP_MODIFIERS_H_

flang/lib/Semantics/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ add_flang_library(FortranSemantics
3131
definable.cpp
3232
expression.cpp
3333
mod-file.cpp
34+
openmp-modifiers.cpp
3435
pointer-assignment.cpp
3536
program-tree.cpp
3637
resolve-labels.cpp

0 commit comments

Comments
 (0)