[flang-commits] [flang] ea2cfcc - [flang][OpenMP] Implement flexible OpenMP clause representation (#81621)
via flang-commits
flang-commits at lists.llvm.org
Thu Mar 14 08:26:07 PDT 2024
Author: Krzysztof Parzyszek
Date: 2024-03-14T10:26:03-05:00
New Revision: ea2cfcc15b4a0c5e823ed173ac8701d1ba6081eb
URL: https://github.com/llvm/llvm-project/commit/ea2cfcc15b4a0c5e823ed173ac8701d1ba6081eb
DIFF: https://github.com/llvm/llvm-project/commit/ea2cfcc15b4a0c5e823ed173ac8701d1ba6081eb.diff
LOG: [flang][OpenMP] Implement flexible OpenMP clause representation (#81621)
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.
This set of classes is geared towards a language-agnostic representation
of OpenMP clauses. While the class members are still based on flang's
`parser::OmpClause`, the classes themselves are actually C++ templates
parameterized with types essential to represent necessary information.
The two parameters are
- `IdType`: the type that can represent object's identity (for flang it
will be `semantics::Symbol *`),
- `ExprType`: the type that can represent expressions, arithmetic and
object references (`semantics::MaybeExpr` in flang).
The templates are defined in a namespace `tomp` (to distinguish it from
`omp`).
This patch introduces file "Clauses.cpp", which contains instantiations
of all of the templates for flang. The instantiations are members of
namespace `omp`, and include an all-encompassing variant class
`omp::Clause`, which is the analog of `parser::OmpClause`.
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. For each specific
clause in the variant `parser::OmpClause`, there exists a `make`
function that will generate the corresponding `omp::Clause` from it. The
intent was to use the make functions as variant visitors. The creation
of a clause instance would then follow the pattern:
```
omp::Clause clause = std::visit([](auto &&s) { return make(s, semaCtx); }, parserClause.u);
```
If a new clause `foo` is added in the future, then:
- a new template `tomp::FooT` representing the clause needs to be added
to ClauseT.h,
- a new instance needs to be created in flang, this can be as simple as
`using Foo = tomp::FooT<...>`,
- a new make function needs to be implemented to create object of class
Foo from `parser::OmpClause::Foo`.
This patch only introduces the new classes, they are not yet used
anywhere.
[Clause representation 1/6]
Added:
flang/lib/Lower/OpenMP/ClauseT.h
flang/lib/Lower/OpenMP/Clauses.cpp
flang/lib/Lower/OpenMP/Clauses.h
Modified:
flang/lib/Lower/CMakeLists.txt
Removed:
################################################################################
diff --git a/flang/lib/Lower/CMakeLists.txt b/flang/lib/Lower/CMakeLists.txt
index 5577a60f1daeac..f92d1a2bc7de18 100644
--- a/flang/lib/Lower/CMakeLists.txt
+++ b/flang/lib/Lower/CMakeLists.txt
@@ -25,6 +25,7 @@ add_flang_library(FortranLower
Mangler.cpp
OpenACC.cpp
OpenMP/ClauseProcessor.cpp
+ OpenMP/Clauses.cpp
OpenMP/DataSharingProcessor.cpp
OpenMP/OpenMP.cpp
OpenMP/ReductionProcessor.cpp
diff --git a/flang/lib/Lower/OpenMP/ClauseT.h b/flang/lib/Lower/OpenMP/ClauseT.h
new file mode 100644
index 00000000000000..2aae29af29214a
--- /dev/null
+++ b/flang/lib/Lower/OpenMP/ClauseT.h
@@ -0,0 +1,714 @@
+//===- ClauseT -- clause template definitions -----------------------------===//
+//
+// 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_LOWER_OPENMP_CLAUSET_H
+#define FORTRAN_LOWER_OPENMP_CLAUSET_H
+
+#include "flang/Parser/parse-tree.h" // For enum reuse
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <algorithm>
+#include <iterator>
+#include <optional>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+#include <variant>
+
+#include "llvm/Frontend/OpenMP/OMP.h.inc"
+
+namespace tomp {
+
+template <typename T>
+using ListT = llvm::SmallVector<T, 0>;
+
+// A specialization of ObjectT<Id, Expr> must provide the following definitions:
+// {
+// using IdType = Id;
+// using ExprType = Expr;
+//
+// auto id() const -> Id {
+// return the identifier of the object (for use in tests for
+// presence/absence of the object)
+// }
+//
+// auto ref() const -> const Expr& {
+// return the expression accessing (referencing) the object
+// }
+// }
+//
+// For example, the ObjectT instance created for "var[x+1]" would have
+// the `id()` return the identifier for `var`, and the `ref()` return the
+// representation of the array-access `var[x+1]`.
+template <typename Id, typename Expr>
+struct ObjectT;
+
+template <typename I, typename E>
+using ObjectListT = ListT<ObjectT<I, E>>;
+
+namespace clause {
+// Helper objects
+
+template <typename I, typename E>
+struct DefinedOperatorT {
+ struct DefinedOpName {
+ using WrapperTrait = std::true_type;
+ ObjectT<I, E> v;
+ };
+ using IntrinsicOperator = Fortran::parser::DefinedOperator::IntrinsicOperator;
+ using UnionTrait = std::true_type;
+ std::variant<DefinedOpName, IntrinsicOperator> u;
+};
+
+template <typename I, typename E>
+struct ProcedureDesignatorT {
+ using WrapperTrait = std::true_type;
+ ObjectT<I, E> v;
+};
+
+template <typename I, typename E>
+struct ReductionOperatorT {
+ using UnionTrait = std::true_type;
+ std::variant<DefinedOperatorT<I, E>, ProcedureDesignatorT<I, E>> u;
+};
+
+template <typename I, typename E>
+struct AcqRelT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct AcquireT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct AdjustArgsT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct AffinityT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct AlignT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct AppendArgsT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct AtT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct BindT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct CancellationConstructTypeT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct CaptureT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct CompareT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct DepobjT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct DestroyT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct DetachT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct DoacrossT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct DynamicAllocatorsT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct ExclusiveT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct FailT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct FlushT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct FullT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct InbranchT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct InclusiveT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct IndirectT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct InitT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct MatchT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct MemoryOrderT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct MergeableT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct MessageT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct NogroupT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct NotinbranchT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct NowaitT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct OmpxAttributeT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct OmpxBareT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct ReadT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct RelaxedT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct ReleaseT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct ReverseOffloadT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct SeqCstT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct SeverityT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct SimdT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct ThreadprivateT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct ThreadsT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct UnifiedAddressT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct UnifiedSharedMemoryT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct UnknownT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct UntiedT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct UpdateT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct UseT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct UsesAllocatorsT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct WeakT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct WhenT {
+ using EmptyTrait = std::true_type;
+};
+template <typename I, typename E>
+struct WriteT {
+ using EmptyTrait = std::true_type;
+};
+
+template <typename I, typename E>
+struct AlignedT {
+ using TupleTrait = std::true_type;
+ std::tuple<ObjectListT<I, E>, std::optional<E>> t;
+};
+
+template <typename I, typename E>
+struct AllocateT {
+ struct Modifier {
+ struct Allocator {
+ using WrapperTrait = std::true_type;
+ E v;
+ };
+ struct Align {
+ using WrapperTrait = std::true_type;
+ E 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>, ObjectListT<I, E>> t;
+};
+
+template <typename I, typename E>
+struct AllocatorT {
+ using WrapperTrait = std::true_type;
+ E v;
+};
+
+template <typename I, typename E>
+struct AtomicDefaultMemOrderT {
+ using WrapperTrait = std::true_type;
+ using OmpAtomicDefaultMemOrderType =
+ Fortran::common::OmpAtomicDefaultMemOrderType;
+ OmpAtomicDefaultMemOrderType v;
+};
+
+template <typename I, typename E>
+struct CollapseT {
+ using WrapperTrait = std::true_type;
+ E v;
+};
+
+template <typename I, typename E>
+struct CopyinT {
+ using WrapperTrait = std::true_type;
+ ObjectListT<I, E> v;
+};
+
+template <typename I, typename E>
+struct CopyprivateT {
+ using WrapperTrait = std::true_type;
+ ObjectListT<I, E> v;
+};
+
+template <typename I, typename E>
+struct DefaultmapT {
+ using ImplicitBehavior =
+ Fortran::parser::OmpDefaultmapClause::ImplicitBehavior;
+ using VariableCategory =
+ Fortran::parser::OmpDefaultmapClause::VariableCategory;
+ using TupleTrait = std::true_type;
+ std::tuple<ImplicitBehavior, std::optional<VariableCategory>> t;
+};
+
+template <typename I, typename E>
+struct DefaultT {
+ using Type = Fortran::parser::OmpDefaultClause::Type;
+ using WrapperTrait = std::true_type;
+ Type v;
+};
+
+template <typename I, typename E>
+struct DependT {
+ struct Source {
+ using EmptyTrait = std::true_type;
+ };
+ struct Sink {
+ using Length = std::tuple<DefinedOperatorT<I, E>, E>;
+ using Vec = std::tuple<ObjectT<I, E>, std::optional<Length>>;
+ using WrapperTrait = std::true_type;
+ ListT<Vec> v;
+ };
+ using Type = Fortran::parser::OmpDependenceType::Type;
+ struct InOut {
+ using TupleTrait = std::true_type;
+ std::tuple<Type, ObjectListT<I, E>> t;
+ };
+ using UnionTrait = std::true_type;
+ std::variant<Source, Sink, InOut> u;
+};
+
+template <typename I, typename E>
+struct DeviceT {
+ using DeviceModifier = Fortran::parser::OmpDeviceClause::DeviceModifier;
+ using TupleTrait = std::true_type;
+ std::tuple<std::optional<DeviceModifier>, E> t;
+};
+
+template <typename I, typename E>
+struct DeviceTypeT {
+ using Type = Fortran::parser::OmpDeviceTypeClause::Type;
+ using WrapperTrait = std::true_type;
+ Type v;
+};
+
+template <typename I, typename E>
+struct DistScheduleT {
+ using WrapperTrait = std::true_type;
+ std::optional<E> v;
+};
+
+template <typename I, typename E>
+struct EnterT {
+ using WrapperTrait = std::true_type;
+ ObjectListT<I, E> v;
+};
+
+template <typename I, typename E>
+struct FilterT {
+ using WrapperTrait = std::true_type;
+ E v;
+};
+
+template <typename I, typename E>
+struct FinalT {
+ using WrapperTrait = std::true_type;
+ E v;
+};
+
+template <typename I, typename E>
+struct FirstprivateT {
+ using WrapperTrait = std::true_type;
+ ObjectListT<I, E> v;
+};
+
+template <typename I, typename E>
+struct FromT {
+ using WrapperTrait = std::true_type;
+ ObjectListT<I, E> v;
+};
+
+template <typename I, typename E>
+struct GrainsizeT {
+ using WrapperTrait = std::true_type;
+ E v;
+};
+
+template <typename I, typename E>
+struct HasDeviceAddrT {
+ using WrapperTrait = std::true_type;
+ ObjectListT<I, E> v;
+};
+
+template <typename I, typename E>
+struct HintT {
+ using WrapperTrait = std::true_type;
+ E v;
+};
+
+template <typename I, typename E>
+struct IfT {
+ using DirectiveNameModifier =
+ Fortran::parser::OmpIfClause::DirectiveNameModifier;
+ using TupleTrait = std::true_type;
+ std::tuple<std::optional<DirectiveNameModifier>, E> t;
+};
+
+template <typename I, typename E>
+struct InReductionT {
+ using TupleTrait = std::true_type;
+ std::tuple<ReductionOperatorT<I, E>, ObjectListT<I, E>> t;
+};
+
+template <typename I, typename E>
+struct IsDevicePtrT {
+ using WrapperTrait = std::true_type;
+ ObjectListT<I, E> v;
+};
+
+template <typename I, typename E>
+struct LastprivateT {
+ using WrapperTrait = std::true_type;
+ ObjectListT<I, E> v;
+};
+
+template <typename I, typename E>
+struct LinearT {
+ struct Modifier {
+ using Type = Fortran::parser::OmpLinearModifier::Type;
+ using WrapperTrait = std::true_type;
+ Type v;
+ };
+ using TupleTrait = std::true_type;
+ std::tuple<std::optional<Modifier>, ObjectListT<I, E>, std::optional<E>> t;
+};
+
+template <typename I, typename E>
+struct LinkT {
+ using WrapperTrait = std::true_type;
+ ObjectListT<I, E> v;
+};
+
+template <typename I, typename E>
+struct MapT {
+ struct MapType {
+ struct Always {
+ using EmptyTrait = std::true_type;
+ };
+ using Type = Fortran::parser::OmpMapType::Type;
+ using TupleTrait = std::true_type;
+ std::tuple<std::optional<Always>, Type> t;
+ };
+ using TupleTrait = std::true_type;
+ std::tuple<std::optional<MapType>, ObjectListT<I, E>> t;
+};
+
+template <typename I, typename E>
+struct NocontextT {
+ using WrapperTrait = std::true_type;
+ E v;
+};
+
+template <typename I, typename E>
+struct NontemporalT {
+ using WrapperTrait = std::true_type;
+ ObjectListT<I, E> v;
+};
+
+template <typename I, typename E>
+struct NovariantsT {
+ using WrapperTrait = std::true_type;
+ E v;
+};
+
+template <typename I, typename E>
+struct NumTasksT {
+ using WrapperTrait = std::true_type;
+ E v;
+};
+
+template <typename I, typename E>
+struct NumTeamsT {
+ using WrapperTrait = std::true_type;
+ E v;
+};
+
+template <typename I, typename E>
+struct NumThreadsT {
+ using WrapperTrait = std::true_type;
+ E v;
+};
+
+template <typename I, typename E>
+struct OmpxDynCgroupMemT {
+ using WrapperTrait = std::true_type;
+ E v;
+};
+
+template <typename I, typename E>
+struct OrderedT {
+ using WrapperTrait = std::true_type;
+ std::optional<E> v;
+};
+
+template <typename I, typename E>
+struct OrderT {
+ using Kind = Fortran::parser::OmpOrderModifier::Kind;
+ using Type = Fortran::parser::OmpOrderClause::Type;
+ using TupleTrait = std::true_type;
+ std::tuple<std::optional<Kind>, Type> t;
+};
+
+template <typename I, typename E>
+struct PartialT {
+ using WrapperTrait = std::true_type;
+ std::optional<E> v;
+};
+
+template <typename I, typename E>
+struct PriorityT {
+ using WrapperTrait = std::true_type;
+ E v;
+};
+
+template <typename I, typename E>
+struct PrivateT {
+ using WrapperTrait = std::true_type;
+ ObjectListT<I, E> v;
+};
+
+template <typename I, typename E>
+struct ProcBindT {
+ using Type = Fortran::parser::OmpProcBindClause::Type;
+ using WrapperTrait = std::true_type;
+ Type v;
+};
+
+template <typename I, typename E>
+struct ReductionT {
+ using TupleTrait = std::true_type;
+ std::tuple<ReductionOperatorT<I, E>, ObjectListT<I, E>> t;
+};
+
+template <typename I, typename E>
+struct SafelenT {
+ using WrapperTrait = std::true_type;
+ E v;
+};
+
+template <typename I, typename E>
+struct ScheduleT {
+ using ModType = Fortran::parser::OmpScheduleModifierType::ModType;
+ struct ScheduleModifier {
+ using TupleTrait = std::true_type;
+ std::tuple<ModType, std::optional<ModType>> t;
+ };
+ using ScheduleType = Fortran::parser::OmpScheduleClause::ScheduleType;
+ using TupleTrait = std::true_type;
+ std::tuple<std::optional<ScheduleModifier>, ScheduleType, std::optional<E>> t;
+};
+
+template <typename I, typename E>
+struct SharedT {
+ using WrapperTrait = std::true_type;
+ ObjectListT<I, E> v;
+};
+
+template <typename I, typename E>
+struct SimdlenT {
+ using WrapperTrait = std::true_type;
+ E v;
+};
+
+template <typename I, typename E>
+struct SizesT {
+ using WrapperTrait = std::true_type;
+ ListT<E> v;
+};
+
+template <typename I, typename E>
+struct TaskReductionT {
+ using TupleTrait = std::true_type;
+ std::tuple<ReductionOperatorT<I, E>, ObjectListT<I, E>> t;
+};
+
+template <typename I, typename E>
+struct ThreadLimitT {
+ using WrapperTrait = std::true_type;
+ E v;
+};
+
+template <typename I, typename E>
+struct ToT {
+ using WrapperTrait = std::true_type;
+ ObjectListT<I, E> v;
+};
+
+template <typename I, typename E>
+struct UniformT {
+ using WrapperTrait = std::true_type;
+ ObjectListT<I, E> v;
+};
+
+template <typename I, typename E>
+struct UseDeviceAddrT {
+ using WrapperTrait = std::true_type;
+ ObjectListT<I, E> v;
+};
+
+template <typename I, typename E>
+struct UseDevicePtrT {
+ using WrapperTrait = std::true_type;
+ ObjectListT<I, E> v;
+};
+
+template <typename I, typename E>
+using UnionOfAllClausesT = std::variant<
+ AcqRelT<I, E>, AcquireT<I, E>, AdjustArgsT<I, E>, AffinityT<I, E>,
+ AlignT<I, E>, AlignedT<I, E>, AllocateT<I, E>, AllocatorT<I, E>,
+ AppendArgsT<I, E>, AtT<I, E>, AtomicDefaultMemOrderT<I, E>, BindT<I, E>,
+ CancellationConstructTypeT<I, E>, CaptureT<I, E>, CollapseT<I, E>,
+ CompareT<I, E>, CopyprivateT<I, E>, CopyinT<I, E>, DefaultT<I, E>,
+ DefaultmapT<I, E>, DependT<I, E>, DepobjT<I, E>, DestroyT<I, E>,
+ DetachT<I, E>, DeviceT<I, E>, DeviceTypeT<I, E>, DistScheduleT<I, E>,
+ DoacrossT<I, E>, DynamicAllocatorsT<I, E>, EnterT<I, E>, ExclusiveT<I, E>,
+ FailT<I, E>, FilterT<I, E>, FinalT<I, E>, FirstprivateT<I, E>, FlushT<I, E>,
+ FromT<I, E>, FullT<I, E>, GrainsizeT<I, E>, HasDeviceAddrT<I, E>,
+ HintT<I, E>, IfT<I, E>, InReductionT<I, E>, InbranchT<I, E>,
+ InclusiveT<I, E>, IndirectT<I, E>, InitT<I, E>, IsDevicePtrT<I, E>,
+ LastprivateT<I, E>, LinearT<I, E>, LinkT<I, E>, MapT<I, E>, MatchT<I, E>,
+ MemoryOrderT<I, E>, MergeableT<I, E>, MessageT<I, E>, NogroupT<I, E>,
+ NowaitT<I, E>, NocontextT<I, E>, NontemporalT<I, E>, NotinbranchT<I, E>,
+ NovariantsT<I, E>, NumTasksT<I, E>, NumTeamsT<I, E>, NumThreadsT<I, E>,
+ OmpxAttributeT<I, E>, OmpxDynCgroupMemT<I, E>, OmpxBareT<I, E>,
+ OrderT<I, E>, OrderedT<I, E>, PartialT<I, E>, PriorityT<I, E>,
+ PrivateT<I, E>, ProcBindT<I, E>, ReadT<I, E>, ReductionT<I, E>,
+ RelaxedT<I, E>, ReleaseT<I, E>, ReverseOffloadT<I, E>, SafelenT<I, E>,
+ ScheduleT<I, E>, SeqCstT<I, E>, SeverityT<I, E>, SharedT<I, E>, SimdT<I, E>,
+ SimdlenT<I, E>, SizesT<I, E>, TaskReductionT<I, E>, ThreadLimitT<I, E>,
+ ThreadprivateT<I, E>, ThreadsT<I, E>, ToT<I, E>, UnifiedAddressT<I, E>,
+ UnifiedSharedMemoryT<I, E>, UniformT<I, E>, UnknownT<I, E>, UntiedT<I, E>,
+ UpdateT<I, E>, UseT<I, E>, UseDeviceAddrT<I, E>, UseDevicePtrT<I, E>,
+ UsesAllocatorsT<I, E>, WeakT<I, E>, WhenT<I, E>, WriteT<I, E>>;
+} // namespace clause
+
+template <typename Id, typename Expr>
+struct ClauseT {
+ llvm::omp::Clause id; // The numeric id of the clause
+ using UnionTrait = std::true_type;
+ clause::UnionOfAllClausesT<Id, Expr> u;
+};
+
+} // namespace tomp
+
+#endif // FORTRAN_LOWER_OPENMP_CLAUSET_H
diff --git a/flang/lib/Lower/OpenMP/Clauses.cpp b/flang/lib/Lower/OpenMP/Clauses.cpp
new file mode 100644
index 00000000000000..6085d7baacc313
--- /dev/null
+++ b/flang/lib/Lower/OpenMP/Clauses.cpp
@@ -0,0 +1,732 @@
+//===-- Clauses.cpp -- OpenMP clause handling -----------------------------===//
+//
+// 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 "Clauses.h"
+
+#include "flang/Common/idioms.h"
+#include "flang/Evaluate/expression.h"
+#include "flang/Parser/parse-tree.h"
+#include "flang/Semantics/expression.h"
+#include "flang/Semantics/symbol.h"
+
+#include "llvm/Frontend/OpenMP/OMPConstants.h"
+
+#include <list>
+#include <optional>
+#include <tuple>
+#include <utility>
+#include <variant>
+
+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 SymbolWithDesignator = std::tuple<semantics::Symbol *, MaybeExpr>;
+
+struct SymbolAndDesignatorExtractor {
+ template <typename T>
+ static T &&AsRvalueRef(T &&t) {
+ return std::move(t);
+ }
+ template <typename T>
+ static T AsRvalueRef(const T &t) {
+ return t;
+ }
+
+ static semantics::Symbol *symbol_addr(const evaluate::SymbolRef &ref) {
+ // 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 const_cast<semantics::Symbol *>(&ref.get());
+ }
+
+ template <typename T>
+ static SymbolWithDesignator visit(T &&) {
+ // Use this to see missing overloads:
+ // llvm::errs() << "NULL: " << __PRETTY_FUNCTION__ << '\n';
+ return SymbolWithDesignator{};
+ }
+
+ template <typename T>
+ static SymbolWithDesignator visit(const evaluate::Designator<T> &e) {
+ return std::make_tuple(symbol_addr(*e.GetLastSymbol()),
+ evaluate::AsGenericExpr(AsRvalueRef(e)));
+ }
+
+ static SymbolWithDesignator visit(const evaluate::ProcedureDesignator &e) {
+ return std::make_tuple(symbol_addr(*e.GetSymbol()), std::nullopt);
+ }
+
+ template <typename T>
+ static SymbolWithDesignator visit(const evaluate::Expr<T> &e) {
+ return std::visit([](auto &&s) { return visit(s); }, e.u);
+ }
+
+ static void verify(const SymbolWithDesignator &sd) {
+ const semantics::Symbol *symbol = std::get<0>(sd);
+ assert(symbol && "Expecting symbol");
+ auto &maybeDsg = std::get<1>(sd);
+ if (!maybeDsg)
+ return; // Symbol with no designator -> OK
+ std::optional<evaluate::DataRef> maybeRef =
+ evaluate::ExtractDataRef(*maybeDsg);
+ if (maybeRef) {
+ if (&maybeRef->GetLastSymbol() == symbol)
+ return; // Symbol with a designator for it -> OK
+ llvm_unreachable("Expecting designator for given symbol");
+ } else {
+ // This could still be a Substring or ComplexPart, but at least Substring
+ // is not allowed in OpenMP.
+ maybeDsg->dump();
+ llvm_unreachable("Expecting DataRef designator");
+ }
+ }
+};
+
+SymbolWithDesignator getSymbolAndDesignator(const MaybeExpr &expr) {
+ if (!expr)
+ return SymbolWithDesignator{};
+ return std::visit(
+ [](auto &&s) { return SymbolAndDesignatorExtractor::visit(s); }, expr->u);
+}
+
+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};
+ SymbolWithDesignator sd = getSymbolAndDesignator(ea.Analyze(dsg));
+ SymbolAndDesignatorExtractor::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};
+ SymbolWithDesignator sd = getSymbolAndDesignator(ea.Analyze(comp));
+ SymbolAndDesignatorExtractor::verify(sd);
+ return Object{std::get<0>(sd), std::move(std::get<1>(sd))};
+}
+
+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};
+ }
+ // OmpObject is std::variant<Designator, /*common block*/ Name>;
+ return makeObject(std::get<parser::Designator>(object.u), semaCtx);
+}
+
+std::optional<Object>
+getBaseObject(const Object &object,
+ Fortran::semantics::SemanticsContext &semaCtx) {
+ // If it's just the symbol, then there is no base.
+ if (!object.id())
+ return std::nullopt;
+
+ auto maybeRef = evaluate::ExtractDataRef(*object.ref());
+ if (!maybeRef)
+ return std::nullopt;
+
+ evaluate::DataRef ref = *maybeRef;
+
+ if (std::get_if<evaluate::SymbolRef>(&ref.u)) {
+ return std::nullopt;
+ } else if (auto *comp = std::get_if<evaluate::Component>(&ref.u)) {
+ const evaluate::DataRef &base = comp->base();
+ return Object{
+ SymbolAndDesignatorExtractor::symbol_addr(base.GetLastSymbol()),
+ evaluate::AsGenericExpr(
+ SymbolAndDesignatorExtractor::AsRvalueRef(base))};
+ } else if (auto *arr = std::get_if<evaluate::ArrayRef>(&ref.u)) {
+ const evaluate::NamedEntity &base = arr->base();
+ evaluate::ExpressionAnalyzer ea{semaCtx};
+ if (auto *comp = base.UnwrapComponent()) {
+ return Object{SymbolAndDesignatorExtractor::symbol_addr(comp->symbol()),
+ ea.Designate(evaluate::DataRef{
+ SymbolAndDesignatorExtractor::AsRvalueRef(*comp)})};
+ } else if (base.UnwrapSymbolRef()) {
+ return std::nullopt;
+ }
+ } else {
+ assert(std::holds_alternative<evaluate::CoarrayRef>(ref.u) &&
+ "Unexpected variant alternative");
+ llvm_unreachable("Coarray reference not supported at the moment");
+ }
+ return std::nullopt;
+}
+
+namespace clause {
+// Helper objects
+#ifdef EMPTY_CLASS
+#undef EMPTY_CLASS
+#endif
+#define EMPTY_CLASS(cls) \
+ cls make(const parser::OmpClause::cls &, semantics::SemanticsContext &) { \
+ return cls{}; \
+ } \
+ [[maybe_unused]] extern int xyzzy_semicolon_absorber
+
+#ifdef WRAPPER_CLASS
+#undef WRAPPER_CLASS
+#endif
+#define WRAPPER_CLASS(cls, content) \
+ [[maybe_unused]] extern int xyzzy_semicolon_absorber
+#define GEN_FLANG_CLAUSE_PARSER_CLASSES
+#include "llvm/Frontend/OpenMP/OMP.inc"
+#undef EMPTY_CLASS
+#undef WRAPPER_CLASS
+
+using DefinedOperator = tomp::clause::DefinedOperatorT<SymIdent, SymReference>;
+using ProcedureDesignator =
+ tomp::clause::ProcedureDesignatorT<SymIdent, SymReference>;
+using ReductionOperator =
+ tomp::clause::ReductionOperatorT<SymIdent, SymReference>;
+
+DefinedOperator makeDefinedOperator(const parser::DefinedOperator &inp,
+ semantics::SemanticsContext &semaCtx) {
+ return std::visit(
+ common::visitors{
+ [&](const parser::DefinedOpName &s) {
+ return DefinedOperator{
+ DefinedOperator::DefinedOpName{makeObject(s.v, semaCtx)}};
+ },
+ [&](const parser::DefinedOperator::IntrinsicOperator &s) {
+ return DefinedOperator{s};
+ },
+ },
+ inp.u);
+}
+
+ProcedureDesignator
+makeProcedureDesignator(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)};
+}
+
+ReductionOperator makeReductionOperator(const parser::OmpReductionOperator &inp,
+ semantics::SemanticsContext &semaCtx) {
+ return std::visit(
+ common::visitors{
+ [&](const parser::DefinedOperator &s) {
+ return ReductionOperator{makeDefinedOperator(s, semaCtx)};
+ },
+ [&](const parser::ProcedureDesignator &s) {
+ return ReductionOperator{makeProcedureDesignator(s, semaCtx)};
+ },
+ },
+ inp.u);
+}
+
+// Actual clauses. Each T (where OmpClause::T exists) has its "make".
+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(makeExprFn(semaCtx), t1),
+ }};
+}
+
+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 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)}};
+}
+
+Allocator make(const parser::OmpClause::Allocator &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::ScalarIntExpr
+ return Allocator{makeExpr(inp.v, semaCtx)};
+}
+
+// Never called, but needed for using "make" as a Clause visitor.
+// See comment about "requires" clauses in Clauses.h.
+AtomicDefaultMemOrder make(const parser::OmpClause::AtomicDefaultMemOrder &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpAtomicDefaultMemOrderClause
+ return AtomicDefaultMemOrder{inp.v.v};
+}
+
+Collapse make(const parser::OmpClause::Collapse &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::ScalarIntConstantExpr
+ return Collapse{makeExpr(inp.v, semaCtx)};
+}
+
+Copyin make(const parser::OmpClause::Copyin &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpObjectList
+ return Copyin{makeList(inp.v, semaCtx)};
+}
+
+Copyprivate make(const parser::OmpClause::Copyprivate &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpObjectList
+ return Copyprivate{makeList(inp.v, semaCtx)};
+}
+
+Defaultmap make(const parser::OmpClause::Defaultmap &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpDefaultmapClause
+ using wrapped = parser::OmpDefaultmapClause;
+
+ auto &t0 = std::get<wrapped::ImplicitBehavior>(inp.v.t);
+ auto &t1 = std::get<std::optional<wrapped::VariableCategory>>(inp.v.t);
+ return Defaultmap{{t0, t1}};
+}
+
+Default make(const parser::OmpClause::Default &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpDefaultClause
+ return Default{inp.v.v};
+}
+
+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{makeDefinedOperator(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{{t0.v, makeList(t1, convert)}}};
+ },
+ },
+ inp.v.u);
+}
+
+Device make(const parser::OmpClause::Device &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpDeviceClause
+ using wrapped = parser::OmpDeviceClause;
+
+ auto &t0 = std::get<std::optional<wrapped::DeviceModifier>>(inp.v.t);
+ auto &t1 = std::get<parser::ScalarIntExpr>(inp.v.t);
+ return Device{{t0, makeExpr(t1, semaCtx)}};
+}
+
+DeviceType make(const parser::OmpClause::DeviceType &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpDeviceTypeClause
+ return DeviceType{inp.v.v};
+}
+
+DistSchedule make(const parser::OmpClause::DistSchedule &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> std::optional<parser::ScalarIntExpr>
+ return DistSchedule{maybeApply(makeExprFn(semaCtx), inp.v)};
+}
+
+Enter make(const parser::OmpClause::Enter &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpObjectList
+ return Enter{makeList(inp.v, semaCtx)};
+}
+
+Filter make(const parser::OmpClause::Filter &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::ScalarIntExpr
+ return Filter{makeExpr(inp.v, semaCtx)};
+}
+
+Final make(const parser::OmpClause::Final &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::ScalarLogicalExpr
+ return Final{makeExpr(inp.v, semaCtx)};
+}
+
+Firstprivate make(const parser::OmpClause::Firstprivate &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpObjectList
+ return Firstprivate{makeList(inp.v, semaCtx)};
+}
+
+From make(const parser::OmpClause::From &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpObjectList
+ return From{makeList(inp.v, semaCtx)};
+}
+
+Grainsize make(const parser::OmpClause::Grainsize &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::ScalarIntExpr
+ return Grainsize{makeExpr(inp.v, semaCtx)};
+}
+
+HasDeviceAddr make(const parser::OmpClause::HasDeviceAddr &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpObjectList
+ return HasDeviceAddr{makeList(inp.v, semaCtx)};
+}
+
+Hint make(const parser::OmpClause::Hint &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::ConstantExpr
+ return Hint{makeExpr(inp.v, semaCtx)};
+}
+
+If make(const parser::OmpClause::If &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpIfClause
+ using wrapped = parser::OmpIfClause;
+
+ auto &t0 = std::get<std::optional<wrapped::DirectiveNameModifier>>(inp.v.t);
+ auto &t1 = std::get<parser::ScalarLogicalExpr>(inp.v.t);
+ return If{{t0, makeExpr(t1, semaCtx)}};
+}
+
+InReduction make(const parser::OmpClause::InReduction &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpInReductionClause
+ auto &t0 = std::get<parser::OmpReductionOperator>(inp.v.t);
+ auto &t1 = std::get<parser::OmpObjectList>(inp.v.t);
+ return InReduction{
+ {makeReductionOperator(t0, semaCtx), makeList(t1, semaCtx)}};
+}
+
+IsDevicePtr make(const parser::OmpClause::IsDevicePtr &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpObjectList
+ return IsDevicePtr{makeList(inp.v, semaCtx)};
+}
+
+Lastprivate make(const parser::OmpClause::Lastprivate &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpObjectList
+ return Lastprivate{makeList(inp.v, semaCtx)};
+}
+
+Linear make(const parser::OmpClause::Linear &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpLinearClause
+ using wrapped = parser::OmpLinearClause;
+
+ return std::visit(
+ common::visitors{
+ [&](const wrapped::WithModifier &s) {
+ return Linear{{Linear::Modifier{s.modifier.v},
+ makeList(s.names, makeObjectFn(semaCtx)),
+ maybeApply(makeExprFn(semaCtx), s.step)}};
+ },
+ [&](const wrapped::WithoutModifier &s) {
+ return Linear{{std::nullopt,
+ makeList(s.names, makeObjectFn(semaCtx)),
+ maybeApply(makeExprFn(semaCtx), s.step)}};
+ },
+ },
+ inp.v.u);
+}
+
+Link make(const parser::OmpClause::Link &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpObjectList
+ return Link{makeList(inp.v, semaCtx)};
+}
+
+Map make(const parser::OmpClause::Map &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpMapClause
+ auto &t0 = std::get<std::optional<parser::OmpMapType>>(inp.v.t);
+ auto &t1 = std::get<parser::OmpObjectList>(inp.v.t);
+ auto convert = [](const parser::OmpMapType &s) {
+ auto &s0 = std::get<std::optional<parser::OmpMapType::Always>>(s.t);
+ auto &s1 = std::get<parser::OmpMapType::Type>(s.t);
+ auto convertT = [](parser::OmpMapType::Always) {
+ return Map::MapType::Always{};
+ };
+ return Map::MapType{{maybeApply(convertT, s0), s1}};
+ };
+ return Map{{maybeApply(convert, t0), makeList(t1, semaCtx)}};
+}
+
+Nocontext make(const parser::OmpClause::Nocontext &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::ScalarLogicalExpr
+ return Nocontext{makeExpr(inp.v, semaCtx)};
+}
+
+Nontemporal make(const parser::OmpClause::Nontemporal &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> std::list<parser::Name>
+ return Nontemporal{makeList(inp.v, makeObjectFn(semaCtx))};
+}
+
+Novariants make(const parser::OmpClause::Novariants &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::ScalarLogicalExpr
+ return Novariants{makeExpr(inp.v, semaCtx)};
+}
+
+NumTasks make(const parser::OmpClause::NumTasks &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::ScalarIntExpr
+ return NumTasks{makeExpr(inp.v, semaCtx)};
+}
+
+NumTeams make(const parser::OmpClause::NumTeams &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::ScalarIntExpr
+ return NumTeams{makeExpr(inp.v, semaCtx)};
+}
+
+NumThreads make(const parser::OmpClause::NumThreads &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::ScalarIntExpr
+ return NumThreads{makeExpr(inp.v, semaCtx)};
+}
+
+OmpxDynCgroupMem make(const parser::OmpClause::OmpxDynCgroupMem &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::ScalarIntExpr
+ return OmpxDynCgroupMem{makeExpr(inp.v, semaCtx)};
+}
+
+Ordered make(const parser::OmpClause::Ordered &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> std::optional<parser::ScalarIntConstantExpr>
+ return Ordered{maybeApply(makeExprFn(semaCtx), inp.v)};
+}
+
+Order make(const parser::OmpClause::Order &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpOrderClause
+ using wrapped = parser::OmpOrderClause;
+ auto &t0 = std::get<std::optional<parser::OmpOrderModifier>>(inp.v.t);
+ auto &t1 = std::get<wrapped::Type>(inp.v.t);
+ auto convert = [](const parser::OmpOrderModifier &s) -> Order::Kind {
+ return std::get<parser::OmpOrderModifier::Kind>(s.u);
+ };
+ return Order{{maybeApply(convert, t0), t1}};
+}
+
+Partial make(const parser::OmpClause::Partial &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> std::optional<parser::ScalarIntConstantExpr>
+ return Partial{maybeApply(makeExprFn(semaCtx), inp.v)};
+}
+
+Priority make(const parser::OmpClause::Priority &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::ScalarIntExpr
+ return Priority{makeExpr(inp.v, semaCtx)};
+}
+
+Private make(const parser::OmpClause::Private &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpObjectList
+ return Private{makeList(inp.v, semaCtx)};
+}
+
+ProcBind make(const parser::OmpClause::ProcBind &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpProcBindClause
+ return ProcBind{inp.v.v};
+}
+
+Reduction make(const parser::OmpClause::Reduction &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpReductionClause
+ auto &t0 = std::get<parser::OmpReductionOperator>(inp.v.t);
+ auto &t1 = std::get<parser::OmpObjectList>(inp.v.t);
+ return Reduction{{makeReductionOperator(t0, semaCtx), makeList(t1, semaCtx)}};
+}
+
+Safelen make(const parser::OmpClause::Safelen &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::ScalarIntConstantExpr
+ return Safelen{makeExpr(inp.v, semaCtx)};
+}
+
+Schedule make(const parser::OmpClause::Schedule &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpScheduleClause
+ using wrapped = parser::OmpScheduleClause;
+
+ auto &t0 = std::get<std::optional<parser::OmpScheduleModifier>>(inp.v.t);
+ auto &t1 = std::get<wrapped::ScheduleType>(inp.v.t);
+ auto &t2 = std::get<std::optional<parser::ScalarIntExpr>>(inp.v.t);
+
+ auto convert = [](auto &&s) -> Schedule::ScheduleModifier {
+ auto &s0 = std::get<parser::OmpScheduleModifier::Modifier1>(s.t);
+ auto &s1 =
+ std::get<std::optional<parser::OmpScheduleModifier::Modifier2>>(s.t);
+
+ auto convert1 = [](auto &&v) { // Modifier1 or Modifier2
+ return v.v.v;
+ };
+ return Schedule::ScheduleModifier{{s0.v.v, maybeApply(convert1, s1)}};
+ };
+
+ return Schedule{
+ {maybeApply(convert, t0), t1, maybeApply(makeExprFn(semaCtx), t2)}};
+}
+
+Shared make(const parser::OmpClause::Shared &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpObjectList
+ return Shared{makeList(inp.v, semaCtx)};
+}
+
+Simdlen make(const parser::OmpClause::Simdlen &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::ScalarIntConstantExpr
+ return Simdlen{makeExpr(inp.v, semaCtx)};
+}
+
+Sizes make(const parser::OmpClause::Sizes &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> std::list<parser::ScalarIntExpr>
+ return Sizes{makeList(inp.v, makeExprFn(semaCtx))};
+}
+
+TaskReduction make(const parser::OmpClause::TaskReduction &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpReductionClause
+ auto &t0 = std::get<parser::OmpReductionOperator>(inp.v.t);
+ auto &t1 = std::get<parser::OmpObjectList>(inp.v.t);
+ return TaskReduction{
+ {makeReductionOperator(t0, semaCtx), makeList(t1, semaCtx)}};
+}
+
+ThreadLimit make(const parser::OmpClause::ThreadLimit &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::ScalarIntExpr
+ return ThreadLimit{makeExpr(inp.v, semaCtx)};
+}
+
+To make(const parser::OmpClause::To &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpObjectList
+ return To{makeList(inp.v, semaCtx)};
+}
+
+Uniform make(const parser::OmpClause::Uniform &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> std::list<parser::Name>
+ return Uniform{makeList(inp.v, makeObjectFn(semaCtx))};
+}
+
+UseDeviceAddr make(const parser::OmpClause::UseDeviceAddr &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpObjectList
+ return UseDeviceAddr{makeList(inp.v, semaCtx)};
+}
+
+UseDevicePtr make(const parser::OmpClause::UseDevicePtr &inp,
+ semantics::SemanticsContext &semaCtx) {
+ // inp.v -> parser::OmpObjectList
+ return UseDevicePtr{makeList(inp.v, semaCtx)};
+}
+} // namespace clause
+
+Clause makeClause(const Fortran::parser::OmpClause &cls,
+ semantics::SemanticsContext &semaCtx) {
+ return std::visit(
+ [&](auto &&s) {
+ return makeClause(getClauseId(cls), clause::make(s, semaCtx),
+ cls.source);
+ },
+ cls.u);
+}
+
+omp::List<Clause> makeList(const parser::OmpClauseList &clauses,
+ semantics::SemanticsContext &semaCtx) {
+ return makeList(clauses.v, [&](const parser::OmpClause &s) {
+ return makeClause(s, semaCtx);
+ });
+}
+} // namespace omp
diff --git a/flang/lib/Lower/OpenMP/Clauses.h b/flang/lib/Lower/OpenMP/Clauses.h
new file mode 100644
index 00000000000000..1576eec766ccf1
--- /dev/null
+++ b/flang/lib/Lower/OpenMP/Clauses.h
@@ -0,0 +1,205 @@
+//===-- Clauses.h -- OpenMP clause handling -------------------------------===//
+//
+// 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_LOWER_OPENMP_CLAUSES_H
+#define FORTRAN_LOWER_OPENMP_CLAUSES_H
+
+#include "ClauseT.h"
+
+#include "flang/Evaluate/expression.h"
+#include "flang/Parser/parse-tree.h"
+#include "flang/Semantics/expression.h"
+#include "flang/Semantics/semantics.h"
+#include "flang/Semantics/symbol.h"
+
+#include "llvm/ADT/STLExtras.h"
+
+#include <optional>
+#include <type_traits>
+#include <utility>
+
+namespace omp {
+using namespace Fortran;
+using SomeType = evaluate::SomeType;
+using SomeExpr = semantics::SomeExpr;
+using MaybeExpr = semantics::MaybeExpr;
+
+using SymIdent = semantics::Symbol *;
+using SymReference = SomeExpr;
+
+template <typename T>
+using List = tomp::ListT<T>;
+} // namespace omp
+
+namespace tomp {
+template <>
+struct ObjectT<omp::SymIdent, omp::SymReference> {
+ using IdType = omp::SymIdent;
+ using ExprType = omp::SymReference;
+
+ const IdType &id() const { return symbol; }
+ const std::optional<ExprType> &ref() const { return designator; }
+
+ IdType symbol;
+ std::optional<ExprType> designator;
+};
+} // namespace tomp
+
+namespace omp {
+
+using Object = tomp::ObjectT<SymIdent, SymReference>;
+using ObjectList = tomp::ObjectListT<SymIdent, SymReference>;
+
+Object makeObject(const parser::OmpObject &object,
+ semantics::SemanticsContext &semaCtx);
+Object makeObject(const parser::Name &name,
+ semantics::SemanticsContext &semaCtx);
+Object makeObject(const parser::Designator &dsg,
+ semantics::SemanticsContext &semaCtx);
+Object makeObject(const parser::StructureComponent &comp,
+ semantics::SemanticsContext &semaCtx);
+
+inline auto makeObjectFn(semantics::SemanticsContext &semaCtx) {
+ return [&](auto &&s) { return makeObject(s, semaCtx); };
+}
+
+template <typename T>
+SomeExpr makeExpr(T &&pftExpr, semantics::SemanticsContext &semaCtx) {
+ auto maybeExpr = evaluate::ExpressionAnalyzer(semaCtx).Analyze(pftExpr);
+ assert(maybeExpr);
+ return std::move(*maybeExpr);
+}
+
+inline auto makeExprFn(semantics::SemanticsContext &semaCtx) {
+ return [&](auto &&s) { return makeExpr(s, semaCtx); };
+}
+
+template <
+ typename ContainerTy, typename FunctionTy,
+ typename ElemTy = typename llvm::remove_cvref_t<ContainerTy>::value_type,
+ typename ResultTy = std::invoke_result_t<FunctionTy, ElemTy>>
+List<ResultTy> makeList(ContainerTy &&container, FunctionTy &&func) {
+ List<ResultTy> v;
+ llvm::transform(container, std::back_inserter(v), func);
+ return v;
+}
+
+inline ObjectList makeList(const parser::OmpObjectList &objects,
+ semantics::SemanticsContext &semaCtx) {
+ return makeList(objects.v, makeObjectFn(semaCtx));
+}
+
+template <typename FuncTy, typename ElemTy,
+ typename ResultTy = std::invoke_result_t<FuncTy, ElemTy>>
+std::optional<ResultTy> maybeApply(FuncTy &&func,
+ const std::optional<ElemTy> &inp) {
+ if (!inp)
+ return std::nullopt;
+ return std::move(func(*inp));
+}
+
+std::optional<Object>
+getBaseObject(const Object &object,
+ Fortran::semantics::SemanticsContext &semaCtx);
+
+namespace clause {
+#ifdef EMPTY_CLASS
+#undef EMPTY_CLASS
+#endif
+#define EMPTY_CLASS(cls) \
+ using cls = tomp::clause::cls##T<SymIdent, SymReference>
+
+#ifdef WRAPPER_CLASS
+#undef WRAPPER_CLASS
+#endif
+#define WRAPPER_CLASS(cls, content) \
+ [[maybe_unused]] extern int xyzzy_semicolon_absorber
+#define GEN_FLANG_CLAUSE_PARSER_CLASSES
+#include "llvm/Frontend/OpenMP/OMP.inc"
+#undef EMPTY_CLASS
+#undef WRAPPER_CLASS
+
+// "Requires" clauses are handled early on, and the aggregated information
+// is stored in the Symbol details of modules, programs, and subprograms.
+// These clauses are still handled here to cover all alternatives in the
+// main clause variant.
+
+using Aligned = tomp::clause::AlignedT<SymIdent, SymReference>;
+using Allocate = tomp::clause::AllocateT<SymIdent, SymReference>;
+using Allocator = tomp::clause::AllocatorT<SymIdent, SymReference>;
+using AtomicDefaultMemOrder =
+ tomp::clause::AtomicDefaultMemOrderT<SymIdent, SymReference>;
+using Collapse = tomp::clause::CollapseT<SymIdent, SymReference>;
+using Copyin = tomp::clause::CopyinT<SymIdent, SymReference>;
+using Copyprivate = tomp::clause::CopyprivateT<SymIdent, SymReference>;
+using Defaultmap = tomp::clause::DefaultmapT<SymIdent, SymReference>;
+using Default = tomp::clause::DefaultT<SymIdent, SymReference>;
+using Depend = tomp::clause::DependT<SymIdent, SymReference>;
+using Device = tomp::clause::DeviceT<SymIdent, SymReference>;
+using DeviceType = tomp::clause::DeviceTypeT<SymIdent, SymReference>;
+using DistSchedule = tomp::clause::DistScheduleT<SymIdent, SymReference>;
+using Enter = tomp::clause::EnterT<SymIdent, SymReference>;
+using Filter = tomp::clause::FilterT<SymIdent, SymReference>;
+using Final = tomp::clause::FinalT<SymIdent, SymReference>;
+using Firstprivate = tomp::clause::FirstprivateT<SymIdent, SymReference>;
+using From = tomp::clause::FromT<SymIdent, SymReference>;
+using Grainsize = tomp::clause::GrainsizeT<SymIdent, SymReference>;
+using HasDeviceAddr = tomp::clause::HasDeviceAddrT<SymIdent, SymReference>;
+using Hint = tomp::clause::HintT<SymIdent, SymReference>;
+using If = tomp::clause::IfT<SymIdent, SymReference>;
+using InReduction = tomp::clause::InReductionT<SymIdent, SymReference>;
+using IsDevicePtr = tomp::clause::IsDevicePtrT<SymIdent, SymReference>;
+using Lastprivate = tomp::clause::LastprivateT<SymIdent, SymReference>;
+using Linear = tomp::clause::LinearT<SymIdent, SymReference>;
+using Link = tomp::clause::LinkT<SymIdent, SymReference>;
+using Map = tomp::clause::MapT<SymIdent, SymReference>;
+using Nocontext = tomp::clause::NocontextT<SymIdent, SymReference>;
+using Nontemporal = tomp::clause::NontemporalT<SymIdent, SymReference>;
+using Novariants = tomp::clause::NovariantsT<SymIdent, SymReference>;
+using NumTasks = tomp::clause::NumTasksT<SymIdent, SymReference>;
+using NumTeams = tomp::clause::NumTeamsT<SymIdent, SymReference>;
+using NumThreads = tomp::clause::NumThreadsT<SymIdent, SymReference>;
+using OmpxDynCgroupMem =
+ tomp::clause::OmpxDynCgroupMemT<SymIdent, SymReference>;
+using Ordered = tomp::clause::OrderedT<SymIdent, SymReference>;
+using Order = tomp::clause::OrderT<SymIdent, SymReference>;
+using Partial = tomp::clause::PartialT<SymIdent, SymReference>;
+using Priority = tomp::clause::PriorityT<SymIdent, SymReference>;
+using Private = tomp::clause::PrivateT<SymIdent, SymReference>;
+using ProcBind = tomp::clause::ProcBindT<SymIdent, SymReference>;
+using Reduction = tomp::clause::ReductionT<SymIdent, SymReference>;
+using Safelen = tomp::clause::SafelenT<SymIdent, SymReference>;
+using Schedule = tomp::clause::ScheduleT<SymIdent, SymReference>;
+using Shared = tomp::clause::SharedT<SymIdent, SymReference>;
+using Simdlen = tomp::clause::SimdlenT<SymIdent, SymReference>;
+using Sizes = tomp::clause::SizesT<SymIdent, SymReference>;
+using TaskReduction = tomp::clause::TaskReductionT<SymIdent, SymReference>;
+using ThreadLimit = tomp::clause::ThreadLimitT<SymIdent, SymReference>;
+using To = tomp::clause::ToT<SymIdent, SymReference>;
+using Uniform = tomp::clause::UniformT<SymIdent, SymReference>;
+using UseDeviceAddr = tomp::clause::UseDeviceAddrT<SymIdent, SymReference>;
+using UseDevicePtr = tomp::clause::UseDevicePtrT<SymIdent, SymReference>;
+} // namespace clause
+
+struct Clause : public tomp::ClauseT<SymIdent, SymReference> {
+ parser::CharBlock source;
+};
+
+template <typename Specific>
+Clause makeClause(llvm::omp::Clause id, Specific &&specific,
+ parser::CharBlock source = {}) {
+ return Clause{id, specific, source};
+}
+
+Clause makeClause(const Fortran::parser::OmpClause &cls,
+ semantics::SemanticsContext &semaCtx);
+
+List<Clause> makeList(const parser::OmpClauseList &clauses,
+ semantics::SemanticsContext &semaCtx);
+} // namespace omp
+
+#endif // FORTRAN_LOWER_OPENMP_CLAUSES_H
More information about the flang-commits
mailing list