[flang-commits] [flang] [NFC][flang][OpenMP] Split `DataSharing` and `Clause` processors (PR #81973)

via flang-commits flang-commits at lists.llvm.org
Fri Feb 16 00:09:02 PST 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-flang-openmp

Author: Kareem Ergawy (ergawy)

<details>
<summary>Changes</summary>

This started as an experiment to reduce the compilation time of iterating over `Lower/OpenMP.cpp` a bit since it is too slow at the moment. Trying to do that, I split the `DataSharingProcessor` and `ClauseProcessor` into their own files and extracted some shared code into a util file.

This resulted is a slightly better orgnaization of the OpenMP lowering code and hence opening this NFC.

As for the compilation time, this unfortunately does not affect it much (it shaves off a few seconds of `OpenMP.cpp` compilation) since from what I learned the bottleneck is in `DirectivesCommon.h` and `PFTBuilder.h` which both consume a lot of time in template instantiation it seems.

### This is just a proposal, please let me know if you disagree with this reorganization for any reason.

---

Patch is 183.18 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/81973.diff


8 Files Affected:

- (modified) flang/lib/Lower/CMakeLists.txt (+3) 
- (added) flang/lib/Lower/ClauseProcessor.cpp (+1298) 
- (added) flang/lib/Lower/ClauseProcessor.h (+414) 
- (added) flang/lib/Lower/DataSharingProcessor.cpp (+350) 
- (added) flang/lib/Lower/DataSharingProcessor.h (+89) 
- (modified) flang/lib/Lower/OpenMP.cpp (+4-2034) 
- (added) flang/lib/Lower/OpenMPUtils.cpp (+56) 
- (added) flang/lib/Lower/OpenMPUtils.h (+27) 


``````````diff
diff --git a/flang/lib/Lower/CMakeLists.txt b/flang/lib/Lower/CMakeLists.txt
index b13d415e02f1d9..dfd942ac21aeb1 100644
--- a/flang/lib/Lower/CMakeLists.txt
+++ b/flang/lib/Lower/CMakeLists.txt
@@ -5,6 +5,7 @@ add_flang_library(FortranLower
   Allocatable.cpp
   Bridge.cpp
   CallInterface.cpp
+  ClauseProcessor.cpp
   Coarray.cpp
   ComponentPath.cpp
   ConvertArrayConstructor.cpp
@@ -16,6 +17,7 @@ add_flang_library(FortranLower
   ConvertType.cpp
   ConvertVariable.cpp
   CustomIntrinsicCall.cpp
+  DataSharingProcessor.cpp
   DumpEvaluateExpr.cpp
   HlfirIntrinsics.cpp
   HostAssociations.cpp
@@ -25,6 +27,7 @@ add_flang_library(FortranLower
   Mangler.cpp
   OpenACC.cpp
   OpenMP.cpp
+  OpenMPUtils.cpp
   PFTBuilder.cpp
   Runtime.cpp
   SymbolMap.cpp
diff --git a/flang/lib/Lower/ClauseProcessor.cpp b/flang/lib/Lower/ClauseProcessor.cpp
new file mode 100644
index 00000000000000..b5c5586fa1e3ea
--- /dev/null
+++ b/flang/lib/Lower/ClauseProcessor.cpp
@@ -0,0 +1,1298 @@
+//===-- ClauseProcessor.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClauseProcessor.h"
+
+#include "flang/Lower/PFTBuilder.h"
+#include "flang/Parser/tools.h"
+#include "flang/Semantics/tools.h"
+
+namespace Fortran {
+namespace lower {
+namespace omp {
+
+//===----------------------------------------------------------------------===//
+// Common helper functions
+//===----------------------------------------------------------------------===//
+
+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);
+  }
+}
+
+void gatherFuncAndVarSyms(
+    const Fortran::parser::OmpObjectList &objList,
+    mlir::omp::DeclareTargetCaptureClause clause,
+    llvm::SmallVectorImpl<DeclareTargetCapturePair> &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);
+  }
+}
+
+//===----------------------------------------------------------------------===//
+// 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 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;
+}
+
+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,
+                  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;
+  const Fortran::parser::OmpObjectList &ompObjectList =
+      std::get<Fortran::parser::OmpObjectList>(ompAllocateClause.t);
+  const auto &allocateModifier = std::get<
+      std::optional<Fortran::parser::OmpAllocateClause::AllocateModifier>>(
+      ompAllocateClause.t);
+
+  // If the allocate modifier is present, check if we only use the allocator
+  // submodifier.  ALIGN in this context is unimplemented
+  const bool onlyAllocator =
+      allocateModifier &&
+      std::holds_alternative<
+          Fortran::parser::OmpAllocateClause::AllocateModifier::Allocator>(
+          allocateModifier->u);
+
+  if (allocateModifier && !onlyAllocator) {
+    TODO(currentLocation, "OmpAllocateClause ALIGN modifier");
+  }
+
+  // Check if allocate clause has allocator specified. If so, add it
+  // to list of allocators, otherwise, add default allocator to
+  // list of allocators.
+  if (onlyAllocator) {
+    const auto &allocatorValue = std::get<
+        Fortran::parser::OmpAllocateClause::AllocateModifier::Allocator>(
+        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 mlir::omp::ClauseProcBindKindAttr genProcBindKindAttr(
+    fir::FirOpBuilder &firOpBuilder,
+    const Fortran::parser::OmpClause::ProcBind *procBindClause) {
+  mlir::omp::ClauseProcBindKind procBindKind;
+  switch (procBindClause->v.v) {
+  case Fortran::parser::OmpProcBindClause::Type::Master:
+    procBindKind = mlir::omp::ClauseProcBindKind::Master;
+    break;
+  case Fortran::parser::OmpProcBindClause::Type::Close:
+    procBindKind = mlir::omp::ClauseProcBindKind::Close;
+    break;
+  case Fortran::parser::OmpProcBindClause::Type::Spread:
+    procBindKind = mlir::omp::ClauseProcBindKind::Spread;
+    break;
+  case Fortran::parser::OmpProcBindClause::Type::Primary:
+    procBindKind = mlir::omp::ClauseProcBindKind::Primary;
+    break;
+  }
+  return mlir::omp::ClauseProcBindKindAttr::get(firOpBuilder.getContext(),
+                                                procBindKind);
+}
+
+static mlir::omp::ClauseTaskDependAttr
+genDependKindAttr(fir::FirOpBuilder &firOpBuilder,
+                  const Fortran::parser::OmpClause::Depend *dependClause) {
+  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 = mlir::omp::ClauseTaskDepend::taskdependin;
+    break;
+  case Fortran::parser::OmpDependenceType::Type::Out:
+    pbKind = mlir::omp::ClauseTaskDepend::taskdependout;
+    break;
+  case Fortran::parser::OmpDependenceType::Type::Inout:
+    pbKind = mlir::omp::ClauseTaskDepend::taskdependinout;
+    break;
+  default:
+    llvm_unreachable("unknown parser task dependence type");
+    break;
+  }
+  return mlir::omp::ClauseTaskDependAttr::get(firOpBuilder.getContext(),
+                                              pbKind);
+}
+
+static mlir::Value getIfClauseOperand(
+    Fortran::lower::AbstractConverter &converter,
+    const Fortran::parser::OmpClause::If *ifClause,
+    Fortran::parser::OmpIfClause::DirectiveNameModifier directiveName,
+    mlir::Location clauseLocation) {
+  // Only consider the clause if it's intended for the given directive.
+  auto &directive = std::get<
+      std::optional<Fortran::parser::OmpIfClause::DirectiveNameModifier>>(
+      ifClause->v.t);
+  if (directive && directive.value() != directiveName)
+    return nullptr;
+
+  Fortran::lower::StatementContext stmtCtx;
+  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);
+}
+
+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::processNumTeams(Fortran::lower::StatementContext &stmtCtx,
+                                      mlir::Value &result) const {
+  // TODO Get lower and upper bounds for num_teams when parser is updated to
+  // accept both.
+  if (auto *numTeamsClause = findUniqueClause<ClauseTy::NumTeams>()) {
+    result = fir::getBase(converter.genExprValue(
+        *Fortran::semantics::GetExpr(numTeamsClause->v), stmtCtx));
+    return true;
+  }
+  return false;
+}
+
+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;
+...
[truncated]

``````````

</details>


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


More information about the flang-commits mailing list