|
| 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_ |
0 commit comments