-
Notifications
You must be signed in to change notification settings - Fork 12.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[flang][OpenMP] Change clause modifier representation in parser #116656
Conversation
This is the first part of the effort to make parsing of clause modifiers more uniform and robust. Currently, when multiple modifiers are allowed, the parser will expect them to appear in a hard-coded order. Additionally, modifier properties (such as "ultimate") are checked separately for each case. The overall plan is 1. Extract all modifiers into their own top-level classes, and then equip them with sets of common properties that will allow performing the property checks generically, without refering to the specific kind of the modifier. 2. Define a parser (as a separate class) for each modifier. 3. For each clause define a union (std::variant) of all allowable modifiers, and parse the modifiers as a list of these unions. The intent is also to isolate parts of the code that could eventually be auto-generated. OpenMP modifier overhaul: #1/3
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
@llvm/pr-subscribers-flang-openmp @llvm/pr-subscribers-flang-semantics Author: Krzysztof Parzyszek (kparzysz) ChangesThe 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 Patch is 21.10 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/116656.diff 5 Files Affected:
diff --git a/flang/include/flang/Semantics/openmp-modifiers.h b/flang/include/flang/Semantics/openmp-modifiers.h
new file mode 100644
index 00000000000000..6be582761ed687
--- /dev/null
+++ b/flang/include/flang/Semantics/openmp-modifiers.h
@@ -0,0 +1,391 @@
+//===-- flang/lib/Semantics/openmp-modifiers.h ------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FORTRAN_SEMANTICS_OPENMP_MODIFIERS_H_
+#define FORTRAN_SEMANTICS_OPENMP_MODIFIERS_H_
+
+#include "flang/Common/enum-set.h"
+#include "flang/Parser/parse-tree.h"
+#include "flang/Semantics/semantics.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Frontend/OpenMP/OMP.h"
+
+#include <cassert>
+#include <map>
+#include <optional>
+#include <variant>
+
+namespace Fortran::semantics {
+
+// Ref: [5.2:58]
+//
+// Syntactic properties for Clauses, Arguments and Modifiers
+//
+// Inverse properties:
+// not Required -> Optional
+// not Unique -> Repeatable
+// not Exclusive -> Compatible
+// not Ultimate -> Free
+//
+// Clause defaults: Optional, Repeatable, Compatible, Free
+// Argument defaults: Required, Unique, Compatible, Free
+// Modifier defaults: Optional, Unique, Compatible, Free
+//
+// ---
+// Each modifier is used as either pre-modifier (i.e. modifier: item),
+// or post-modifier (i.e. item: modifier). The default is pre-.
+// Add an additional property that reflects the type of modifier.
+
+ENUM_CLASS(OmpProperty, Required, Unique, Exclusive, Ultimate, Post);
+using OmpProperties = common::EnumSet<OmpProperty, OmpProperty_enumSize>;
+using OmpClauses =
+ common::EnumSet<llvm::omp::Clause, llvm::omp::Clause_enumSize>;
+
+struct OmpModifierDescriptor {
+ // Modifier name for use in diagnostic messages.
+ const OmpProperties &props(unsigned version) const;
+ const OmpClauses &clauses(unsigned version) const;
+
+ const llvm::StringRef name;
+ // Version-dependent properties of the modifier.
+ const std::map<unsigned, OmpProperties> props_;
+ // Version-dependent set of clauses to which the modifier can apply.
+ const std::map<unsigned, OmpClauses> clauses_;
+};
+
+template <typename SpecificTy> const OmpModifierDescriptor &OmpGetDescriptor();
+
+template <>
+const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpDependenceType>();
+template <>
+const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpIterator>();
+template <>
+const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpLinearModifier>();
+template <>
+const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpReductionIdentifier>();
+template <>
+const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpTaskDependenceType>();
+
+// Explanation of terminology:
+//
+// A typical clause with modifier[s] looks like this (with parts that are
+// not relevant here removed):
+// struct OmpSomeClause {
+// struct Modifier {
+// using Variant = std::variant<Specific1, Specific2...>;
+// Variant u;
+// };
+// std::tuple<std::optional<std::list<Modifier>>, ...> t;
+// };
+//
+// The Speficic1, etc. refer to parser classes that represent modifiers,
+// e.g. OmpIterator or OmpTaskDependenceType. The Variant type contains
+// all modifiers that are allowed for a given clause. The Modifier class
+// is there to wrap the variant into the form that the parse tree visitor
+// expects, i.e. with traits, member "u", etc.
+//
+// To avoid ambiguities with the word "modifier" (e.g. is it "any modifier",
+// or "this specific modifier"?), the following code uses different terms:
+//
+// - UnionTy: refers to the nested "Modifier" class, i.e.
+// "OmpSomeClause::Modifier" in the example above.
+// - SpecificTy: refers to any of the alternatives, i.e. "Specific1" or
+// "Specific2".
+
+template <typename UnionTy>
+const OmpModifierDescriptor &OmpGetDescriptor(const UnionTy &modifier) {
+ return common::visit(
+ [](auto &&m) -> decltype(auto) {
+ using SpecificTy = llvm::remove_cvref_t<decltype(m)>;
+ return OmpGetDescriptor<SpecificTy>();
+ },
+ modifier.u);
+}
+
+/// Return the optional list of modifiers for a given `Omp[...]Clause`.
+/// Specifically, the parameter type `ClauseTy` is the class that OmpClause::v
+/// holds.
+template <typename ClauseTy>
+const std::optional<std::list<typename ClauseTy::Modifier>> &OmpGetModifiers(
+ const ClauseTy &clause) {
+ using UnionTy = typename ClauseTy::Modifier;
+ return std::get<std::optional<std::list<UnionTy>>>(clause.t);
+}
+
+namespace detail {
+/// Finds the first entry in the iterator range that holds the `SpecificTy`
+/// alternative, or the end iterator if it does not exist.
+/// The `SpecificTy` should be provided, the `UnionTy` is expected to be
+/// auto-deduced, e.g.
+/// const std::optional<std::list<X>> &modifiers = ...
+/// ... = findInRange<OmpIterator>(modifiers->begin(), modifiers->end());
+template <typename SpecificTy, typename UnionTy>
+typename std::list<UnionTy>::const_iterator findInRange(
+ typename std::list<UnionTy>::const_iterator begin,
+ typename std::list<UnionTy>::const_iterator end) {
+ for (auto it{begin}; it != end; ++it) {
+ if (std::holds_alternative<SpecificTy>(it->u)) {
+ return it;
+ }
+ }
+ return end;
+}
+} // namespace detail
+
+/// Finds the entry in the list that holds the `SpecificTy` alternative,
+/// and returns the pointer to that alternative. If such an entry does not
+/// exist, it returns nullptr.
+/// The list is assumed to contain at most one such item, with a check
+/// whether the condition is met.
+/// This function should only be called after the verification of modifier
+/// properties has been performed, since it will assert if multiple items
+/// are found.
+template <typename SpecificTy, typename UnionTy>
+const SpecificTy *OmpGetUniqueModifier(
+ const std::optional<std::list<UnionTy>> &modifiers) {
+ const SpecificTy *found{nullptr};
+ if (modifiers) {
+ auto end{modifiers->cend()};
+ // typename std::list<UnionTy>::iterator end{modifiers->end()};
+ auto at{detail::findInRange<SpecificTy, UnionTy>(modifiers->cbegin(), end)};
+ if (at != end) {
+ found = &std::get<SpecificTy>(at->u);
+#ifndef NDEBUG
+ auto another{
+ detail::findInRange<SpecificTy, UnionTy>(std::next(at), end)};
+ assert(another == end && "repeated modifier");
+#endif
+ }
+ }
+ return found;
+}
+
+namespace detail {
+template <typename T> constexpr const T *make_nullptr() {
+ return static_cast<const T *>(nullptr);
+}
+
+/// Helper function for verifying the Required property:
+/// For a specific SpecificTy, if SpecificTy is has the Required property,
+/// check if the list has an item that holds SpecificTy as an alternative.
+/// If SpecificTy does not have the Required property, ignore it.
+template <typename SpecificTy, typename UnionTy>
+bool verifyIfRequired(const SpecificTy *,
+ const std::optional<std::list<UnionTy>> &modifiers,
+ parser::CharBlock clauseSource, SemanticsContext &semaCtx) {
+ unsigned version{semaCtx.langOptions().OpenMPVersion};
+ const OmpModifierDescriptor &desc{OmpGetDescriptor<SpecificTy>()};
+ if (!desc.props(version).test(OmpProperty::Required)) {
+ // If the modifier is not required, there is nothing to do.
+ return true;
+ }
+ bool present{modifiers.has_value()};
+ present = present && llvm::any_of(*modifiers, [](auto &&m) {
+ return std::holds_alternative<SpecificTy>(m.u);
+ });
+ if (!present) {
+ semaCtx.Say(
+ clauseSource, "A %s modifier is required"_err_en_US, desc.name.str());
+ }
+ return present;
+}
+
+/// Helper function for verifying the Required property:
+/// Visit all specific types in UnionTy, and verify the Required property
+/// for each one of them.
+template <typename UnionTy, size_t... Idxs>
+bool verifyRequiredPack(const std::optional<std::list<UnionTy>> &modifiers,
+ parser::CharBlock clauseSource, SemanticsContext &semaCtx,
+ std::integer_sequence<size_t, Idxs...>) {
+ using VariantTy = typename UnionTy::Variant;
+ return (verifyIfRequired(
+ make_nullptr<std::variant_alternative_t<Idxs, VariantTy>>(),
+ modifiers, clauseSource, semaCtx) &&
+ ...);
+}
+
+/// Verify the Required property for the given list. Return true if the
+/// list is valid, or false otherwise.
+template <typename UnionTy>
+bool verifyRequired(const std::optional<std::list<UnionTy>> &modifiers,
+ parser::CharBlock clauseSource, SemanticsContext &semaCtx) {
+ using VariantTy = typename UnionTy::Variant;
+ return verifyRequiredPack(modifiers, clauseSource, semaCtx,
+ std::make_index_sequence<std::variant_size_v<VariantTy>>{});
+}
+
+/// Helper function to verify the Unique property.
+/// If SpecificTy has the Unique property, and an item is found holding
+/// it as the alternative, verify that none of the elements that follow
+/// hold SpecificTy as the alternative.
+template <typename UnionTy, typename SpecificTy>
+bool verifyIfUnique(const SpecificTy *,
+ typename std::list<UnionTy>::const_iterator specific,
+ typename std::list<UnionTy>::const_iterator end,
+ SemanticsContext &semaCtx) {
+ // `specific` is the location of the modifier of type SpecificTy.
+ assert(specific != end && "`specific` must be a valid location");
+
+ unsigned version{semaCtx.langOptions().OpenMPVersion};
+ const OmpModifierDescriptor &desc{OmpGetDescriptor<SpecificTy>()};
+ // Ultimate implies Unique.
+ if (!desc.props(version).test(OmpProperty::Unique) &&
+ !desc.props(version).test(OmpProperty::Ultimate)) {
+ return true;
+ }
+ if (std::next(specific) != end) {
+ auto next{
+ detail::findInRange<SpecificTy, UnionTy>(std::next(specific), end)};
+ if (next != end) {
+ semaCtx.Say(next->source, "A %s cannot occur multiple times"_err_en_US,
+ desc.name.str());
+ }
+ }
+ return true;
+}
+
+/// Verify the Unique property for the given list. Return true if the
+/// list is valid, or false otherwise.
+template <typename UnionTy>
+bool verifyUnique(const std::optional<std::list<UnionTy>> &modifiers,
+ parser::CharBlock clauseSource, SemanticsContext &semaCtx) {
+ if (!modifiers) {
+ return true;
+ }
+ bool result{true};
+ for (auto it{modifiers->cbegin()}, end{modifiers->cend()}; it != end; ++it) {
+ result = common::visit(
+ [&](auto &&m) {
+ return verifyIfUnique<UnionTy>(&m, it, end, semaCtx);
+ },
+ it->u) &&
+ result;
+ }
+ return result;
+}
+
+/// Verify the Ultimate property for the given list. Return true if the
+/// list is valid, or false otherwise.
+template <typename UnionTy>
+bool verifyUltimate(const std::optional<std::list<UnionTy>> &modifiers,
+ parser::CharBlock clauseSource, SemanticsContext &semaCtx) {
+ if (!modifiers || modifiers->size() <= 1) {
+ return true;
+ }
+ unsigned version{semaCtx.langOptions().OpenMPVersion};
+ bool result{true};
+ auto first{modifiers->cbegin()};
+ auto last{std::prev(modifiers->cend())};
+
+ // Any item that has the Ultimate property has to be either at the back
+ // or at the front of the list (depending on whether it's a pre- or a post-
+ // modifier).
+ // Walk over the list, and if a given item has the Ultimate property but is
+ // not at the right position, mark it as an error.
+ for (auto it{first}, end{modifiers->cend()}; it != end; ++it) {
+ result =
+ common::visit(
+ [&](auto &&m) {
+ using SpecificTy = llvm::remove_cvref_t<decltype(m)>;
+ const OmpModifierDescriptor &desc{OmpGetDescriptor<SpecificTy>()};
+ auto &props{desc.props(version)};
+
+ if (props.test(OmpProperty::Ultimate)) {
+ bool isPre = !props.test(OmpProperty::Post);
+ if (it == (isPre ? last : first)) {
+ // Skip, since this is the correct place for this modifier.
+ return true;
+ }
+ llvm::StringRef where{isPre ? "last" : "first"};
+ semaCtx.Say(it->source,
+ "The %s should be the %s modifier"_err_en_US,
+ desc.name.str(), where.str());
+ return false;
+ }
+ return true;
+ },
+ it->u) &&
+ result;
+ }
+ return result;
+}
+
+/// Verify the Exclusive property for the given list. Return true if the
+/// list is valid, or false otherwise.
+template <typename UnionTy>
+bool verifyExclusive(const std::optional<std::list<UnionTy>> &modifiers,
+ parser::CharBlock clauseSource, SemanticsContext &semaCtx) {
+ if (!modifiers || modifiers->size() <= 1) {
+ return true;
+ }
+ unsigned version{semaCtx.langOptions().OpenMPVersion};
+ const UnionTy &front{modifiers->front()};
+ const OmpModifierDescriptor &frontDesc{OmpGetDescriptor(front)};
+
+ auto second{std::next(modifiers->cbegin())};
+ auto end{modifiers->end()};
+
+ auto emitErrorMessage{[&](const UnionTy &excl, const UnionTy &other) {
+ const OmpModifierDescriptor &descExcl{OmpGetDescriptor(excl)};
+ const OmpModifierDescriptor &descOther{OmpGetDescriptor(other)};
+ parser::MessageFormattedText txt(
+ "An exclusive %s cannot be specified together with a modifier of a different type"_err_en_US,
+ descExcl.name.str());
+ parser::Message message(excl.source, txt);
+ message.Attach(
+ other.source, "%s provided here"_en_US, descOther.name.str());
+ semaCtx.Say(std::move(message));
+ }};
+
+ if (frontDesc.props(version).test(OmpProperty::Exclusive)) {
+ // If the first item has the Exclusive property, then check if there is
+ // another item in the rest of the list with a different SpecificTy as
+ // the alternative, and mark it as an error. This allows multiple Exclusive
+ // items to coexist as long as they hold the same SpecificTy.
+ bool result{true};
+ size_t frontIndex{front.u.index()};
+ for (auto it{second}; it != end; ++it) {
+ if (it->u.index() != frontIndex) {
+ emitErrorMessage(front, *it);
+ result = false;
+ break;
+ }
+ }
+ return result;
+ } else {
+ // If the first item does not have the Exclusive property, then check
+ // if there is an item in the rest of the list that is Exclusive, and
+ // mark it as an error if so.
+ bool result{true};
+ for (auto it{second}; it != end; ++it) {
+ const OmpModifierDescriptor &desc{OmpGetDescriptor(*it)};
+ if (desc.props(version).test(OmpProperty::Exclusive)) {
+ emitErrorMessage(*it, front);
+ result = false;
+ break;
+ }
+ }
+ return result;
+ }
+}
+} // namespace detail
+
+template <typename ClauseTy>
+bool OmpVerifyModifiers(const ClauseTy &clause, parser::CharBlock clauseSource,
+ SemanticsContext &semaCtx) {
+ auto &modifiers{OmpGetModifiers(clause)};
+ bool result{detail::verifyRequired(modifiers, clauseSource, semaCtx)};
+ result = detail::verifyUnique(modifiers, clauseSource, semaCtx) && result;
+ result = detail::verifyUltimate(modifiers, clauseSource, semaCtx) && result;
+ result = detail::verifyExclusive(modifiers, clauseSource, semaCtx) && result;
+ return result;
+}
+} // namespace Fortran::semantics
+
+#endif // FORTRAN_SEMANTICS_OPENMP_MODIFIERS_H_
diff --git a/flang/lib/Semantics/CMakeLists.txt b/flang/lib/Semantics/CMakeLists.txt
index 41406ecf50e004..7855ae7eed1387 100644
--- a/flang/lib/Semantics/CMakeLists.txt
+++ b/flang/lib/Semantics/CMakeLists.txt
@@ -31,6 +31,7 @@ add_flang_library(FortranSemantics
definable.cpp
expression.cpp
mod-file.cpp
+ openmp-modifiers.cpp
pointer-assignment.cpp
program-tree.cpp
resolve-labels.cpp
diff --git a/flang/lib/Semantics/openmp-modifiers.cpp b/flang/lib/Semantics/openmp-modifiers.cpp
new file mode 100644
index 00000000000000..70ca988cddce59
--- /dev/null
+++ b/flang/lib/Semantics/openmp-modifiers.cpp
@@ -0,0 +1,146 @@
+//===-- flang/lib/Semantics/openmp-modifiers.cpp --------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "flang/Semantics/openmp-modifiers.h"
+
+#include "flang/Parser/parse-tree.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/Frontend/OpenMP/OMP.h"
+
+#include <algorithm>
+#include <cassert>
+#include <map>
+
+namespace Fortran::semantics {
+using namespace llvm::omp;
+
+/// Find the highest version that exists as a key in the given map,
+/// and is less than or equal to `version`.
+/// Account for "version" not being a value from getOpenMPVersions().
+template <typename ValueTy>
+static unsigned findVersion(
+ unsigned version, const std::map<unsigned, ValueTy> &map) {
+ llvm::ArrayRef<unsigned> versions{llvm::omp::getOpenMPVersions()};
+ assert(!versions.empty() && "getOpenMPVersions returned empty list");
+ version = std::clamp(version, versions.front(), versions.back());
+
+ // std::map is sorted with respect to keys, by default in the ascending
+ // order.
+ unsigned found{0};
+ for (auto &[v, _] : map) {
+ if (v <= version) {
+ found = v;
+ } else {
+ break;
+ }
+ }
+
+ assert(found != 0 && "cannot locate entry for version in map");
+ return found;
+}
+
+const OmpProperties &OmpModifierDescriptor::props(unsigned version) const {
+ return props_.at(findVersion(version, props_));
+}
+
+const OmpClauses &OmpModifierDescriptor::clauses(unsigned version) const {
+ return clauses_.at(findVersion(version, clauses_));
+}
+
+// Note: The intent for these functions is to have them be automatically-
+// generated in the future.
+
+template <>
+const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpDependenceType>() {
+ static const OmpModifierDescriptor desc{
+ /*name=*/"dependence-type",
+ /*props=*/
+ {
+ {45, {OmpProperty::Required, OmpProperty::Ultimate}},
+ },
+ /*clauses=*/
+ {
+ {45, {Clause::OMPC_depend}},
+ {51, {Clause::OMPC_depend, Clause::OMPC_update}},
+ {52, {Clause::OMPC_doacross}},
+ },
+ };
+ return desc;
+}
+
+template <>
+const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpIterator>() {
+ static const OmpModifierDescriptor desc{
+ /*name=*/"iterator",
+ /*props=*/
+ {
+ {50, {OmpProperty::Unique}},
+ },
+ /*clauses=*/
+ {
+ {50, {Clause::OMPC_affinity, Clause::OMPC_depend}},
+ {51,
+ {Clause::OMPC_affinity, Clause::OMPC_depend, Clause::OMPC_from,
+ Clause::OMPC_map, Clause::OMPC_to}},
+ },
+ };
+ return desc;
+}
+
+template <>
+const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpLinearModifier>() {
+ static const OmpModifierDescriptor desc{
+ /*name=*/"linear-modifier",
+ /*props=*/
+ {
+ {45, {OmpProperty::Unique}},
+ },
+ /*clauses=*/
+ {
+ {45, {Clause::OMPC_linear}},
+ },
+ };
+ return desc;
+}
+
+template <>
+const OmpModifierDescriptor &
+OmpGetDescriptor<parser::OmpReductionIdentifier>() {
+ static const OmpModifierDescriptor desc{
+ /*name=*/"reduction-identifier",
+ /*props=*/
+ {
+ {45, {OmpProperty::Required, OmpProperty::Ultimate}},
+ },
+ /*clauses=*/
+ {
+ {45, {Clause::OMPC_reduction}},
+ {50,
+ {Clause::OMPC_in_reduction, Clause::OMPC_reduction,
+ Clause::OMPC_task_reduction}},
+ },
+ };
+ return desc;
+}
+
+template <>
+const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpTaskDependenceType>() {
+ static const OmpModifierDescriptor desc{
+ /*name=*/"task-dependence-type",
+ /*props=*/
+ {
+ {52, {OmpProperty::Required, OmpProperty::Ultimate}},
+ },
+ /*clauses=*/
+ {
+ {52, {Clause::OMPC_depend, Clause::OMPC_update}},
+ },
+ };
+ return desc;
+}
+} // namespace Fortran::semant...
[truncated]
|
…parzysz/spr/m02-openmp-descriptors
…parzysz/spr/m02-openmp-descriptors
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is really nice. LGTM
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/89/builds/10972 Here is the relevant piece of the build log for the reference
|
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/157/builds/13264 Here is the relevant piece of the build log for the reference
|
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/53/builds/8216 Here is the relevant piece of the build log for the reference
|
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/143/builds/3564 Here is the relevant piece of the build log for the reference
|
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/17/builds/4019 Here is the relevant piece of the build log for the reference
|
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/146/builds/1650 Here is the relevant piece of the build log for the reference
|
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