[flang-commits] [flang] [flang][OpenMP] Implement flexible OpenMP clause representation (PR #81621)
via flang-commits
flang-commits at lists.llvm.org
Tue Feb 13 08:30:56 PST 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-openmp
Author: Krzysztof Parzyszek (kparzysz)
<details>
<summary>Changes</summary>
The new set of classes representing OpenMP classes mimics the contents of parser::OmpClause, but differs in a few aspects:
- it can be easily created, copied, etc.
- is based on semantics::SomeExpr instead of parser objects.
The class `OmpObject` is represented by `omp::Object`, which contains the symbol associated with the object, and semantics::MaybeExpr representing the designator for the symbol reference.
This patch only introduces the new classes, they are not yet used anywhere.
[Clause representation 1/6]
---
Patch is 35.74 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/81621.diff
1 Files Affected:
- (modified) flang/lib/Lower/OpenMP.cpp (+1115)
``````````diff
diff --git a/flang/lib/Lower/OpenMP.cpp b/flang/lib/Lower/OpenMP.cpp
index 06850bebd7d05a..24bef1d999548b 100644
--- a/flang/lib/Lower/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP.cpp
@@ -142,6 +142,1121 @@ 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
+
+// Helper objects
+
+struct DefinedOperator {
+ struct DefinedOpName {
+ using WrapperTrait = std::true_type;
+ Object v;
+ };
+ ENUM_CLASS(IntrinsicOperator, Power, Multiply, Divide, Add, Subtract, Concat,
+ LT, LE, EQ, NE, GE, GT, NOT, AND, OR, EQV, NEQV)
+ using UnionTrait = std::true_type;
+ std::variant<DefinedOpName, IntrinsicOperator> u;
+};
+
+DefinedOperator makeDefOp(const parser::DefinedOperator &inp,
+ semantics::SemanticsContext &semaCtx) {
+ return DefinedOperator{
+ std::visit(common::visitors{
+ [&](const parser::DefinedOpName &s) {
+ return DefinedOperator{DefinedOperator::DefinedOpName{
+ makeObject(s.v, semaCtx)}};
+ },
+ [&](const parser::DefinedOperator::IntrinsicOperator &s) {
+ return DefinedOperator{
+ enum_cast<DefinedOperator::IntrinsicOperator>(s)};
+ },
+ },
+ inp.u),
+ };
+}
+
+struct ProcedureDesignator {
+ using WrapperTrait = std::true_type;
+ Object v;
+};
+
+ProcedureDesignator makeProcDsg(const parser::ProcedureDesignator &inp,
+ semantics::SemanticsContext &semaCtx) {
+ return ProcedureDesignator{std::visit(
+ common::visitors{
+ [&](const parser::Name &t) { return makeObject(t, semaCtx); },
+ [&](const parser::ProcComponentRef &t) {
+ return makeObject(t.v.thing, semaCtx);
+ },
+ },
+ inp.u)};
+}
+
+struct ReductionOperator {
+ using UnionTrait = std::true_type;
+ std::variant<DefinedOperator, ProcedureDesignator> u;
+};
+
+ReductionOperator makeRedOp(const parser::OmpReductionOperator &inp,
+ semantics::SemanticsContext &semaCtx) {
+ return std::visit(common::visitors{
+ [&](const parser::DefinedOperator &s) {
+ return ReductionOperator{makeDefOp(s, semaCtx)};
+ },
+ [&](const parser::ProcedureDesignator &s) {
+ return ReductionOperator{makeProcDsg(s, semaCtx)};
+ },
+ },
+ inp.u);
+}
+
+// Actual clauses. Each T (where OmpClause::T exists) has its "make".
+
+struct Aligned {
+ using TupleTrait = std::true_type;
+ std::tuple<ObjectList, MaybeExpr> t;
+};
+
+Aligned make(const parser::OmpClause::Aligned &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpAlignedClause
+ auto &t0 = std::get<parser::OmpObjectList>(inp.v.t);
+ auto &t1 = std::get<std::optional<parser::ScalarIntConstantExpr>>(inp.v.t);
+
+ return Aligned{{
+ makeList(t0, semaCtx),
+ maybeApply(makeExpr(semaCtx), t1),
+ }};
+}
+
+struct Allocate {
+ struct Modifier {
+ struct Allocator {
+ using WrapperTrait = std::true_type;
+ SomeExpr v;
+ };
+ struct Align {
+ using WrapperTrait = std::true_type;
+ SomeExpr v;
+ };
+ struct ComplexModifier {
+ using TupleTrait = std::true_type;
+ std::tuple<Allocator, Align> t;
+ };
+ using UnionTrait = std::true_type;
+ std::variant<Allocator, ComplexModifier, Align> u;
+ };
+ using TupleTrait = std::true_type;
+ std::tuple<std::optional<Modifier>, ObjectList> t;
+};
+
+Allocate make(const parser::OmpClause::Allocate &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpAllocateClause
+ using wrapped = parser::OmpAllocateClause;
+ auto &t0 = std::get<std::optional<wrapped::AllocateModifier>>(inp.v.t);
+ auto &t1 = std::get<parser::OmpObjectList>(inp.v.t);
+
+ auto convert = [&](auto &&s) -> Allocate::Modifier {
+ using Modifier = Allocate::Modifier;
+ using Allocator = Modifier::Allocator;
+ using Align = Modifier::Align;
+ using ComplexModifier = Modifier::ComplexModifier;
+
+ return Modifier{
+ std::visit(
+ common::visitors{
+ [&](const wrapped::AllocateModifier::Allocator &v) {
+ return Modifier{Allocator{makeExpr(v.v, semaCtx)}};
+ },
+ [&](const wrapped::AllocateModifier::ComplexModifier &v) {
+ auto &s0 =
+ std::get<wrapped::AllocateModifier::Allocator>(v.t);
+ auto &s1 = std::get<wrapped::AllocateModifier::Align>(v.t);
+ return Modifier{ComplexModifier{{
+ Allocator{makeExpr(s0.v, semaCtx)},
+ Align{makeExpr(s1.v, semaCtx)},
+ }}};
+ },
+ [&](const wrapped::AllocateModifier::Align &v) {
+ return Modifier{Align{makeExpr(v.v, semaCtx)}};
+ },
+ },
+ s.u),
+ };
+ };
+
+ return Allocate{{maybeApply(convert, t0), makeList(t1, semaCtx)}};
+}
+
+struct Allocator {
+ using WrapperTrait = std::true_type;
+ SomeExpr v;
+};
+
+Allocator make(const parser::OmpClause::Allocator &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::ScalarIntExpr
+ return Allocator{makeExpr(inp.v, semaCtx)};
+}
+
+struct AtomicDefaultMemOrder {
+ using WrapperTrait = std::true_type;
+ common::OmpAtomicDefaultMemOrderType v;
+};
+
+AtomicDefaultMemOrder make(const parser::OmpClause::AtomicDefaultMemOrder &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpAtomicDefaultMemOrderClause
+ return AtomicDefaultMemOrder{inp.v.v};
+}
+
+struct Collapse {
+ using WrapperTrait = std::true_type;
+ SomeExpr v;
+};
+
+Collapse make(const parser::OmpClause::Collapse &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::ScalarIntConstantExpr
+ return Collapse{makeExpr(inp.v, semaCtx)};
+}
+
+struct Copyin {
+ using WrapperTrait = std::true_type;
+ ObjectList v;
+};
+
+Copyin make(const parser::OmpClause::Copyin &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpObjectList
+ return Copyin{makeList(inp.v, semaCtx)};
+}
+
+struct Copyprivate {
+ using WrapperTrait = std::true_type;
+ ObjectList v;
+};
+
+Copyprivate make(const parser::OmpClause::Copyprivate &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpObjectList
+ return Copyprivate{makeList(inp.v, semaCtx)};
+}
+
+struct Defaultmap {
+ ENUM_CLASS(ImplicitBehavior, Alloc, To, From, Tofrom, Firstprivate, None,
+ Default)
+ ENUM_CLASS(VariableCategory, Scalar, Aggregate, Allocatable, Pointer)
+ using TupleTrait = std::true_type;
+ std::tuple<ImplicitBehavior, std::optional<VariableCategory>> t;
+};
+
+Defaultmap make(const parser::OmpClause::Defaultmap &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpDefaultmapClause
+ using wrapped = parser::OmpDefaultmapClause;
+
+ auto convert = [](auto &&s) -> Defaultmap::VariableCategory {
+ return enum_cast<Defaultmap::VariableCategory>(s);
+ };
+ auto &t0 = std::get<wrapped::ImplicitBehavior>(inp.v.t);
+ auto &t1 = std::get<std::optional<wrapped::VariableCategory>>(inp.v.t);
+ auto v0 = enum_cast<Defaultmap::ImplicitBehavior>(t0);
+ return Defaultmap{{v0, maybeApply(convert, t1)}};
+}
+
+struct Default {
+ ENUM_CLASS(Type, Private, Firstprivate, Shared, None)
+ using WrapperTrait = std::true_type;
+ Type v;
+};
+
+Default make(const parser::OmpClause::Default &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpDefaultClause
+ return Default{enum_cast<Default::Type>(inp.v.v)};
+}
+
+struct Depend {
+ struct Source {
+ using EmptyTrait = std::true_type;
+ };
+ struct Sink {
+ using Length = std::tuple<DefinedOperator, SomeExpr>;
+ using Vec = std::tuple<Object, std::optional<Length>>;
+ using WrapperTrait = std::true_type;
+ List<Vec> v;
+ };
+ ENUM_CLASS(Type, In, Out, Inout, Source, Sink)
+ struct InOut {
+ using TupleTrait = std::true_type;
+ std::tuple<Type, ObjectList> t;
+ };
+ using UnionTrait = std::true_type;
+ std::variant<Source, Sink, InOut> u;
+};
+
+Depend make(const parser::OmpClause::Depend &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpDependClause
+ using wrapped = parser::OmpDependClause;
+
+ return std::visit(
+ common::visitors{
+ [&](const wrapped::Source &s) { return Depend{Depend::Source{}}; },
+ [&](const wrapped::Sink &s) {
+ auto convert = [&](const parser::OmpDependSinkVec &v) {
+ auto &t0 = std::get<parser::Name>(v.t);
+ auto &t1 =
+ std::get<std::optional<parser::OmpDependSinkVecLength>>(v.t);
+ auto convert1 = [&](const parser::OmpDependSinkVecLength &u) {
+ auto &s0 = std::get<parser::DefinedOperator>(u.t);
+ auto &s1 = std::get<parser::ScalarIntConstantExpr>(u.t);
+ return Depend::Sink::Length{makeDefOp(s0, semaCtx),
+ makeExpr(s1, semaCtx)};
+ };
+ return Depend::Sink::Vec{makeObject(t0, semaCtx),
+ maybeApply(convert1, t1)};
+ };
+ return Depend{Depend::Sink{makeList(s.v, convert)}};
+ },
+ [&](const wrapped::InOut &s) {
+ auto &t0 = std::get<parser::OmpDependenceType>(s.t);
+ auto &t1 = std::get<std::list<parser::Designator>>(s.t);
+ auto convert = [&](const parser::Designator &t) {
+ return makeObject(t, semaCtx);
+ };
+ return Depend{Depend::InOut{
+ {enum_cast<Depend::Type>(t0.v), makeList(t1, convert)}}};
+ },
+ },
+ inp.v.u);
+}
+
+struct Device {
+ ENUM_CLASS(DeviceModifier, Ancestor, Device_Num)
+ using TupleTrait = std::true_type;
+ std::tuple<std::optional<DeviceModifier>, SomeExpr> t;
+};
+
+Device make(const parser::OmpClause::Device &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpDeviceClause
+ using wrapped = parser::OmpDeviceClause;
+
+ auto convert = [](auto &&s) -> Device::DeviceModifier {
+ return enum_cast<Device::DeviceModifier>(s);
+ };
+ auto &t0 = std::get<std::optional<wrapped::DeviceModifier>>(inp.v.t);
+ auto &t1 = std::get<parser::ScalarIntExpr>(inp.v.t);
+ return Device{{maybeApply(convert, t0), makeExpr(t1, semaCtx)}};
+}
+
+struct DeviceType {
+ ENUM_CLASS(Type, Any, Host, Nohost)
+ using WrapperTrait = std::true_type;
+ Type v;
+};
+
+DeviceType make(const parser::OmpClause::DeviceType &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpDeviceTypeClause
+ return DeviceType{enum_cast<DeviceType::Type>(inp.v.v)};
+}
+
+struct DistSchedule {
+ using WrapperTrait = std::true_type;
+ MaybeExpr v;
+};
+
+DistSchedule make(const parser::OmpClause::DistSchedule &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> std::optional<parser::ScalarIntExpr>
+ return DistSchedule{maybeApply(makeExpr(semaCtx), inp.v)};
+}
+
+struct Enter {
+ using WrapperTrait = std::true_type;
+ ObjectList v;
+};
+
+Enter make(const parser::OmpClause::Enter &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpObjectList
+ return Enter{makeList(inp.v, semaCtx)};
+}
+
+struct Filter {
+ using WrapperTrait = std::true_type;
+ SomeExpr v;
+};
+
+Filter make(const parser::OmpClause::Filter &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::ScalarIntExpr
+ return Filter{makeExpr(inp.v, semaCtx)};
+}
+
+struct Final {
+ using WrapperTrait = std::true_type;
+ SomeExpr v;
+};
+
+Final make(const parser::OmpClause::Final &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::ScalarLogicalExpr
+ return Final{makeExpr(inp.v, semaCtx)};
+}
+
+struct Firstprivate {
+ using WrapperTrait = std::true_type;
+ ObjectList v;
+};
+
+Firstprivate make(const parser::OmpClause::Firstprivate &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpObjectList
+ return Firstprivate{makeList(inp.v, semaCtx)};
+}
+
+struct From {
+ using WrapperTrait = std::true_type;
+ ObjectList v;
+};
+
+From make(const parser::OmpClause::From &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpObjectList
+ return From{makeList(inp.v, semaCtx)};
+}
+
+struct Grainsize {
+ using WrapperTrait = std::true_type;
+ SomeExpr v;
+};
+
+Grainsize make(const parser::OmpClause::Grainsize &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::ScalarIntExpr
+ return Grainsize{makeExpr(inp.v, semaCtx)};...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/81621
More information about the flang-commits
mailing list