[flang-commits] [flang] fcb6a9c - [Flang][OpenMP][Lower] Refactor implementation of PFT to MLIR lowering

Sergio Afonso via flang-commits flang-commits at lists.llvm.org
Mon Jul 31 02:53:03 PDT 2023


Author: Sergio Afonso
Date: 2023-07-31T10:51:39+01:00
New Revision: fcb6a9c07cf7a2bc63d364e3b7f60aaadadd57cc

URL: https://github.com/llvm/llvm-project/commit/fcb6a9c07cf7a2bc63d364e3b7f60aaadadd57cc
DIFF: https://github.com/llvm/llvm-project/commit/fcb6a9c07cf7a2bc63d364e3b7f60aaadadd57cc.diff

LOG: [Flang][OpenMP][Lower] Refactor implementation of PFT to MLIR lowering

This patch makes the following non-functional changes:
  - Extract OpenMP clause processing into a new internal `ClauseProcessor`
    class. Atomic and reduction-related clauses processing is kept unchanged,
    since atomic clauses are stored in `OmpAtomicClauseList` rather than
    `OmpClauseList` and there are many TODO comments related to the current
    implementation of reduction lowering. This has been left unchanged to avoid
    merge conflicts and work duplication.
  - Reorganize functions into sections in the file to improve readability.
  - Explicitly use mlir:: namespace everywhere, rather than just most places.
  - Spell out uses of `auto` in which the type wasn't explicitly stated as part
    of the initialization expression.
  - Normalize a few function names to match the rest and renamed variables in
    'snake_case' to 'camelCase'.

The main purpose is to reduce code duplication and simplify the implementation
of upcoming work to support loop-attached target constructs and teams/
distribute lowering to MLIR.

Differential Revision: https://reviews.llvm.org/D155981

Added: 
    

Modified: 
    flang/lib/Lower/OpenMP.cpp

Removed: 
    


################################################################################
diff  --git a/flang/lib/Lower/OpenMP.cpp b/flang/lib/Lower/OpenMP.cpp
index 7e21281f145303..6456825021e2b6 100644
--- a/flang/lib/Lower/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP.cpp
@@ -25,28 +25,13 @@
 #include "mlir/Dialect/OpenMP/OpenMPDialect.h"
 #include "llvm/Frontend/OpenMP/OMPConstants.h"
 
-using namespace mlir;
+using DeclareTargetCapturePair =
+    std::pair<mlir::omp::DeclareTargetCaptureClause,
+              Fortran::semantics::Symbol>;
 
