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

Krzysztof Parzyszek via flang-commits flang-commits at lists.llvm.org
Thu Feb 22 14:54:28 PST 2024


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

>From e16585a087766f6c8f0fba4ea78de03756b4ec15 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Wed, 21 Feb 2024 12:51:13 -0600
Subject: [PATCH] [flang][OpenMP] Implement flexible OpenMP clause
 representation

Introduce a set of generic classes (templates) that represent OpenMP
clauses in a language-agnostic manner. OpenMP clauses can contain
expressions and data objects and the exact representation of each
depends on the source language of the compiled program. To deal with
this, the templates depend on two type parameters:
- IdType: type that represent object's identity (in a way that
  satisfied OpenMP requirements), and
- ExprType: type that can represent numeric values, as well as
  data references (e.g. x.y[1].z[2]).

In addition to that, implement code instantiating these templates
from flang's AST.

This patch only introduces the new classes, they are not yet used
anywhere.
---
 flang/lib/Lower/CMakeLists.txt     |   1 +
 flang/lib/Lower/OpenMP/ClauseT.h   | 715 ++++++++++++++++++++++++++++
 flang/lib/Lower/OpenMP/Clauses.cpp | 727 +++++++++++++++++++++++++++++
 flang/lib/Lower/OpenMP/Clauses.h   | 198 ++++++++
 4 files changed, 1641 insertions(+)
 create mode 100644 flang/lib/Lower/OpenMP/ClauseT.h
 create mode 100644 flang/lib/Lower/OpenMP/Clauses.cpp
 create mode 100644 flang/lib/Lower/OpenMP/Clauses.h

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..b9fbbd18bf2b94
--- /dev/null
+++ b/flang/lib/Lower/OpenMP/ClauseT.h
@@ -0,0 +1,715 @@
+//===- 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 <vector>
+
+#include "llvm/Frontend/OpenMP/OMP.h.inc"
+
+namespace tomp {
+
+template <typename T>
+using ListT = std::vector<T>;
+
+// 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..dd7ce71f6ddca4
--- /dev/null
+++ b/flang/lib/Lower/OpenMP/Clauses.cpp
@@ -0,0 +1,727 @@
+//===-- 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 SymDsgExtractor {
+  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 SymDsgExtractor::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));
+  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};
+  SymbolWithDesignator sd = getSymbolAndDesignator(ea.Analyze(comp));
+  SymDsgExtractor::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{SymDsgExtractor::symbol_addr(base.GetLastSymbol()),
+                  evaluate::AsGenericExpr(SymDsgExtractor::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{
+          SymDsgExtractor::symbol_addr(comp->symbol()),
+          ea.Designate(evaluate::DataRef{SymDsgExtractor::AsRvalueRef(*comp)})};
+    } else if (base.UnwrapSymbolRef()) {
+      return std::nullopt;
+    }
+  } else {
+    assert(std::holds_alternative<evaluate::CoarrayRef>(ref.u));
+    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 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{s};
+                     },
+                 },
+                 inp.u),
+  };
+}
+
+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)};
+}
+
+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".
+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(makeExprF(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 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)}};
+}
+
+Allocator make(const parser::OmpClause::Allocator &inp,
+               semantics::SemanticsContext &semaCtx) {
+  // inp.v -> parser::ScalarIntExpr
+  return Allocator{makeExpr(inp.v, semaCtx)};
+}
+
+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{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{{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(makeExprF(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{{makeRedOp(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, makeObjectF(semaCtx)),
+                           maybeApply(makeExprF(semaCtx), s.step)}};
+          },
+          [&](const wrapped::WithoutModifier &s) {
+            return Linear{{std::nullopt,
+                           makeList(s.names, makeObjectF(semaCtx)),
+                           maybeApply(makeExprF(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, makeObjectF(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(makeExprF(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(makeExprF(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{{makeRedOp(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(makeExprF(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, makeExprF(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{{makeRedOp(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, makeObjectF(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..e40663565ae603
--- /dev/null
+++ b/flang/lib/Lower/OpenMP/Clauses.h
@@ -0,0 +1,198 @@
+//===-- 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 makeObjectF(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);
+}
+
+inline auto makeExprF(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, makeObjectF(semaCtx));
+}
+
+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));
+}
+
+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
+
+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