[flang-commits] [flang] [flang][OpenMP] Implement flexible OpenMP clause representation (PR #81621)

Krzysztof Parzyszek via flang-commits flang-commits at lists.llvm.org
Wed Feb 21 05:41:58 PST 2024


================
@@ -142,6 +142,1122 @@ static void genNestedEvaluations(Fortran::lower::AbstractConverter &converter,
     converter.genEval(e);
 }
 
+//===----------------------------------------------------------------------===//
+// Clauses
+//===----------------------------------------------------------------------===//
+
+namespace detail {
+template <typename C> //
+llvm::omp::Clause getClauseIdForClass(C &&) {
+  using namespace Fortran;
+  using A = llvm::remove_cvref_t<C>; // A is referenced in OMP.inc
+  // The code included below contains a sequence of checks like the following
+  // for each OpenMP clause
+  //   if constexpr (std::is_same_v<A, parser::OmpClause::AcqRel>)
+  //     return llvm::omp::Clause::OMPC_acq_rel;
+  //   [...]
+#define GEN_FLANG_CLAUSE_PARSER_KIND_MAP
+#include "llvm/Frontend/OpenMP/OMP.inc"
+}
+} // namespace detail
+
+static llvm::omp::Clause getClauseId(const Fortran::parser::OmpClause &clause) {
+  return std::visit([](auto &&s) { return detail::getClauseIdForClass(s); },
+                    clause.u);
+}
+
+namespace omp {
+using namespace Fortran;
+using SomeType = evaluate::SomeType;
+using SomeExpr = semantics::SomeExpr;
+using MaybeExpr = semantics::MaybeExpr;
+
+template <typename T> //
+using List = std::vector<T>;
+
+struct SymDsgExtractor {
+  using SymDsg = std::tuple<semantics::Symbol *, MaybeExpr>;
+
+  template <typename T> //
+  static T &&AsRvalueRef(T &&t) {
+    return std::move(t);
+  }
+  template <typename T> //
+  static T AsRvalueRef(const T &t) {
+    return t;
+  }
+
+  template <typename T> //
+  static SymDsg visit(T &&) {
+    // Use this to see missing overloads:
+    // llvm::errs() << "NULL: " << __PRETTY_FUNCTION__ << '\n';
+    return SymDsg{};
+  }
+
+  template <typename T> //
+  static SymDsg visit(const evaluate::Designator<T> &e) {
+    // Symbols cannot be created after semantic checks, so all symbol
+    // pointers that are non-null must point to one of those pre-existing
+    // objects. Throughout the code, symbols are often pointed to by
+    // non-const pointers, so there is no harm in casting the constness
+    // away.
+    return std::make_tuple(const_cast<semantics::Symbol *>(e.GetLastSymbol()),
+                           evaluate::AsGenericExpr(AsRvalueRef(e)));
+  }
+
+  static SymDsg visit(const evaluate::ProcedureDesignator &e) {
+    // See comment above regarding const_cast.
+    return std::make_tuple(const_cast<semantics::Symbol *>(e.GetSymbol()),
+                           std::nullopt);
+  }
+
+  template <typename T> //
+  static SymDsg visit(const evaluate::Expr<T> &e) {
+    return std::visit([](auto &&s) { return visit(s); }, e.u);
+  }
+
+  static bool verify(const SymDsg &sd) {
+    const semantics::Symbol *symbol = std::get<0>(sd);
+    assert(symbol && "Expecting Symbol");
+    auto &maybeDsg = std::get<1>(sd);
+    if (!maybeDsg)
+      return true;
+    std::optional<evaluate::DataRef> maybeRef =
+        evaluate::ExtractDataRef(*maybeDsg);
+    if (maybeRef) {
+      assert(&maybeRef->GetLastSymbol() == symbol &&
+             "Designator not for symbol");
+      return true;
+    }
+
+    // This could still be a Substring or ComplexPart, but at least Substring
+    // is not allowed in OpenMP.
+    maybeDsg->dump();
+    llvm_unreachable("Expecting DataRef");
+  }
+};
+
+SymDsgExtractor::SymDsg getSymbolAndDesignator(const MaybeExpr &expr) {
+  if (!expr)
+    return SymDsgExtractor::SymDsg{};
+  return std::visit([](auto &&s) { return SymDsgExtractor::visit(s); },
+                    expr->u);
+}
+
+struct Object {
+  semantics::Symbol *sym; // symbol
+  MaybeExpr dsg;          // designator ending with symbol
+};
+
+using ObjectList = List<Object>;
+
+Object makeObject(const parser::OmpObject &object,
+                  semantics::SemanticsContext &semaCtx) {
+  // If object is a common block, expression analyzer won't be able to
+  // do anything.
+  if (const auto *name = std::get_if<parser::Name>(&object.u)) {
+    assert(name->symbol && "Expecting Symbol");
+    return Object{name->symbol, std::nullopt};
+  }
+  evaluate::ExpressionAnalyzer ea{semaCtx};
+  SymDsgExtractor::SymDsg sd = std::visit(
+      [&](auto &&s) { return getSymbolAndDesignator(ea.Analyze(s)); },
+      object.u);
+  SymDsgExtractor::verify(sd);
+  return Object{std::get<0>(sd), std::move(std::get<1>(sd))};
+}
+
+Object makeObject(const parser::Name &name,
+                  semantics::SemanticsContext &semaCtx) {
+  assert(name.symbol && "Expecting Symbol");
+  return Object{name.symbol, std::nullopt};
+}
+
+Object makeObject(const parser::Designator &dsg,
+                  semantics::SemanticsContext &semaCtx) {
+  evaluate::ExpressionAnalyzer ea{semaCtx};
+  SymDsgExtractor::SymDsg sd = getSymbolAndDesignator(ea.Analyze(dsg));
+  SymDsgExtractor::verify(sd);
+  return Object{std::get<0>(sd), std::move(std::get<1>(sd))};
+}
+
+Object makeObject(const parser::StructureComponent &comp,
+                  semantics::SemanticsContext &semaCtx) {
+  evaluate::ExpressionAnalyzer ea{semaCtx};
+  SymDsgExtractor::SymDsg sd = getSymbolAndDesignator(ea.Analyze(comp));
+  SymDsgExtractor::verify(sd);
+  return Object{std::get<0>(sd), std::move(std::get<1>(sd))};
+}
+
+auto makeObject(semantics::SemanticsContext &semaCtx) {
+  return [&](auto &&s) { return makeObject(s, semaCtx); };
+}
+
+template <typename T>
+SomeExpr makeExpr(T &&inp, semantics::SemanticsContext &semaCtx) {
+  auto maybeExpr = evaluate::ExpressionAnalyzer(semaCtx).Analyze(inp);
+  assert(maybeExpr);
+  return std::move(*maybeExpr);
+}
+
+auto makeExpr(semantics::SemanticsContext &semaCtx) {
+  return [&](auto &&s) { return makeExpr(s, semaCtx); };
+}
+
+template <typename C, typename F,
+          typename E = typename llvm::remove_cvref_t<C>::value_type,
+          typename R = std::invoke_result_t<F, E>>
+List<R> makeList(C &&container, F &&func) {
+  List<R> v;
+  llvm::transform(container, std::back_inserter(v), func);
+  return v;
+}
+
+ObjectList makeList(const parser::OmpObjectList &objects,
+                    semantics::SemanticsContext &semaCtx) {
+  return makeList(objects.v, makeObject(semaCtx));
+}
+
+template <typename U, typename T> //
+U enum_cast(T t) {
+  using BareT = llvm::remove_cvref_t<T>;
+  using BareU = llvm::remove_cvref_t<U>;
+  static_assert(std::is_enum_v<BareT> && std::is_enum_v<BareU>);
+
+  return U{static_cast<std::underlying_type_t<BareT>>(t)};
+}
+
+template <typename F, typename T, typename U = std::invoke_result_t<F, T>>
+std::optional<U> maybeApply(F &&func, const std::optional<T> &inp) {
+  if (!inp)
+    return std::nullopt;
+  return std::move(func(*inp));
+}
+
+namespace clause {
+#ifdef EMPTY_CLASS
+#undef EMPTY_CLASS
+#endif
+#define EMPTY_CLASS(cls)                                                       \
+  struct cls {                                                                 \
+    using EmptyTrait = std::true_type;                                         \
+  };                                                                           \
+  cls make(const parser::OmpClause::cls &, semantics::SemanticsContext &) {    \
+    return cls{};                                                              \
+  }
+
+#ifdef WRAPPER_CLASS
+#undef WRAPPER_CLASS
+#endif
+#define WRAPPER_CLASS(cls, content) // Nothing
+#define GEN_FLANG_CLAUSE_PARSER_CLASSES
+#include "llvm/Frontend/OpenMP/OMP.inc"
+#undef EMPTY_CLASS
----------------
kparzysz wrote:

What TG generates here is a list of pairs (class-name, contents).  In some cases we may need both, in other cases we may only need the class name.  Conceptually, both are different pieces of the same root information.  We can indeed generate more information in TG, but we wouldn't need to if the information was done generically from the start.

https://github.com/llvm/llvm-project/pull/81621


More information about the flang-commits mailing list