-void Fortran::lower::genOpenMPTerminator(fir::FirOpBuilder &builder,
-                                         Operation *op, mlir::Location loc) {
-  if (mlir::isa<omp::WsLoopOp, omp::ReductionDeclareOp, omp::AtomicUpdateOp,
-                omp::SimdLoopOp>(op))
-    builder.create<omp::YieldOp>(loc);
-  else
-    builder.create<omp::TerminatorOp>(loc);
-}
-
-int64_t Fortran::lower::getCollapseValue(
-    const Fortran::parser::OmpClauseList &clauseList) {
-  for (const auto &clause : clauseList.v) {
-    if (const auto &collapseClause =
-            std::get_if<Fortran::parser::OmpClause::Collapse>(&clause.u)) {
-      const auto *expr = Fortran::semantics::GetExpr(collapseClause->v);
-      return Fortran::evaluate::ToInt64(*expr).value();
-    }
-  }
-  return 1;
-}
+//===----------------------------------------------------------------------===//
+// Common helper functions
+//===----------------------------------------------------------------------===//
 
 static Fortran::semantics::Symbol *
 getOmpObjectSymbol(const Fortran::parser::OmpObject &ompObject) {
@@ -64,6 +49,54 @@ getOmpObjectSymbol(const Fortran::parser::OmpObject &ompObject) {
   return sym;
 }
 
+static void genObjectList(const Fortran::parser::OmpObjectList &objectList,
+                          Fortran::lower::AbstractConverter &converter,
+                          llvm::SmallVectorImpl<mlir::Value> &operands) {
+  auto addOperands = [&](Fortran::lower::SymbolRef sym) {
+    const mlir::Value variable = converter.getSymbolAddress(sym);
+    if (variable) {
+      operands.push_back(variable);
+    } else {
+      if (const auto *details =
+              sym->detailsIf<Fortran::semantics::HostAssocDetails>()) {
+        operands.push_back(converter.getSymbolAddress(details->symbol()));
+        converter.copySymbolBinding(details->symbol(), sym);
+      }
+    }
+  };
+  for (const Fortran::parser::OmpObject &ompObject : objectList.v) {
+    Fortran::semantics::Symbol *sym = getOmpObjectSymbol(ompObject);
+    addOperands(*sym);
+  }
+}
+
+static void gatherFuncAndVarSyms(
+    const Fortran::parser::OmpObjectList &objList,
+    mlir::omp::DeclareTargetCaptureClause clause,
+    llvm::SmallVectorImpl<std::pair<mlir::omp::DeclareTargetCaptureClause,
+                                    Fortran::semantics::Symbol>>
+        &symbolAndClause) {
+  for (const Fortran::parser::OmpObject &ompObject : objList.v) {
+    Fortran::common::visit(
+        Fortran::common::visitors{
+            [&](const Fortran::parser::Designator &designator) {
+              if (const Fortran::parser::Name *name =
+                      Fortran::semantics::getDesignatorNameIfDataRef(
+                          designator)) {
+                symbolAndClause.emplace_back(clause, *name->symbol);
+              }
+            },
+            [&](const Fortran::parser::Name &name) {
+              symbolAndClause.emplace_back(clause, *name.symbol);
+            }},
+        ompObject.u);
+  }
+}
+
+//===----------------------------------------------------------------------===//
+// DataSharingProcessor
+//===----------------------------------------------------------------------===//
+
 class DataSharingProcessor {
   bool hasLastPrivateOp;
   mlir::OpBuilder::InsertPoint lastPrivIP;
@@ -115,7 +148,7 @@ class DataSharingProcessor {
   // MLIR operation to insert the last private update. Step2 adds
   // dealocation code as well.
   void processStep1();
-  void processStep2(mlir::Operation *op, bool is_loop);
+  void processStep2(mlir::Operation *op, bool isLoop);
 };
 
 void DataSharingProcessor::processStep1() {
@@ -126,12 +159,12 @@ void DataSharingProcessor::processStep1() {
   insertBarrier();
 }
 
-void DataSharingProcessor::processStep2(mlir::Operation *op, bool is_loop) {
+void DataSharingProcessor::processStep2(mlir::Operation *op, bool isLoop) {
   insPt = firOpBuilder.saveInsertionPoint();
   copyLastPrivatize(op);
   firOpBuilder.restoreInsertionPoint(insPt);
 
-  if (is_loop) {
+  if (isLoop) {
     // push deallocs out of the loop
     firOpBuilder.setInsertionPointAfter(op);
     insertDeallocs();
@@ -145,7 +178,7 @@ void DataSharingProcessor::processStep2(mlir::Operation *op, bool is_loop) {
 }
 
 void DataSharingProcessor::insertDeallocs() {
-  for (auto sym : privatizedSymbols)
+  for (const Fortran::semantics::Symbol *sym : privatizedSymbols)
     if (Fortran::semantics::IsAllocatable(sym->GetUltimate())) {
       converter.createHostAssociateVarCloneDealloc(*sym);
     }
@@ -203,7 +236,7 @@ void DataSharingProcessor::collectSymbolsForPrivatization() {
     }
   }
 
-  for (auto *ps : privatizedSymbols) {
+  for (const Fortran::semantics::Symbol *ps : privatizedSymbols) {
     if (ps->has<Fortran::semantics::CommonBlockDetails>())
       TODO(converter.getCurrentLocation(),
            "Common Block in privatization clause");
@@ -214,7 +247,7 @@ void DataSharingProcessor::collectSymbolsForPrivatization() {
 }
 
 bool DataSharingProcessor ::needBarrier() {
-  for (auto sym : privatizedSymbols) {
+  for (const Fortran::semantics::Symbol *sym : privatizedSymbols) {
     if (sym->test(Fortran::semantics::Symbol::Flag::OmpFirstPrivate) &&
         sym->test(Fortran::semantics::Symbol::Flag::OmpLastPrivate))
       return true;
@@ -241,7 +274,7 @@ void DataSharingProcessor::insertLastPrivateCompare(mlir::Operation *op) {
   for (const Fortran::parser::OmpClause &clause : opClauseList.v) {
     if (std::get_if<Fortran::parser::OmpClause::Lastprivate>(&clause.u)) {
       // TODO: Add lastprivate support for simd construct
-      if (mlir::isa<omp::SectionOp>(op)) {
+      if (mlir::isa<mlir::omp::SectionOp>(op)) {
         if (&eval == &eval.parentConstruct->getLastNestedEvaluation()) {
           // For `omp.sections`, lastprivatized variables occur in
           // lexically final `omp.section` operation. The following FIR
@@ -313,7 +346,7 @@ void DataSharingProcessor::insertLastPrivateCompare(mlir::Operation *op) {
             firOpBuilder.restoreInsertionPoint(unstructuredSectionsIP);
           }
         }
-      } else if (mlir::isa<omp::WsLoopOp>(op)) {
+      } else if (mlir::isa<mlir::omp::WsLoopOp>(op)) {
         mlir::Operation *lastOper = op->getRegion(0).back().getTerminator();
         firOpBuilder.setInsertionPoint(lastOper);
 
@@ -358,7 +391,7 @@ void DataSharingProcessor::collectSymbols(
   converter.collectSymbolSet(eval, defaultSymbols, flag,
                              /*collectSymbols=*/true,
                              /*collectHostAssociatedSymbols=*/true);
-  for (auto &e : eval.getNestedEvaluations()) {
+  for (Fortran::lower::pft::Evaluation &e : eval.getNestedEvaluations()) {
     if (e.hasNestedEvaluations())
       converter.collectSymbolSet(e, symbolsInNestedRegions, flag,
                                  /*collectSymbols=*/true,
@@ -385,7 +418,7 @@ void DataSharingProcessor::collectDefaultSymbols() {
 }
 
 void DataSharingProcessor::privatize() {
-  for (auto sym : privatizedSymbols) {
+  for (const Fortran::semantics::Symbol *sym : privatizedSymbols) {
     cloneSymbol(sym);
     copyFirstPrivateSymbol(sym);
   }
@@ -393,12 +426,12 @@ void DataSharingProcessor::privatize() {
 
 void DataSharingProcessor::copyLastPrivatize(mlir::Operation *op) {
   insertLastPrivateCompare(op);
-  for (auto sym : privatizedSymbols)
+  for (const Fortran::semantics::Symbol *sym : privatizedSymbols)
     copyLastPrivateSymbol(sym, &lastPrivIP);
 }
 
 void DataSharingProcessor::defaultPrivatize() {
-  for (auto sym : defaultSymbols) {
+  for (const Fortran::semantics::Symbol *sym : defaultSymbols) {
     if (!symbolsInNestedRegions.contains(sym) &&
         !symbolsInParentRegions.contains(sym) &&
         !privatizedSymbols.contains(sym)) {
@@ -408,590 +441,516 @@ void DataSharingProcessor::defaultPrivatize() {
   }
 }
 
-/// The COMMON block is a global structure. \p commonValue is the base address
-/// of the the COMMON block. As the offset from the symbol \p sym, generate the
-/// COMMON block member value (commonValue + offset) for the symbol.
-/// FIXME: Share the code with `instantiateCommon` in ConvertVariable.cpp.
-static mlir::Value
-genCommonBlockMember(Fortran::lower::AbstractConverter &converter,
-                     const Fortran::semantics::Symbol &sym,
-                     mlir::Value commonValue) {
-  auto &firOpBuilder = converter.getFirOpBuilder();
-  mlir::Location currentLocation = converter.getCurrentLocation();
-  mlir::IntegerType i8Ty = firOpBuilder.getIntegerType(8);
-  mlir::Type i8Ptr = firOpBuilder.getRefType(i8Ty);
-  mlir::Type seqTy = firOpBuilder.getRefType(firOpBuilder.getVarLenSeqTy(i8Ty));
-  mlir::Value base =
-      firOpBuilder.createConvert(currentLocation, seqTy, commonValue);
-  std::size_t byteOffset = sym.GetUltimate().offset();
-  mlir::Value offs = firOpBuilder.createIntegerConstant(
-      currentLocation, firOpBuilder.getIndexType(), byteOffset);
-  mlir::Value varAddr = firOpBuilder.create<fir::CoordinateOp>(
-      currentLocation, i8Ptr, base, mlir::ValueRange{offs});
-  mlir::Type symType = converter.genType(sym);
-  return firOpBuilder.createConvert(currentLocation,
-                                    firOpBuilder.getRefType(symType), varAddr);
-}
-
-// Get the extended value for \p val by extracting additional variable
-// information from \p base.
-static fir::ExtendedValue getExtendedValue(fir::ExtendedValue base,
-                                           mlir::Value val) {
-  return base.match(
-      [&](const fir::MutableBoxValue &box) -> fir::ExtendedValue {
-        return fir::MutableBoxValue(val, box.nonDeferredLenParams(), {});
-      },
-      [&](const auto &) -> fir::ExtendedValue {
-        return fir::substBase(base, val);
-      });
-}
-
-static void threadPrivatizeVars(Fortran::lower::AbstractConverter &converter,
-                                Fortran::lower::pft::Evaluation &eval) {
-  auto &firOpBuilder = converter.getFirOpBuilder();
-  mlir::Location currentLocation = converter.getCurrentLocation();
-  auto insPt = firOpBuilder.saveInsertionPoint();
-  firOpBuilder.setInsertionPointToStart(firOpBuilder.getAllocaBlock());
+//===----------------------------------------------------------------------===//
+// ClauseProcessor
+//===----------------------------------------------------------------------===//
 
-  // Get the original ThreadprivateOp corresponding to the symbol and use the
-  // symbol value from that opeartion to create one ThreadprivateOp copy
-  // operation inside the parallel region.
-  auto genThreadprivateOp = [&](Fortran::lower::SymbolRef sym) -> mlir::Value {
-    mlir::Value symOriThreadprivateValue = converter.getSymbolAddress(sym);
-    mlir::Operation *op = symOriThreadprivateValue.getDefiningOp();
-    assert(mlir::isa<mlir::omp::ThreadprivateOp>(op) &&
-           "The threadprivate operation not created");
-    mlir::Value symValue =
-        mlir::dyn_cast<mlir::omp::ThreadprivateOp>(op).getSymAddr();
-    return firOpBuilder.create<mlir::omp::ThreadprivateOp>(
-        currentLocation, symValue.getType(), symValue);
-  };
+/// Class that handles the processing of OpenMP clauses.
+///
+/// Its `process<ClauseName>()` methods perform MLIR code generation for their
+/// corresponding clause if it is present in the clause list. Otherwise, they
+/// will return `false` to signal that the clause was not found.
+///
+/// The intended use is of this class is to move clause processing outside of
+/// construct processing, since the same clauses can appear attached to
+/// 
diff erent constructs and constructs can be combined, so that code
+/// duplication is minimized.
+///
+/// Each construct-lowering function only calls the `process<ClauseName>()`
+/// methods that relate to clauses that can impact the lowering of that
+/// construct.
+class ClauseProcessor {
+  using ClauseTy = Fortran::parser::OmpClause;
 
-  llvm::SetVector<const Fortran::semantics::Symbol *> threadprivateSyms;
-  converter.collectSymbolSet(
-      eval, threadprivateSyms,
-      Fortran::semantics::Symbol::Flag::OmpThreadprivate);
-  std::set<Fortran::semantics::SourceName> threadprivateSymNames;
+public:
+  ClauseProcessor(Fortran::lower::AbstractConverter &converter,
+                  const Fortran::parser::OmpClauseList &clauses)
+      : converter(converter), clauses(clauses) {}
 
-  // For a COMMON block, the ThreadprivateOp is generated for itself instead of
-  // its members, so only bind the value of the new copied ThreadprivateOp
-  // inside the parallel region to the common block symbol only once for
-  // multiple members in one COMMON block.
-  llvm::SetVector<const Fortran::semantics::Symbol *> commonSyms;
-  for (std::size_t i = 0; i < threadprivateSyms.size(); i++) {
-    auto sym = threadprivateSyms[i];
-    mlir::Value symThreadprivateValue;
-    // The variable may be used more than once, and each reference has one
-    // symbol with the same name. Only do once for references of one variable.
-    if (threadprivateSymNames.find(sym->name()) != threadprivateSymNames.end())
-      continue;
-    threadprivateSymNames.insert(sym->name());
-    if (const Fortran::semantics::Symbol *common =
-            Fortran::semantics::FindCommonBlockContaining(sym->GetUltimate())) {
-      mlir::Value commonThreadprivateValue;
-      if (commonSyms.contains(common)) {
-        commonThreadprivateValue = converter.getSymbolAddress(*common);
-      } else {
-        commonThreadprivateValue = genThreadprivateOp(*common);
-        converter.bindSymbol(*common, commonThreadprivateValue);
-        commonSyms.insert(common);
+  // 'Unique' clauses: They can appear at most once in the clause list.
+  bool
+  processCollapse(mlir::Location currentLocation,
+                  Fortran::lower::pft::Evaluation &eval,
+                  llvm::SmallVectorImpl<mlir::Value> &lowerBound,
+                  llvm::SmallVectorImpl<mlir::Value> &upperBound,
+                  llvm::SmallVectorImpl<mlir::Value> &step,
+                  llvm::SmallVectorImpl<const Fortran::semantics::Symbol *> &iv,
+                  std::size_t &loopVarTypeSize) const;
+  bool processDefault() const;
+  bool processDevice(Fortran::lower::StatementContext &stmtCtx,
+                     mlir::Value &result) const;
+  bool processDeviceType(mlir::omp::DeclareTargetDeviceType &result) const;
+  bool processFinal(Fortran::lower::StatementContext &stmtCtx,
+                    mlir::Value &result) const;
+  bool processHint(mlir::IntegerAttr &result) const;
+  bool processMergeable(mlir::UnitAttr &result) const;
+  bool processNowait(mlir::UnitAttr &result) const;
+  bool processNumThreads(Fortran::lower::StatementContext &stmtCtx,
+                         mlir::Value &result) const;
+  bool processOrdered(mlir::IntegerAttr &result) const;
+  bool processPriority(Fortran::lower::StatementContext &stmtCtx,
+                       mlir::Value &result) const;
+  bool processProcBind(mlir::omp::ClauseProcBindKindAttr &result) const;
+  bool processSafelen(mlir::IntegerAttr &result) const;
+  bool processSchedule(mlir::omp::ClauseScheduleKindAttr &valAttr,
+                       mlir::omp::ScheduleModifierAttr &modifierAttr,
+                       mlir::UnitAttr &simdModifierAttr) const;
+  bool processScheduleChunk(Fortran::lower::StatementContext &stmtCtx,
+                            mlir::Value &result) const;
+  bool processSimdlen(mlir::IntegerAttr &result) const;
+  bool processThreadLimit(Fortran::lower::StatementContext &stmtCtx,
+                          mlir::Value &result) const;
+  bool processUntied(mlir::UnitAttr &result) const;
+
+  // 'Repeatable' clauses: They can appear multiple times in the clause list.
+  bool
+  processAllocate(llvm::SmallVectorImpl<mlir::Value> &allocatorOperands,
+                  llvm::SmallVectorImpl<mlir::Value> &allocateOperands) const;
+  bool processCopyin() const;
+  bool processDepend(llvm::SmallVectorImpl<mlir::Attribute> &dependTypeOperands,
+                     llvm::SmallVectorImpl<mlir::Value> &dependOperands) const;
+  bool processIf(Fortran::lower::StatementContext &stmtCtx,
+                 mlir::Value &result) const;
+  bool
+  processLink(llvm::SmallVectorImpl<DeclareTargetCapturePair> &result) const;
+  bool processMap(llvm::SmallVectorImpl<mlir::Value> &mapOperands,
+                  llvm::SmallVectorImpl<mlir::IntegerAttr> &mapTypes) const;
+  bool processReduction(
+      mlir::Location currentLocation,
+      llvm::SmallVectorImpl<mlir::Value> &reductionVars,
+      llvm::SmallVectorImpl<mlir::Attribute> &reductionDeclSymbols) const;
+  bool processSectionsReduction(mlir::Location currentLocation) const;
+  bool processTo(llvm::SmallVectorImpl<DeclareTargetCapturePair> &result) const;
+  bool
+  processUseDeviceAddr(llvm::SmallVectorImpl<mlir::Value> &operands,
+                       llvm::SmallVectorImpl<mlir::Type> &useDeviceTypes,
+                       llvm::SmallVectorImpl<mlir::Location> &useDeviceLocs,
+                       llvm::SmallVectorImpl<const Fortran::semantics::Symbol *>
+                           &useDeviceSymbols) const;
+  bool
+  processUseDevicePtr(llvm::SmallVectorImpl<mlir::Value> &operands,
+                      llvm::SmallVectorImpl<mlir::Type> &useDeviceTypes,
+                      llvm::SmallVectorImpl<mlir::Location> &useDeviceLocs,
+                      llvm::SmallVectorImpl<const Fortran::semantics::Symbol *>
+                          &useDeviceSymbols) const;
+
+private:
+  using ClauseIterator = std::list<ClauseTy>::const_iterator;
+
+  /// Utility to find a clause within a range in the clause list.
+  template <typename T>
+  static ClauseIterator findClause(ClauseIterator begin, ClauseIterator end) {
+    for (ClauseIterator it = begin; it != end; ++it)
+      if (std::get_if<T>(&it->u))
+        return it;
+
+    return end;
+  }
+
+  /// Return the first instance of the given clause found in the clause list or
+  /// `nullptr` if not present. If more than one instance is expected, use
+  /// `findRepeatableClause` instead.
+  template <typename T>
+  const T *
+  findUniqueClause(const Fortran::parser::CharBlock **source = nullptr) const {
+    ClauseIterator it = findClause<T>(clauses.v.begin(), clauses.v.end());
+    if (it != clauses.v.end()) {
+      if (source)
+        *source = &it->source;
+      return &std::get<T>(it->u);
+    }
+    return nullptr;
+  }
+
+  /// Call `callbackFn` for each occurrence of the given clause. Return `true`
+  /// if at least one instance was found.
+  template <typename T>
+  bool findRepeatableClause(
+      std::function<void(const T *, const Fortran::parser::CharBlock &source)>
+          callbackFn) const {
+    bool found = false;
+    ClauseIterator nextIt, endIt = clauses.v.end();
+    for (ClauseIterator it = clauses.v.begin(); it != endIt; it = nextIt) {
+      nextIt = findClause<T>(it, endIt);
+
+      if (nextIt != endIt) {
+        callbackFn(&std::get<T>(nextIt->u), nextIt->source);
+        found = true;
+        ++nextIt;
       }
-      symThreadprivateValue =
-          genCommonBlockMember(converter, *sym, commonThreadprivateValue);
-    } else {
-      symThreadprivateValue = genThreadprivateOp(*sym);
     }
+    return found;
+  }
 
-    fir::ExtendedValue sexv = converter.getSymbolExtendedValue(*sym);
-    fir::ExtendedValue symThreadprivateExv =
-        getExtendedValue(sexv, symThreadprivateValue);
-    converter.bindSymbol(*sym, symThreadprivateExv);
+  /// Set the `result` to a new `mlir::UnitAttr` if the clause is present.
+  template <typename T>
+  bool markClauseOccurrence(mlir::UnitAttr &result) const {
+    if (findUniqueClause<T>()) {
+      result = converter.getFirOpBuilder().getUnitAttr();
+      return true;
+    }
+    return false;
   }
 
-  firOpBuilder.restoreInsertionPoint(insPt);
+  Fortran::lower::AbstractConverter &converter;
+  const Fortran::parser::OmpClauseList &clauses;
+};
+
+//===----------------------------------------------------------------------===//
+// ClauseProcessor helper functions
+//===----------------------------------------------------------------------===//
+
+/// Check for unsupported map operand types.
+static void checkMapType(mlir::Location location, mlir::Type type) {
+  if (auto refType = type.dyn_cast<fir::ReferenceType>())
+    type = refType.getElementType();
+  if (auto boxType = type.dyn_cast_or_null<fir::BoxType>())
+    if (!boxType.getElementType().isa<fir::PointerType>())
+      TODO(location, "OMPD_target_data MapOperand BoxType");
 }
 
-static void
-genCopyinClause(Fortran::lower::AbstractConverter &converter,
-                const Fortran::parser::OmpClauseList &opClauseList) {
-  fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
-  mlir::OpBuilder::InsertPoint insPt = firOpBuilder.saveInsertionPoint();
-  firOpBuilder.setInsertionPointToStart(firOpBuilder.getAllocaBlock());
-  bool hasCopyin = false;
-  for (const Fortran::parser::OmpClause &clause : opClauseList.v) {
-    if (const auto &copyinClause =
-            std::get_if<Fortran::parser::OmpClause::Copyin>(&clause.u)) {
-      hasCopyin = true;
-      const Fortran::parser::OmpObjectList &ompObjectList = copyinClause->v;
-      for (const Fortran::parser::OmpObject &ompObject : ompObjectList.v) {
-        Fortran::semantics::Symbol *sym = getOmpObjectSymbol(ompObject);
-        if (sym->has<Fortran::semantics::CommonBlockDetails>())
-          TODO(converter.getCurrentLocation(), "common block in Copyin clause");
-        if (Fortran::semantics::IsAllocatableOrPointer(sym->GetUltimate()))
-          TODO(converter.getCurrentLocation(),
-               "pointer or allocatable variables in Copyin clause");
-        assert(sym->has<Fortran::semantics::HostAssocDetails>() &&
-               "No host-association found");
-        converter.copyHostAssociateVar(*sym);
-      }
-    }
-  }
-  // [OMP 5.0, 2.19.6.1] The copy is done after the team is formed and prior to
-  // the execution of the associated structured block. Emit implicit barrier to
-  // synchronize threads and avoid data races on propagation master's thread
-  // values of threadprivate variables to local instances of that variables of
-  // all other implicit threads.
-  if (hasCopyin)
-    firOpBuilder.create<mlir::omp::BarrierOp>(converter.getCurrentLocation());
-  firOpBuilder.restoreInsertionPoint(insPt);
+static std::string getReductionName(llvm::StringRef name, mlir::Type ty) {
+  return (llvm::Twine(name) +
+          (ty.isIntOrIndex() ? llvm::Twine("_i_") : llvm::Twine("_f_")) +
+          llvm::Twine(ty.getIntOrFloatBitWidth()))
+      .str();
 }
 
-static void genObjectList(const Fortran::parser::OmpObjectList &objectList,
-                          Fortran::lower::AbstractConverter &converter,
-                          llvm::SmallVectorImpl<Value> &operands) {
-  auto addOperands = [&](Fortran::lower::SymbolRef sym) {
-    const mlir::Value variable = converter.getSymbolAddress(sym);
-    if (variable) {
-      operands.push_back(variable);
-    } else {
-      if (const auto *details =
-              sym->detailsIf<Fortran::semantics::HostAssocDetails>()) {
-        operands.push_back(converter.getSymbolAddress(details->symbol()));
-        converter.copySymbolBinding(details->symbol(), sym);
-      }
-    }
-  };
-  for (const Fortran::parser::OmpObject &ompObject : objectList.v) {
-    Fortran::semantics::Symbol *sym = getOmpObjectSymbol(ompObject);
-    addOperands(*sym);
+static std::string getReductionName(
+    Fortran::parser::DefinedOperator::IntrinsicOperator intrinsicOp,
+    mlir::Type ty) {
+  std::string reductionName;
+
+  switch (intrinsicOp) {
+  case Fortran::parser::DefinedOperator::IntrinsicOperator::Add:
+    reductionName = "add_reduction";
+    break;
+  case Fortran::parser::DefinedOperator::IntrinsicOperator::Multiply:
+    reductionName = "multiply_reduction";
+    break;
+  case Fortran::parser::DefinedOperator::IntrinsicOperator::AND:
+    return "and_reduction";
+  case Fortran::parser::DefinedOperator::IntrinsicOperator::EQV:
+    return "eqv_reduction";
+  case Fortran::parser::DefinedOperator::IntrinsicOperator::OR:
+    return "or_reduction";
+  case Fortran::parser::DefinedOperator::IntrinsicOperator::NEQV:
+    return "neqv_reduction";
+  default:
+    reductionName = "other_reduction";
+    break;
   }
-}
 
-static mlir::Value
-getIfClauseOperand(Fortran::lower::AbstractConverter &converter,
-                   Fortran::lower::StatementContext &stmtCtx,
-                   const Fortran::parser::OmpClause::If *ifClause,
-                   mlir::Location clauseLocation) {
-  fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
-  auto &expr = std::get<Fortran::parser::ScalarLogicalExpr>(ifClause->v.t);
-  mlir::Value ifVal = fir::getBase(
-      converter.genExprValue(*Fortran::semantics::GetExpr(expr), stmtCtx));
-  return firOpBuilder.createConvert(clauseLocation, firOpBuilder.getI1Type(),
-                                    ifVal);
+  return getReductionName(reductionName, ty);
 }
 
-static mlir::Type getLoopVarType(Fortran::lower::AbstractConverter &converter,
-                                 std::size_t loopVarTypeSize) {
-  // OpenMP runtime requires 32-bit or 64-bit loop variables.
-  loopVarTypeSize = loopVarTypeSize * 8;
-  if (loopVarTypeSize < 32) {
-    loopVarTypeSize = 32;
-  } else if (loopVarTypeSize > 64) {
-    loopVarTypeSize = 64;
-    mlir::emitWarning(converter.getCurrentLocation(),
-                      "OpenMP loop iteration variable cannot have more than 64 "
-                      "bits size and will be narrowed into 64 bits.");
-  }
-  assert((loopVarTypeSize == 32 || loopVarTypeSize == 64) &&
-         "OpenMP loop iteration variable size must be transformed into 32-bit "
-         "or 64-bit");
-  return converter.getFirOpBuilder().getIntegerType(loopVarTypeSize);
+/// This function returns the identity value of the operator \p reductionOpName.
+/// For example:
+///    0 + x = x,
+///    1 * x = x
+static int getOperationIdentity(llvm::StringRef reductionOpName,
+                                mlir::Location loc) {
+  if (reductionOpName.contains("add") || reductionOpName.contains("or") ||
+      reductionOpName.contains("neqv"))
+    return 0;
+  if (reductionOpName.contains("multiply") || reductionOpName.contains("and") ||
+      reductionOpName.contains("eqv"))
+    return 1;
+  TODO(loc, "Reduction of some intrinsic operators is not supported");
 }
 
-/// Create empty blocks for the current region.
-/// These blocks replace blocks parented to an enclosing region.
-void createEmptyRegionBlocks(
-    fir::FirOpBuilder &firOpBuilder,
-    std::list<Fortran::lower::pft::Evaluation> &evaluationList) {
-  auto *region = &firOpBuilder.getRegion();
-  for (auto &eval : evaluationList) {
-    if (eval.block) {
-      if (eval.block->empty()) {
-        eval.block->erase();
-        eval.block = firOpBuilder.createBlock(region);
-      } else {
-        [[maybe_unused]] auto &terminatorOp = eval.block->back();
-        assert((mlir::isa<mlir::omp::TerminatorOp>(terminatorOp) ||
-                mlir::isa<mlir::omp::YieldOp>(terminatorOp)) &&
-               "expected terminator op");
-      }
-    }
-    if (!eval.isDirective() && eval.hasNestedEvaluations())
-      createEmptyRegionBlocks(firOpBuilder, eval.getNestedEvaluations());
-  }
-}
-
-void resetBeforeTerminator(fir::FirOpBuilder &firOpBuilder,
-                           mlir::Operation *storeOp, mlir::Block &block) {
-  if (storeOp)
-    firOpBuilder.setInsertionPointAfter(storeOp);
-  else
-    firOpBuilder.setInsertionPointToStart(&block);
-}
-
-/// Create the body (block) for an OpenMP Operation.
-///
-/// \param [in]    op - the operation the body belongs to.
-/// \param [inout] converter - converter to use for the clauses.
-/// \param [in]    loc - location in source code.
-/// \param [in]    eval - current PFT node/evaluation.
-/// \oaran [in]    clauses - list of clauses to process.
-/// \param [in]    args - block arguments (induction variable[s]) for the
-////                      region.
-/// \param [in]    outerCombined - is this an outer operation - prevents
-///                                privatization.
-template <typename Op>
-static void
-createBodyOfOp(Op &op, Fortran::lower::AbstractConverter &converter,
-               mlir::Location &loc, Fortran::lower::pft::Evaluation &eval,
-               const Fortran::parser::OmpClauseList *clauses = nullptr,
-               const SmallVector<const Fortran::semantics::Symbol *> &args = {},
-               bool outerCombined = false,
-               DataSharingProcessor *dsp = nullptr) {
-  fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
-  // If an argument for the region is provided then create the block with that
-  // argument. Also update the symbol's address with the mlir argument value.
-  // e.g. For loops the argument is the induction variable. And all further
-  // uses of the induction variable should use this mlir value.
-  mlir::Operation *storeOp = nullptr;
-  if (args.size()) {
-    std::size_t loopVarTypeSize = 0;
-    for (const Fortran::semantics::Symbol *arg : args)
-      loopVarTypeSize = std::max(loopVarTypeSize, arg->GetUltimate().size());
-    mlir::Type loopVarType = getLoopVarType(converter, loopVarTypeSize);
-    SmallVector<Type> tiv;
-    SmallVector<Location> locs;
-    for (int i = 0; i < (int)args.size(); i++) {
-      tiv.push_back(loopVarType);
-      locs.push_back(loc);
+static mlir::Value getReductionInitValue(mlir::Location loc, mlir::Type type,
+                                         llvm::StringRef reductionOpName,
+                                         fir::FirOpBuilder &builder) {
+  assert((fir::isa_integer(type) || fir::isa_real(type) ||
+          type.isa<fir::LogicalType>()) &&
+         "only integer, logical and real types are currently supported");
+  if (reductionOpName.contains("max")) {
+    if (auto ty = type.dyn_cast<mlir::FloatType>()) {
+      const llvm::fltSemantics &sem = ty.getFloatSemantics();
+      return builder.createRealConstant(
+          loc, type, llvm::APFloat::getLargest(sem, /*Negative=*/true));
     }
-    firOpBuilder.createBlock(&op.getRegion(), {}, tiv, locs);
-    int argIndex = 0;
-    // The argument is not currently in memory, so make a temporary for the
-    // argument, and store it there, then bind that location to the argument.
-    for (const Fortran::semantics::Symbol *arg : args) {
-      mlir::Value val =
-          fir::getBase(op.getRegion().front().getArgument(argIndex));
-      mlir::Value temp = firOpBuilder.createTemporary(
-          loc, loopVarType,
-          llvm::ArrayRef<mlir::NamedAttribute>{
-              Fortran::lower::getAdaptToByRefAttr(firOpBuilder)});
-      storeOp = firOpBuilder.create<fir::StoreOp>(loc, val, temp);
-      converter.bindSymbol(*arg, temp);
-      argIndex++;
+    unsigned bits = type.getIntOrFloatBitWidth();
+    int64_t minInt = llvm::APInt::getSignedMinValue(bits).getSExtValue();
+    return builder.createIntegerConstant(loc, type, minInt);
+  } else if (reductionOpName.contains("min")) {
+    if (auto ty = type.dyn_cast<mlir::FloatType>()) {
+      const llvm::fltSemantics &sem = ty.getFloatSemantics();
+      return builder.createRealConstant(
+          loc, type, llvm::APFloat::getSmallest(sem, /*Negative=*/true));
     }
+    unsigned bits = type.getIntOrFloatBitWidth();
+    int64_t maxInt = llvm::APInt::getSignedMaxValue(bits).getSExtValue();
+    return builder.createIntegerConstant(loc, type, maxInt);
+  } else if (reductionOpName.contains("ior")) {
+    unsigned bits = type.getIntOrFloatBitWidth();
+    int64_t zeroInt = llvm::APInt::getZero(bits).getSExtValue();
+    return builder.createIntegerConstant(loc, type, zeroInt);
+  } else if (reductionOpName.contains("ieor")) {
+    unsigned bits = type.getIntOrFloatBitWidth();
+    int64_t zeroInt = llvm::APInt::getZero(bits).getSExtValue();
+    return builder.createIntegerConstant(loc, type, zeroInt);
+  } else if (reductionOpName.contains("iand")) {
+    unsigned bits = type.getIntOrFloatBitWidth();
+    int64_t allOnInt = llvm::APInt::getAllOnes(bits).getSExtValue();
+    return builder.createIntegerConstant(loc, type, allOnInt);
   } else {
-    firOpBuilder.createBlock(&op.getRegion());
-  }
-  // Set the insert for the terminator operation to go at the end of the
-  // block - this is either empty or the block with the stores above,
-  // the end of the block works for both.
-  mlir::Block &block = op.getRegion().back();
-  firOpBuilder.setInsertionPointToEnd(&block);
+    if (type.isa<mlir::FloatType>())
+      return builder.create<mlir::arith::ConstantOp>(
+          loc, type,
+          builder.getFloatAttr(
+              type, (double)getOperationIdentity(reductionOpName, loc)));
 
-  // If it is an unstructured region and is not the outer region of a combined
-  // construct, create empty blocks for all evaluations.
-  if (eval.lowerAsUnstructured() && !outerCombined)
-    createEmptyRegionBlocks(firOpBuilder, eval.getNestedEvaluations());
+    if (type.isa<fir::LogicalType>()) {
+      mlir::Value intConst = builder.create<mlir::arith::ConstantOp>(
+          loc, builder.getI1Type(),
+          builder.getIntegerAttr(builder.getI1Type(),
+                                 getOperationIdentity(reductionOpName, loc)));
+      return builder.createConvert(loc, type, intConst);
+    }
 
-  // Insert the terminator.
-  if constexpr (std::is_same_v<Op, omp::WsLoopOp> ||
-                std::is_same_v<Op, omp::SimdLoopOp>) {
-    mlir::ValueRange results;
-    firOpBuilder.create<mlir::omp::YieldOp>(loc, results);
-  } else {
-    firOpBuilder.create<mlir::omp::TerminatorOp>(loc);
+    return builder.create<mlir::arith::ConstantOp>(
+        loc, type,
+        builder.getIntegerAttr(type,
+                               getOperationIdentity(reductionOpName, loc)));
   }
-  // Reset the insert point to before the terminator.
-  resetBeforeTerminator(firOpBuilder, storeOp, block);
+}
 
-  // Handle privatization. Do not privatize if this is the outer operation.
-  if (clauses && !outerCombined) {
-    constexpr bool is_loop = std::is_same_v<Op, omp::WsLoopOp> ||
-                             std::is_same_v<Op, omp::SimdLoopOp>;
-    if (!dsp) {
-      DataSharingProcessor proc(converter, *clauses, eval);
-      proc.processStep1();
-      proc.processStep2(op, is_loop);
-    } else {
-      dsp->processStep2(op, is_loop);
-    }
+template <typename FloatOp, typename IntegerOp>
+static mlir::Value getReductionOperation(fir::FirOpBuilder &builder,
+                                         mlir::Type type, mlir::Location loc,
+                                         mlir::Value op1, mlir::Value op2) {
+  assert(type.isIntOrIndexOrFloat() &&
+         "only integer and float types are currently supported");
+  if (type.isIntOrIndex())
+    return builder.create<IntegerOp>(loc, op1, op2);
+  return builder.create<FloatOp>(loc, op1, op2);
+}
 
-    if (storeOp)
-      firOpBuilder.setInsertionPointAfter(storeOp);
-  }
+static mlir::omp::ReductionDeclareOp
+createMinimalReductionDecl(fir::FirOpBuilder &builder,
+                           llvm::StringRef reductionOpName, mlir::Type type,
+                           mlir::Location loc) {
+  mlir::ModuleOp module = builder.getModule();
+  mlir::OpBuilder modBuilder(module.getBodyRegion());
 
-  if constexpr (std::is_same_v<Op, omp::ParallelOp>) {
-    threadPrivatizeVars(converter, eval);
-    if (clauses)
-      genCopyinClause(converter, *clauses);
-  }
+  mlir::omp::ReductionDeclareOp decl =
+      modBuilder.create<mlir::omp::ReductionDeclareOp>(loc, reductionOpName,
+                                                       type);
+  builder.createBlock(&decl.getInitializerRegion(),
+                      decl.getInitializerRegion().end(), {type}, {loc});
+  builder.setInsertionPointToEnd(&decl.getInitializerRegion().back());
+  mlir::Value init = getReductionInitValue(loc, type, reductionOpName, builder);
+  builder.create<mlir::omp::YieldOp>(loc, init);
+
+  builder.createBlock(&decl.getReductionRegion(),
+                      decl.getReductionRegion().end(), {type, type},
+                      {loc, loc});
+
+  return decl;
 }
 
-static void createBodyOfTargetOp(
-    Fortran::lower::AbstractConverter &converter, mlir::omp::DataOp &dataOp,
-    const llvm::SmallVector<mlir::Type> &useDeviceTypes,
-    const llvm::SmallVector<mlir::Location> &useDeviceLocs,
-    const SmallVector<const Fortran::semantics::Symbol *> &useDeviceSymbols,
-    const mlir::Location &currentLocation) {
-  fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
-  mlir::Region &region = dataOp.getRegion();
+/// Creates an OpenMP reduction declaration and inserts it into the provided
+/// symbol table. The declaration has a constant initializer with the neutral
+/// value `initValue`, and the reduction combiner carried over from `reduce`.
+/// TODO: Generalize this for non-integer types, add atomic region.
+static mlir::omp::ReductionDeclareOp
+createReductionDecl(fir::FirOpBuilder &builder, llvm::StringRef reductionOpName,
+                    const Fortran::parser::ProcedureDesignator &procDesignator,
+                    mlir::Type type, mlir::Location loc) {
+  mlir::OpBuilder::InsertionGuard guard(builder);
+  mlir::ModuleOp module = builder.getModule();
 
-  firOpBuilder.createBlock(&region, {}, useDeviceTypes, useDeviceLocs);
-  firOpBuilder.create<mlir::omp::TerminatorOp>(currentLocation);
-  firOpBuilder.setInsertionPointToStart(&region.front());
+  auto decl =
+      module.lookupSymbol<mlir::omp::ReductionDeclareOp>(reductionOpName);
+  if (decl)
+    return decl;
 
-  unsigned argIndex = 0;
-  for (auto *sym : useDeviceSymbols) {
-    const mlir::BlockArgument &arg = region.front().getArgument(argIndex);
-    mlir::Value val = fir::getBase(arg);
-    fir::ExtendedValue extVal = converter.getSymbolExtendedValue(*sym);
-    if (auto refType = val.getType().dyn_cast<fir::ReferenceType>()) {
-      if (fir::isa_builtin_cptr_type(refType.getElementType())) {
-        converter.bindSymbol(*sym, val);
-      } else {
-        extVal.match(
-            [&](const fir::MutableBoxValue &mbv) {
-              converter.bindSymbol(
-                  *sym,
-                  fir::MutableBoxValue(
-                      val, fir::factory::getNonDeferredLenParams(extVal), {}));
-            },
-            [&](const auto &) {
-              TODO(converter.getCurrentLocation(),
-                   "use_device clause operand unsupported type");
-            });
-      }
+  decl = createMinimalReductionDecl(builder, reductionOpName, type, loc);
+  builder.setInsertionPointToEnd(&decl.getReductionRegion().back());
+  mlir::Value op1 = decl.getReductionRegion().front().getArgument(0);
+  mlir::Value op2 = decl.getReductionRegion().front().getArgument(1);
+
+  mlir::Value reductionOp;
+  if (const auto *name{
+          Fortran::parser::Unwrap<Fortran::parser::Name>(procDesignator)}) {
+    if (name->source == "max") {
+      reductionOp =
+          getReductionOperation<mlir::arith::MaxFOp, mlir::arith::MaxSIOp>(
+              builder, type, loc, op1, op2);
+    } else if (name->source == "min") {
+      reductionOp =
+          getReductionOperation<mlir::arith::MinFOp, mlir::arith::MinSIOp>(
+              builder, type, loc, op1, op2);
+    } else if (name->source == "ior") {
+      assert((type.isIntOrIndex()) && "only integer is expected");
+      reductionOp = builder.create<mlir::arith::OrIOp>(loc, op1, op2);
+    } else if (name->source == "ieor") {
+      assert((type.isIntOrIndex()) && "only integer is expected");
+      reductionOp = builder.create<mlir::arith::XOrIOp>(loc, op1, op2);
+    } else if (name->source == "iand") {
+      assert((type.isIntOrIndex()) && "only integer is expected");
+      reductionOp = builder.create<mlir::arith::AndIOp>(loc, op1, op2);
     } else {
-      TODO(converter.getCurrentLocation(),
-           "use_device clause operand unsupported type");
+      TODO(loc, "Reduction of some intrinsic operators is not supported");
     }
-    argIndex++;
   }
+
+  builder.create<mlir::omp::YieldOp>(loc, reductionOp);
+  return decl;
 }
 
-static void createTargetOp(Fortran::lower::AbstractConverter &converter,
-                           const Fortran::parser::OmpClauseList &opClauseList,
-                           const llvm::omp::Directive &directive,
-                           mlir::Location currentLocation,
-                           Fortran::lower::pft::Evaluation *eval = nullptr) {
-  Fortran::lower::StatementContext stmtCtx;
-  fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+/// Creates an OpenMP reduction declaration and inserts it into the provided
+/// symbol table. The declaration has a constant initializer with the neutral
+/// value `initValue`, and the reduction combiner carried over from `reduce`.
+/// TODO: Generalize this for non-integer types, add atomic region.
+static mlir::omp::ReductionDeclareOp createReductionDecl(
+    fir::FirOpBuilder &builder, llvm::StringRef reductionOpName,
+    Fortran::parser::DefinedOperator::IntrinsicOperator intrinsicOp,
+    mlir::Type type, mlir::Location loc) {
+  mlir::OpBuilder::InsertionGuard guard(builder);
+  mlir::ModuleOp module = builder.getModule();
 
-  mlir::Value ifClauseOperand, deviceOperand, threadLmtOperand;
-  mlir::UnitAttr nowaitAttr;
-  llvm::SmallVector<mlir::Value> mapOperands, devicePtrOperands,
-      deviceAddrOperands;
-  llvm::SmallVector<mlir::IntegerAttr> mapTypes;
-  llvm::SmallVector<mlir::Type> useDeviceTypes;
-  llvm::SmallVector<mlir::Location> useDeviceLocs;
-  SmallVector<const Fortran::semantics::Symbol *> useDeviceSymbols;
-
-  /// Check for unsupported map operand types.
-  auto checkType = [](mlir::Location location, mlir::Type type) {
-    if (auto refType = type.dyn_cast<fir::ReferenceType>())
-      type = refType.getElementType();
-    if (auto boxType = type.dyn_cast_or_null<fir::BoxType>())
-      if (!boxType.getElementType().isa<fir::PointerType>())
-        TODO(location, "OMPD_target_data MapOperand BoxType");
-  };
+  auto decl =
+      module.lookupSymbol<mlir::omp::ReductionDeclareOp>(reductionOpName);
+  if (decl)
+    return decl;
 
-  auto addMapClause = [&](const auto &mapClause, mlir::Location &location) {
-    const auto &oMapType =
-        std::get<std::optional<Fortran::parser::OmpMapType>>(mapClause->v.t);
-    llvm::omp::OpenMPOffloadMappingFlags mapTypeBits =
-        llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_NONE;
-    // If the map type is specified, then process it else Tofrom is the default.
-    if (oMapType) {
-      const Fortran::parser::OmpMapType::Type &mapType =
-          std::get<Fortran::parser::OmpMapType::Type>(oMapType->t);
-      switch (mapType) {
-      case Fortran::parser::OmpMapType::Type::To:
-        mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TO;
-        break;
-      case Fortran::parser::OmpMapType::Type::From:
-        mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_FROM;
-        break;
-      case Fortran::parser::OmpMapType::Type::Tofrom:
-        mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TO |
-                       llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_FROM;
-        break;
-      case Fortran::parser::OmpMapType::Type::Alloc:
-      case Fortran::parser::OmpMapType::Type::Release:
-        // alloc and release is the default map_type for the Target Data Ops,
-        // i.e. if no bits for map_type is supplied then alloc/release is
-        // implicitly assumed based on the target directive. Default value for
-        // Target Data and Enter Data is alloc and for Exit Data it is release.
-        break;
-      case Fortran::parser::OmpMapType::Type::Delete:
-        mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_DELETE;
-      }
+  decl = createMinimalReductionDecl(builder, reductionOpName, type, loc);
+  builder.setInsertionPointToEnd(&decl.getReductionRegion().back());
+  mlir::Value op1 = decl.getReductionRegion().front().getArgument(0);
+  mlir::Value op2 = decl.getReductionRegion().front().getArgument(1);
 
-      if (std::get<std::optional<Fortran::parser::OmpMapType::Always>>(
-              oMapType->t))
-        mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_ALWAYS;
-    } else {
-      mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TO |
-                     llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_FROM;
-    }
+  mlir::Value reductionOp;
+  switch (intrinsicOp) {
+  case Fortran::parser::DefinedOperator::IntrinsicOperator::Add:
+    reductionOp =
+        getReductionOperation<mlir::arith::AddFOp, mlir::arith::AddIOp>(
+            builder, type, loc, op1, op2);
+    break;
+  case Fortran::parser::DefinedOperator::IntrinsicOperator::Multiply:
+    reductionOp =
+        getReductionOperation<mlir::arith::MulFOp, mlir::arith::MulIOp>(
+            builder, type, loc, op1, op2);
+    break;
+  case Fortran::parser::DefinedOperator::IntrinsicOperator::AND: {
+    mlir::Value op1I1 = builder.createConvert(loc, builder.getI1Type(), op1);
+    mlir::Value op2I1 = builder.createConvert(loc, builder.getI1Type(), op2);
 
-    // TODO: Add support MapTypeModifiers close, mapper, present, iterator
+    mlir::Value andiOp = builder.create<mlir::arith::AndIOp>(loc, op1I1, op2I1);
 
-    mlir::IntegerAttr mapTypeAttr = firOpBuilder.getIntegerAttr(
-        firOpBuilder.getI64Type(),
-        static_cast<
-            std::underlying_type_t<llvm::omp::OpenMPOffloadMappingFlags>>(
-            mapTypeBits));
+    reductionOp = builder.createConvert(loc, type, andiOp);
+    break;
+  }
+  case Fortran::parser::DefinedOperator::IntrinsicOperator::OR: {
+    mlir::Value op1I1 = builder.createConvert(loc, builder.getI1Type(), op1);
+    mlir::Value op2I1 = builder.createConvert(loc, builder.getI1Type(), op2);
 
-    llvm::SmallVector<mlir::Value> mapOperand;
-    /// Check for unsupported map operand types.
-    for (const Fortran::parser::OmpObject &ompObject :
-         std::get<Fortran::parser::OmpObjectList>(mapClause->v.t).v) {
-      if (Fortran::parser::Unwrap<Fortran::parser::ArrayElement>(ompObject) ||
-          Fortran::parser::Unwrap<Fortran::parser::StructureComponent>(
-              ompObject))
-        TODO(location,
-             "OMPD_target_data for Array Expressions or Structure Components");
-    }
-    genObjectList(std::get<Fortran::parser::OmpObjectList>(mapClause->v.t),
-                  converter, mapOperand);
+    mlir::Value oriOp = builder.create<mlir::arith::OrIOp>(loc, op1I1, op2I1);
 
-    for (mlir::Value mapOp : mapOperand) {
-      checkType(mapOp.getLoc(), mapOp.getType());
-      mapOperands.push_back(mapOp);
-      mapTypes.push_back(mapTypeAttr);
-    }
-  };
+    reductionOp = builder.createConvert(loc, type, oriOp);
+    break;
+  }
+  case Fortran::parser::DefinedOperator::IntrinsicOperator::EQV: {
+    mlir::Value op1I1 = builder.createConvert(loc, builder.getI1Type(), op1);
+    mlir::Value op2I1 = builder.createConvert(loc, builder.getI1Type(), op2);
 
-  auto addUseDeviceClause = [&](const auto &useDeviceClause, auto &operands) {
-    genObjectList(useDeviceClause, converter, operands);
-    for (auto &operand : operands) {
-      checkType(operand.getLoc(), operand.getType());
-      useDeviceTypes.push_back(operand.getType());
-      useDeviceLocs.push_back(operand.getLoc());
-    }
-    for (const Fortran::parser::OmpObject &ompObject : useDeviceClause.v) {
-      Fortran::semantics::Symbol *sym = getOmpObjectSymbol(ompObject);
-      useDeviceSymbols.push_back(sym);
-    }
-  };
+    mlir::Value cmpiOp = builder.create<mlir::arith::CmpIOp>(
+        loc, mlir::arith::CmpIPredicate::eq, op1I1, op2I1);
 
-  for (const Fortran::parser::OmpClause &clause : opClauseList.v) {
-    mlir::Location clauseLocation = converter.genLocation(clause.source);
-    if (const auto &ifClause =
-            std::get_if<Fortran::parser::OmpClause::If>(&clause.u)) {
-      ifClauseOperand =
-          getIfClauseOperand(converter, stmtCtx, ifClause, clauseLocation);
-    } else if (const auto &deviceClause =
-                   std::get_if<Fortran::parser::OmpClause::Device>(&clause.u)) {
-      if (auto deviceModifier = std::get<
-              std::optional<Fortran::parser::OmpDeviceClause::DeviceModifier>>(
-              deviceClause->v.t)) {
-        if (deviceModifier ==
-            Fortran::parser::OmpDeviceClause::DeviceModifier::Ancestor) {
-          TODO(clauseLocation, "OMPD_target Device Modifier Ancestor");
-        }
-      }
-      if (const auto *deviceExpr = Fortran::semantics::GetExpr(
-              std::get<Fortran::parser::ScalarIntExpr>(deviceClause->v.t))) {
-        deviceOperand =
-            fir::getBase(converter.genExprValue(*deviceExpr, stmtCtx));
-      }
-    } else if (const auto &threadLmtClause =
-                   std::get_if<Fortran::parser::OmpClause::ThreadLimit>(
-                       &clause.u)) {
-      threadLmtOperand = fir::getBase(converter.genExprValue(
-          *Fortran::semantics::GetExpr(threadLmtClause->v), stmtCtx));
-    } else if (std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u)) {
-      nowaitAttr = firOpBuilder.getUnitAttr();
-    } else if (const auto &devPtrClause =
-                   std::get_if<Fortran::parser::OmpClause::UseDevicePtr>(
-                       &clause.u)) {
-      addUseDeviceClause(devPtrClause->v, devicePtrOperands);
-    } else if (const auto &devAddrClause =
-                   std::get_if<Fortran::parser::OmpClause::UseDeviceAddr>(
-                       &clause.u)) {
-      addUseDeviceClause(devAddrClause->v, deviceAddrOperands);
-    } else if (const auto &mapClause =
-                   std::get_if<Fortran::parser::OmpClause::Map>(&clause.u)) {
-      addMapClause(mapClause, clauseLocation);
-    } else {
-      TODO(clauseLocation, "OMPD_target unhandled clause");
-    }
+    reductionOp = builder.createConvert(loc, type, cmpiOp);
+    break;
   }
+  case Fortran::parser::DefinedOperator::IntrinsicOperator::NEQV: {
+    mlir::Value op1I1 = builder.createConvert(loc, builder.getI1Type(), op1);
+    mlir::Value op2I1 = builder.createConvert(loc, builder.getI1Type(), op2);
 
-  llvm::SmallVector<mlir::Attribute> mapTypesAttr(mapTypes.begin(),
-                                                  mapTypes.end());
-  mlir::ArrayAttr mapTypesArrayAttr =
-      ArrayAttr::get(firOpBuilder.getContext(), mapTypesAttr);
+    mlir::Value cmpiOp = builder.create<mlir::arith::CmpIOp>(
+        loc, mlir::arith::CmpIPredicate::ne, op1I1, op2I1);
 
-  if (directive == llvm::omp::Directive::OMPD_target) {
-    auto targetOp = firOpBuilder.create<omp::TargetOp>(
-        currentLocation, ifClauseOperand, deviceOperand, threadLmtOperand,
-        nowaitAttr, mapOperands, mapTypesArrayAttr);
-    createBodyOfOp(targetOp, converter, currentLocation, *eval, &opClauseList);
-  } else if (directive == llvm::omp::Directive::OMPD_target_data) {
-    auto dataOp = firOpBuilder.create<omp::DataOp>(
-        currentLocation, ifClauseOperand, deviceOperand, devicePtrOperands,
-        deviceAddrOperands, mapOperands, mapTypesArrayAttr);
-    createBodyOfTargetOp(converter, dataOp, useDeviceTypes, useDeviceLocs,
-                         useDeviceSymbols, currentLocation);
-  } else if (directive == llvm::omp::Directive::OMPD_target_enter_data) {
-    firOpBuilder.create<omp::EnterDataOp>(currentLocation, ifClauseOperand,
-                                          deviceOperand, nowaitAttr,
-                                          mapOperands, mapTypesArrayAttr);
-  } else if (directive == llvm::omp::Directive::OMPD_target_exit_data) {
-    firOpBuilder.create<omp::ExitDataOp>(currentLocation, ifClauseOperand,
-                                         deviceOperand, nowaitAttr, mapOperands,
-                                         mapTypesArrayAttr);
-  } else {
-    TODO(currentLocation, "OMPD_target directive unknown");
+    reductionOp = builder.createConvert(loc, type, cmpiOp);
+    break;
   }
+  default:
+    TODO(loc, "Reduction of some intrinsic operators is not supported");
+  }
+
+  builder.create<mlir::omp::YieldOp>(loc, reductionOp);
+  return decl;
 }
 
-static void genOMP(Fortran::lower::AbstractConverter &converter,
-                   Fortran::lower::pft::Evaluation &eval,
-                   const Fortran::parser::OpenMPSimpleStandaloneConstruct
-                       &simpleStandaloneConstruct) {
-  const auto &directive =
-      std::get<Fortran::parser::OmpSimpleStandaloneDirective>(
-          simpleStandaloneConstruct.t);
-  fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
-  const Fortran::parser::OmpClauseList &opClauseList =
-      std::get<Fortran::parser::OmpClauseList>(simpleStandaloneConstruct.t);
-  mlir::Location currentLocation = converter.genLocation(directive.source);
+static mlir::omp::ScheduleModifier
+translateScheduleModifier(const Fortran::parser::OmpScheduleModifierType &m) {
+  switch (m.v) {
+  case Fortran::parser::OmpScheduleModifierType::ModType::Monotonic:
+    return mlir::omp::ScheduleModifier::monotonic;
+  case Fortran::parser::OmpScheduleModifierType::ModType::Nonmonotonic:
+    return mlir::omp::ScheduleModifier::nonmonotonic;
+  case Fortran::parser::OmpScheduleModifierType::ModType::Simd:
+    return mlir::omp::ScheduleModifier::simd;
+  }
+  return mlir::omp::ScheduleModifier::none;
+}
 
-  switch (directive.v) {
-  default:
-    break;
-  case llvm::omp::Directive::OMPD_barrier:
-    firOpBuilder.create<omp::BarrierOp>(currentLocation);
-    break;
-  case llvm::omp::Directive::OMPD_taskwait:
-    firOpBuilder.create<omp::TaskwaitOp>(currentLocation);
-    break;
-  case llvm::omp::Directive::OMPD_taskyield:
-    firOpBuilder.create<omp::TaskyieldOp>(currentLocation);
-    break;
-  case llvm::omp::Directive::OMPD_target_data:
-  case llvm::omp::Directive::OMPD_target_enter_data:
-  case llvm::omp::Directive::OMPD_target_exit_data:
-    createTargetOp(converter, opClauseList, directive.v, currentLocation);
-    break;
-  case llvm::omp::Directive::OMPD_target_update:
-    TODO(currentLocation, "OMPD_target_update");
-  case llvm::omp::Directive::OMPD_ordered:
-    TODO(currentLocation, "OMPD_ordered");
+static mlir::omp::ScheduleModifier
+getScheduleModifier(const Fortran::parser::OmpScheduleClause &x) {
+  const auto &modifier =
+      std::get<std::optional<Fortran::parser::OmpScheduleModifier>>(x.t);
+  // The input may have the modifier any order, so we look for one that isn't
+  // SIMD. If modifier is not set at all, fall down to the bottom and return
+  // "none".
+  if (modifier) {
+    const auto &modType1 =
+        std::get<Fortran::parser::OmpScheduleModifier::Modifier1>(modifier->t);
+    if (modType1.v.v ==
+        Fortran::parser::OmpScheduleModifierType::ModType::Simd) {
+      const auto &modType2 = std::get<
+          std::optional<Fortran::parser::OmpScheduleModifier::Modifier2>>(
+          modifier->t);
+      if (modType2 &&
+          modType2->v.v !=
+              Fortran::parser::OmpScheduleModifierType::ModType::Simd)
+        return translateScheduleModifier(modType2->v);
+
+      return mlir::omp::ScheduleModifier::none;
+    }
+
+    return translateScheduleModifier(modType1.v);
   }
+  return mlir::omp::ScheduleModifier::none;
+}
+
+static mlir::omp::ScheduleModifier
+getSimdModifier(const Fortran::parser::OmpScheduleClause &x) {
+  const auto &modifier =
+      std::get<std::optional<Fortran::parser::OmpScheduleModifier>>(x.t);
+  // Either of the two possible modifiers in the input can be the SIMD modifier,
+  // so look in either one, and return simd if we find one. Not found = return
+  // "none".
+  if (modifier) {
+    const auto &modType1 =
+        std::get<Fortran::parser::OmpScheduleModifier::Modifier1>(modifier->t);
+    if (modType1.v.v == Fortran::parser::OmpScheduleModifierType::ModType::Simd)
+      return mlir::omp::ScheduleModifier::simd;
+
+    const auto &modType2 = std::get<
+        std::optional<Fortran::parser::OmpScheduleModifier::Modifier2>>(
+        modifier->t);
+    if (modType2 && modType2->v.v ==
+                        Fortran::parser::OmpScheduleModifierType::ModType::Simd)
+      return mlir::omp::ScheduleModifier::simd;
+  }
+  return mlir::omp::ScheduleModifier::none;
 }
 
 static void
 genAllocateClause(Fortran::lower::AbstractConverter &converter,
                   const Fortran::parser::OmpAllocateClause &ompAllocateClause,
-                  SmallVector<Value> &allocatorOperands,
-                  SmallVector<Value> &allocateOperands) {
-  auto &firOpBuilder = converter.getFirOpBuilder();
-  auto currentLocation = converter.getCurrentLocation();
+                  llvm::SmallVectorImpl<mlir::Value> &allocatorOperands,
+                  llvm::SmallVectorImpl<mlir::Value> &allocateOperands) {
+  fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+  mlir::Location currentLocation = converter.getCurrentLocation();
   Fortran::lower::StatementContext stmtCtx;
 
   mlir::Value allocatorOperand;
@@ -1010,7 +969,7 @@ genAllocateClause(Fortran::lower::AbstractConverter &converter,
           allocateModifier->u);
 
   if (allocateModifier && !onlyAllocator) {
-    TODO(converter.getCurrentLocation(), "OmpAllocateClause ALIGN modifier");
+    TODO(currentLocation, "OmpAllocateClause ALIGN modifier");
   }
 
   // Check if allocate clause has allocator specified. If so, add it
@@ -1022,592 +981,1293 @@ genAllocateClause(Fortran::lower::AbstractConverter &converter,
         allocateModifier->u);
     allocatorOperand = fir::getBase(converter.genExprValue(
         *Fortran::semantics::GetExpr(allocatorValue.v), stmtCtx));
-    allocatorOperands.insert(allocatorOperands.end(), ompObjectList.v.size(),
-                             allocatorOperand);
-  } else {
-    allocatorOperand = firOpBuilder.createIntegerConstant(
-        currentLocation, firOpBuilder.getI32Type(), 1);
-    allocatorOperands.insert(allocatorOperands.end(), ompObjectList.v.size(),
-                             allocatorOperand);
-  }
-  genObjectList(ompObjectList, converter, allocateOperands);
-}
-
-static void
-genOMP(Fortran::lower::AbstractConverter &converter,
-       Fortran::lower::pft::Evaluation &eval,
-       const Fortran::parser::OpenMPStandaloneConstruct &standaloneConstruct) {
-  std::visit(
-      Fortran::common::visitors{
-          [&](const Fortran::parser::OpenMPSimpleStandaloneConstruct
-                  &simpleStandaloneConstruct) {
-            genOMP(converter, eval, simpleStandaloneConstruct);
-          },
-          [&](const Fortran::parser::OpenMPFlushConstruct &flushConstruct) {
-            SmallVector<Value, 4> operandRange;
-            if (const auto &ompObjectList =
-                    std::get<std::optional<Fortran::parser::OmpObjectList>>(
-                        flushConstruct.t))
-              genObjectList(*ompObjectList, converter, operandRange);
-            const auto &memOrderClause = std::get<std::optional<
-                std::list<Fortran::parser::OmpMemoryOrderClause>>>(
-                flushConstruct.t);
-            if (memOrderClause.has_value() && memOrderClause->size() > 0)
-              TODO(converter.getCurrentLocation(),
-                   "Handle OmpMemoryOrderClause");
-            converter.getFirOpBuilder().create<mlir::omp::FlushOp>(
-                converter.getCurrentLocation(), operandRange);
-          },
-          [&](const Fortran::parser::OpenMPCancelConstruct &cancelConstruct) {
-            TODO(converter.getCurrentLocation(), "OpenMPCancelConstruct");
-          },
-          [&](const Fortran::parser::OpenMPCancellationPointConstruct
-                  &cancellationPointConstruct) {
-            TODO(converter.getCurrentLocation(), "OpenMPCancelConstruct");
-          },
-      },
-      standaloneConstruct.u);
+    allocatorOperands.insert(allocatorOperands.end(), ompObjectList.v.size(),
+                             allocatorOperand);
+  } else {
+    allocatorOperand = firOpBuilder.createIntegerConstant(
+        currentLocation, firOpBuilder.getI32Type(), 1);
+    allocatorOperands.insert(allocatorOperands.end(), ompObjectList.v.size(),
+                             allocatorOperand);
+  }
+  genObjectList(ompObjectList, converter, allocateOperands);
 }
 
-static omp::ClauseProcBindKindAttr genProcBindKindAttr(
+static mlir::omp::ClauseProcBindKindAttr genProcBindKindAttr(
     fir::FirOpBuilder &firOpBuilder,
     const Fortran::parser::OmpClause::ProcBind *procBindClause) {
-  omp::ClauseProcBindKind pbKind;
+  mlir::omp::ClauseProcBindKind procBindKind;
   switch (procBindClause->v.v) {
   case Fortran::parser::OmpProcBindClause::Type::Master:
-    pbKind = omp::ClauseProcBindKind::Master;
+    procBindKind = mlir::omp::ClauseProcBindKind::Master;
     break;
   case Fortran::parser::OmpProcBindClause::Type::Close:
-    pbKind = omp::ClauseProcBindKind::Close;
+    procBindKind = mlir::omp::ClauseProcBindKind::Close;
     break;
   case Fortran::parser::OmpProcBindClause::Type::Spread:
-    pbKind = omp::ClauseProcBindKind::Spread;
+    procBindKind = mlir::omp::ClauseProcBindKind::Spread;
     break;
   case Fortran::parser::OmpProcBindClause::Type::Primary:
-    pbKind = omp::ClauseProcBindKind::Primary;
+    procBindKind = mlir::omp::ClauseProcBindKind::Primary;
     break;
   }
-  return omp::ClauseProcBindKindAttr::get(firOpBuilder.getContext(), pbKind);
+  return mlir::omp::ClauseProcBindKindAttr::get(firOpBuilder.getContext(),
+                                                procBindKind);
 }
 
-static omp::ClauseTaskDependAttr
+static mlir::omp::ClauseTaskDependAttr
 genDependKindAttr(fir::FirOpBuilder &firOpBuilder,
                   const Fortran::parser::OmpClause::Depend *dependClause) {
-  omp::ClauseTaskDepend pbKind;
+  mlir::omp::ClauseTaskDepend pbKind;
   switch (
       std::get<Fortran::parser::OmpDependenceType>(
           std::get<Fortran::parser::OmpDependClause::InOut>(dependClause->v.u)
               .t)
           .v) {
   case Fortran::parser::OmpDependenceType::Type::In:
-    pbKind = omp::ClauseTaskDepend::taskdependin;
+    pbKind = mlir::omp::ClauseTaskDepend::taskdependin;
     break;
   case Fortran::parser::OmpDependenceType::Type::Out:
-    pbKind = omp::ClauseTaskDepend::taskdependout;
+    pbKind = mlir::omp::ClauseTaskDepend::taskdependout;
     break;
   case Fortran::parser::OmpDependenceType::Type::Inout:
-    pbKind = omp::ClauseTaskDepend::taskdependinout;
+    pbKind = mlir::omp::ClauseTaskDepend::taskdependinout;
     break;
   default:
     llvm_unreachable("unknown parser task dependence type");
     break;
   }
-  return omp::ClauseTaskDependAttr::get(firOpBuilder.getContext(), pbKind);
+  return mlir::omp::ClauseTaskDependAttr::get(firOpBuilder.getContext(),
+                                              pbKind);
+}
+
+static mlir::Value
+getIfClauseOperand(Fortran::lower::AbstractConverter &converter,
+                   Fortran::lower::StatementContext &stmtCtx,
+                   const Fortran::parser::OmpClause::If *ifClause,
+                   mlir::Location clauseLocation) {
+  fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+  auto &expr = std::get<Fortran::parser::ScalarLogicalExpr>(ifClause->v.t);
+  mlir::Value ifVal = fir::getBase(
+      converter.genExprValue(*Fortran::semantics::GetExpr(expr), stmtCtx));
+  return firOpBuilder.createConvert(clauseLocation, firOpBuilder.getI1Type(),
+                                    ifVal);
+}
+
+/// Creates a reduction declaration and associates it with an OpenMP block
+/// directive.
+static void
+addReductionDecl(mlir::Location currentLocation,
+                 Fortran::lower::AbstractConverter &converter,
+                 const Fortran::parser::OmpReductionClause &reduction,
+                 llvm::SmallVectorImpl<mlir::Value> &reductionVars,
+                 llvm::SmallVectorImpl<mlir::Attribute> &reductionDeclSymbols) {
+  fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+  mlir::omp::ReductionDeclareOp decl;
+  const auto &redOperator{
+      std::get<Fortran::parser::OmpReductionOperator>(reduction.t)};
+  const auto &objectList{std::get<Fortran::parser::OmpObjectList>(reduction.t)};
+  if (const auto &redDefinedOp =
+          std::get_if<Fortran::parser::DefinedOperator>(&redOperator.u)) {
+    const auto &intrinsicOp{
+        std::get<Fortran::parser::DefinedOperator::IntrinsicOperator>(
+            redDefinedOp->u)};
+    switch (intrinsicOp) {
+    case Fortran::parser::DefinedOperator::IntrinsicOperator::Add:
+    case Fortran::parser::DefinedOperator::IntrinsicOperator::Multiply:
+    case Fortran::parser::DefinedOperator::IntrinsicOperator::AND:
+    case Fortran::parser::DefinedOperator::IntrinsicOperator::EQV:
+    case Fortran::parser::DefinedOperator::IntrinsicOperator::OR:
+    case Fortran::parser::DefinedOperator::IntrinsicOperator::NEQV:
+      break;
+
+    default:
+      TODO(currentLocation,
+           "Reduction of some intrinsic operators is not supported");
+      break;
+    }
+    for (const Fortran::parser::OmpObject &ompObject : objectList.v) {
+      if (const auto *name{
+              Fortran::parser::Unwrap<Fortran::parser::Name>(ompObject)}) {
+        if (const Fortran::semantics::Symbol * symbol{name->symbol}) {
+          mlir::Value symVal = converter.getSymbolAddress(*symbol);
+          mlir::Type redType =
+              symVal.getType().cast<fir::ReferenceType>().getEleTy();
+          reductionVars.push_back(symVal);
+          if (redType.isa<fir::LogicalType>())
+            decl = createReductionDecl(
+                firOpBuilder,
+                getReductionName(intrinsicOp, firOpBuilder.getI1Type()),
+                intrinsicOp, redType, currentLocation);
+          else if (redType.isIntOrIndexOrFloat()) {
+            decl = createReductionDecl(firOpBuilder,
+                                       getReductionName(intrinsicOp, redType),
+                                       intrinsicOp, redType, currentLocation);
+          } else {
+            TODO(currentLocation, "Reduction of some types is not supported");
+          }
+          reductionDeclSymbols.push_back(mlir::SymbolRefAttr::get(
+              firOpBuilder.getContext(), decl.getSymName()));
+        }
+      }
+    }
+  } else if (const auto *reductionIntrinsic =
+                 std::get_if<Fortran::parser::ProcedureDesignator>(
+                     &redOperator.u)) {
+    if (const auto *name{Fortran::parser::Unwrap<Fortran::parser::Name>(
+            reductionIntrinsic)}) {
+      if ((name->source != "max") && (name->source != "min") &&
+          (name->source != "ior") && (name->source != "ieor") &&
+          (name->source != "iand")) {
+        TODO(currentLocation,
+             "Reduction of intrinsic procedures is not supported");
+      }
+      std::string intrinsicOp = name->ToString();
+      for (const Fortran::parser::OmpObject &ompObject : objectList.v) {
+        if (const auto *name{
+                Fortran::parser::Unwrap<Fortran::parser::Name>(ompObject)}) {
+          if (const Fortran::semantics::Symbol * symbol{name->symbol}) {
+            mlir::Value symVal = converter.getSymbolAddress(*symbol);
+            mlir::Type redType =
+                symVal.getType().cast<fir::ReferenceType>().getEleTy();
+            reductionVars.push_back(symVal);
+            assert(redType.isIntOrIndexOrFloat() &&
+                   "Unsupported reduction type");
+            decl = createReductionDecl(
+                firOpBuilder, getReductionName(intrinsicOp, redType),
+                *reductionIntrinsic, redType, currentLocation);
+            reductionDeclSymbols.push_back(mlir::SymbolRefAttr::get(
+                firOpBuilder.getContext(), decl.getSymName()));
+          }
+        }
+      }
+    }
+  }
+}
+
+static void
+addUseDeviceClause(Fortran::lower::AbstractConverter &converter,
+                   const Fortran::parser::OmpObjectList &useDeviceClause,
+                   llvm::SmallVectorImpl<mlir::Value> &operands,
+                   llvm::SmallVectorImpl<mlir::Type> &useDeviceTypes,
+                   llvm::SmallVectorImpl<mlir::Location> &useDeviceLocs,
+                   llvm::SmallVectorImpl<const Fortran::semantics::Symbol *>
+                       &useDeviceSymbols) {
+  genObjectList(useDeviceClause, converter, operands);
+  for (mlir::Value &operand : operands) {
+    checkMapType(operand.getLoc(), operand.getType());
+    useDeviceTypes.push_back(operand.getType());
+    useDeviceLocs.push_back(operand.getLoc());
+  }
+  for (const Fortran::parser::OmpObject &ompObject : useDeviceClause.v) {
+    Fortran::semantics::Symbol *sym = getOmpObjectSymbol(ompObject);
+    useDeviceSymbols.push_back(sym);
+  }
+}
+
+//===----------------------------------------------------------------------===//
+// ClauseProcessor unique clauses
+//===----------------------------------------------------------------------===//
+
+bool ClauseProcessor::processCollapse(
+    mlir::Location currentLocation, Fortran::lower::pft::Evaluation &eval,
+    llvm::SmallVectorImpl<mlir::Value> &lowerBound,
+    llvm::SmallVectorImpl<mlir::Value> &upperBound,
+    llvm::SmallVectorImpl<mlir::Value> &step,
+    llvm::SmallVectorImpl<const Fortran::semantics::Symbol *> &iv,
+    std::size_t &loopVarTypeSize) const {
+  bool found = false;
+  fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+
+  // Collect the loops to collapse.
+  Fortran::lower::pft::Evaluation *doConstructEval =
+      &eval.getFirstNestedEvaluation();
+  if (doConstructEval->getIf<Fortran::parser::DoConstruct>()
+          ->IsDoConcurrent()) {
+    TODO(currentLocation, "Do Concurrent in Worksharing loop construct");
+  }
+
+  std::int64_t collapseValue = 1l;
+  if (auto *collapseClause = findUniqueClause<ClauseTy::Collapse>()) {
+    const auto *expr = Fortran::semantics::GetExpr(collapseClause->v);
+    collapseValue = Fortran::evaluate::ToInt64(*expr).value();
+    found = true;
+  }
+
+  loopVarTypeSize = 0;
+  do {
+    Fortran::lower::pft::Evaluation *doLoop =
+        &doConstructEval->getFirstNestedEvaluation();
+    auto *doStmt = doLoop->getIf<Fortran::parser::NonLabelDoStmt>();
+    assert(doStmt && "Expected do loop to be in the nested evaluation");
+    const auto &loopControl =
+        std::get<std::optional<Fortran::parser::LoopControl>>(doStmt->t);
+    const Fortran::parser::LoopControl::Bounds *bounds =
+        std::get_if<Fortran::parser::LoopControl::Bounds>(&loopControl->u);
+    assert(bounds && "Expected bounds for worksharing do loop");
+    Fortran::lower::StatementContext stmtCtx;
+    lowerBound.push_back(fir::getBase(converter.genExprValue(
+        *Fortran::semantics::GetExpr(bounds->lower), stmtCtx)));
+    upperBound.push_back(fir::getBase(converter.genExprValue(
+        *Fortran::semantics::GetExpr(bounds->upper), stmtCtx)));
+    if (bounds->step) {
+      step.push_back(fir::getBase(converter.genExprValue(
+          *Fortran::semantics::GetExpr(bounds->step), stmtCtx)));
+    } else { // If `step` is not present, assume it as `1`.
+      step.push_back(firOpBuilder.createIntegerConstant(
+          currentLocation, firOpBuilder.getIntegerType(32), 1));
+    }
+    iv.push_back(bounds->name.thing.symbol);
+    loopVarTypeSize = std::max(loopVarTypeSize,
+                               bounds->name.thing.symbol->GetUltimate().size());
+    collapseValue--;
+    doConstructEval =
+        &*std::next(doConstructEval->getNestedEvaluations().begin());
+  } while (collapseValue > 0);
+
+  return found;
+}
+
+bool ClauseProcessor::processDefault() const {
+  if (auto *defaultClause = findUniqueClause<ClauseTy::Default>()) {
+    // Private, Firstprivate, Shared, None
+    switch (defaultClause->v.v) {
+    case Fortran::parser::OmpDefaultClause::Type::Shared:
+    case Fortran::parser::OmpDefaultClause::Type::None:
+      // Default clause with shared or none do not require any handling since
+      // Shared is the default behavior in the IR and None is only required
+      // for semantic checks.
+      break;
+    case Fortran::parser::OmpDefaultClause::Type::Private:
+      // TODO Support default(private)
+      break;
+    case Fortran::parser::OmpDefaultClause::Type::Firstprivate:
+      // TODO Support default(firstprivate)
+      break;
+    }
+    return true;
+  }
+  return false;
+}
+
+bool ClauseProcessor::processDevice(Fortran::lower::StatementContext &stmtCtx,
+                                    mlir::Value &result) const {
+  const Fortran::parser::CharBlock *source = nullptr;
+  if (auto *deviceClause = findUniqueClause<ClauseTy::Device>(&source)) {
+    mlir::Location clauseLocation = converter.genLocation(*source);
+    if (auto deviceModifier = std::get<
+            std::optional<Fortran::parser::OmpDeviceClause::DeviceModifier>>(
+            deviceClause->v.t)) {
+      if (deviceModifier ==
+          Fortran::parser::OmpDeviceClause::DeviceModifier::Ancestor) {
+        TODO(clauseLocation, "OMPD_target Device Modifier Ancestor");
+      }
+    }
+    if (const auto *deviceExpr = Fortran::semantics::GetExpr(
+            std::get<Fortran::parser::ScalarIntExpr>(deviceClause->v.t))) {
+      result = fir::getBase(converter.genExprValue(*deviceExpr, stmtCtx));
+    }
+    return true;
+  }
+  return false;
+}
+
+bool ClauseProcessor::processDeviceType(
+    mlir::omp::DeclareTargetDeviceType &result) const {
+  if (auto *deviceTypeClause = findUniqueClause<ClauseTy::DeviceType>()) {
+    // Case: declare target ... device_type(any | host | nohost)
+    switch (deviceTypeClause->v.v) {
+    case Fortran::parser::OmpDeviceTypeClause::Type::Nohost:
+      result = mlir::omp::DeclareTargetDeviceType::nohost;
+      break;
+    case Fortran::parser::OmpDeviceTypeClause::Type::Host:
+      result = mlir::omp::DeclareTargetDeviceType::host;
+      break;
+    case Fortran::parser::OmpDeviceTypeClause::Type::Any:
+      result = mlir::omp::DeclareTargetDeviceType::any;
+      break;
+    }
+    return true;
+  }
+  return false;
+}
+
+bool ClauseProcessor::processFinal(Fortran::lower::StatementContext &stmtCtx,
+                                   mlir::Value &result) const {
+  const Fortran::parser::CharBlock *source = nullptr;
+  if (auto *finalClause = findUniqueClause<ClauseTy::Final>(&source)) {
+    fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+    mlir::Location clauseLocation = converter.genLocation(*source);
+
+    mlir::Value finalVal = fir::getBase(converter.genExprValue(
+        *Fortran::semantics::GetExpr(finalClause->v), stmtCtx));
+    result = firOpBuilder.createConvert(clauseLocation,
+                                        firOpBuilder.getI1Type(), finalVal);
+    return true;
+  }
+  return false;
+}
+
+bool ClauseProcessor::processHint(mlir::IntegerAttr &result) const {
+  if (auto *hintClause = findUniqueClause<ClauseTy::Hint>()) {
+    fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+    const auto *expr = Fortran::semantics::GetExpr(hintClause->v);
+    int64_t hintValue = *Fortran::evaluate::ToInt64(*expr);
+    result = firOpBuilder.getI64IntegerAttr(hintValue);
+    return true;
+  }
+  return false;
+}
+
+bool ClauseProcessor::processMergeable(mlir::UnitAttr &result) const {
+  return markClauseOccurrence<ClauseTy::Mergeable>(result);
+}
+
+bool ClauseProcessor::processNowait(mlir::UnitAttr &result) const {
+  return markClauseOccurrence<ClauseTy::Nowait>(result);
+}
+
+bool ClauseProcessor::processNumThreads(
+    Fortran::lower::StatementContext &stmtCtx, mlir::Value &result) const {
+  if (auto *numThreadsClause = findUniqueClause<ClauseTy::NumThreads>()) {
+    // OMPIRBuilder expects `NUM_THREADS` clause as a `Value`.
+    result = fir::getBase(converter.genExprValue(
+        *Fortran::semantics::GetExpr(numThreadsClause->v), stmtCtx));
+    return true;
+  }
+  return false;
+}
+
+bool ClauseProcessor::processOrdered(mlir::IntegerAttr &result) const {
+  if (auto *orderedClause = findUniqueClause<ClauseTy::Ordered>()) {
+    fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+    int64_t orderedClauseValue = 0l;
+    if (orderedClause->v.has_value()) {
+      const auto *expr = Fortran::semantics::GetExpr(orderedClause->v);
+      orderedClauseValue = *Fortran::evaluate::ToInt64(*expr);
+    }
+    result = firOpBuilder.getI64IntegerAttr(orderedClauseValue);
+    return true;
+  }
+  return false;
+}
+
+bool ClauseProcessor::processPriority(Fortran::lower::StatementContext &stmtCtx,
+                                      mlir::Value &result) const {
+  if (auto *priorityClause = findUniqueClause<ClauseTy::Priority>()) {
+    result = fir::getBase(converter.genExprValue(
+        *Fortran::semantics::GetExpr(priorityClause->v), stmtCtx));
+    return true;
+  }
+  return false;
+}
+
+bool ClauseProcessor::processProcBind(
+    mlir::omp::ClauseProcBindKindAttr &result) const {
+  if (auto *procBindClause = findUniqueClause<ClauseTy::ProcBind>()) {
+    fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+    result = genProcBindKindAttr(firOpBuilder, procBindClause);
+    return true;
+  }
+  return false;
+}
+
+bool ClauseProcessor::processSafelen(mlir::IntegerAttr &result) const {
+  if (auto *safelenClause = findUniqueClause<ClauseTy::Safelen>()) {
+    fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+    const auto *expr = Fortran::semantics::GetExpr(safelenClause->v);
+    const std::optional<std::int64_t> safelenVal =
+        Fortran::evaluate::ToInt64(*expr);
+    result = firOpBuilder.getI64IntegerAttr(*safelenVal);
+    return true;
+  }
+  return false;
+}
+
+bool ClauseProcessor::processSchedule(
+    mlir::omp::ClauseScheduleKindAttr &valAttr,
+    mlir::omp::ScheduleModifierAttr &modifierAttr,
+    mlir::UnitAttr &simdModifierAttr) const {
+  if (auto *scheduleClause = findUniqueClause<ClauseTy::Schedule>()) {
+    fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+    mlir::MLIRContext *context = firOpBuilder.getContext();
+    const Fortran::parser::OmpScheduleClause &scheduleType = scheduleClause->v;
+    const auto &scheduleClauseKind =
+        std::get<Fortran::parser::OmpScheduleClause::ScheduleType>(
+            scheduleType.t);
+
+    mlir::omp::ClauseScheduleKind scheduleKind;
+    switch (scheduleClauseKind) {
+    case Fortran::parser::OmpScheduleClause::ScheduleType::Static:
+      scheduleKind = mlir::omp::ClauseScheduleKind::Static;
+      break;
+    case Fortran::parser::OmpScheduleClause::ScheduleType::Dynamic:
+      scheduleKind = mlir::omp::ClauseScheduleKind::Dynamic;
+      break;
+    case Fortran::parser::OmpScheduleClause::ScheduleType::Guided:
+      scheduleKind = mlir::omp::ClauseScheduleKind::Guided;
+      break;
+    case Fortran::parser::OmpScheduleClause::ScheduleType::Auto:
+      scheduleKind = mlir::omp::ClauseScheduleKind::Auto;
+      break;
+    case Fortran::parser::OmpScheduleClause::ScheduleType::Runtime:
+      scheduleKind = mlir::omp::ClauseScheduleKind::Runtime;
+      break;
+    }
+
+    mlir::omp::ScheduleModifier scheduleModifier =
+        getScheduleModifier(scheduleClause->v);
+
+    if (scheduleModifier != mlir::omp::ScheduleModifier::none)
+      modifierAttr =
+          mlir::omp::ScheduleModifierAttr::get(context, scheduleModifier);
+
+    if (getSimdModifier(scheduleClause->v) != mlir::omp::ScheduleModifier::none)
+      simdModifierAttr = firOpBuilder.getUnitAttr();
+
+    valAttr = mlir::omp::ClauseScheduleKindAttr::get(context, scheduleKind);
+    return true;
+  }
+  return false;
+}
+
+bool ClauseProcessor::processScheduleChunk(
+    Fortran::lower::StatementContext &stmtCtx, mlir::Value &result) const {
+  if (auto *scheduleClause = findUniqueClause<ClauseTy::Schedule>()) {
+    if (const auto &chunkExpr =
+            std::get<std::optional<Fortran::parser::ScalarIntExpr>>(
+                scheduleClause->v.t)) {
+      if (const auto *expr = Fortran::semantics::GetExpr(*chunkExpr)) {
+        result = fir::getBase(converter.genExprValue(*expr, stmtCtx));
+      }
+    }
+    return true;
+  }
+  return false;
+}
+
+bool ClauseProcessor::processSimdlen(mlir::IntegerAttr &result) const {
+  if (auto *simdlenClause = findUniqueClause<ClauseTy::Simdlen>()) {
+    fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+    const auto *expr = Fortran::semantics::GetExpr(simdlenClause->v);
+    const std::optional<std::int64_t> simdlenVal =
+        Fortran::evaluate::ToInt64(*expr);
+    result = firOpBuilder.getI64IntegerAttr(*simdlenVal);
+    return true;
+  }
+  return false;
+}
+
+bool ClauseProcessor::processThreadLimit(
+    Fortran::lower::StatementContext &stmtCtx, mlir::Value &result) const {
+  if (auto *threadLmtClause = findUniqueClause<ClauseTy::ThreadLimit>()) {
+    result = fir::getBase(converter.genExprValue(
+        *Fortran::semantics::GetExpr(threadLmtClause->v), stmtCtx));
+    return true;
+  }
+  return false;
+}
+
+bool ClauseProcessor::processUntied(mlir::UnitAttr &result) const {
+  return markClauseOccurrence<ClauseTy::Untied>(result);
+}
+
+//===----------------------------------------------------------------------===//
+// ClauseProcessor repeatable clauses
+//===----------------------------------------------------------------------===//
+
+bool ClauseProcessor::processAllocate(
+    llvm::SmallVectorImpl<mlir::Value> &allocatorOperands,
+    llvm::SmallVectorImpl<mlir::Value> &allocateOperands) const {
+  return findRepeatableClause<ClauseTy::Allocate>(
+      [&](const ClauseTy::Allocate *allocateClause,
+          const Fortran::parser::CharBlock &) {
+        genAllocateClause(converter, allocateClause->v, allocatorOperands,
+                          allocateOperands);
+      });
+}
+
+bool ClauseProcessor::processCopyin() const {
+  fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+  mlir::OpBuilder::InsertPoint insPt = firOpBuilder.saveInsertionPoint();
+  firOpBuilder.setInsertionPointToStart(firOpBuilder.getAllocaBlock());
+
+  bool hasCopyin = findRepeatableClause<ClauseTy::Copyin>(
+      [&](const ClauseTy::Copyin *copyinClause,
+          const Fortran::parser::CharBlock &) {
+        const Fortran::parser::OmpObjectList &ompObjectList = copyinClause->v;
+        for (const Fortran::parser::OmpObject &ompObject : ompObjectList.v) {
+          Fortran::semantics::Symbol *sym = getOmpObjectSymbol(ompObject);
+          if (sym->has<Fortran::semantics::CommonBlockDetails>())
+            TODO(converter.getCurrentLocation(),
+                 "common block in Copyin clause");
+          if (Fortran::semantics::IsAllocatableOrPointer(sym->GetUltimate()))
+            TODO(converter.getCurrentLocation(),
+                 "pointer or allocatable variables in Copyin clause");
+          assert(sym->has<Fortran::semantics::HostAssocDetails>() &&
+                 "No host-association found");
+          converter.copyHostAssociateVar(*sym);
+        }
+      });
+
+  // [OMP 5.0, 2.19.6.1] The copy is done after the team is formed and prior to
+  // the execution of the associated structured block. Emit implicit barrier to
+  // synchronize threads and avoid data races on propagation master's thread
+  // values of threadprivate variables to local instances of that variables of
+  // all other implicit threads.
+  if (hasCopyin)
+    firOpBuilder.create<mlir::omp::BarrierOp>(converter.getCurrentLocation());
+  firOpBuilder.restoreInsertionPoint(insPt);
+  return hasCopyin;
 }
 
-/* When parallel is used in a combined construct, then use this function to
- * create the parallel operation. It handles the parallel specific clauses
- * and leaves the rest for handling at the inner operations.
- * TODO: Refactor clause handling
- */
-template <typename Directive>
-static void
-createCombinedParallelOp(Fortran::lower::AbstractConverter &converter,
-                         Fortran::lower::pft::Evaluation &eval,
-                         const Directive &directive) {
+bool ClauseProcessor::processDepend(
+    llvm::SmallVectorImpl<mlir::Attribute> &dependTypeOperands,
+    llvm::SmallVectorImpl<mlir::Value> &dependOperands) const {
   fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
-  mlir::Location currentLocation = converter.getCurrentLocation();
-  Fortran::lower::StatementContext stmtCtx;
-  llvm::ArrayRef<mlir::Type> argTy;
-  mlir::Value ifClauseOperand, numThreadsClauseOperand;
-  SmallVector<Value> allocatorOperands, allocateOperands;
-  mlir::omp::ClauseProcBindKindAttr procBindKindAttr;
-  const auto &opClauseList =
-      std::get<Fortran::parser::OmpClauseList>(directive.t);
-  // TODO: Handle the following clauses
-  // 1. default
-  // Note: rest of the clauses are handled when the inner operation is created
-  for (const Fortran::parser::OmpClause &clause : opClauseList.v) {
-    mlir::Location clauseLocation = converter.genLocation(clause.source);
-    if (const auto &ifClause =
-            std::get_if<Fortran::parser::OmpClause::If>(&clause.u)) {
-      ifClauseOperand =
-          getIfClauseOperand(converter, stmtCtx, ifClause, clauseLocation);
-    } else if (const auto &numThreadsClause =
-                   std::get_if<Fortran::parser::OmpClause::NumThreads>(
-                       &clause.u)) {
-      numThreadsClauseOperand = fir::getBase(converter.genExprValue(
-          *Fortran::semantics::GetExpr(numThreadsClause->v), stmtCtx));
-    } else if (const auto &procBindClause =
-                   std::get_if<Fortran::parser::OmpClause::ProcBind>(
-                       &clause.u)) {
-      procBindKindAttr = genProcBindKindAttr(firOpBuilder, procBindClause);
-    }
-  }
-  // Create and insert the operation.
-  auto parallelOp = firOpBuilder.create<mlir::omp::ParallelOp>(
-      currentLocation, argTy, ifClauseOperand, numThreadsClauseOperand,
-      allocateOperands, allocatorOperands, /*reduction_vars=*/ValueRange(),
-      /*reductions=*/nullptr, procBindKindAttr);
 
-  createBodyOfOp<omp::ParallelOp>(parallelOp, converter, currentLocation, eval,
-                                  &opClauseList, /*iv=*/{},
-                                  /*isCombined=*/true);
+  return findRepeatableClause<ClauseTy::Depend>(
+      [&](const ClauseTy::Depend *dependClause,
+          const Fortran::parser::CharBlock &) {
+        const std::list<Fortran::parser::Designator> &depVal =
+            std::get<std::list<Fortran::parser::Designator>>(
+                std::get<Fortran::parser::OmpDependClause::InOut>(
+                    dependClause->v.u)
+                    .t);
+        mlir::omp::ClauseTaskDependAttr dependTypeOperand =
+            genDependKindAttr(firOpBuilder, dependClause);
+        dependTypeOperands.insert(dependTypeOperands.end(), depVal.size(),
+                                  dependTypeOperand);
+        for (const Fortran::parser::Designator &ompObject : depVal) {
+          Fortran::semantics::Symbol *sym = nullptr;
+          std::visit(
+              Fortran::common::visitors{
+                  [&](const Fortran::parser::DataRef &designator) {
+                    if (const Fortran::parser::Name *name =
+                            std::get_if<Fortran::parser::Name>(&designator.u)) {
+                      sym = name->symbol;
+                    } else if (std::get_if<Fortran::common::Indirection<
+                                   Fortran::parser::ArrayElement>>(
+                                   &designator.u)) {
+                      TODO(converter.getCurrentLocation(),
+                           "array sections not supported for task depend");
+                    }
+                  },
+                  [&](const Fortran::parser::Substring &designator) {
+                    TODO(converter.getCurrentLocation(),
+                         "substring not supported for task depend");
+                  }},
+              (ompObject).u);
+          const mlir::Value variable = converter.getSymbolAddress(*sym);
+          dependOperands.push_back(variable);
+        }
+      });
 }
 
-/// This function returns the identity value of the operator \p reductionOpName.
-/// For example:
-///    0 + x = x,
-///    1 * x = x
-static int getOperationIdentity(llvm::StringRef reductionOpName,
-                                mlir::Location loc) {
-  if (reductionOpName.contains("add") || reductionOpName.contains("or") ||
-      reductionOpName.contains("neqv"))
-    return 0;
-  if (reductionOpName.contains("multiply") || reductionOpName.contains("and") ||
-      reductionOpName.contains("eqv"))
-    return 1;
-  TODO(loc, "Reduction of some intrinsic operators is not supported");
+bool ClauseProcessor::processIf(Fortran::lower::StatementContext &stmtCtx,
+                                mlir::Value &result) const {
+  return findRepeatableClause<ClauseTy::If>(
+      [&](const ClauseTy::If *ifClause,
+          const Fortran::parser::CharBlock &source) {
+        mlir::Location clauseLocation = converter.genLocation(source);
+        // TODO Consider DirectiveNameModifier of the `ifClause` to only search
+        // for an applicable 'if' clause.
+        result =
+            getIfClauseOperand(converter, stmtCtx, ifClause, clauseLocation);
+      });
 }
 
-static Value getReductionInitValue(mlir::Location loc, mlir::Type type,
-                                   llvm::StringRef reductionOpName,
-                                   fir::FirOpBuilder &builder) {
-  assert((fir::isa_integer(type) || fir::isa_real(type) ||
-          type.isa<fir::LogicalType>()) &&
-         "only integer, logical and real types are currently supported");
-  if (reductionOpName.contains("max")) {
-    if (auto ty = type.dyn_cast<mlir::FloatType>()) {
-      const llvm::fltSemantics &sem = ty.getFloatSemantics();
-      return builder.createRealConstant(
-          loc, type, llvm::APFloat::getLargest(sem, /*Negative=*/true));
+bool ClauseProcessor::processLink(
+    llvm::SmallVectorImpl<DeclareTargetCapturePair> &result) const {
+  return findRepeatableClause<ClauseTy::Link>(
+      [&](const ClauseTy::Link *linkClause,
+          const Fortran::parser::CharBlock &) {
+        // Case: declare target link(var1, var2)...
+        gatherFuncAndVarSyms(
+            linkClause->v, mlir::omp::DeclareTargetCaptureClause::link, result);
+      });
+}
+
+bool ClauseProcessor::processMap(
+    llvm::SmallVectorImpl<mlir::Value> &mapOperands,
+    llvm::SmallVectorImpl<mlir::IntegerAttr> &mapTypes) const {
+  fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+
+  return findRepeatableClause<
+      ClauseTy::Map>([&](const ClauseTy::Map *mapClause,
+                         const Fortran::parser::CharBlock &source) {
+    mlir::Location clauseLocation = converter.genLocation(source);
+    const auto &oMapType =
+        std::get<std::optional<Fortran::parser::OmpMapType>>(mapClause->v.t);
+    llvm::omp::OpenMPOffloadMappingFlags mapTypeBits =
+        llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_NONE;
+    // If the map type is specified, then process it else Tofrom is the default.
+    if (oMapType) {
+      const Fortran::parser::OmpMapType::Type &mapType =
+          std::get<Fortran::parser::OmpMapType::Type>(oMapType->t);
+      switch (mapType) {
+      case Fortran::parser::OmpMapType::Type::To:
+        mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TO;
+        break;
+      case Fortran::parser::OmpMapType::Type::From:
+        mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_FROM;
+        break;
+      case Fortran::parser::OmpMapType::Type::Tofrom:
+        mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TO |
+                       llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_FROM;
+        break;
+      case Fortran::parser::OmpMapType::Type::Alloc:
+      case Fortran::parser::OmpMapType::Type::Release:
+        // alloc and release is the default map_type for the Target Data Ops,
+        // i.e. if no bits for map_type is supplied then alloc/release is
+        // implicitly assumed based on the target directive. Default value for
+        // Target Data and Enter Data is alloc and for Exit Data it is release.
+        break;
+      case Fortran::parser::OmpMapType::Type::Delete:
+        mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_DELETE;
+      }
+
+      if (std::get<std::optional<Fortran::parser::OmpMapType::Always>>(
+              oMapType->t))
+        mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_ALWAYS;
+    } else {
+      mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TO |
+                     llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_FROM;
     }
-    unsigned bits = type.getIntOrFloatBitWidth();
-    int64_t minInt = llvm::APInt::getSignedMinValue(bits).getSExtValue();
-    return builder.createIntegerConstant(loc, type, minInt);
-  } else if (reductionOpName.contains("min")) {
-    if (auto ty = type.dyn_cast<mlir::FloatType>()) {
-      const llvm::fltSemantics &sem = ty.getFloatSemantics();
-      return builder.createRealConstant(
-          loc, type, llvm::APFloat::getSmallest(sem, /*Negative=*/true));
+
+    // TODO: Add support MapTypeModifiers close, mapper, present, iterator
+
+    mlir::IntegerAttr mapTypeAttr = firOpBuilder.getIntegerAttr(
+        firOpBuilder.getI64Type(),
+        static_cast<
+            std::underlying_type_t<llvm::omp::OpenMPOffloadMappingFlags>>(
+            mapTypeBits));
+
+    llvm::SmallVector<mlir::Value> mapOperand;
+    // Check for unsupported map operand types.
+    for (const Fortran::parser::OmpObject &ompObject :
+         std::get<Fortran::parser::OmpObjectList>(mapClause->v.t).v) {
+      if (Fortran::parser::Unwrap<Fortran::parser::ArrayElement>(ompObject) ||
+          Fortran::parser::Unwrap<Fortran::parser::StructureComponent>(
+              ompObject))
+        TODO(clauseLocation,
+             "OMPD_target_data for Array Expressions or Structure Components");
     }
-    unsigned bits = type.getIntOrFloatBitWidth();
-    int64_t maxInt = llvm::APInt::getSignedMaxValue(bits).getSExtValue();
-    return builder.createIntegerConstant(loc, type, maxInt);
-  } else if (reductionOpName.contains("ior")) {
-    unsigned bits = type.getIntOrFloatBitWidth();
-    int64_t zeroInt = llvm::APInt::getZero(bits).getSExtValue();
-    return builder.createIntegerConstant(loc, type, zeroInt);
-  } else if (reductionOpName.contains("ieor")) {
-    unsigned bits = type.getIntOrFloatBitWidth();
-    int64_t zeroInt = llvm::APInt::getZero(bits).getSExtValue();
-    return builder.createIntegerConstant(loc, type, zeroInt);
-  } else if (reductionOpName.contains("iand")) {
-    unsigned bits = type.getIntOrFloatBitWidth();
-    int64_t allOnInt = llvm::APInt::getAllOnes(bits).getSExtValue();
-    return builder.createIntegerConstant(loc, type, allOnInt);
-  } else {
-    if (type.isa<FloatType>())
-      return builder.create<mlir::arith::ConstantOp>(
-          loc, type,
-          builder.getFloatAttr(
-              type, (double)getOperationIdentity(reductionOpName, loc)));
+    genObjectList(std::get<Fortran::parser::OmpObjectList>(mapClause->v.t),
+                  converter, mapOperand);
 
-    if (type.isa<fir::LogicalType>()) {
-      Value intConst = builder.create<mlir::arith::ConstantOp>(
-          loc, builder.getI1Type(),
-          builder.getIntegerAttr(builder.getI1Type(),
-                                 getOperationIdentity(reductionOpName, loc)));
-      return builder.createConvert(loc, type, intConst);
+    for (mlir::Value mapOp : mapOperand) {
+      checkMapType(mapOp.getLoc(), mapOp.getType());
+      mapOperands.push_back(mapOp);
+      mapTypes.push_back(mapTypeAttr);
     }
+  });
+}
+
+bool ClauseProcessor::processReduction(
+    mlir::Location currentLocation,
+    llvm::SmallVectorImpl<mlir::Value> &reductionVars,
+    llvm::SmallVectorImpl<mlir::Attribute> &reductionDeclSymbols) const {
+  return findRepeatableClause<ClauseTy::Reduction>(
+      [&](const ClauseTy::Reduction *reductionClause,
+          const Fortran::parser::CharBlock &) {
+        addReductionDecl(currentLocation, converter, reductionClause->v,
+                         reductionVars, reductionDeclSymbols);
+      });
+}
 
-    return builder.create<mlir::arith::ConstantOp>(
-        loc, type,
-        builder.getIntegerAttr(type,
-                               getOperationIdentity(reductionOpName, loc)));
-  }
+bool ClauseProcessor::processSectionsReduction(
+    mlir::Location currentLocation) const {
+  return findRepeatableClause<ClauseTy::Reduction>(
+      [&](const ClauseTy::Reduction *, const Fortran::parser::CharBlock &) {
+        TODO(currentLocation, "OMPC_Reduction");
+      });
 }
 
-template <typename FloatOp, typename IntegerOp>
-static Value getReductionOperation(fir::FirOpBuilder &builder, mlir::Type type,
-                                   mlir::Location loc, mlir::Value op1,
-                                   mlir::Value op2) {
-  assert(type.isIntOrIndexOrFloat() &&
-         "only integer and float types are currently supported");
-  if (type.isIntOrIndex())
-    return builder.create<IntegerOp>(loc, op1, op2);
-  return builder.create<FloatOp>(loc, op1, op2);
+bool ClauseProcessor::processTo(
+    llvm::SmallVectorImpl<DeclareTargetCapturePair> &result) const {
+  return findRepeatableClause<ClauseTy::To>(
+      [&](const ClauseTy::To *toClause, const Fortran::parser::CharBlock &) {
+        // Case: declare target to(func, var1, var2)...
+        gatherFuncAndVarSyms(toClause->v,
+                             mlir::omp::DeclareTargetCaptureClause::to, result);
+      });
 }
 
-static omp::ReductionDeclareOp
-createMinimalReductionDecl(fir::FirOpBuilder &builder,
-                           llvm::StringRef reductionOpName, mlir::Type type,
-                           mlir::Location loc) {
-  mlir::ModuleOp module = builder.getModule();
-  mlir::OpBuilder modBuilder(module.getBodyRegion());
+bool ClauseProcessor::processUseDeviceAddr(
+    llvm::SmallVectorImpl<mlir::Value> &operands,
+    llvm::SmallVectorImpl<mlir::Type> &useDeviceTypes,
+    llvm::SmallVectorImpl<mlir::Location> &useDeviceLocs,
+    llvm::SmallVectorImpl<const Fortran::semantics::Symbol *> &useDeviceSymbols)
+    const {
+  return findRepeatableClause<ClauseTy::UseDeviceAddr>(
+      [&](const ClauseTy::UseDeviceAddr *devAddrClause,
+          const Fortran::parser::CharBlock &) {
+        addUseDeviceClause(converter, devAddrClause->v, operands,
+                           useDeviceTypes, useDeviceLocs, useDeviceSymbols);
+      });
+}
 
-  mlir::omp::ReductionDeclareOp decl =
-      modBuilder.create<omp::ReductionDeclareOp>(loc, reductionOpName, type);
-  builder.createBlock(&decl.getInitializerRegion(),
-                      decl.getInitializerRegion().end(), {type}, {loc});
-  builder.setInsertionPointToEnd(&decl.getInitializerRegion().back());
-  Value init = getReductionInitValue(loc, type, reductionOpName, builder);
-  builder.create<omp::YieldOp>(loc, init);
+bool ClauseProcessor::processUseDevicePtr(
+    llvm::SmallVectorImpl<mlir::Value> &operands,
+    llvm::SmallVectorImpl<mlir::Type> &useDeviceTypes,
+    llvm::SmallVectorImpl<mlir::Location> &useDeviceLocs,
+    llvm::SmallVectorImpl<const Fortran::semantics::Symbol *> &useDeviceSymbols)
+    const {
+  return findRepeatableClause<ClauseTy::UseDevicePtr>(
+      [&](const ClauseTy::UseDevicePtr *devPtrClause,
+          const Fortran::parser::CharBlock &) {
+        addUseDeviceClause(converter, devPtrClause->v, operands, useDeviceTypes,
+                           useDeviceLocs, useDeviceSymbols);
+      });
+}
 
-  builder.createBlock(&decl.getReductionRegion(),
-                      decl.getReductionRegion().end(), {type, type},
-                      {loc, loc});
+//===----------------------------------------------------------------------===//
+// Code generation helper functions
+//===----------------------------------------------------------------------===//
+
+static fir::GlobalOp globalInitialization(
+    Fortran::lower::AbstractConverter &converter,
+    fir::FirOpBuilder &firOpBuilder, const Fortran::semantics::Symbol &sym,
+    const Fortran::lower::pft::Variable &var, mlir::Location currentLocation) {
+  mlir::Type ty = converter.genType(sym);
+  std::string globalName = converter.mangleName(sym);
+  mlir::StringAttr linkage = firOpBuilder.createInternalLinkage();
+  fir::GlobalOp global =
+      firOpBuilder.createGlobal(currentLocation, ty, globalName, linkage);
+
+  // Create default initialization for non-character scalar.
+  if (Fortran::semantics::IsAllocatableOrPointer(sym)) {
+    mlir::Type baseAddrType = ty.dyn_cast<fir::BoxType>().getEleTy();
+    Fortran::lower::createGlobalInitialization(
+        firOpBuilder, global, [&](fir::FirOpBuilder &b) {
+          mlir::Value nullAddr =
+              b.createNullConstant(currentLocation, baseAddrType);
+          mlir::Value box =
+              b.create<fir::EmboxOp>(currentLocation, ty, nullAddr);
+          b.create<fir::HasValueOp>(currentLocation, box);
+        });
+  } else {
+    Fortran::lower::createGlobalInitialization(
+        firOpBuilder, global, [&](fir::FirOpBuilder &b) {
+          mlir::Value undef = b.create<fir::UndefOp>(currentLocation, ty);
+          b.create<fir::HasValueOp>(currentLocation, undef);
+        });
+  }
+
+  return global;
+}
+
+static mlir::Operation *getCompareFromReductionOp(mlir::Operation *reductionOp,
+                                                  mlir::Value loadVal) {
+  for (mlir::Value reductionOperand : reductionOp->getOperands()) {
+    if (mlir::Operation *compareOp = reductionOperand.getDefiningOp()) {
+      if (compareOp->getOperand(0) == loadVal ||
+          compareOp->getOperand(1) == loadVal)
+        assert((mlir::isa<mlir::arith::CmpIOp>(compareOp) ||
+                mlir::isa<mlir::arith::CmpFOp>(compareOp)) &&
+               "Expected comparison not found in reduction intrinsic");
+      return compareOp;
+    }
+  }
+  return nullptr;
+}
+
+/// The COMMON block is a global structure. \p commonValue is the base address
+/// of the the COMMON block. As the offset from the symbol \p sym, generate the
+/// COMMON block member value (commonValue + offset) for the symbol.
+/// FIXME: Share the code with `instantiateCommon` in ConvertVariable.cpp.
+static mlir::Value
+genCommonBlockMember(Fortran::lower::AbstractConverter &converter,
+                     const Fortran::semantics::Symbol &sym,
+                     mlir::Value commonValue) {
+  fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+  mlir::Location currentLocation = converter.getCurrentLocation();
+  mlir::IntegerType i8Ty = firOpBuilder.getIntegerType(8);
+  mlir::Type i8Ptr = firOpBuilder.getRefType(i8Ty);
+  mlir::Type seqTy = firOpBuilder.getRefType(firOpBuilder.getVarLenSeqTy(i8Ty));
+  mlir::Value base =
+      firOpBuilder.createConvert(currentLocation, seqTy, commonValue);
+  std::size_t byteOffset = sym.GetUltimate().offset();
+  mlir::Value offs = firOpBuilder.createIntegerConstant(
+      currentLocation, firOpBuilder.getIndexType(), byteOffset);
+  mlir::Value varAddr = firOpBuilder.create<fir::CoordinateOp>(
+      currentLocation, i8Ptr, base, mlir::ValueRange{offs});
+  mlir::Type symType = converter.genType(sym);
+  return firOpBuilder.createConvert(currentLocation,
+                                    firOpBuilder.getRefType(symType), varAddr);
+}
 
-  return decl;
+// Get the extended value for \p val by extracting additional variable
+// information from \p base.
+static fir::ExtendedValue getExtendedValue(fir::ExtendedValue base,
+                                           mlir::Value val) {
+  return base.match(
+      [&](const fir::MutableBoxValue &box) -> fir::ExtendedValue {
+        return fir::MutableBoxValue(val, box.nonDeferredLenParams(), {});
+      },
+      [&](const auto &) -> fir::ExtendedValue {
+        return fir::substBase(base, val);
+      });
 }
 
-/// Creates an OpenMP reduction declaration and inserts it into the provided
-/// symbol table. The declaration has a constant initializer with the neutral
-/// value `initValue`, and the reduction combiner carried over from `reduce`.
-/// TODO: Generalize this for non-integer types, add atomic region.
-static omp::ReductionDeclareOp
-createReductionDecl(fir::FirOpBuilder &builder, llvm::StringRef reductionOpName,
-                    const Fortran::parser::ProcedureDesignator &procDesignator,
-                    mlir::Type type, mlir::Location loc) {
-  OpBuilder::InsertionGuard guard(builder);
-  mlir::ModuleOp module = builder.getModule();
+static void threadPrivatizeVars(Fortran::lower::AbstractConverter &converter,
+                                Fortran::lower::pft::Evaluation &eval) {
+  fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+  mlir::Location currentLocation = converter.getCurrentLocation();
+  mlir::OpBuilder::InsertPoint insPt = firOpBuilder.saveInsertionPoint();
+  firOpBuilder.setInsertionPointToStart(firOpBuilder.getAllocaBlock());
 
-  auto decl =
-      module.lookupSymbol<mlir::omp::ReductionDeclareOp>(reductionOpName);
-  if (decl)
-    return decl;
+  // Get the original ThreadprivateOp corresponding to the symbol and use the
+  // symbol value from that opeartion to create one ThreadprivateOp copy
+  // operation inside the parallel region.
+  auto genThreadprivateOp = [&](Fortran::lower::SymbolRef sym) -> mlir::Value {
+    mlir::Value symOriThreadprivateValue = converter.getSymbolAddress(sym);
+    mlir::Operation *op = symOriThreadprivateValue.getDefiningOp();
+    assert(mlir::isa<mlir::omp::ThreadprivateOp>(op) &&
+           "The threadprivate operation not created");
+    mlir::Value symValue =
+        mlir::dyn_cast<mlir::omp::ThreadprivateOp>(op).getSymAddr();
+    return firOpBuilder.create<mlir::omp::ThreadprivateOp>(
+        currentLocation, symValue.getType(), symValue);
+  };
 
-  decl = createMinimalReductionDecl(builder, reductionOpName, type, loc);
-  builder.setInsertionPointToEnd(&decl.getReductionRegion().back());
-  mlir::Value op1 = decl.getReductionRegion().front().getArgument(0);
-  mlir::Value op2 = decl.getReductionRegion().front().getArgument(1);
+  llvm::SetVector<const Fortran::semantics::Symbol *> threadprivateSyms;
+  converter.collectSymbolSet(
+      eval, threadprivateSyms,
+      Fortran::semantics::Symbol::Flag::OmpThreadprivate);
+  std::set<Fortran::semantics::SourceName> threadprivateSymNames;
 
-  Value reductionOp;
-  if (const auto *name{
-          Fortran::parser::Unwrap<Fortran::parser::Name>(procDesignator)}) {
-    if (name->source == "max") {
-      reductionOp =
-          getReductionOperation<mlir::arith::MaxFOp, mlir::arith::MaxSIOp>(
-              builder, type, loc, op1, op2);
-    } else if (name->source == "min") {
-      reductionOp =
-          getReductionOperation<mlir::arith::MinFOp, mlir::arith::MinSIOp>(
-              builder, type, loc, op1, op2);
-    } else if (name->source == "ior") {
-      assert((type.isIntOrIndex()) && "only integer is expected");
-      reductionOp = builder.create<mlir::arith::OrIOp>(loc, op1, op2);
-    } else if (name->source == "ieor") {
-      assert((type.isIntOrIndex()) && "only integer is expected");
-      reductionOp = builder.create<mlir::arith::XOrIOp>(loc, op1, op2);
-    } else if (name->source == "iand") {
-      assert((type.isIntOrIndex()) && "only integer is expected");
-      reductionOp = builder.create<mlir::arith::AndIOp>(loc, op1, op2);
+  // For a COMMON block, the ThreadprivateOp is generated for itself instead of
+  // its members, so only bind the value of the new copied ThreadprivateOp
+  // inside the parallel region to the common block symbol only once for
+  // multiple members in one COMMON block.
+  llvm::SetVector<const Fortran::semantics::Symbol *> commonSyms;
+  for (std::size_t i = 0; i < threadprivateSyms.size(); i++) {
+    const Fortran::semantics::Symbol *sym = threadprivateSyms[i];
+    mlir::Value symThreadprivateValue;
+    // The variable may be used more than once, and each reference has one
+    // symbol with the same name. Only do once for references of one variable.
+    if (threadprivateSymNames.find(sym->name()) != threadprivateSymNames.end())
+      continue;
+    threadprivateSymNames.insert(sym->name());
+    if (const Fortran::semantics::Symbol *common =
+            Fortran::semantics::FindCommonBlockContaining(sym->GetUltimate())) {
+      mlir::Value commonThreadprivateValue;
+      if (commonSyms.contains(common)) {
+        commonThreadprivateValue = converter.getSymbolAddress(*common);
+      } else {
+        commonThreadprivateValue = genThreadprivateOp(*common);
+        converter.bindSymbol(*common, commonThreadprivateValue);
+        commonSyms.insert(common);
+      }
+      symThreadprivateValue =
+          genCommonBlockMember(converter, *sym, commonThreadprivateValue);
     } else {
-      TODO(loc, "Reduction of some intrinsic operators is not supported");
+      symThreadprivateValue = genThreadprivateOp(*sym);
     }
+
+    fir::ExtendedValue sexv = converter.getSymbolExtendedValue(*sym);
+    fir::ExtendedValue symThreadprivateExv =
+        getExtendedValue(sexv, symThreadprivateValue);
+    converter.bindSymbol(*sym, symThreadprivateExv);
   }
 
-  builder.create<omp::YieldOp>(loc, reductionOp);
-  return decl;
+  firOpBuilder.restoreInsertionPoint(insPt);
 }
 
-/// Creates an OpenMP reduction declaration and inserts it into the provided
-/// symbol table. The declaration has a constant initializer with the neutral
-/// value `initValue`, and the reduction combiner carried over from `reduce`.
-/// TODO: Generalize this for non-integer types, add atomic region.
-static omp::ReductionDeclareOp createReductionDecl(
-    fir::FirOpBuilder &builder, llvm::StringRef reductionOpName,
-    Fortran::parser::DefinedOperator::IntrinsicOperator intrinsicOp,
-    mlir::Type type, mlir::Location loc) {
-  OpBuilder::InsertionGuard guard(builder);
-  mlir::ModuleOp module = builder.getModule();
-
-  auto decl =
-      module.lookupSymbol<mlir::omp::ReductionDeclareOp>(reductionOpName);
-  if (decl)
-    return decl;
-
-  decl = createMinimalReductionDecl(builder, reductionOpName, type, loc);
-  builder.setInsertionPointToEnd(&decl.getReductionRegion().back());
-  mlir::Value op1 = decl.getReductionRegion().front().getArgument(0);
-  mlir::Value op2 = decl.getReductionRegion().front().getArgument(1);
-
-  Value reductionOp;
-  switch (intrinsicOp) {
-  case Fortran::parser::DefinedOperator::IntrinsicOperator::Add:
-    reductionOp =
-        getReductionOperation<mlir::arith::AddFOp, mlir::arith::AddIOp>(
-            builder, type, loc, op1, op2);
-    break;
-  case Fortran::parser::DefinedOperator::IntrinsicOperator::Multiply:
-    reductionOp =
-        getReductionOperation<mlir::arith::MulFOp, mlir::arith::MulIOp>(
-            builder, type, loc, op1, op2);
-    break;
-  case Fortran::parser::DefinedOperator::IntrinsicOperator::AND: {
-    Value op1I1 = builder.createConvert(loc, builder.getI1Type(), op1);
-    Value op2I1 = builder.createConvert(loc, builder.getI1Type(), op2);
-
-    Value andiOp = builder.create<mlir::arith::AndIOp>(loc, op1I1, op2I1);
+static mlir::Type getLoopVarType(Fortran::lower::AbstractConverter &converter,
+                                 std::size_t loopVarTypeSize) {
+  // OpenMP runtime requires 32-bit or 64-bit loop variables.
+  loopVarTypeSize = loopVarTypeSize * 8;
+  if (loopVarTypeSize < 32) {
+    loopVarTypeSize = 32;
+  } else if (loopVarTypeSize > 64) {
+    loopVarTypeSize = 64;
+    mlir::emitWarning(converter.getCurrentLocation(),
+                      "OpenMP loop iteration variable cannot have more than 64 "
+                      "bits size and will be narrowed into 64 bits.");
+  }
+  assert((loopVarTypeSize == 32 || loopVarTypeSize == 64) &&
+         "OpenMP loop iteration variable size must be transformed into 32-bit "
+         "or 64-bit");
+  return converter.getFirOpBuilder().getIntegerType(loopVarTypeSize);
+}
 
-    reductionOp = builder.createConvert(loc, type, andiOp);
-    break;
+/// Create empty blocks for the current region.
+/// These blocks replace blocks parented to an enclosing region.
+static void createEmptyRegionBlocks(
+    fir::FirOpBuilder &firOpBuilder,
+    std::list<Fortran::lower::pft::Evaluation> &evaluationList) {
+  mlir::Region *region = &firOpBuilder.getRegion();
+  for (Fortran::lower::pft::Evaluation &eval : evaluationList) {
+    if (eval.block) {
+      if (eval.block->empty()) {
+        eval.block->erase();
+        eval.block = firOpBuilder.createBlock(region);
+      } else {
+        [[maybe_unused]] mlir::Operation &terminatorOp = eval.block->back();
+        assert((mlir::isa<mlir::omp::TerminatorOp>(terminatorOp) ||
+                mlir::isa<mlir::omp::YieldOp>(terminatorOp)) &&
+               "expected terminator op");
+      }
+    }
+    if (!eval.isDirective() && eval.hasNestedEvaluations())
+      createEmptyRegionBlocks(firOpBuilder, eval.getNestedEvaluations());
   }
-  case Fortran::parser::DefinedOperator::IntrinsicOperator::OR: {
-    Value op1I1 = builder.createConvert(loc, builder.getI1Type(), op1);
-    Value op2I1 = builder.createConvert(loc, builder.getI1Type(), op2);
+}
 
-    Value oriOp = builder.create<mlir::arith::OrIOp>(loc, op1I1, op2I1);
+static void resetBeforeTerminator(fir::FirOpBuilder &firOpBuilder,
+                                  mlir::Operation *storeOp,
+                                  mlir::Block &block) {
+  if (storeOp)
+    firOpBuilder.setInsertionPointAfter(storeOp);
+  else
+    firOpBuilder.setInsertionPointToStart(&block);
+}
 
-    reductionOp = builder.createConvert(loc, type, oriOp);
-    break;
+/// Create the body (block) for an OpenMP Operation.
+///
+/// \param [in]    op - the operation the body belongs to.
+/// \param [inout] converter - converter to use for the clauses.
+/// \param [in]    loc - location in source code.
+/// \param [in]    eval - current PFT node/evaluation.
+/// \oaran [in]    clauses - list of clauses to process.
+/// \param [in]    args - block arguments (induction variable[s]) for the
+////                      region.
+/// \param [in]    outerCombined - is this an outer operation - prevents
+///                                privatization.
+template <typename Op>
+static void createBodyOfOp(
+    Op &op, Fortran::lower::AbstractConverter &converter, mlir::Location &loc,
+    Fortran::lower::pft::Evaluation &eval,
+    const Fortran::parser::OmpClauseList *clauses = nullptr,
+    const llvm::SmallVector<const Fortran::semantics::Symbol *> &args = {},
+    bool outerCombined = false, DataSharingProcessor *dsp = nullptr) {
+  fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+  // If an argument for the region is provided then create the block with that
+  // argument. Also update the symbol's address with the mlir argument value.
+  // e.g. For loops the argument is the induction variable. And all further
+  // uses of the induction variable should use this mlir value.
+  mlir::Operation *storeOp = nullptr;
+  if (args.size()) {
+    std::size_t loopVarTypeSize = 0;
+    for (const Fortran::semantics::Symbol *arg : args)
+      loopVarTypeSize = std::max(loopVarTypeSize, arg->GetUltimate().size());
+    mlir::Type loopVarType = getLoopVarType(converter, loopVarTypeSize);
+    llvm::SmallVector<mlir::Type> tiv;
+    llvm::SmallVector<mlir::Location> locs;
+    for (int i = 0; i < (int)args.size(); i++) {
+      tiv.push_back(loopVarType);
+      locs.push_back(loc);
+    }
+    firOpBuilder.createBlock(&op.getRegion(), {}, tiv, locs);
+    int argIndex = 0;
+    // The argument is not currently in memory, so make a temporary for the
+    // argument, and store it there, then bind that location to the argument.
+    for (const Fortran::semantics::Symbol *arg : args) {
+      mlir::Value val =
+          fir::getBase(op.getRegion().front().getArgument(argIndex));
+      mlir::Value temp = firOpBuilder.createTemporary(
+          loc, loopVarType,
+          llvm::ArrayRef<mlir::NamedAttribute>{
+              Fortran::lower::getAdaptToByRefAttr(firOpBuilder)});
+      storeOp = firOpBuilder.create<fir::StoreOp>(loc, val, temp);
+      converter.bindSymbol(*arg, temp);
+      argIndex++;
+    }
+  } else {
+    firOpBuilder.createBlock(&op.getRegion());
   }
-  case Fortran::parser::DefinedOperator::IntrinsicOperator::EQV: {
-    Value op1I1 = builder.createConvert(loc, builder.getI1Type(), op1);
-    Value op2I1 = builder.createConvert(loc, builder.getI1Type(), op2);
+  // Set the insert for the terminator operation to go at the end of the
+  // block - this is either empty or the block with the stores above,
+  // the end of the block works for both.
+  mlir::Block &block = op.getRegion().back();
+  firOpBuilder.setInsertionPointToEnd(&block);
 
-    Value cmpiOp = builder.create<mlir::arith::CmpIOp>(
-        loc, arith::CmpIPredicate::eq, op1I1, op2I1);
+  // If it is an unstructured region and is not the outer region of a combined
+  // construct, create empty blocks for all evaluations.
+  if (eval.lowerAsUnstructured() && !outerCombined)
+    createEmptyRegionBlocks(firOpBuilder, eval.getNestedEvaluations());
 
-    reductionOp = builder.createConvert(loc, type, cmpiOp);
-    break;
+  // Insert the terminator.
+  if constexpr (std::is_same_v<Op, mlir::omp::WsLoopOp> ||
+                std::is_same_v<Op, mlir::omp::SimdLoopOp>) {
+    mlir::ValueRange results;
+    firOpBuilder.create<mlir::omp::YieldOp>(loc, results);
+  } else {
+    firOpBuilder.create<mlir::omp::TerminatorOp>(loc);
   }
-  case Fortran::parser::DefinedOperator::IntrinsicOperator::NEQV: {
-    Value op1I1 = builder.createConvert(loc, builder.getI1Type(), op1);
-    Value op2I1 = builder.createConvert(loc, builder.getI1Type(), op2);
+  // Reset the insert point to before the terminator.
+  resetBeforeTerminator(firOpBuilder, storeOp, block);
 
-    Value cmpiOp = builder.create<mlir::arith::CmpIOp>(
-        loc, arith::CmpIPredicate::ne, op1I1, op2I1);
+  // Handle privatization. Do not privatize if this is the outer operation.
+  if (clauses && !outerCombined) {
+    constexpr bool is_loop = std::is_same_v<Op, mlir::omp::WsLoopOp> ||
+                             std::is_same_v<Op, mlir::omp::SimdLoopOp>;
+    if (!dsp) {
+      DataSharingProcessor proc(converter, *clauses, eval);
+      proc.processStep1();
+      proc.processStep2(op, is_loop);
+    } else {
+      dsp->processStep2(op, is_loop);
+    }
 
-    reductionOp = builder.createConvert(loc, type, cmpiOp);
-    break;
-  }
-  default:
-    TODO(loc, "Reduction of some intrinsic operators is not supported");
+    if (storeOp)
+      firOpBuilder.setInsertionPointAfter(storeOp);
   }
 
-  builder.create<omp::YieldOp>(loc, reductionOp);
-  return decl;
-}
-
-static mlir::omp::ScheduleModifier
-translateModifier(const Fortran::parser::OmpScheduleModifierType &m) {
-  switch (m.v) {
-  case Fortran::parser::OmpScheduleModifierType::ModType::Monotonic:
-    return mlir::omp::ScheduleModifier::monotonic;
-  case Fortran::parser::OmpScheduleModifierType::ModType::Nonmonotonic:
-    return mlir::omp::ScheduleModifier::nonmonotonic;
-  case Fortran::parser::OmpScheduleModifierType::ModType::Simd:
-    return mlir::omp::ScheduleModifier::simd;
+  if constexpr (std::is_same_v<Op, mlir::omp::ParallelOp>) {
+    threadPrivatizeVars(converter, eval);
+    if (clauses)
+      ClauseProcessor(converter, *clauses).processCopyin();
   }
-  return mlir::omp::ScheduleModifier::none;
 }
 
-static mlir::omp::ScheduleModifier
-getScheduleModifier(const Fortran::parser::OmpScheduleClause &x) {
-  const auto &modifier =
-      std::get<std::optional<Fortran::parser::OmpScheduleModifier>>(x.t);
-  // The input may have the modifier any order, so we look for one that isn't
-  // SIMD. If modifier is not set at all, fall down to the bottom and return
-  // "none".
-  if (modifier) {
-    const auto &modType1 =
-        std::get<Fortran::parser::OmpScheduleModifier::Modifier1>(modifier->t);
-    if (modType1.v.v ==
-        Fortran::parser::OmpScheduleModifierType::ModType::Simd) {
-      const auto &modType2 = std::get<
-          std::optional<Fortran::parser::OmpScheduleModifier::Modifier2>>(
-          modifier->t);
-      if (modType2 &&
-          modType2->v.v !=
-              Fortran::parser::OmpScheduleModifierType::ModType::Simd)
-        return translateModifier(modType2->v);
+static void
+createBodyOfTargetOp(Fortran::lower::AbstractConverter &converter,
+                     mlir::omp::DataOp &dataOp,
+                     const llvm::SmallVector<mlir::Type> &useDeviceTypes,
+                     const llvm::SmallVector<mlir::Location> &useDeviceLocs,
+                     const llvm::SmallVector<const Fortran::semantics::Symbol *>
+                         &useDeviceSymbols,
+                     const mlir::Location &currentLocation) {
+  fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+  mlir::Region &region = dataOp.getRegion();
 
-      return mlir::omp::ScheduleModifier::none;
-    }
+  firOpBuilder.createBlock(&region, {}, useDeviceTypes, useDeviceLocs);
+  firOpBuilder.create<mlir::omp::TerminatorOp>(currentLocation);
+  firOpBuilder.setInsertionPointToStart(&region.front());
 
-    return translateModifier(modType1.v);
+  unsigned argIndex = 0;
+  for (const Fortran::semantics::Symbol *sym : useDeviceSymbols) {
+    const mlir::BlockArgument &arg = region.front().getArgument(argIndex);
+    mlir::Value val = fir::getBase(arg);
+    fir::ExtendedValue extVal = converter.getSymbolExtendedValue(*sym);
+    if (auto refType = val.getType().dyn_cast<fir::ReferenceType>()) {
+      if (fir::isa_builtin_cptr_type(refType.getElementType())) {
+        converter.bindSymbol(*sym, val);
+      } else {
+        extVal.match(
+            [&](const fir::MutableBoxValue &mbv) {
+              converter.bindSymbol(
+                  *sym,
+                  fir::MutableBoxValue(
+                      val, fir::factory::getNonDeferredLenParams(extVal), {}));
+            },
+            [&](const auto &) {
+              TODO(converter.getCurrentLocation(),
+                   "use_device clause operand unsupported type");
+            });
+      }
+    } else {
+      TODO(converter.getCurrentLocation(),
+           "use_device clause operand unsupported type");
+    }
+    argIndex++;
   }
-  return mlir::omp::ScheduleModifier::none;
 }
 
-static mlir::omp::ScheduleModifier
-getSIMDModifier(const Fortran::parser::OmpScheduleClause &x) {
-  const auto &modifier =
-      std::get<std::optional<Fortran::parser::OmpScheduleModifier>>(x.t);
-  // Either of the two possible modifiers in the input can be the SIMD modifier,
-  // so look in either one, and return simd if we find one. Not found = return
-  // "none".
-  if (modifier) {
-    const auto &modType1 =
-        std::get<Fortran::parser::OmpScheduleModifier::Modifier1>(modifier->t);
-    if (modType1.v.v == Fortran::parser::OmpScheduleModifierType::ModType::Simd)
-      return mlir::omp::ScheduleModifier::simd;
+static void createTargetOp(Fortran::lower::AbstractConverter &converter,
+                           const Fortran::parser::OmpClauseList &opClauseList,
+                           const llvm::omp::Directive &directive,
+                           mlir::Location currentLocation,
+                           Fortran::lower::pft::Evaluation *eval = nullptr) {
+  fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+  Fortran::lower::StatementContext stmtCtx;
+  mlir::Value ifClauseOperand, deviceOperand, threadLmtOperand;
+  mlir::UnitAttr nowaitAttr;
+  llvm::SmallVector<mlir::Value> mapOperands, devicePtrOperands,
+      deviceAddrOperands;
+  llvm::SmallVector<mlir::IntegerAttr> mapTypes;
+  llvm::SmallVector<mlir::Type> useDeviceTypes;
+  llvm::SmallVector<mlir::Location> useDeviceLocs;
+  llvm::SmallVector<const Fortran::semantics::Symbol *> useDeviceSymbols;
+
+  ClauseProcessor cp(converter, opClauseList);
+  cp.processIf(stmtCtx, ifClauseOperand);
+  cp.processDevice(stmtCtx, deviceOperand);
+  cp.processThreadLimit(stmtCtx, threadLmtOperand);
+  cp.processNowait(nowaitAttr);
+  cp.processUseDevicePtr(devicePtrOperands, useDeviceTypes, useDeviceLocs,
+                         useDeviceSymbols);
+  cp.processUseDeviceAddr(deviceAddrOperands, useDeviceTypes, useDeviceLocs,
+                          useDeviceSymbols);
+  cp.processMap(mapOperands, mapTypes);
 
-    const auto &modType2 = std::get<
-        std::optional<Fortran::parser::OmpScheduleModifier::Modifier2>>(
-        modifier->t);
-    if (modType2 && modType2->v.v ==
-                        Fortran::parser::OmpScheduleModifierType::ModType::Simd)
-      return mlir::omp::ScheduleModifier::simd;
+  for (const Fortran::parser::OmpClause &clause : opClauseList.v) {
+    if (!std::get_if<Fortran::parser::OmpClause::If>(&clause.u) &&
+        !std::get_if<Fortran::parser::OmpClause::Device>(&clause.u) &&
+        !std::get_if<Fortran::parser::OmpClause::ThreadLimit>(&clause.u) &&
+        !std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u) &&
+        !std::get_if<Fortran::parser::OmpClause::UseDevicePtr>(&clause.u) &&
+        !std::get_if<Fortran::parser::OmpClause::UseDeviceAddr>(&clause.u) &&
+        !std::get_if<Fortran::parser::OmpClause::Map>(&clause.u)) {
+      mlir::Location clauseLocation = converter.genLocation(clause.source);
+      TODO(clauseLocation, "OMPD_target unhandled clause");
+    }
   }
-  return mlir::omp::ScheduleModifier::none;
-}
-
-static std::string getReductionName(llvm::StringRef name, mlir::Type ty) {
-  return (llvm::Twine(name) +
-          (ty.isIntOrIndex() ? llvm::Twine("_i_") : llvm::Twine("_f_")) +
-          llvm::Twine(ty.getIntOrFloatBitWidth()))
-      .str();
-}
 
-static std::string getReductionName(
-    Fortran::parser::DefinedOperator::IntrinsicOperator intrinsicOp,
-    mlir::Type ty) {
-  std::string reductionName;
+  llvm::SmallVector<mlir::Attribute> mapTypesAttr(mapTypes.begin(),
+                                                  mapTypes.end());
+  mlir::ArrayAttr mapTypesArrayAttr =
+      mlir::ArrayAttr::get(firOpBuilder.getContext(), mapTypesAttr);
 
-  switch (intrinsicOp) {
-  case Fortran::parser::DefinedOperator::IntrinsicOperator::Add:
-    reductionName = "add_reduction";
-    break;
-  case Fortran::parser::DefinedOperator::IntrinsicOperator::Multiply:
-    reductionName = "multiply_reduction";
-    break;
-  case Fortran::parser::DefinedOperator::IntrinsicOperator::AND:
-    return "and_reduction";
-  case Fortran::parser::DefinedOperator::IntrinsicOperator::EQV:
-    return "eqv_reduction";
-  case Fortran::parser::DefinedOperator::IntrinsicOperator::OR:
-    return "or_reduction";
-  case Fortran::parser::DefinedOperator::IntrinsicOperator::NEQV:
-    return "neqv_reduction";
-  default:
-    reductionName = "other_reduction";
-    break;
+  if (directive == llvm::omp::Directive::OMPD_target) {
+    auto targetOp = firOpBuilder.create<mlir::omp::TargetOp>(
+        currentLocation, ifClauseOperand, deviceOperand, threadLmtOperand,
+        nowaitAttr, mapOperands, mapTypesArrayAttr);
+    createBodyOfOp(targetOp, converter, currentLocation, *eval, &opClauseList);
+  } else if (directive == llvm::omp::Directive::OMPD_target_data) {
+    auto dataOp = firOpBuilder.create<mlir::omp::DataOp>(
+        currentLocation, ifClauseOperand, deviceOperand, devicePtrOperands,
+        deviceAddrOperands, mapOperands, mapTypesArrayAttr);
+    createBodyOfTargetOp(converter, dataOp, useDeviceTypes, useDeviceLocs,
+                         useDeviceSymbols, currentLocation);
+  } else if (directive == llvm::omp::Directive::OMPD_target_enter_data) {
+    firOpBuilder.create<mlir::omp::EnterDataOp>(
+        currentLocation, ifClauseOperand, deviceOperand, nowaitAttr,
+        mapOperands, mapTypesArrayAttr);
+  } else if (directive == llvm::omp::Directive::OMPD_target_exit_data) {
+    firOpBuilder.create<mlir::omp::ExitDataOp>(currentLocation, ifClauseOperand,
+                                               deviceOperand, nowaitAttr,
+                                               mapOperands, mapTypesArrayAttr);
+  } else {
+    TODO(currentLocation, "OMPD_target directive unknown");
   }
-
-  return getReductionName(reductionName, ty);
 }
 
-/// Creates a reduction declaration and associates it with an
-/// OpenMP block directive
-static void
-addReductionDecl(mlir::Location currentLocation,
-                 Fortran::lower::AbstractConverter &converter,
-                 const Fortran::parser::OmpReductionClause &reduction,
-                 SmallVector<Value> &reductionVars,
-                 SmallVector<Attribute> &reductionDeclSymbols) {
+static void genOMP(Fortran::lower::AbstractConverter &converter,
+                   Fortran::lower::pft::Evaluation &eval,
+                   const Fortran::parser::OpenMPSimpleStandaloneConstruct
+                       &simpleStandaloneConstruct) {
+  const auto &directive =
+      std::get<Fortran::parser::OmpSimpleStandaloneDirective>(
+          simpleStandaloneConstruct.t);
   fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
-  omp::ReductionDeclareOp decl;
-  const auto &redOperator{
-      std::get<Fortran::parser::OmpReductionOperator>(reduction.t)};
-  const auto &objectList{std::get<Fortran::parser::OmpObjectList>(reduction.t)};
-  if (const auto &redDefinedOp =
-          std::get_if<Fortran::parser::DefinedOperator>(&redOperator.u)) {
-    const auto &intrinsicOp{
-        std::get<Fortran::parser::DefinedOperator::IntrinsicOperator>(
-            redDefinedOp->u)};
-    switch (intrinsicOp) {
-    case Fortran::parser::DefinedOperator::IntrinsicOperator::Add:
-    case Fortran::parser::DefinedOperator::IntrinsicOperator::Multiply:
-    case Fortran::parser::DefinedOperator::IntrinsicOperator::AND:
-    case Fortran::parser::DefinedOperator::IntrinsicOperator::EQV:
-    case Fortran::parser::DefinedOperator::IntrinsicOperator::OR:
-    case Fortran::parser::DefinedOperator::IntrinsicOperator::NEQV:
-      break;
+  const auto &opClauseList =
+      std::get<Fortran::parser::OmpClauseList>(simpleStandaloneConstruct.t);
+  mlir::Location currentLocation = converter.genLocation(directive.source);
 
-    default:
-      TODO(currentLocation,
-           "Reduction of some intrinsic operators is not supported");
-      break;
-    }
-    for (const auto &ompObject : objectList.v) {
-      if (const auto *name{
-              Fortran::parser::Unwrap<Fortran::parser::Name>(ompObject)}) {
-        if (const auto *symbol{name->symbol}) {
-          mlir::Value symVal = converter.getSymbolAddress(*symbol);
-          mlir::Type redType =
-              symVal.getType().cast<fir::ReferenceType>().getEleTy();
-          reductionVars.push_back(symVal);
-          if (redType.isa<fir::LogicalType>())
-            decl = createReductionDecl(
-                firOpBuilder,
-                getReductionName(intrinsicOp, firOpBuilder.getI1Type()),
-                intrinsicOp, redType, currentLocation);
-          else if (redType.isIntOrIndexOrFloat()) {
-            decl = createReductionDecl(firOpBuilder,
-                                       getReductionName(intrinsicOp, redType),
-                                       intrinsicOp, redType, currentLocation);
-          } else {
-            TODO(currentLocation, "Reduction of some types is not supported");
-          }
-          reductionDeclSymbols.push_back(
-              SymbolRefAttr::get(firOpBuilder.getContext(), decl.getSymName()));
-        }
-      }
-    }
-  } else if (auto reductionIntrinsic =
-                 std::get_if<Fortran::parser::ProcedureDesignator>(
-                     &redOperator.u)) {
-    if (const auto *name{Fortran::parser::Unwrap<Fortran::parser::Name>(
-            reductionIntrinsic)}) {
-      if ((name->source != "max") && (name->source != "min") &&
-          (name->source != "ior") && (name->source != "ieor") &&
-          (name->source != "iand")) {
-        TODO(currentLocation,
-             "Reduction of intrinsic procedures is not supported");
-      }
-      std::string intrinsicOp = name->ToString();
-      for (const auto &ompObject : objectList.v) {
-        if (const auto *name{
-                Fortran::parser::Unwrap<Fortran::parser::Name>(ompObject)}) {
-          if (const auto *symbol{name->symbol}) {
-            mlir::Value symVal = converter.getSymbolAddress(*symbol);
-            mlir::Type redType =
-                symVal.getType().cast<fir::ReferenceType>().getEleTy();
-            reductionVars.push_back(symVal);
-            assert(redType.isIntOrIndexOrFloat() &&
-                   "Unsupported reduction type");
-            decl = createReductionDecl(
-                firOpBuilder, getReductionName(intrinsicOp, redType),
-                *reductionIntrinsic, redType, currentLocation);
-            reductionDeclSymbols.push_back(SymbolRefAttr::get(
-                firOpBuilder.getContext(), decl.getSymName()));
-          }
-        }
-      }
-    }
+  switch (directive.v) {
+  default:
+    break;
+  case llvm::omp::Directive::OMPD_barrier:
+    firOpBuilder.create<mlir::omp::BarrierOp>(currentLocation);
+    break;
+  case llvm::omp::Directive::OMPD_taskwait:
+    firOpBuilder.create<mlir::omp::TaskwaitOp>(currentLocation);
+    break;
+  case llvm::omp::Directive::OMPD_taskyield:
+    firOpBuilder.create<mlir::omp::TaskyieldOp>(currentLocation);
+    break;
+  case llvm::omp::Directive::OMPD_target_data:
+  case llvm::omp::Directive::OMPD_target_enter_data:
+  case llvm::omp::Directive::OMPD_target_exit_data:
+    createTargetOp(converter, opClauseList, directive.v, currentLocation);
+    break;
+  case llvm::omp::Directive::OMPD_target_update:
+    TODO(currentLocation, "OMPD_target_update");
+  case llvm::omp::Directive::OMPD_ordered:
+    TODO(currentLocation, "OMPD_ordered");
   }
 }
 
+static void
+genOMP(Fortran::lower::AbstractConverter &converter,
+       Fortran::lower::pft::Evaluation &eval,
+       const Fortran::parser::OpenMPStandaloneConstruct &standaloneConstruct) {
+  std::visit(
+      Fortran::common::visitors{
+          [&](const Fortran::parser::OpenMPSimpleStandaloneConstruct
+                  &simpleStandaloneConstruct) {
+            genOMP(converter, eval, simpleStandaloneConstruct);
+          },
+          [&](const Fortran::parser::OpenMPFlushConstruct &flushConstruct) {
+            llvm::SmallVector<mlir::Value, 4> operandRange;
+            if (const auto &ompObjectList =
+                    std::get<std::optional<Fortran::parser::OmpObjectList>>(
+                        flushConstruct.t))
+              genObjectList(*ompObjectList, converter, operandRange);
+            const auto &memOrderClause = std::get<std::optional<
+                std::list<Fortran::parser::OmpMemoryOrderClause>>>(
+                flushConstruct.t);
+            if (memOrderClause && memOrderClause->size() > 0)
+              TODO(converter.getCurrentLocation(),
+                   "Handle OmpMemoryOrderClause");
+            converter.getFirOpBuilder().create<mlir::omp::FlushOp>(
+                converter.getCurrentLocation(), operandRange);
+          },
+          [&](const Fortran::parser::OpenMPCancelConstruct &cancelConstruct) {
+            TODO(converter.getCurrentLocation(), "OpenMPCancelConstruct");
+          },
+          [&](const Fortran::parser::OpenMPCancellationPointConstruct
+                  &cancellationPointConstruct) {
+            TODO(converter.getCurrentLocation(), "OpenMPCancelConstruct");
+          },
+      },
+      standaloneConstruct.u);
+}
+
+/* When parallel is used in a combined construct, then use this function to
+ * create the parallel operation. It handles the parallel specific clauses
+ * and leaves the rest for handling at the inner operations.
+ */
+template <typename Directive>
+static void
+createCombinedParallelOp(Fortran::lower::AbstractConverter &converter,
+                         Fortran::lower::pft::Evaluation &eval,
+                         const Directive &directive) {
+  fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+  mlir::Location currentLocation = converter.getCurrentLocation();
+  Fortran::lower::StatementContext stmtCtx;
+  llvm::ArrayRef<mlir::Type> argTy;
+  mlir::Value ifClauseOperand, numThreadsClauseOperand;
+  llvm::SmallVector<mlir::Value> allocatorOperands, allocateOperands;
+  mlir::omp::ClauseProcBindKindAttr procBindKindAttr;
+  const auto &opClauseList =
+      std::get<Fortran::parser::OmpClauseList>(directive.t);
+  // TODO: Handle the following clauses
+  // 1. default
+  // Note: rest of the clauses are handled when the inner operation is created
+  ClauseProcessor cp(converter, opClauseList);
+  cp.processIf(stmtCtx, ifClauseOperand);
+  cp.processNumThreads(stmtCtx, numThreadsClauseOperand);
+  cp.processProcBind(procBindKindAttr);
+
+  // Create and insert the operation.
+  auto parallelOp = firOpBuilder.create<mlir::omp::ParallelOp>(
+      currentLocation, argTy, ifClauseOperand, numThreadsClauseOperand,
+      allocateOperands, allocatorOperands,
+      /*reduction_vars=*/mlir::ValueRange(),
+      /*reductions=*/nullptr, procBindKindAttr);
+
+  createBodyOfOp<mlir::omp::ParallelOp>(parallelOp, converter, currentLocation,
+                                        eval, &opClauseList, /*iv=*/{},
+                                        /*isCombined=*/true);
+}
+
 static void genOMP(Fortran::lower::AbstractConverter &converter,
                    Fortran::lower::pft::Evaluation &eval,
                    const Fortran::parser::OpenMPLoopConstruct &loopConstruct) {
-
   fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
   llvm::SmallVector<mlir::Value> lowerBound, upperBound, step, linearVars,
       linearStepVars, reductionVars, alignedVars, nontemporalVars;
   mlir::Value scheduleChunkClauseOperand, ifClauseOperand;
-  mlir::Attribute scheduleClauseOperand, noWaitClauseOperand,
-      orderedClauseOperand, orderClauseOperand;
+  mlir::IntegerAttr orderedClauseOperand;
+  mlir::omp::ClauseOrderKindAttr orderClauseOperand;
+  mlir::omp::ClauseScheduleKindAttr scheduleValClauseOperand;
+  mlir::omp::ScheduleModifierAttr scheduleModClauseOperand;
+  mlir::UnitAttr nowaitClauseOperand, scheduleSimdClauseOperand;
   mlir::IntegerAttr simdlenClauseOperand, safelenClauseOperand;
-  SmallVector<Attribute> reductionDeclSymbols;
+  llvm::SmallVector<mlir::Attribute> reductionDeclSymbols;
   Fortran::lower::StatementContext stmtCtx;
-  const auto &loopOpClauseList = std::get<Fortran::parser::OmpClauseList>(
-      std::get<Fortran::parser::OmpBeginLoopDirective>(loopConstruct.t).t);
+  std::size_t loopVarTypeSize;
+  llvm::SmallVector<const Fortran::semantics::Symbol *> iv;
 
   const auto &beginLoopDirective =
       std::get<Fortran::parser::OmpBeginLoopDirective>(loopConstruct.t);
+  const auto &loopOpClauseList =
+      std::get<Fortran::parser::OmpClauseList>(beginLoopDirective.t);
   mlir::Location currentLocation =
       converter.genLocation(beginLoopDirective.source);
   const auto ompDirective =
@@ -1625,84 +2285,14 @@ static void genOMP(Fortran::lower::AbstractConverter &converter,
   DataSharingProcessor dsp(converter, loopOpClauseList, eval);
   dsp.processStep1();
 
-  // Collect the loops to collapse.
-  auto *doConstructEval = &eval.getFirstNestedEvaluation();
-  if (doConstructEval->getIf<Fortran::parser::DoConstruct>()
-          ->IsDoConcurrent()) {
-    TODO(currentLocation, "Do Concurrent in Worksharing loop construct");
-  }
-
-  std::int64_t collapseValue =
-      Fortran::lower::getCollapseValue(loopOpClauseList);
-  std::size_t loopVarTypeSize = 0;
-  SmallVector<const Fortran::semantics::Symbol *> iv;
-  do {
-    auto *doLoop = &doConstructEval->getFirstNestedEvaluation();
-    auto *doStmt = doLoop->getIf<Fortran::parser::NonLabelDoStmt>();
-    assert(doStmt && "Expected do loop to be in the nested evaluation");
-    const auto &loopControl =
-        std::get<std::optional<Fortran::parser::LoopControl>>(doStmt->t);
-    const Fortran::parser::LoopControl::Bounds *bounds =
-        std::get_if<Fortran::parser::LoopControl::Bounds>(&loopControl->u);
-    assert(bounds && "Expected bounds for worksharing do loop");
-    Fortran::lower::StatementContext stmtCtx;
-    lowerBound.push_back(fir::getBase(converter.genExprValue(
-        *Fortran::semantics::GetExpr(bounds->lower), stmtCtx)));
-    upperBound.push_back(fir::getBase(converter.genExprValue(
-        *Fortran::semantics::GetExpr(bounds->upper), stmtCtx)));
-    if (bounds->step) {
-      step.push_back(fir::getBase(converter.genExprValue(
-          *Fortran::semantics::GetExpr(bounds->step), stmtCtx)));
-    } else { // If `step` is not present, assume it as `1`.
-      step.push_back(firOpBuilder.createIntegerConstant(
-          currentLocation, firOpBuilder.getIntegerType(32), 1));
-    }
-    iv.push_back(bounds->name.thing.symbol);
-    loopVarTypeSize = std::max(loopVarTypeSize,
-                               bounds->name.thing.symbol->GetUltimate().size());
-
-    collapseValue--;
-    doConstructEval =
-        &*std::next(doConstructEval->getNestedEvaluations().begin());
-  } while (collapseValue > 0);
-
-  for (const auto &clause : loopOpClauseList.v) {
-    mlir::Location clauseLocation = converter.genLocation(clause.source);
-    if (const auto &scheduleClause =
-            std::get_if<Fortran::parser::OmpClause::Schedule>(&clause.u)) {
-      if (const auto &chunkExpr =
-              std::get<std::optional<Fortran::parser::ScalarIntExpr>>(
-                  scheduleClause->v.t)) {
-        if (const auto *expr = Fortran::semantics::GetExpr(*chunkExpr)) {
-          scheduleChunkClauseOperand =
-              fir::getBase(converter.genExprValue(*expr, stmtCtx));
-        }
-      }
-    } else if (const auto &ifClause =
-                   std::get_if<Fortran::parser::OmpClause::If>(&clause.u)) {
-      ifClauseOperand =
-          getIfClauseOperand(converter, stmtCtx, ifClause, clauseLocation);
-    } else if (const auto &reductionClause =
-                   std::get_if<Fortran::parser::OmpClause::Reduction>(
-                       &clause.u)) {
-      addReductionDecl(currentLocation, converter, reductionClause->v,
-                       reductionVars, reductionDeclSymbols);
-    } else if (const auto &simdlenClause =
-                   std::get_if<Fortran::parser::OmpClause::Simdlen>(
-                       &clause.u)) {
-      const auto *expr = Fortran::semantics::GetExpr(simdlenClause->v);
-      const std::optional<std::int64_t> simdlenVal =
-          Fortran::evaluate::ToInt64(*expr);
-      simdlenClauseOperand = firOpBuilder.getI64IntegerAttr(*simdlenVal);
-    } else if (const auto &safelenClause =
-                   std::get_if<Fortran::parser::OmpClause::Safelen>(
-                       &clause.u)) {
-      const auto *expr = Fortran::semantics::GetExpr(safelenClause->v);
-      const std::optional<std::int64_t> safelenVal =
-          Fortran::evaluate::ToInt64(*expr);
-      safelenClauseOperand = firOpBuilder.getI64IntegerAttr(*safelenVal);
-    }
-  }
+  ClauseProcessor cp(converter, loopOpClauseList);
+  cp.processCollapse(currentLocation, eval, lowerBound, upperBound, step, iv,
+                     loopVarTypeSize);
+  cp.processScheduleChunk(stmtCtx, scheduleChunkClauseOperand);
+  cp.processIf(stmtCtx, ifClauseOperand);
+  cp.processReduction(currentLocation, reductionVars, reductionDeclSymbols);
+  cp.processSimdlen(simdlenClauseOperand);
+  cp.processSafelen(safelenClauseOperand);
 
   // The types of lower bound, upper bound, and step are converted into the
   // type of the loop variable if necessary.
@@ -1719,16 +2309,15 @@ static void genOMP(Fortran::lower::AbstractConverter &converter,
   // 2.9.3.1 SIMD construct
   // TODO: Support all the clauses
   if (llvm::omp::OMPD_simd == ompDirective) {
-    TypeRange resultType;
+    mlir::TypeRange resultType;
     auto simdLoopOp = firOpBuilder.create<mlir::omp::SimdLoopOp>(
         currentLocation, resultType, lowerBound, upperBound, step, alignedVars,
-        nullptr, ifClauseOperand, nontemporalVars,
-        orderClauseOperand.dyn_cast_or_null<omp::ClauseOrderKindAttr>(),
-        simdlenClauseOperand, safelenClauseOperand,
+        /*alignment_values=*/nullptr, ifClauseOperand, nontemporalVars,
+        orderClauseOperand, simdlenClauseOperand, safelenClauseOperand,
         /*inclusive=*/firOpBuilder.getUnitAttr());
-    createBodyOfOp<omp::SimdLoopOp>(simdLoopOp, converter, currentLocation,
-                                    eval, &loopOpClauseList, iv,
-                                    /*outer=*/false, &dsp);
+    createBodyOfOp<mlir::omp::SimdLoopOp>(
+        simdLoopOp, converter, currentLocation, eval, &loopOpClauseList, iv,
+        /*outer=*/false, &dsp);
     return;
   }
 
@@ -1742,66 +2331,21 @@ static void genOMP(Fortran::lower::AbstractConverter &converter,
           ? nullptr
           : mlir::ArrayAttr::get(firOpBuilder.getContext(),
                                  reductionDeclSymbols),
-      scheduleClauseOperand.dyn_cast_or_null<omp::ClauseScheduleKindAttr>(),
-      scheduleChunkClauseOperand, /*schedule_modifiers=*/nullptr,
-      /*simd_modifier=*/nullptr,
-      noWaitClauseOperand.dyn_cast_or_null<UnitAttr>(),
-      orderedClauseOperand.dyn_cast_or_null<IntegerAttr>(),
-      orderClauseOperand.dyn_cast_or_null<omp::ClauseOrderKindAttr>(),
+      scheduleValClauseOperand, scheduleChunkClauseOperand,
+      /*schedule_modifiers=*/nullptr,
+      /*simd_modifier=*/nullptr, nowaitClauseOperand, orderedClauseOperand,
+      orderClauseOperand,
       /*inclusive=*/firOpBuilder.getUnitAttr());
 
   // Handle attribute based clauses.
-  for (const Fortran::parser::OmpClause &clause : loopOpClauseList.v) {
-    if (const auto &orderedClause =
-            std::get_if<Fortran::parser::OmpClause::Ordered>(&clause.u)) {
-      if (orderedClause->v.has_value()) {
-        const auto *expr = Fortran::semantics::GetExpr(orderedClause->v);
-        const std::optional<std::int64_t> orderedClauseValue =
-            Fortran::evaluate::ToInt64(*expr);
-        wsLoopOp.setOrderedValAttr(
-            firOpBuilder.getI64IntegerAttr(*orderedClauseValue));
-      } else {
-        wsLoopOp.setOrderedValAttr(firOpBuilder.getI64IntegerAttr(0));
-      }
-    } else if (const auto &scheduleClause =
-                   std::get_if<Fortran::parser::OmpClause::Schedule>(
-                       &clause.u)) {
-      mlir::MLIRContext *context = firOpBuilder.getContext();
-      const auto &scheduleType = scheduleClause->v;
-      const auto &scheduleKind =
-          std::get<Fortran::parser::OmpScheduleClause::ScheduleType>(
-              scheduleType.t);
-      switch (scheduleKind) {
-      case Fortran::parser::OmpScheduleClause::ScheduleType::Static:
-        wsLoopOp.setScheduleValAttr(omp::ClauseScheduleKindAttr::get(
-            context, omp::ClauseScheduleKind::Static));
-        break;
-      case Fortran::parser::OmpScheduleClause::ScheduleType::Dynamic:
-        wsLoopOp.setScheduleValAttr(omp::ClauseScheduleKindAttr::get(
-            context, omp::ClauseScheduleKind::Dynamic));
-        break;
-      case Fortran::parser::OmpScheduleClause::ScheduleType::Guided:
-        wsLoopOp.setScheduleValAttr(omp::ClauseScheduleKindAttr::get(
-            context, omp::ClauseScheduleKind::Guided));
-        break;
-      case Fortran::parser::OmpScheduleClause::ScheduleType::Auto:
-        wsLoopOp.setScheduleValAttr(omp::ClauseScheduleKindAttr::get(
-            context, omp::ClauseScheduleKind::Auto));
-        break;
-      case Fortran::parser::OmpScheduleClause::ScheduleType::Runtime:
-        wsLoopOp.setScheduleValAttr(omp::ClauseScheduleKindAttr::get(
-            context, omp::ClauseScheduleKind::Runtime));
-        break;
-      }
-      mlir::omp::ScheduleModifier scheduleModifier =
-          getScheduleModifier(scheduleClause->v);
-      if (scheduleModifier != mlir::omp::ScheduleModifier::none)
-        wsLoopOp.setScheduleModifierAttr(
-            omp::ScheduleModifierAttr::get(context, scheduleModifier));
-      if (getSIMDModifier(scheduleClause->v) !=
-          mlir::omp::ScheduleModifier::none)
-        wsLoopOp.setSimdModifierAttr(firOpBuilder.getUnitAttr());
-    }
+  if (cp.processOrdered(orderedClauseOperand))
+    wsLoopOp.setOrderedValAttr(orderedClauseOperand);
+
+  if (cp.processSchedule(scheduleValClauseOperand, scheduleModClauseOperand,
+                         scheduleSimdClauseOperand)) {
+    wsLoopOp.setScheduleValAttr(scheduleValClauseOperand);
+    wsLoopOp.setScheduleModifierAttr(scheduleModClauseOperand);
+    wsLoopOp.setSimdModifierAttr(scheduleSimdClauseOperand);
   }
   // In FORTRAN `nowait` clause occur at the end of `omp do` directive.
   // i.e
@@ -1813,13 +2357,14 @@ static void genOMP(Fortran::lower::AbstractConverter &converter,
               loopConstruct.t)) {
     const auto &clauseList =
         std::get<Fortran::parser::OmpClauseList>((*endClauseList).t);
-    for (const Fortran::parser::OmpClause &clause : clauseList.v)
-      if (std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u))
-        wsLoopOp.setNowaitAttr(firOpBuilder.getUnitAttr());
+    if (ClauseProcessor(converter, clauseList)
+            .processNowait(nowaitClauseOperand))
+      wsLoopOp.setNowaitAttr(nowaitClauseOperand);
   }
 
-  createBodyOfOp<omp::WsLoopOp>(wsLoopOp, converter, currentLocation, eval,
-                                &loopOpClauseList, iv, /*outer=*/false, &dsp);
+  createBodyOfOp<mlir::omp::WsLoopOp>(wsLoopOp, converter, currentLocation,
+                                      eval, &loopOpClauseList, iv,
+                                      /*outer=*/false, &dsp);
 }
 
 static void
@@ -1840,140 +2385,60 @@ genOMP(Fortran::lower::AbstractConverter &converter,
   mlir::Value ifClauseOperand, numThreadsClauseOperand, finalClauseOperand,
       priorityClauseOperand;
   mlir::omp::ClauseProcBindKindAttr procBindKindAttr;
-  SmallVector<Value> allocateOperands, allocatorOperands, dependOperands,
-      reductionVars;
-  SmallVector<Attribute> dependTypeOperands, reductionDeclSymbols;
+  llvm::SmallVector<mlir::Value> allocateOperands, allocatorOperands,
+      dependOperands, reductionVars;
+  llvm::SmallVector<mlir::Attribute> dependTypeOperands, reductionDeclSymbols;
   mlir::UnitAttr nowaitAttr, untiedAttr, mergeableAttr;
 
   const auto &opClauseList =
       std::get<Fortran::parser::OmpClauseList>(beginBlockDirective.t);
-  for (const auto &clause : opClauseList.v) {
-    mlir::Location clauseLocation = converter.genLocation(clause.source);
-    if (const auto &ifClause =
-            std::get_if<Fortran::parser::OmpClause::If>(&clause.u)) {
-      ifClauseOperand =
-          getIfClauseOperand(converter, stmtCtx, ifClause, clauseLocation);
-    } else if (const auto &numThreadsClause =
-                   std::get_if<Fortran::parser::OmpClause::NumThreads>(
-                       &clause.u)) {
-      // OMPIRBuilder expects `NUM_THREAD` clause as a `Value`.
-      numThreadsClauseOperand = fir::getBase(converter.genExprValue(
-          *Fortran::semantics::GetExpr(numThreadsClause->v), stmtCtx));
-    } else if (const auto &procBindClause =
-                   std::get_if<Fortran::parser::OmpClause::ProcBind>(
-                       &clause.u)) {
-      procBindKindAttr = genProcBindKindAttr(firOpBuilder, procBindClause);
-    } else if (const auto &allocateClause =
-                   std::get_if<Fortran::parser::OmpClause::Allocate>(
-                       &clause.u)) {
-      genAllocateClause(converter, allocateClause->v, allocatorOperands,
-                        allocateOperands);
-    } else if (std::get_if<Fortran::parser::OmpClause::Private>(&clause.u) ||
-               std::get_if<Fortran::parser::OmpClause::Firstprivate>(
-                   &clause.u) ||
-               std::get_if<Fortran::parser::OmpClause::Copyin>(&clause.u)) {
-      // Privatisation and copyin clauses are handled elsewhere.
-      continue;
-    } else if (std::get_if<Fortran::parser::OmpClause::Shared>(&clause.u)) {
-      // Shared is the default behavior in the IR, so no handling is required.
-      continue;
-    } else if (const auto &defaultClause =
-                   std::get_if<Fortran::parser::OmpClause::Default>(
-                       &clause.u)) {
-      if ((defaultClause->v.v ==
-           Fortran::parser::OmpDefaultClause::Type::Shared) ||
-          (defaultClause->v.v ==
-           Fortran::parser::OmpDefaultClause::Type::None)) {
-        // Default clause with shared or none do not require any handling since
-        // Shared is the default behavior in the IR and None is only required
-        // for semantic checks.
-        continue;
-      }
-    } else if (std::get_if<Fortran::parser::OmpClause::Threads>(&clause.u)) {
-      // Nothing needs to be done for threads clause.
-      continue;
-    } else if (std::get_if<Fortran::parser::OmpClause::Map>(&clause.u)) {
-      // Map clause is exclusive to Target Data directives. It is handled
-      // as part of the TargetOp creation.
-      continue;
-    } else if (std::get_if<Fortran::parser::OmpClause::UseDevicePtr>(
-                   &clause.u)) {
-      // UseDevicePtr clause is exclusive to Target Data directives. It is
-      // handled as part of the TargetOp creation.
-      continue;
-    } else if (std::get_if<Fortran::parser::OmpClause::UseDeviceAddr>(
-                   &clause.u)) {
-      // UseDeviceAddr clause is exclusive to Target Data directives. It is
-      // handled as part of the TargetOp creation.
-      continue;
-    } else if (std::get_if<Fortran::parser::OmpClause::ThreadLimit>(
-                   &clause.u)) {
-      // Handled as part of TargetOp creation.
-      continue;
-    } else if (const auto &finalClause =
-                   std::get_if<Fortran::parser::OmpClause::Final>(&clause.u)) {
-      mlir::Value finalVal = fir::getBase(converter.genExprValue(
-          *Fortran::semantics::GetExpr(finalClause->v), stmtCtx));
-      finalClauseOperand = firOpBuilder.createConvert(
-          currentLocation, firOpBuilder.getI1Type(), finalVal);
-    } else if (std::get_if<Fortran::parser::OmpClause::Untied>(&clause.u)) {
-      untiedAttr = firOpBuilder.getUnitAttr();
-    } else if (std::get_if<Fortran::parser::OmpClause::Mergeable>(&clause.u)) {
-      mergeableAttr = firOpBuilder.getUnitAttr();
-    } else if (const auto &priorityClause =
-                   std::get_if<Fortran::parser::OmpClause::Priority>(
-                       &clause.u)) {
-      priorityClauseOperand = fir::getBase(converter.genExprValue(
-          *Fortran::semantics::GetExpr(priorityClause->v), stmtCtx));
-    } else if (const auto &reductionClause =
-                   std::get_if<Fortran::parser::OmpClause::Reduction>(
-                       &clause.u)) {
-      addReductionDecl(currentLocation, converter, reductionClause->v,
-                       reductionVars, reductionDeclSymbols);
-    } else if (const auto &dependClause =
-                   std::get_if<Fortran::parser::OmpClause::Depend>(&clause.u)) {
-      const std::list<Fortran::parser::Designator> &depVal =
-          std::get<std::list<Fortran::parser::Designator>>(
-              std::get<Fortran::parser::OmpDependClause::InOut>(
-                  dependClause->v.u)
-                  .t);
-      omp::ClauseTaskDependAttr dependTypeOperand =
-          genDependKindAttr(firOpBuilder, dependClause);
-      dependTypeOperands.insert(dependTypeOperands.end(), depVal.size(),
-                                dependTypeOperand);
-      for (const Fortran::parser::Designator &ompObject : depVal) {
-        Fortran::semantics::Symbol *sym = nullptr;
-        std::visit(
-            Fortran::common::visitors{
-                [&](const Fortran::parser::DataRef &designator) {
-                  if (const Fortran::parser::Name *name =
-                          std::get_if<Fortran::parser::Name>(&designator.u)) {
-                    sym = name->symbol;
-                  } else if (std::get_if<Fortran::common::Indirection<
-                                 Fortran::parser::ArrayElement>>(
-                                 &designator.u)) {
-                    TODO(converter.getCurrentLocation(),
-                         "array sections not supported for task depend");
-                  }
-                },
-                [&](const Fortran::parser::Substring &designator) {
-                  TODO(converter.getCurrentLocation(),
-                       "substring not supported for task depend");
-                }},
-            (ompObject).u);
-        const mlir::Value variable = converter.getSymbolAddress(*sym);
-        dependOperands.push_back(((variable)));
-      }
-    } else {
+  ClauseProcessor cp(converter, opClauseList);
+  cp.processIf(stmtCtx, ifClauseOperand);
+  cp.processNumThreads(stmtCtx, numThreadsClauseOperand);
+  cp.processProcBind(procBindKindAttr);
+  cp.processAllocate(allocatorOperands, allocateOperands);
+  cp.processDefault();
+  cp.processFinal(stmtCtx, finalClauseOperand);
+  cp.processUntied(untiedAttr);
+  cp.processMergeable(mergeableAttr);
+  cp.processPriority(stmtCtx, priorityClauseOperand);
+  cp.processReduction(currentLocation, reductionVars, reductionDeclSymbols);
+  cp.processDepend(dependTypeOperands, dependOperands);
+
+  for (const Fortran::parser::OmpClause &clause : opClauseList.v) {
+    if (!std::get_if<Fortran::parser::OmpClause::If>(&clause.u) &&
+        !std::get_if<Fortran::parser::OmpClause::NumThreads>(&clause.u) &&
+        !std::get_if<Fortran::parser::OmpClause::ProcBind>(&clause.u) &&
+        !std::get_if<Fortran::parser::OmpClause::Allocate>(&clause.u) &&
+        !std::get_if<Fortran::parser::OmpClause::Default>(&clause.u) &&
+        !std::get_if<Fortran::parser::OmpClause::Final>(&clause.u) &&
+        !std::get_if<Fortran::parser::OmpClause::Untied>(&clause.u) &&
+        !std::get_if<Fortran::parser::OmpClause::Mergeable>(&clause.u) &&
+        !std::get_if<Fortran::parser::OmpClause::Priority>(&clause.u) &&
+        !std::get_if<Fortran::parser::OmpClause::Reduction>(&clause.u) &&
+        !std::get_if<Fortran::parser::OmpClause::Depend>(&clause.u) &&
+        // Privatisation and copyin clauses are handled elsewhere.
+        !std::get_if<Fortran::parser::OmpClause::Private>(&clause.u) &&
+        !std::get_if<Fortran::parser::OmpClause::Firstprivate>(&clause.u) &&
+        !std::get_if<Fortran::parser::OmpClause::Copyin>(&clause.u) &&
+        // Shared is the default behavior in the IR, so no handling is required.
+        !std::get_if<Fortran::parser::OmpClause::Shared>(&clause.u) &&
+        // Nothing needs to be done for threads clause.
+        !std::get_if<Fortran::parser::OmpClause::Threads>(&clause.u) &&
+        // Map, UseDevicePtr, UseDeviceAddr and ThreadLimit clauses are
+        // exclusive to Target directives. They are handled as part of the
+        // TargetOp creation.
+        !std::get_if<Fortran::parser::OmpClause::Map>(&clause.u) &&
+        !std::get_if<Fortran::parser::OmpClause::UseDevicePtr>(&clause.u) &&
+        !std::get_if<Fortran::parser::OmpClause::UseDeviceAddr>(&clause.u) &&
+        !std::get_if<Fortran::parser::OmpClause::ThreadLimit>(&clause.u)) {
       TODO(converter.getCurrentLocation(), "OpenMP Block construct clause");
     }
   }
 
-  for (const auto &clause :
-       std::get<Fortran::parser::OmpClauseList>(endBlockDirective.t).v) {
-    if (std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u))
-      nowaitAttr = firOpBuilder.getUnitAttr();
-  }
+  ClauseProcessor(converter,
+                  std::get<Fortran::parser::OmpClauseList>(endBlockDirective.t))
+      .processNowait(nowaitAttr);
 
   if (blockDirective.v == llvm::omp::OMPD_parallel) {
     // Create and insert the operation.
@@ -1985,26 +2450,27 @@ genOMP(Fortran::lower::AbstractConverter &converter,
             : mlir::ArrayAttr::get(firOpBuilder.getContext(),
                                    reductionDeclSymbols),
         procBindKindAttr);
-    createBodyOfOp<omp::ParallelOp>(parallelOp, converter, currentLocation,
-                                    eval, &opClauseList);
+    createBodyOfOp<mlir::omp::ParallelOp>(parallelOp, converter,
+                                          currentLocation, eval, &opClauseList);
   } else if (blockDirective.v == llvm::omp::OMPD_master) {
     auto masterOp =
         firOpBuilder.create<mlir::omp::MasterOp>(currentLocation, argTy);
-    createBodyOfOp<omp::MasterOp>(masterOp, converter, currentLocation, eval);
+    createBodyOfOp<mlir::omp::MasterOp>(masterOp, converter, currentLocation,
+                                        eval);
   } else if (blockDirective.v == llvm::omp::OMPD_single) {
     auto singleOp = firOpBuilder.create<mlir::omp::SingleOp>(
         currentLocation, allocateOperands, allocatorOperands, nowaitAttr);
-    createBodyOfOp<omp::SingleOp>(singleOp, converter, currentLocation, eval,
-                                  &opClauseList);
+    createBodyOfOp<mlir::omp::SingleOp>(singleOp, converter, currentLocation,
+                                        eval, &opClauseList);
   } else if (blockDirective.v == llvm::omp::OMPD_ordered) {
     auto orderedOp = firOpBuilder.create<mlir::omp::OrderedRegionOp>(
         currentLocation, /*simd=*/false);
-    createBodyOfOp<omp::OrderedRegionOp>(orderedOp, converter, currentLocation,
-                                         eval);
+    createBodyOfOp<mlir::omp::OrderedRegionOp>(orderedOp, converter,
+                                               currentLocation, eval);
   } else if (blockDirective.v == llvm::omp::OMPD_task) {
     auto taskOp = firOpBuilder.create<mlir::omp::TaskOp>(
         currentLocation, ifClauseOperand, finalClauseOperand, untiedAttr,
-        mergeableAttr, /*in_reduction_vars=*/ValueRange(),
+        mergeableAttr, /*in_reduction_vars=*/mlir::ValueRange(),
         /*in_reductions=*/nullptr, priorityClauseOperand,
         dependTypeOperands.empty()
             ? nullptr
@@ -2015,7 +2481,7 @@ genOMP(Fortran::lower::AbstractConverter &converter,
   } else if (blockDirective.v == llvm::omp::OMPD_taskgroup) {
     // TODO: Add task_reduction support
     auto taskGroupOp = firOpBuilder.create<mlir::omp::TaskGroupOp>(
-        currentLocation, /*task_reduction_vars=*/ValueRange(),
+        currentLocation, /*task_reduction_vars=*/mlir::ValueRange(),
         /*task_reductions=*/nullptr, allocateOperands, allocatorOperands);
     createBodyOfOp(taskGroupOp, converter, currentLocation, eval,
                    &opClauseList);
@@ -2036,6 +2502,7 @@ genOMP(Fortran::lower::AbstractConverter &converter,
        const Fortran::parser::OpenMPCriticalConstruct &criticalConstruct) {
   fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
   mlir::Location currentLocation = converter.getCurrentLocation();
+  mlir::IntegerAttr hintClauseOp;
   std::string name;
   const Fortran::parser::OmpCriticalDirective &cd =
       std::get<Fortran::parser::OmpCriticalDirective>(criticalConstruct.t);
@@ -2044,42 +2511,35 @@ genOMP(Fortran::lower::AbstractConverter &converter,
         std::get<std::optional<Fortran::parser::Name>>(cd.t).value().ToString();
   }
 
-  uint64_t hint = 0;
   const auto &clauseList = std::get<Fortran::parser::OmpClauseList>(cd.t);
-  for (const Fortran::parser::OmpClause &clause : clauseList.v)
-    if (auto hintClause =
-            std::get_if<Fortran::parser::OmpClause::Hint>(&clause.u)) {
-      const auto *expr = Fortran::semantics::GetExpr(hintClause->v);
-      hint = *Fortran::evaluate::ToInt64(*expr);
-      break;
-    }
+  ClauseProcessor(converter, clauseList).processHint(hintClauseOp);
 
   mlir::omp::CriticalOp criticalOp = [&]() {
     if (name.empty()) {
-      return firOpBuilder.create<mlir::omp::CriticalOp>(currentLocation,
-                                                        FlatSymbolRefAttr());
-    } else {
-      mlir::ModuleOp module = firOpBuilder.getModule();
-      mlir::OpBuilder modBuilder(module.getBodyRegion());
-      auto global = module.lookupSymbol<mlir::omp::CriticalDeclareOp>(name);
-      if (!global)
-        global = modBuilder.create<mlir::omp::CriticalDeclareOp>(
-            currentLocation, name, hint);
       return firOpBuilder.create<mlir::omp::CriticalOp>(
-          currentLocation, mlir::FlatSymbolRefAttr::get(
-                               firOpBuilder.getContext(), global.getSymName()));
+          currentLocation, mlir::FlatSymbolRefAttr());
     }
+    mlir::ModuleOp module = firOpBuilder.getModule();
+    mlir::OpBuilder modBuilder(module.getBodyRegion());
+    auto global = module.lookupSymbol<mlir::omp::CriticalDeclareOp>(name);
+    if (!global)
+      global = modBuilder.create<mlir::omp::CriticalDeclareOp>(
+          currentLocation,
+          mlir::StringAttr::get(firOpBuilder.getContext(), name), hintClauseOp);
+    return firOpBuilder.create<mlir::omp::CriticalOp>(
+        currentLocation, mlir::FlatSymbolRefAttr::get(firOpBuilder.getContext(),
+                                                      global.getSymName()));
   }();
-  createBodyOfOp<omp::CriticalOp>(criticalOp, converter, currentLocation, eval);
+  createBodyOfOp<mlir::omp::CriticalOp>(criticalOp, converter, currentLocation,
+                                        eval);
 }
 
 static void
 genOMP(Fortran::lower::AbstractConverter &converter,
        Fortran::lower::pft::Evaluation &eval,
        const Fortran::parser::OpenMPSectionConstruct &sectionConstruct) {
-
-  auto &firOpBuilder = converter.getFirOpBuilder();
-  auto currentLocation = converter.getCurrentLocation();
+  fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+  mlir::Location currentLocation = converter.getCurrentLocation();
   const Fortran::parser::OpenMPConstruct *parentOmpConstruct =
       eval.parentConstruct->getIf<Fortran::parser::OpenMPConstruct>();
   assert(parentOmpConstruct &&
@@ -2098,51 +2558,37 @@ genOMP(Fortran::lower::AbstractConverter &converter,
   // all privatization is done within `omp.section` operations.
   mlir::omp::SectionOp sectionOp =
       firOpBuilder.create<mlir::omp::SectionOp>(currentLocation);
-  createBodyOfOp<omp::SectionOp>(sectionOp, converter, currentLocation, eval,
-                                 &sectionsClauseList);
+  createBodyOfOp<mlir::omp::SectionOp>(sectionOp, converter, currentLocation,
+                                       eval, &sectionsClauseList);
 }
 
 static void
 genOMP(Fortran::lower::AbstractConverter &converter,
        Fortran::lower::pft::Evaluation &eval,
        const Fortran::parser::OpenMPSectionsConstruct &sectionsConstruct) {
-  auto &firOpBuilder = converter.getFirOpBuilder();
-  auto currentLocation = converter.getCurrentLocation();
-  SmallVector<Value> reductionVars, allocateOperands, allocatorOperands;
-  mlir::UnitAttr noWaitClauseOperand;
-  const auto &sectionsClauseList = std::get<Fortran::parser::OmpClauseList>(
-      std::get<Fortran::parser::OmpBeginSectionsDirective>(sectionsConstruct.t)
-          .t);
-  for (const Fortran::parser::OmpClause &clause : sectionsClauseList.v) {
-
-    // Reduction Clause
-    if (std::get_if<Fortran::parser::OmpClause::Reduction>(&clause.u)) {
-      TODO(currentLocation, "OMPC_Reduction");
-
-      // Allocate clause
-    } else if (const auto &allocateClause =
-                   std::get_if<Fortran::parser::OmpClause::Allocate>(
-                       &clause.u)) {
-      genAllocateClause(converter, allocateClause->v, allocatorOperands,
-                        allocateOperands);
-    }
-  }
-  const auto &endSectionsClauseList =
+  fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+  mlir::Location currentLocation = converter.getCurrentLocation();
+  llvm::SmallVector<mlir::Value> reductionVars, allocateOperands,
+      allocatorOperands;
+  mlir::UnitAttr nowaitClauseOperand;
+  const auto &beginSectionsDirective =
+      std::get<Fortran::parser::OmpBeginSectionsDirective>(sectionsConstruct.t);
+  const auto &sectionsClauseList =
+      std::get<Fortran::parser::OmpClauseList>(beginSectionsDirective.t);
+
+  ClauseProcessor cp(converter, sectionsClauseList);
+  cp.processSectionsReduction(currentLocation);
+  cp.processAllocate(allocatorOperands, allocateOperands);
+
+  const auto &endSectionsDirective =
       std::get<Fortran::parser::OmpEndSectionsDirective>(sectionsConstruct.t);
-  const auto &clauseList =
-      std::get<Fortran::parser::OmpClauseList>(endSectionsClauseList.t);
-  for (const auto &clause : clauseList.v) {
-    // Nowait clause
-    if (std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u)) {
-      noWaitClauseOperand = firOpBuilder.getUnitAttr();
-    }
-  }
+  const auto &endSectionsClauseList =
+      std::get<Fortran::parser::OmpClauseList>(endSectionsDirective.t);
+  ClauseProcessor(converter, endSectionsClauseList)
+      .processNowait(nowaitClauseOperand);
 
   llvm::omp::Directive dir =
-      std::get<Fortran::parser::OmpSectionsDirective>(
-          std::get<Fortran::parser::OmpBeginSectionsDirective>(
-              sectionsConstruct.t)
-              .t)
+      std::get<Fortran::parser::OmpSectionsDirective>(beginSectionsDirective.t)
           .v;
 
   // Parallel Sections Construct
@@ -2152,7 +2598,7 @@ genOMP(Fortran::lower::AbstractConverter &converter,
         std::get<Fortran::parser::OmpBeginSectionsDirective>(
             sectionsConstruct.t));
     auto sectionsOp = firOpBuilder.create<mlir::omp::SectionsOp>(
-        currentLocation, /*reduction_vars*/ ValueRange(),
+        currentLocation, /*reduction_vars*/ mlir::ValueRange(),
         /*reductions=*/nullptr, allocateOperands, allocatorOperands,
         /*nowait=*/nullptr);
     createBodyOfOp(sectionsOp, converter, currentLocation, eval);
@@ -2160,10 +2606,10 @@ genOMP(Fortran::lower::AbstractConverter &converter,
     // Sections Construct
   } else if (dir == llvm::omp::Directive::OMPD_sections) {
     auto sectionsOp = firOpBuilder.create<mlir::omp::SectionsOp>(
-        currentLocation, reductionVars, /*reductions = */ nullptr,
-        allocateOperands, allocatorOperands, noWaitClauseOperand);
-    createBodyOfOp<omp::SectionsOp>(sectionsOp, converter, currentLocation,
-                                    eval);
+        currentLocation, reductionVars, /*reductions=*/nullptr,
+        allocateOperands, allocatorOperands, nowaitClauseOperand);
+    createBodyOfOp<mlir::omp::SectionsOp>(sectionsOp, converter,
+                                          currentLocation, eval);
   }
 }
 
@@ -2204,34 +2650,39 @@ static void genOmpAtomicHintAndMemoryOrderClauses(
     const Fortran::parser::OmpAtomicClauseList &clauseList,
     mlir::IntegerAttr &hint,
     mlir::omp::ClauseMemoryOrderKindAttr &memoryOrder) {
-  auto &firOpBuilder = converter.getFirOpBuilder();
-  for (const auto &clause : clauseList.v) {
-    if (auto ompClause = std::get_if<Fortran::parser::OmpClause>(&clause.u)) {
-      if (auto hintClause =
+  fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+  for (const Fortran::parser::OmpAtomicClause &clause : clauseList.v) {
+    if (const auto *ompClause =
+            std::get_if<Fortran::parser::OmpClause>(&clause.u)) {
+      if (const auto *hintClause =
               std::get_if<Fortran::parser::OmpClause::Hint>(&ompClause->u)) {
         const auto *expr = Fortran::semantics::GetExpr(hintClause->v);
         uint64_t hintExprValue = *Fortran::evaluate::ToInt64(*expr);
         hint = firOpBuilder.getI64IntegerAttr(hintExprValue);
       }
-    } else if (auto ompMemoryOrderClause =
+    } else if (const auto *ompMemoryOrderClause =
                    std::get_if<Fortran::parser::OmpMemoryOrderClause>(
                        &clause.u)) {
       if (std::get_if<Fortran::parser::OmpClause::Acquire>(
               &ompMemoryOrderClause->v.u)) {
         memoryOrder = mlir::omp::ClauseMemoryOrderKindAttr::get(
-            firOpBuilder.getContext(), omp::ClauseMemoryOrderKind::Acquire);
+            firOpBuilder.getContext(),
+            mlir::omp::ClauseMemoryOrderKind::Acquire);
       } else if (std::get_if<Fortran::parser::OmpClause::Relaxed>(
                      &ompMemoryOrderClause->v.u)) {
         memoryOrder = mlir::omp::ClauseMemoryOrderKindAttr::get(
-            firOpBuilder.getContext(), omp::ClauseMemoryOrderKind::Relaxed);
+            firOpBuilder.getContext(),
+            mlir::omp::ClauseMemoryOrderKind::Relaxed);
       } else if (std::get_if<Fortran::parser::OmpClause::SeqCst>(
                      &ompMemoryOrderClause->v.u)) {
         memoryOrder = mlir::omp::ClauseMemoryOrderKindAttr::get(
-            firOpBuilder.getContext(), omp::ClauseMemoryOrderKind::Seq_cst);
+            firOpBuilder.getContext(),
+            mlir::omp::ClauseMemoryOrderKind::Seq_cst);
       } else if (std::get_if<Fortran::parser::OmpClause::Release>(
                      &ompMemoryOrderClause->v.u)) {
         memoryOrder = mlir::omp::ClauseMemoryOrderKindAttr::get(
-            firOpBuilder.getContext(), omp::ClauseMemoryOrderKind::Release);
+            firOpBuilder.getContext(),
+            mlir::omp::ClauseMemoryOrderKind::Release);
       }
     }
   }
@@ -2239,65 +2690,65 @@ static void genOmpAtomicHintAndMemoryOrderClauses(
 
 static void genOmpAtomicCaptureStatement(
     Fortran::lower::AbstractConverter &converter,
-    Fortran::lower::pft::Evaluation &eval, mlir::Value from_address,
-    mlir::Value to_address,
+    Fortran::lower::pft::Evaluation &eval, mlir::Value fromAddress,
+    mlir::Value toAddress,
     const Fortran::parser::OmpAtomicClauseList *leftHandClauseList,
     const Fortran::parser::OmpAtomicClauseList *rightHandClauseList,
     mlir::Type elementType) {
   // Generate `omp.atomic.read` operation for atomic assigment statements
-  auto &firOpBuilder = converter.getFirOpBuilder();
-  auto currentLocation = converter.getCurrentLocation();
+  fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+  mlir::Location currentLocation = converter.getCurrentLocation();
 
   // If no hint clause is specified, the effect is as if
   // hint(omp_sync_hint_none) had been specified.
   mlir::IntegerAttr hint = nullptr;
 
-  mlir::omp::ClauseMemoryOrderKindAttr memory_order = nullptr;
+  mlir::omp::ClauseMemoryOrderKindAttr memoryOrder = nullptr;
   if (leftHandClauseList)
     genOmpAtomicHintAndMemoryOrderClauses(converter, *leftHandClauseList, hint,
-                                          memory_order);
+                                          memoryOrder);
   if (rightHandClauseList)
     genOmpAtomicHintAndMemoryOrderClauses(converter, *rightHandClauseList, hint,
-                                          memory_order);
+                                          memoryOrder);
   firOpBuilder.create<mlir::omp::AtomicReadOp>(
-      currentLocation, from_address, to_address,
-      mlir::TypeAttr::get(elementType), hint, memory_order);
+      currentLocation, fromAddress, toAddress, mlir::TypeAttr::get(elementType),
+      hint, memoryOrder);
 }
 
 static void genOmpAtomicWriteStatement(
     Fortran::lower::AbstractConverter &converter,
-    Fortran::lower::pft::Evaluation &eval, mlir::Value lhs_addr,
-    mlir::Value rhs_expr,
+    Fortran::lower::pft::Evaluation &eval, mlir::Value lhsAddr,
+    mlir::Value rhsExpr,
     const Fortran::parser::OmpAtomicClauseList *leftHandClauseList,
     const Fortran::parser::OmpAtomicClauseList *rightHandClauseList,
     mlir::Value *evaluatedExprValue = nullptr) {
   // Generate `omp.atomic.write` operation for atomic assignment statements
-  auto &firOpBuilder = converter.getFirOpBuilder();
-  auto currentLocation = converter.getCurrentLocation();
+  fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+  mlir::Location currentLocation = converter.getCurrentLocation();
   // If no hint clause is specified, the effect is as if
   // hint(omp_sync_hint_none) had been specified.
   mlir::IntegerAttr hint = nullptr;
-  mlir::omp::ClauseMemoryOrderKindAttr memory_order = nullptr;
+  mlir::omp::ClauseMemoryOrderKindAttr memoryOrder = nullptr;
   if (leftHandClauseList)
     genOmpAtomicHintAndMemoryOrderClauses(converter, *leftHandClauseList, hint,
-                                          memory_order);
+                                          memoryOrder);
   if (rightHandClauseList)
     genOmpAtomicHintAndMemoryOrderClauses(converter, *rightHandClauseList, hint,
-                                          memory_order);
-  firOpBuilder.create<mlir::omp::AtomicWriteOp>(currentLocation, lhs_addr,
-                                                rhs_expr, hint, memory_order);
+                                          memoryOrder);
+  firOpBuilder.create<mlir::omp::AtomicWriteOp>(currentLocation, lhsAddr,
+                                                rhsExpr, hint, memoryOrder);
 }
 
 static void genOmpAtomicUpdateStatement(
     Fortran::lower::AbstractConverter &converter,
-    Fortran::lower::pft::Evaluation &eval, mlir::Value lhs_addr,
+    Fortran::lower::pft::Evaluation &eval, mlir::Value lhsAddr,
     mlir::Type varType, const Fortran::parser::Variable &assignmentStmtVariable,
     const Fortran::parser::Expr &assignmentStmtExpr,
     const Fortran::parser::OmpAtomicClauseList *leftHandClauseList,
     const Fortran::parser::OmpAtomicClauseList *rightHandClauseList) {
   // Generate `omp.atomic.update` operation for atomic assignment statements
-  auto &firOpBuilder = converter.getFirOpBuilder();
-  auto currentLocation = converter.getCurrentLocation();
+  fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+  mlir::Location currentLocation = converter.getCurrentLocation();
 
   // If no hint clause is specified, the effect is as if
   // hint(omp_sync_hint_none) had been specified.
@@ -2310,22 +2761,22 @@ static void genOmpAtomicUpdateStatement(
     genOmpAtomicHintAndMemoryOrderClauses(converter, *rightHandClauseList, hint,
                                           memoryOrder);
   auto atomicUpdateOp = firOpBuilder.create<mlir::omp::AtomicUpdateOp>(
-      currentLocation, lhs_addr, hint, memoryOrder);
+      currentLocation, lhsAddr, hint, memoryOrder);
 
   //// Generate body of Atomic Update operation
   // If an argument for the region is provided then create the block with that
   // argument. Also update the symbol's address with the argument mlir value.
-  SmallVector<Type> varTys = {varType};
-  SmallVector<Location> locs = {currentLocation};
+  llvm::SmallVector<mlir::Type> varTys = {varType};
+  llvm::SmallVector<mlir::Location> locs = {currentLocation};
   firOpBuilder.createBlock(&atomicUpdateOp.getRegion(), {}, varTys, locs);
   mlir::Value val =
       fir::getBase(atomicUpdateOp.getRegion().front().getArgument(0));
-  auto varDesignator =
+  const auto *varDesignator =
       std::get_if<Fortran::common::Indirection<Fortran::parser::Designator>>(
           &assignmentStmtVariable.u);
   assert(varDesignator && "Variable designator for atomic update assignment "
                           "statement does not exist");
-  const auto *name =
+  const Fortran::parser::Name *name =
       Fortran::semantics::getDesignatorNameIfDataRef(varDesignator->value());
   if (!name)
     TODO(converter.getCurrentLocation(),
@@ -2339,10 +2790,10 @@ static void genOmpAtomicUpdateStatement(
   firOpBuilder.setInsertionPointToEnd(&block);
 
   Fortran::lower::StatementContext stmtCtx;
-  mlir::Value rhs_expr = fir::getBase(converter.genExprValue(
+  mlir::Value rhsExpr = fir::getBase(converter.genExprValue(
       *Fortran::semantics::GetExpr(assignmentStmtExpr), stmtCtx));
   mlir::Value convertResult =
-      firOpBuilder.createConvert(currentLocation, varType, rhs_expr);
+      firOpBuilder.createConvert(currentLocation, varType, rhsExpr);
   // Insert the terminator: YieldOp.
   firOpBuilder.create<mlir::omp::YieldOp>(currentLocation, convertResult);
   // Reset the insert point to before the terminator.
@@ -2363,12 +2814,11 @@ genOmpAtomicWrite(Fortran::lower::AbstractConverter &converter,
   const Fortran::evaluate::Assignment &assign = *stmt.typedAssignment->v;
   Fortran::lower::StatementContext stmtCtx;
   // Get the value and address of atomic write operands.
-  mlir::Value rhs_expr =
+  mlir::Value rhsExpr =
       fir::getBase(converter.genExprValue(assign.rhs, stmtCtx));
-
-  mlir::Value lhs_addr =
+  mlir::Value lhsAddr =
       fir::getBase(converter.genExprAddr(assign.lhs, stmtCtx));
-  genOmpAtomicWriteStatement(converter, eval, lhs_addr, rhs_expr,
+  genOmpAtomicWriteStatement(converter, eval, lhsAddr, rhsExpr,
                              &leftHandClauseList, &rightHandClauseList);
 }
 
@@ -2412,14 +2862,14 @@ genOmpAtomicUpdate(Fortran::lower::AbstractConverter &converter,
       std::get<3>(atomicUpdate.t).statement.t);
 
   Fortran::lower::StatementContext stmtCtx;
-  mlir::Value lhs_addr = fir::getBase(converter.genExprAddr(
+  mlir::Value lhsAddr = fir::getBase(converter.genExprAddr(
       *Fortran::semantics::GetExpr(assignmentStmtVariable), stmtCtx));
   mlir::Type varType =
       fir::getBase(
           converter.genExprValue(
               *Fortran::semantics::GetExpr(assignmentStmtVariable), stmtCtx))
           .getType();
-  genOmpAtomicUpdateStatement(converter, eval, lhs_addr, varType,
+  genOmpAtomicUpdateStatement(converter, eval, lhsAddr, varType,
                               assignmentStmtVariable, assignmentStmtExpr,
                               &leftHandClauseList, &rightHandClauseList);
 }
@@ -2438,7 +2888,7 @@ static void genOmpAtomic(Fortran::lower::AbstractConverter &converter,
           atomicConstruct.t)
           .statement.t);
   Fortran::lower::StatementContext stmtCtx;
-  mlir::Value lhs_addr = fir::getBase(converter.genExprAddr(
+  mlir::Value lhsAddr = fir::getBase(converter.genExprAddr(
       *Fortran::semantics::GetExpr(assignmentStmtVariable), stmtCtx));
   mlir::Type varType =
       fir::getBase(
@@ -2447,7 +2897,7 @@ static void genOmpAtomic(Fortran::lower::AbstractConverter &converter,
           .getType();
   // If atomic-clause is not present on the construct, the behaviour is as if
   // the update clause is specified
-  genOmpAtomicUpdateStatement(converter, eval, lhs_addr, varType,
+  genOmpAtomicUpdateStatement(converter, eval, lhsAddr, varType,
                               assignmentStmtVariable, assignmentStmtExpr,
                               &atomicClauseList, nullptr);
 }
@@ -2460,15 +2910,15 @@ genOmpAtomicCapture(Fortran::lower::AbstractConverter &converter,
   mlir::Location currentLocation = converter.getCurrentLocation();
 
   mlir::IntegerAttr hint = nullptr;
-  mlir::omp::ClauseMemoryOrderKindAttr memory_order = nullptr;
+  mlir::omp::ClauseMemoryOrderKindAttr memoryOrder = nullptr;
   const Fortran::parser::OmpAtomicClauseList &rightHandClauseList =
       std::get<2>(atomicCapture.t);
   const Fortran::parser::OmpAtomicClauseList &leftHandClauseList =
       std::get<0>(atomicCapture.t);
   genOmpAtomicHintAndMemoryOrderClauses(converter, leftHandClauseList, hint,
-                                        memory_order);
+                                        memoryOrder);
   genOmpAtomicHintAndMemoryOrderClauses(converter, rightHandClauseList, hint,
-                                        memory_order);
+                                        memoryOrder);
 
   const Fortran::parser::AssignmentStmt &stmt1 =
       std::get<3>(atomicCapture.t).v.statement;
@@ -2519,7 +2969,7 @@ genOmpAtomicCapture(Fortran::lower::AbstractConverter &converter,
           .getType();
 
   auto atomicCaptureOp = firOpBuilder.create<mlir::omp::AtomicCaptureOp>(
-      currentLocation, hint, memory_order);
+      currentLocation, hint, memoryOrder);
   firOpBuilder.createBlock(&atomicCaptureOp.getRegion());
   mlir::Block &block = atomicCaptureOp.getRegion().back();
   firOpBuilder.setInsertionPointToStart(&block);
@@ -2550,55 +3000,143 @@ genOmpAtomicCapture(Fortran::lower::AbstractConverter &converter,
                                  /*leftHandClauseList=*/nullptr,
                                  /*rightHandClauseList=*/nullptr);
     }
-  } else {
-    // Atomic capture construct is of the form [update-stmt, capture-stmt]
-    firOpBuilder.setInsertionPointToEnd(&block);
-    const Fortran::semantics::SomeExpr &fromExpr =
-        *Fortran::semantics::GetExpr(stmt2Expr);
-    elementType = converter.genType(fromExpr);
-    genOmpAtomicCaptureStatement(converter, eval, stmt1LHSArg, stmt2LHSArg,
-                                 /*leftHandClauseList=*/nullptr,
-                                 /*rightHandClauseList=*/nullptr, elementType);
-    firOpBuilder.setInsertionPointToStart(&block);
-    genOmpAtomicUpdateStatement(converter, eval, stmt1LHSArg, stmt1VarType,
-                                stmt1Var, stmt1Expr,
-                                /*leftHandClauseList=*/nullptr,
-                                /*rightHandClauseList=*/nullptr);
+  } else {
+    // Atomic capture construct is of the form [update-stmt, capture-stmt]
+    firOpBuilder.setInsertionPointToEnd(&block);
+    const Fortran::semantics::SomeExpr &fromExpr =
+        *Fortran::semantics::GetExpr(stmt2Expr);
+    elementType = converter.genType(fromExpr);
+    genOmpAtomicCaptureStatement(converter, eval, stmt1LHSArg, stmt2LHSArg,
+                                 /*leftHandClauseList=*/nullptr,
+                                 /*rightHandClauseList=*/nullptr, elementType);
+    firOpBuilder.setInsertionPointToStart(&block);
+    genOmpAtomicUpdateStatement(converter, eval, stmt1LHSArg, stmt1VarType,
+                                stmt1Var, stmt1Expr,
+                                /*leftHandClauseList=*/nullptr,
+                                /*rightHandClauseList=*/nullptr);
+  }
+  firOpBuilder.setInsertionPointToEnd(&block);
+  firOpBuilder.create<mlir::omp::TerminatorOp>(currentLocation);
+  firOpBuilder.setInsertionPointToStart(&block);
+}
+
+static void
+genOMP(Fortran::lower::AbstractConverter &converter,
+       Fortran::lower::pft::Evaluation &eval,
+       const Fortran::parser::OpenMPAtomicConstruct &atomicConstruct) {
+  std::visit(Fortran::common::visitors{
+                 [&](const Fortran::parser::OmpAtomicRead &atomicRead) {
+                   genOmpAtomicRead(converter, eval, atomicRead);
+                 },
+                 [&](const Fortran::parser::OmpAtomicWrite &atomicWrite) {
+                   genOmpAtomicWrite(converter, eval, atomicWrite);
+                 },
+                 [&](const Fortran::parser::OmpAtomic &atomicConstruct) {
+                   genOmpAtomic(converter, eval, atomicConstruct);
+                 },
+                 [&](const Fortran::parser::OmpAtomicUpdate &atomicUpdate) {
+                   genOmpAtomicUpdate(converter, eval, atomicUpdate);
+                 },
+                 [&](const Fortran::parser::OmpAtomicCapture &atomicCapture) {
+                   genOmpAtomicCapture(converter, eval, atomicCapture);
+                 },
+             },
+             atomicConstruct.u);
+}
+
+static void genOMP(Fortran::lower::AbstractConverter &converter,
+                   Fortran::lower::pft::Evaluation &eval,
+                   const Fortran::parser::OpenMPDeclareTargetConstruct
+                       &declareTargetConstruct) {
+  llvm::SmallVector<DeclareTargetCapturePair, 0> symbolAndClause;
+  mlir::ModuleOp mod = converter.getFirOpBuilder().getModule();
+
+  // The default capture type
+  mlir::omp::DeclareTargetDeviceType deviceType =
+      mlir::omp::DeclareTargetDeviceType::any;
+  const auto &spec = std::get<Fortran::parser::OmpDeclareTargetSpecifier>(
+      declareTargetConstruct.t);
+  if (const auto *objectList{
+          Fortran::parser::Unwrap<Fortran::parser::OmpObjectList>(spec.u)}) {
+    // Case: declare target(func, var1, var2)
+    gatherFuncAndVarSyms(*objectList, mlir::omp::DeclareTargetCaptureClause::to,
+                         symbolAndClause);
+  } else if (const auto *clauseList{
+                 Fortran::parser::Unwrap<Fortran::parser::OmpClauseList>(
+                     spec.u)}) {
+    if (clauseList->v.empty()) {
+      // Case: declare target, implicit capture of function
+      symbolAndClause.emplace_back(
+          mlir::omp::DeclareTargetCaptureClause::to,
+          eval.getOwningProcedure()->getSubprogramSymbol());
+    }
+
+    ClauseProcessor cp(converter, *clauseList);
+    cp.processTo(symbolAndClause);
+    cp.processLink(symbolAndClause);
+    cp.processDeviceType(deviceType);
+  }
+
+  for (const DeclareTargetCapturePair &symClause : symbolAndClause) {
+    mlir::Operation *op = mod.lookupSymbol(
+        converter.mangleName(std::get<Fortran::semantics::Symbol>(symClause)));
+    // There's several cases this can currently be triggered and it could be
+    // one of the following:
+    // 1) Invalid argument passed to a declare target that currently isn't
+    // captured by a frontend semantic check
+    // 2) The symbol of a valid argument is not correctly updated by one of
+    // the prior passes, resulting in missing symbol information
+    // 3) It's a variable internal to a module or program, that is legal by
+    // Fortran OpenMP standards, but is currently unhandled as they do not
+    // appear in the symbol table as they are represented as allocas
+    if (!op)
+      TODO(converter.getCurrentLocation(),
+           "Missing symbol, possible case of currently unsupported use of "
+           "a program local variable in declare target or erroneous symbol "
+           "information ");
+
+    auto declareTargetOp =
+        llvm::dyn_cast<mlir::omp::DeclareTargetInterface>(op);
+    if (!declareTargetOp)
+      fir::emitFatalError(
+          converter.getCurrentLocation(),
+          "Attempt to apply declare target on unsupported operation");
+
+    // The function or global already has a declare target applied to it, very
+    // likely through implicit capture (usage in another declare target
+    // function/subroutine). It should be marked as any if it has been assigned
+    // both host and nohost, else we skip, as there is no change
+    if (declareTargetOp.isDeclareTarget()) {
+      if (declareTargetOp.getDeclareTargetDeviceType() != deviceType)
+        declareTargetOp.setDeclareTarget(
+            mlir::omp::DeclareTargetDeviceType::any,
+            std::get<mlir::omp::DeclareTargetCaptureClause>(symClause));
+      continue;
+    }
+
+    declareTargetOp.setDeclareTarget(
+        deviceType, std::get<mlir::omp::DeclareTargetCaptureClause>(symClause));
   }
-  firOpBuilder.setInsertionPointToEnd(&block);
-  firOpBuilder.create<mlir::omp::TerminatorOp>(currentLocation);
-  firOpBuilder.setInsertionPointToStart(&block);
 }
 
-static void
-genOMP(Fortran::lower::AbstractConverter &converter,
-       Fortran::lower::pft::Evaluation &eval,
-       const Fortran::parser::OpenMPAtomicConstruct &atomicConstruct) {
-  std::visit(Fortran::common::visitors{
-                 [&](const Fortran::parser::OmpAtomicRead &atomicRead) {
-                   genOmpAtomicRead(converter, eval, atomicRead);
-                 },
-                 [&](const Fortran::parser::OmpAtomicWrite &atomicWrite) {
-                   genOmpAtomicWrite(converter, eval, atomicWrite);
-                 },
-                 [&](const Fortran::parser::OmpAtomic &atomicConstruct) {
-                   genOmpAtomic(converter, eval, atomicConstruct);
-                 },
-                 [&](const Fortran::parser::OmpAtomicUpdate &atomicUpdate) {
-                   genOmpAtomicUpdate(converter, eval, atomicUpdate);
-                 },
-                 [&](const Fortran::parser::OmpAtomicCapture &atomicCapture) {
-                   genOmpAtomicCapture(converter, eval, atomicCapture);
-                 },
-             },
-             atomicConstruct.u);
+//===----------------------------------------------------------------------===//
+// Public functions
+//===----------------------------------------------------------------------===//
+
+void Fortran::lower::genOpenMPTerminator(fir::FirOpBuilder &builder,
+                                         mlir::Operation *op,
+                                         mlir::Location loc) {
+  if (mlir::isa<mlir::omp::WsLoopOp, mlir::omp::ReductionDeclareOp,
+                mlir::omp::AtomicUpdateOp, mlir::omp::SimdLoopOp>(op))
+    builder.create<mlir::omp::YieldOp>(loc);
+  else
+    builder.create<mlir::omp::TerminatorOp>(loc);
 }
 
 void Fortran::lower::genOpenMPConstruct(
     Fortran::lower::AbstractConverter &converter,
     Fortran::lower::pft::Evaluation &eval,
     const Fortran::parser::OpenMPConstruct &ompConstruct) {
-
   std::visit(
       common::visitors{
           [&](const Fortran::parser::OpenMPStandaloneConstruct
@@ -2641,37 +3179,51 @@ void Fortran::lower::genOpenMPConstruct(
       ompConstruct.u);
 }
 
-fir::GlobalOp globalInitialization(Fortran::lower::AbstractConverter &converter,
-                                   fir::FirOpBuilder &firOpBuilder,
-                                   const Fortran::semantics::Symbol &sym,
-                                   const Fortran::lower::pft::Variable &var,
-                                   mlir::Location currentLocation) {
-  mlir::Type ty = converter.genType(sym);
-  std::string globalName = converter.mangleName(sym);
-  mlir::StringAttr linkage = firOpBuilder.createInternalLinkage();
-  fir::GlobalOp global =
-      firOpBuilder.createGlobal(currentLocation, ty, globalName, linkage);
+void Fortran::lower::genOpenMPDeclarativeConstruct(
+    Fortran::lower::AbstractConverter &converter,
+    Fortran::lower::pft::Evaluation &eval,
+    const Fortran::parser::OpenMPDeclarativeConstruct &ompDeclConstruct) {
+  std::visit(
+      common::visitors{
+          [&](const Fortran::parser::OpenMPDeclarativeAllocate
+                  &declarativeAllocate) {
+            TODO(converter.getCurrentLocation(), "OpenMPDeclarativeAllocate");
+          },
+          [&](const Fortran::parser::OpenMPDeclareReductionConstruct
+                  &declareReductionConstruct) {
+            TODO(converter.getCurrentLocation(),
+                 "OpenMPDeclareReductionConstruct");
+          },
+          [&](const Fortran::parser::OpenMPDeclareSimdConstruct
+                  &declareSimdConstruct) {
+            TODO(converter.getCurrentLocation(), "OpenMPDeclareSimdConstruct");
+          },
+          [&](const Fortran::parser::OpenMPDeclareTargetConstruct
+                  &declareTargetConstruct) {
+            genOMP(converter, eval, declareTargetConstruct);
+          },
+          [&](const Fortran::parser::OpenMPRequiresConstruct
+                  &requiresConstruct) {
+            TODO(converter.getCurrentLocation(), "OpenMPRequiresConstruct");
+          },
+          [&](const Fortran::parser::OpenMPThreadprivate &threadprivate) {
+            // The directive is lowered when instantiating the variable to
+            // support the case of threadprivate variable declared in module.
+          },
+      },
+      ompDeclConstruct.u);
+}
 
-  // Create default initialization for non-character scalar.
-  if (Fortran::semantics::IsAllocatableOrPointer(sym)) {
-    mlir::Type baseAddrType = ty.dyn_cast<fir::BoxType>().getEleTy();
-    Fortran::lower::createGlobalInitialization(
-        firOpBuilder, global, [&](fir::FirOpBuilder &b) {
-          mlir::Value nullAddr =
-              b.createNullConstant(currentLocation, baseAddrType);
-          mlir::Value box =
-              b.create<fir::EmboxOp>(currentLocation, ty, nullAddr);
-          b.create<fir::HasValueOp>(currentLocation, box);
-        });
-  } else {
-    Fortran::lower::createGlobalInitialization(
-        firOpBuilder, global, [&](fir::FirOpBuilder &b) {
-          mlir::Value undef = b.create<fir::UndefOp>(currentLocation, ty);
-          b.create<fir::HasValueOp>(currentLocation, undef);
-        });
+int64_t Fortran::lower::getCollapseValue(
+    const Fortran::parser::OmpClauseList &clauseList) {
+  for (const Fortran::parser::OmpClause &clause : clauseList.v) {
+    if (const auto &collapseClause =
+            std::get_if<Fortran::parser::OmpClause::Collapse>(&clause.u)) {
+      const auto *expr = Fortran::semantics::GetExpr(collapseClause->v);
+      return Fortran::evaluate::ToInt64(*expr).value();
+    }
   }
-
-  return global;
+  return 1;
 }
 
 void Fortran::lower::genThreadprivateOp(
@@ -2745,181 +3297,6 @@ void Fortran::lower::genDeclareTargetIntGlobal(
   }
 }
 
-void handleDeclareTarget(Fortran::lower::AbstractConverter &converter,
-                         Fortran::lower::pft::Evaluation &eval,
-                         const Fortran::parser::OpenMPDeclareTargetConstruct
-                             &declareTargetConstruct) {
-  llvm::SmallVector<std::pair<mlir::omp::DeclareTargetCaptureClause,
-                              Fortran::semantics::Symbol>,
-                    0>
-      symbolAndClause;
-  mlir::ModuleOp mod = converter.getFirOpBuilder().getModule();
-
-  auto findFuncAndVarSyms = [&](const Fortran::parser::OmpObjectList &objList,
-                                mlir::omp::DeclareTargetCaptureClause clause) {
-    for (const Fortran::parser::OmpObject &ompObject : objList.v) {
-      Fortran::common::visit(
-          Fortran::common::visitors{
-              [&](const Fortran::parser::Designator &designator) {
-                if (const Fortran::parser::Name *name =
-                        Fortran::semantics::getDesignatorNameIfDataRef(
-                            designator)) {
-                  symbolAndClause.push_back(
-                      std::make_pair(clause, *name->symbol));
-                }
-              },
-              [&](const Fortran::parser::Name &name) {
-                symbolAndClause.push_back(std::make_pair(clause, *name.symbol));
-              }},
-          ompObject.u);
-    }
-  };
-
-  // The default capture type
-  Fortran::parser::OmpDeviceTypeClause::Type deviceType =
-      Fortran::parser::OmpDeviceTypeClause::Type::Any;
-  const auto &spec = std::get<Fortran::parser::OmpDeclareTargetSpecifier>(
-      declareTargetConstruct.t);
-  if (const auto *objectList{
-          Fortran::parser::Unwrap<Fortran::parser::OmpObjectList>(spec.u)}) {
-    // Case: declare target(func, var1, var2)
-    findFuncAndVarSyms(*objectList, mlir::omp::DeclareTargetCaptureClause::to);
-  } else if (const auto *clauseList{
-                 Fortran::parser::Unwrap<Fortran::parser::OmpClauseList>(
-                     spec.u)}) {
-    if (clauseList->v.empty()) {
-      // Case: declare target, implicit capture of function
-      symbolAndClause.push_back(
-          std::make_pair(mlir::omp::DeclareTargetCaptureClause::to,
-                         eval.getOwningProcedure()->getSubprogramSymbol()));
-    }
-
-    for (const Fortran::parser::OmpClause &clause : clauseList->v) {
-      if (const auto *toClause =
-              std::get_if<Fortran::parser::OmpClause::To>(&clause.u)) {
-        // Case: declare target to(func, var1, var2)...
-        findFuncAndVarSyms(toClause->v,
-                           mlir::omp::DeclareTargetCaptureClause::to);
-      } else if (const auto *linkClause =
-                     std::get_if<Fortran::parser::OmpClause::Link>(&clause.u)) {
-        // Case: declare target link(var1, var2)...
-        findFuncAndVarSyms(linkClause->v,
-                           mlir::omp::DeclareTargetCaptureClause::link);
-      } else if (const auto *deviceClause =
-                     std::get_if<Fortran::parser::OmpClause::DeviceType>(
-                         &clause.u)) {
-        // Case: declare target ... device_type(any | host | nohost)
-        deviceType = deviceClause->v.v;
-      }
-    }
-  }
-
-  for (std::pair<mlir::omp::DeclareTargetCaptureClause,
-                 Fortran::semantics::Symbol>
-           symClause : symbolAndClause) {
-    mlir::Operation *op =
-        mod.lookupSymbol(converter.mangleName(std::get<1>(symClause)));
-    // There's several cases this can currently be triggered and it could be
-    // one of the following:
-    // 1) Invalid argument passed to a declare target that currently isn't
-    // captured by a frontend semantic check
-    // 2) The symbol of a valid argument is not correctly updated by one of
-    // the prior passes, resulting in missing symbol information
-    // 3) It's a variable internal to a module or program, that is legal by
-    // Fortran OpenMP standards, but is currently unhandled as they do not
-    // appear in the symbol table as they are represented as allocas
-    if (!op)
-      TODO(converter.getCurrentLocation(),
-           "Missing symbol, possible case of currently unsupported use of "
-           "a program local variable in declare target or erroneous symbol "
-           "information ");
-
-    auto declareTargetOp = dyn_cast<mlir::omp::DeclareTargetInterface>(op);
-    if (!declareTargetOp)
-      fir::emitFatalError(
-          converter.getCurrentLocation(),
-          "Attempt to apply declare target on unsupported operation");
-
-    mlir::omp::DeclareTargetDeviceType newDeviceType;
-    switch (deviceType) {
-    case Fortran::parser::OmpDeviceTypeClause::Type::Nohost:
-      newDeviceType = mlir::omp::DeclareTargetDeviceType::nohost;
-      break;
-    case Fortran::parser::OmpDeviceTypeClause::Type::Host:
-      newDeviceType = mlir::omp::DeclareTargetDeviceType::host;
-      break;
-    case Fortran::parser::OmpDeviceTypeClause::Type::Any:
-      newDeviceType = mlir::omp::DeclareTargetDeviceType::any;
-      break;
-    }
-
-    // The function or global already has a declare target applied to it,
-    // very likely through implicit capture (usage in another declare
-    // target function/subroutine). It should be marked as any if it has
-    // been assigned both host and nohost, else we skip, as there is no
-    // change
-    if (declareTargetOp.isDeclareTarget()) {
-      if (declareTargetOp.getDeclareTargetDeviceType() != newDeviceType)
-        declareTargetOp.setDeclareTarget(
-            mlir::omp::DeclareTargetDeviceType::any, std::get<0>(symClause));
-      continue;
-    }
-
-    declareTargetOp.setDeclareTarget(newDeviceType, std::get<0>(symClause));
-  }
-}
-
-void Fortran::lower::genOpenMPDeclarativeConstruct(
-    Fortran::lower::AbstractConverter &converter,
-    Fortran::lower::pft::Evaluation &eval,
-    const Fortran::parser::OpenMPDeclarativeConstruct &ompDeclConstruct) {
-
-  std::visit(
-      common::visitors{
-          [&](const Fortran::parser::OpenMPDeclarativeAllocate
-                  &declarativeAllocate) {
-            TODO(converter.getCurrentLocation(), "OpenMPDeclarativeAllocate");
-          },
-          [&](const Fortran::parser::OpenMPDeclareReductionConstruct
-                  &declareReductionConstruct) {
-            TODO(converter.getCurrentLocation(),
-                 "OpenMPDeclareReductionConstruct");
-          },
-          [&](const Fortran::parser::OpenMPDeclareSimdConstruct
-                  &declareSimdConstruct) {
-            TODO(converter.getCurrentLocation(), "OpenMPDeclareSimdConstruct");
-          },
-          [&](const Fortran::parser::OpenMPDeclareTargetConstruct
-                  &declareTargetConstruct) {
-            handleDeclareTarget(converter, eval, declareTargetConstruct);
-          },
-          [&](const Fortran::parser::OpenMPRequiresConstruct
-                  &requiresConstruct) {
-            TODO(converter.getCurrentLocation(), "OpenMPRequiresConstruct");
-          },
-          [&](const Fortran::parser::OpenMPThreadprivate &threadprivate) {
-            // The directive is lowered when instantiating the variable to
-            // support the case of threadprivate variable declared in module.
-          },
-      },
-      ompDeclConstruct.u);
-}
-
-static mlir::Operation *getCompareFromReductionOp(mlir::Operation *reductionOp,
-                                                  mlir::Value loadVal) {
-  for (auto reductionOperand : reductionOp->getOperands()) {
-    if (auto compareOp = reductionOperand.getDefiningOp()) {
-      if (compareOp->getOperand(0) == loadVal ||
-          compareOp->getOperand(1) == loadVal)
-        assert((mlir::isa<mlir::arith::CmpIOp>(compareOp) ||
-                mlir::isa<mlir::arith::CmpFOp>(compareOp)) &&
-               "Expected comparison not found in reduction intrinsic");
-      return compareOp;
-    }
-  }
-  return nullptr;
-}
-
 // Generate an OpenMP reduction operation.
 // TODO: Currently assumes it is either an integer addition/multiplication
 // reduction, or a logical and reduction. Generalize this for various reduction
@@ -2932,14 +3309,14 @@ void Fortran::lower::genOpenMPReduction(
     const Fortran::parser::OmpClauseList &clauseList) {
   fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
 
-  for (const auto &clause : clauseList.v) {
+  for (const Fortran::parser::OmpClause &clause : clauseList.v) {
     if (const auto &reductionClause =
             std::get_if<Fortran::parser::OmpClause::Reduction>(&clause.u)) {
       const auto &redOperator{std::get<Fortran::parser::OmpReductionOperator>(
           reductionClause->v.t)};
       const auto &objectList{
           std::get<Fortran::parser::OmpObjectList>(reductionClause->v.t)};
-      if (auto reductionOp =
+      if (const auto *reductionOp =
               std::get_if<Fortran::parser::DefinedOperator>(&redOperator.u)) {
         const auto &intrinsicOp{
             std::get<Fortran::parser::DefinedOperator::IntrinsicOperator>(
@@ -2956,10 +3333,10 @@ void Fortran::lower::genOpenMPReduction(
         default:
           continue;
         }
-        for (const auto &ompObject : objectList.v) {
+        for (const Fortran::parser::OmpObject &ompObject : objectList.v) {
           if (const auto *name{
                   Fortran::parser::Unwrap<Fortran::parser::Name>(ompObject)}) {
-            if (const auto *symbol{name->symbol}) {
+            if (const Fortran::semantics::Symbol * symbol{name->symbol}) {
               mlir::Value reductionVal = converter.getSymbolAddress(*symbol);
               mlir::Type reductionType =
                   reductionVal.getType().cast<fir::ReferenceType>().getEleTy();
@@ -2978,7 +3355,7 @@ void Fortran::lower::genOpenMPReduction(
                     updateReduction(reductionOp, firOpBuilder, loadVal,
                                     reductionVal, &convertOp);
                     removeStoreOp(reductionOp, reductionVal);
-                  } else if (auto reductionOp =
+                  } else if (mlir::Operation *reductionOp =
                                  findReductionChain(loadVal, &reductionVal)) {
                     updateReduction(reductionOp, firOpBuilder, loadVal,
                                     reductionVal);
@@ -2988,7 +3365,7 @@ void Fortran::lower::genOpenMPReduction(
             }
           }
         }
-      } else if (auto reductionIntrinsic =
+      } else if (const auto *reductionIntrinsic =
                      std::get_if<Fortran::parser::ProcedureDesignator>(
                          &redOperator.u)) {
         if (const auto *name{Fortran::parser::Unwrap<Fortran::parser::Name>(
@@ -2999,12 +3376,12 @@ void Fortran::lower::genOpenMPReduction(
               (name->source != "iand")) {
             continue;
           }
-          for (const auto &ompObject : objectList.v) {
+          for (const Fortran::parser::OmpObject &ompObject : objectList.v) {
             if (const auto *name{Fortran::parser::Unwrap<Fortran::parser::Name>(
                     ompObject)}) {
-              if (const auto *symbol{name->symbol}) {
+              if (const Fortran::semantics::Symbol * symbol{name->symbol}) {
                 mlir::Value reductionVal = converter.getSymbolAddress(*symbol);
-                for (mlir::OpOperand &reductionValUse :
+                for (const mlir::OpOperand &reductionValUse :
                      reductionVal.getUses()) {
                   if (auto loadOp = mlir::dyn_cast<fir::LoadOp>(
                           reductionValUse.getOwner())) {
@@ -3044,10 +3421,10 @@ void Fortran::lower::genOpenMPReduction(
 mlir::Operation *Fortran::lower::findReductionChain(mlir::Value loadVal,
                                                     mlir::Value *reductionVal) {
   for (mlir::OpOperand &loadOperand : loadVal.getUses()) {
-    if (auto reductionOp = loadOperand.getOwner()) {
+    if (mlir::Operation *reductionOp = loadOperand.getOwner()) {
       if (auto convertOp = mlir::dyn_cast<fir::ConvertOp>(reductionOp)) {
         for (mlir::OpOperand &convertOperand : convertOp.getRes().getUses()) {
-          if (auto reductionOp = convertOperand.getOwner())
+          if (mlir::Operation *reductionOp = convertOperand.getOwner())
             return reductionOp;
         }
       }
@@ -3065,6 +3442,23 @@ mlir::Operation *Fortran::lower::findReductionChain(mlir::Value loadVal,
   return nullptr;
 }
 
+// for a logical operator 'op' reduction X = X op Y
+// This function returns the operation responsible for converting Y from
+// fir.logical<4> to i1
+fir::ConvertOp
+Fortran::lower::getConvertFromReductionOp(mlir::Operation *reductionOp,
+                                          mlir::Value loadVal) {
+  for (mlir::Value reductionOperand : reductionOp->getOperands()) {
+    if (auto convertOp =
+            mlir::dyn_cast<fir::ConvertOp>(reductionOperand.getDefiningOp())) {
+      if (convertOp.getOperand() == loadVal)
+        continue;
+      return convertOp;
+    }
+  }
+  return nullptr;
+}
+
 void Fortran::lower::updateReduction(mlir::Operation *op,
                                      fir::FirOpBuilder &firOpBuilder,
                                      mlir::Value loadVal,
@@ -3086,29 +3480,13 @@ void Fortran::lower::updateReduction(mlir::Operation *op,
   firOpBuilder.restoreInsertionPoint(insertPtDel);
 }
 
-// for a logical operator 'op' reduction X = X op Y
-// This function returns the operation responsible for converting Y from
-// fir.logical<4> to i1
-fir::ConvertOp
-Fortran::lower::getConvertFromReductionOp(mlir::Operation *reductionOp,
-                                          mlir::Value loadVal) {
-  for (auto reductionOperand : reductionOp->getOperands()) {
-    if (auto convertOp =
-            mlir::dyn_cast<fir::ConvertOp>(reductionOperand.getDefiningOp())) {
-      if (convertOp.getOperand() == loadVal)
-        continue;
-      return convertOp;
-    }
-  }
-  return nullptr;
-}
-
 void Fortran::lower::removeStoreOp(mlir::Operation *reductionOp,
                                    mlir::Value symVal) {
-  for (auto reductionOpUse : reductionOp->getUsers()) {
+  for (mlir::Operation *reductionOpUse : reductionOp->getUsers()) {
     if (auto convertReduction =
             mlir::dyn_cast<fir::ConvertOp>(reductionOpUse)) {
-      for (auto convertReductionUse : convertReduction.getRes().getUsers()) {
+      for (mlir::Operation *convertReductionUse :
+           convertReduction.getRes().getUsers()) {
         if (auto storeOp = mlir::dyn_cast<fir::StoreOp>(convertReductionUse)) {
           if (storeOp.getMemref() == symVal)
             storeOp.erase();


        


More information about the flang-commits mailing list