[llvm-branch-commits] [flang] WIP: OpenMP changes (PR #74867)
Krzysztof Parzyszek via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Sat Dec 9 13:05:15 PST 2023
https://github.com/kparzysz updated https://github.com/llvm/llvm-project/pull/74867
>From c851cb7cd004b0aad988816e6721ecfd4150833c Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Fri, 8 Dec 2023 10:39:06 -0600
Subject: [PATCH] WIP: OpenMP changes
---
flang/include/flang/Lower/OpenMP.h | 39 -
flang/lib/Lower/OpenMP.cpp | 2127 ++++++++++++++--------------
flang/lib/Lower/OpenMPMixin.h | 91 +-
3 files changed, 1180 insertions(+), 1077 deletions(-)
diff --git a/flang/include/flang/Lower/OpenMP.h b/flang/include/flang/Lower/OpenMP.h
index c9162761a08d54..6764c470db9368 100644
--- a/flang/include/flang/Lower/OpenMP.h
+++ b/flang/include/flang/Lower/OpenMP.h
@@ -13,66 +13,27 @@
#ifndef FORTRAN_LOWER_OPENMP_H
#define FORTRAN_LOWER_OPENMP_H
-#include <cinttypes>
-
namespace mlir {
-class Value;
class Operation;
class Location;
} // namespace mlir
namespace fir {
class FirOpBuilder;
-class ConvertOp;
} // namespace fir
namespace Fortran {
namespace parser {
struct OpenMPConstruct;
-struct OpenMPDeclarativeConstruct;
-struct OmpEndLoopDirective;
-struct OmpClauseList;
} // namespace parser
-namespace semantics {
-class Symbol;
-class SemanticsContext;
-} // namespace semantics
-
namespace lower {
-class AbstractConverter;
-
-namespace pft {
-struct Evaluation;
-struct Variable;
-} // namespace pft
-
// Generate the OpenMP terminator for Operation at Location.
void genOpenMPTerminator(fir::FirOpBuilder &, mlir::Operation *,
mlir::Location);
-void genOpenMPConstruct(AbstractConverter &, semantics::SemanticsContext &,
- pft::Evaluation &, const parser::OpenMPConstruct &);
-void genOpenMPDeclarativeConstruct(AbstractConverter &, pft::Evaluation &,
- const parser::OpenMPDeclarativeConstruct &);
-int64_t getCollapseValue(const Fortran::parser::OmpClauseList &clauseList);
-void genThreadprivateOp(AbstractConverter &, const pft::Variable &);
-void genDeclareTargetIntGlobal(AbstractConverter &, const pft::Variable &);
-void genOpenMPReduction(AbstractConverter &,
- const Fortran::parser::OmpClauseList &clauseList);
-
-mlir::Operation *findReductionChain(mlir::Value, mlir::Value * = nullptr);
-fir::ConvertOp getConvertFromReductionOp(mlir::Operation *, mlir::Value);
-void updateReduction(mlir::Operation *, fir::FirOpBuilder &, mlir::Value,
- mlir::Value, fir::ConvertOp * = nullptr);
-void removeStoreOp(mlir::Operation *, mlir::Value);
-
bool isOpenMPTargetConstruct(const parser::OpenMPConstruct &);
-bool isOpenMPDeviceDeclareTarget(Fortran::lower::AbstractConverter &,
- Fortran::lower::pft::Evaluation &,
- const parser::OpenMPDeclarativeConstruct &);
-void genOpenMPRequires(mlir::Operation *, const Fortran::semantics::Symbol *);
} // namespace lower
} // namespace Fortran
diff --git a/flang/lib/Lower/OpenMP.cpp b/flang/lib/Lower/OpenMP.cpp
index 5ca7be5da26a60..f531402120f796 100644
--- a/flang/lib/Lower/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP.cpp
@@ -10,10 +10,11 @@
//
//===----------------------------------------------------------------------===//
+#include "ConverterMixin.h"
+#include "DirectivesCommon.h"
#include "FirConverter.h"
#include "OpenMPMixin.h"
-#include "DirectivesCommon.h"
#include "flang/Common/idioms.h"
#include "flang/Lower/Bridge.h"
#include "flang/Lower/ConvertExpr.h"
@@ -35,6 +36,19 @@
#include "llvm/Frontend/OpenMP/OMPConstants.h"
#include "llvm/Support/CommandLine.h"
+#include <algorithm>
+#include <functional>
+#include <iterator>
+#include <list>
+#include <numeric>
+#include <optional>
+#include <sstream>
+#include <string>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+#include <variant>
+
static llvm::cl::opt<bool> treatIndexAsSection(
"openmp-treat-index-as-section",
llvm::cl::desc("In the OpenMP data clauses treat `a(N)` as `a(N:N)`."),
@@ -61,6 +75,31 @@ template <> Fortran::lower::SymMap &OpenMPMixin<FirConverter>::getSymTable() {
return This()->FirConverter::getSymTable();
}
+void 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);
+}
+
+bool isOpenMPTargetConstruct(const parser::OpenMPConstruct &omp) {
+ llvm::omp::Directive dir = llvm::omp::Directive::OMPD_unknown;
+ if (const auto *block =
+ std::get_if<Fortran::parser::OpenMPBlockConstruct>(&omp.u)) {
+ const auto &begin =
+ std::get<Fortran::parser::OmpBeginBlockDirective>(block->t);
+ dir = std::get<Fortran::parser::OmpBlockDirective>(begin.t).v;
+ } else if (const auto *loop =
+ std::get_if<Fortran::parser::OpenMPLoopConstruct>(&omp.u)) {
+ const auto &begin =
+ std::get<Fortran::parser::OmpBeginLoopDirective>(loop->t);
+ dir = std::get<Fortran::parser::OmpLoopDirective>(begin.t).v;
+ }
+ return llvm::omp::allTargetSet.test(dir);
+}
+
} // namespace Fortran::lower
//===----------------------------------------------------------------------===//
@@ -283,7 +322,7 @@ void DataSharingProcessor::collectSymbolsForPrivatization() {
TODO(converter.getCurrentLocation(), "Collapse clause with lastprivate");
}
-bool DataSharingProcessor ::needBarrier() {
+bool DataSharingProcessor::needBarrier() {
for (const Fortran::semantics::Symbol *sym : privatizedSymbols) {
if (sym->test(Fortran::semantics::Symbol::Flag::OmpFirstPrivate) &&
sym->test(Fortran::semantics::Symbol::Flag::OmpLastPrivate))
@@ -292,7 +331,7 @@ bool DataSharingProcessor ::needBarrier() {
return false;
}
-void DataSharingProcessor ::insertBarrier() {
+void DataSharingProcessor::insertBarrier() {
// Emit implicit barrier to synchronize threads and avoid data races on
// initialization of firstprivate variables and post-update of lastprivate
// variables.
@@ -1934,55 +1973,9 @@ void ClauseProcessor::processTODO(mlir::Location currentLocation,
// 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::IsAllocatableOrObjectPointer(&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;
-}
-
// Get the extended value for \p val by extracting additional variable
// information from \p base.
+// SA
static fir::ExtendedValue getExtendedValue(fir::ExtendedValue base,
mlir::Value val) {
return base.match(
@@ -1994,6 +1987,7 @@ static fir::ExtendedValue getExtendedValue(fir::ExtendedValue base,
});
}
+// FC
static void threadPrivatizeVars(Fortran::lower::AbstractConverter &converter,
Fortran::lower::pft::Evaluation &eval) {
fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
@@ -2061,6 +2055,7 @@ static void threadPrivatizeVars(Fortran::lower::AbstractConverter &converter,
firOpBuilder.restoreInsertionPoint(insPt);
}
+// FC
static mlir::Type getLoopVarType(Fortran::lower::AbstractConverter &converter,
std::size_t loopVarTypeSize) {
// OpenMP runtime requires 32-bit or 64-bit loop variables.
@@ -2121,6 +2116,7 @@ createAndSetPrivatizedLoopVar(Fortran::lower::AbstractConverter &converter,
//// region.
/// \param [in] outerCombined - is this an outer operation - prevents
/// privatization.
+// FC
template <typename Op>
static void createBodyOfOp(
Op &op, Fortran::lower::AbstractConverter &converter, mlir::Location &loc,
@@ -2139,12 +2135,8 @@ static void createBodyOfOp(
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);
- }
+ llvm::SmallVector<mlir::Type> tiv(args.size(), loopVarType);
+ llvm::SmallVector<mlir::Location> locs(args.size(), loc);
firOpBuilder.createBlock(&op.getRegion(), {}, tiv, locs);
int argIndex = 0;
// The argument is not currently in memory, so make a temporary for the
@@ -2172,7 +2164,13 @@ static void createBodyOfOp(
firOpBuilder, eval.getNestedEvaluations());
// Insert the terminator.
- Fortran::lower::genOpenMPTerminator(firOpBuilder, op.getOperation(), loc);
+ 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);
+ }
// Reset the insert point to before the terminator.
resetBeforeTerminator(firOpBuilder, storeOp, block);
@@ -2201,68 +2199,7 @@ static void createBodyOfOp(
}
}
-static void genBodyOfTargetDataOp(
- Fortran::lower::AbstractConverter &converter,
- Fortran::lower::pft::Evaluation &eval, 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 ¤tLocation) {
- fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
- mlir::Region ®ion = dataOp.getRegion();
-
- firOpBuilder.createBlock(®ion, {}, useDeviceTypes, useDeviceLocs);
-
- unsigned argIndex = 0;
- for (const Fortran::semantics::Symbol *sym : useDeviceSymbols) {
- const mlir::BlockArgument &arg = region.front().getArgument(argIndex);
- fir::ExtendedValue extVal = converter.getSymbolExtendedValue(*sym);
- if (auto refType = arg.getType().dyn_cast<fir::ReferenceType>()) {
- if (fir::isa_builtin_cptr_type(refType.getElementType())) {
- converter.bindSymbol(*sym, arg);
- } else {
- extVal.match(
- [&](const fir::MutableBoxValue &mbv) {
- converter.bindSymbol(
- *sym,
- fir::MutableBoxValue(
- arg, 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++;
- }
-
- // Insert dummy instruction to remember the insertion position. The
- // marker will be deleted by clean up passes since there are no uses.
- // Remembering the position for further insertion is important since
- // there are hlfir.declares inserted above while setting block arguments
- // and new code from the body should be inserted after that.
- mlir::Value undefMarker = firOpBuilder.create<fir::UndefOp>(
- dataOp.getOperation()->getLoc(), firOpBuilder.getIndexType());
-
- // Create blocks for unstructured regions. This has to be done since
- // blocks are initially allocated with the function as the parent region.
- if (eval.lowerAsUnstructured()) {
- Fortran::lower::createEmptyRegionBlocks<mlir::omp::TerminatorOp,
- mlir::omp::YieldOp>(
- firOpBuilder, eval.getNestedEvaluations());
- }
-
- firOpBuilder.create<mlir::omp::TerminatorOp>(currentLocation);
-
- // Set the insertion point after the marker.
- firOpBuilder.setInsertionPointAfter(undefMarker.getDefiningOp());
-}
-
+// FC
template <typename OpTy, typename... Args>
static OpTy genOpWithBody(Fortran::lower::AbstractConverter &converter,
Fortran::lower::pft::Evaluation &eval,
@@ -2276,6 +2213,7 @@ static OpTy genOpWithBody(Fortran::lower::AbstractConverter &converter,
return op;
}
+// FC
static mlir::omp::MasterOp
genMasterOp(Fortran::lower::AbstractConverter &converter,
Fortran::lower::pft::Evaluation &eval,
@@ -2286,6 +2224,7 @@ genMasterOp(Fortran::lower::AbstractConverter &converter,
/*resultTypes=*/mlir::TypeRange());
}
+// FC
static mlir::omp::OrderedRegionOp
genOrderedRegionOp(Fortran::lower::AbstractConverter &converter,
Fortran::lower::pft::Evaluation &eval,
@@ -2295,6 +2234,7 @@ genOrderedRegionOp(Fortran::lower::AbstractConverter &converter,
/*clauseList=*/nullptr, /*simd=*/false);
}
+// FC
static mlir::omp::ParallelOp
genParallelOp(Fortran::lower::AbstractConverter &converter,
Fortran::lower::pft::Evaluation &eval,
@@ -2330,6 +2270,7 @@ genParallelOp(Fortran::lower::AbstractConverter &converter,
procBindKindAttr);
}
+// FC
static mlir::omp::SingleOp
genSingleOp(Fortran::lower::AbstractConverter &converter,
Fortran::lower::pft::Evaluation &eval,
@@ -2351,6 +2292,7 @@ genSingleOp(Fortran::lower::AbstractConverter &converter,
&beginClauseList, allocateOperands, allocatorOperands, nowaitAttr);
}
+// FC
static mlir::omp::TaskOp
genTaskOp(Fortran::lower::AbstractConverter &converter,
Fortran::lower::pft::Evaluation &eval, mlir::Location currentLocation,
@@ -2389,6 +2331,7 @@ genTaskOp(Fortran::lower::AbstractConverter &converter,
dependOperands, allocateOperands, allocatorOperands);
}
+// FC
static mlir::omp::TaskGroupOp
genTaskGroupOp(Fortran::lower::AbstractConverter &converter,
Fortran::lower::pft::Evaluation &eval,
@@ -2405,6 +2348,70 @@ genTaskGroupOp(Fortran::lower::AbstractConverter &converter,
/*task_reductions=*/nullptr, allocateOperands, allocatorOperands);
}
+// FC
+static void genBodyOfTargetDataOp(
+ Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval, 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 ¤tLocation) {
+ fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+ mlir::Region ®ion = dataOp.getRegion();
+
+ firOpBuilder.createBlock(®ion, {}, useDeviceTypes, useDeviceLocs);
+
+ unsigned argIndex = 0;
+ for (const Fortran::semantics::Symbol *sym : useDeviceSymbols) {
+ const mlir::BlockArgument &arg = region.front().getArgument(argIndex);
+ fir::ExtendedValue extVal = converter.getSymbolExtendedValue(*sym);
+ if (auto refType = arg.getType().dyn_cast<fir::ReferenceType>()) {
+ if (fir::isa_builtin_cptr_type(refType.getElementType())) {
+ converter.bindSymbol(*sym, arg);
+ } else {
+ extVal.match(
+ [&](const fir::MutableBoxValue &mbv) {
+ converter.bindSymbol(
+ *sym,
+ fir::MutableBoxValue(
+ arg, 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++;
+ }
+
+ // Insert dummy instruction to remember the insertion position. The
+ // marker will be deleted by clean up passes since there are no uses.
+ // Remembering the position for further insertion is important since
+ // there are hlfir.declares inserted above while setting block arguments
+ // and new code from the body should be inserted after that.
+ mlir::Value undefMarker = firOpBuilder.create<fir::UndefOp>(
+ dataOp.getOperation()->getLoc(), firOpBuilder.getIndexType());
+
+ // Create blocks for unstructured regions. This has to be done since
+ // blocks are initially allocated with the function as the parent region.
+ if (eval.lowerAsUnstructured()) {
+ Fortran::lower::createEmptyRegionBlocks<mlir::omp::TerminatorOp,
+ mlir::omp::YieldOp>(
+ firOpBuilder, eval.getNestedEvaluations());
+ }
+
+ firOpBuilder.create<mlir::omp::TerminatorOp>(currentLocation);
+
+ // Set the insertion point after the marker.
+ firOpBuilder.setInsertionPointAfter(undefMarker.getDefiningOp());
+}
+
+// FC
static mlir::omp::DataOp
genDataOp(Fortran::lower::AbstractConverter &converter,
Fortran::lower::pft::Evaluation &eval,
@@ -2438,6 +2445,7 @@ genDataOp(Fortran::lower::AbstractConverter &converter,
return dataOp;
}
+// FC
template <typename OpTy>
static OpTy
genEnterExitDataOp(Fortran::lower::AbstractConverter &converter,
@@ -2479,6 +2487,7 @@ genEnterExitDataOp(Fortran::lower::AbstractConverter &converter,
// This functions creates a block for the body of the targetOp's region. It adds
// all the symbols present in mapSymbols as block arguments to this block.
+// FC
static void genBodyOfTargetOp(
Fortran::lower::AbstractConverter &converter,
Fortran::lower::pft::Evaluation &eval, mlir::omp::TargetOp &targetOp,
@@ -2625,6 +2634,7 @@ static void genBodyOfTargetOp(
firOpBuilder.setInsertionPointAfter(undefMarker.getDefiningOp());
}
+// FC
static mlir::omp::TargetOp
genTargetOp(Fortran::lower::AbstractConverter &converter,
Fortran::lower::pft::Evaluation &eval,
@@ -2735,6 +2745,7 @@ genTargetOp(Fortran::lower::AbstractConverter &converter,
return targetOp;
}
+// FC
static mlir::omp::TeamsOp
genTeamsOp(Fortran::lower::AbstractConverter &converter,
Fortran::lower::pft::Evaluation &eval,
@@ -2770,6 +2781,7 @@ genTeamsOp(Fortran::lower::AbstractConverter &converter,
/// Extract the list of function and variable symbols affected by the given
/// 'declare target' directive and return the intended device type for them.
+// FC
static mlir::omp::DeclareTargetDeviceType getDeclareTargetInfo(
Fortran::lower::AbstractConverter &converter,
Fortran::lower::pft::Evaluation &eval,
@@ -2810,6 +2822,7 @@ static mlir::omp::DeclareTargetDeviceType getDeclareTargetInfo(
return deviceType;
}
+// FC
static std::optional<mlir::omp::DeclareTargetDeviceType>
getDeclareTargetFunctionDevice(
Fortran::lower::AbstractConverter &converter,
@@ -2835,793 +2848,258 @@ getDeclareTargetFunctionDevice(
}
//===----------------------------------------------------------------------===//
-// genOMP() Code generation helper functions
+// Public functions
//===----------------------------------------------------------------------===//
-static void
-genOmpSimpleStandalone(Fortran::lower::AbstractConverter &converter,
- Fortran::lower::pft::Evaluation &eval,
- Fortran::semantics::SemanticsContext &semanticsContext,
- const Fortran::parser::OpenMPSimpleStandaloneConstruct
- &simpleStandaloneConstruct) {
- const auto &directive =
- std::get<Fortran::parser::OmpSimpleStandaloneDirective>(
- simpleStandaloneConstruct.t);
- fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
- const auto &opClauseList =
- std::get<Fortran::parser::OmpClauseList>(simpleStandaloneConstruct.t);
- mlir::Location currentLocation = converter.genLocation(directive.source);
-
- switch (directive.v) {
- default:
- break;
- case llvm::omp::Directive::OMPD_barrier:
- firOpBuilder.create<mlir::omp::BarrierOp>(currentLocation);
- break;
- case llvm::omp::Directive::OMPD_taskwait:
- ClauseProcessor(converter, opClauseList)
- .processTODO<Fortran::parser::OmpClause::Depend,
- Fortran::parser::OmpClause::Nowait>(
- currentLocation, 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:
- genDataOp(converter, eval, semanticsContext, currentLocation, opClauseList);
- break;
- case llvm::omp::Directive::OMPD_target_enter_data:
- genEnterExitDataOp<mlir::omp::EnterDataOp>(converter, semanticsContext,
- currentLocation, opClauseList);
- break;
- case llvm::omp::Directive::OMPD_target_exit_data:
- genEnterExitDataOp<mlir::omp::ExitDataOp>(converter, semanticsContext,
- currentLocation, opClauseList);
- break;
- case llvm::omp::Directive::OMPD_target_update:
- TODO(currentLocation, "OMPD_target_update");
- case llvm::omp::Directive::OMPD_ordered:
- TODO(currentLocation, "OMPD_ordered");
+// SA
+static int64_t
+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 1;
}
-static void
-genOmpFlush(Fortran::lower::AbstractConverter &converter,
- Fortran::lower::pft::Evaluation &eval,
- 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);
-}
-
-static void
-genOMP(Fortran::lower::AbstractConverter &converter,
- Fortran::lower::pft::Evaluation &eval,
- Fortran::semantics::SemanticsContext &semanticsContext,
- const Fortran::parser::OpenMPStandaloneConstruct &standaloneConstruct) {
- std::visit(
- Fortran::common::visitors{
- [&](const Fortran::parser::OpenMPSimpleStandaloneConstruct
- &simpleStandaloneConstruct) {
- genOmpSimpleStandalone(converter, eval, semanticsContext,
- simpleStandaloneConstruct);
- },
- [&](const Fortran::parser::OpenMPFlushConstruct &flushConstruct) {
- genOmpFlush(converter, eval, flushConstruct);
- },
- [&](const Fortran::parser::OpenMPCancelConstruct &cancelConstruct) {
- TODO(converter.getCurrentLocation(), "OpenMPCancelConstruct");
- },
- [&](const Fortran::parser::OpenMPCancellationPointConstruct
- &cancellationPointConstruct) {
- TODO(converter.getCurrentLocation(), "OpenMPCancelConstruct");
- },
- },
- standaloneConstruct.u);
-}
-
-static void genOMP(Fortran::lower::AbstractConverter &converter,
- Fortran::lower::pft::Evaluation &eval,
- Fortran::semantics::SemanticsContext &semanticsContext,
- const Fortran::parser::OpenMPLoopConstruct &loopConstruct) {
- fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
- llvm::SmallVector<mlir::Value> lowerBound, upperBound, step, linearVars,
- linearStepVars, reductionVars;
- mlir::Value scheduleChunkClauseOperand;
- mlir::IntegerAttr orderedClauseOperand;
- mlir::omp::ClauseOrderKindAttr orderClauseOperand;
- mlir::omp::ClauseScheduleKindAttr scheduleValClauseOperand;
- mlir::omp::ScheduleModifierAttr scheduleModClauseOperand;
- mlir::UnitAttr nowaitClauseOperand, scheduleSimdClauseOperand;
- llvm::SmallVector<mlir::Attribute> reductionDeclSymbols;
- Fortran::lower::StatementContext stmtCtx;
- 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 =
- std::get<Fortran::parser::OmpLoopDirective>(beginLoopDirective.t).v;
+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);
- bool validDirective = false;
- if (llvm::omp::topTaskloopSet.test(ompDirective)) {
- validDirective = true;
- TODO(currentLocation, "Taskloop construct");
+ // Create default initialization for non-character scalar.
+ if (Fortran::semantics::IsAllocatableOrObjectPointer(&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 {
- // Create omp.{target, teams, distribute, parallel} nested operations
- if ((llvm::omp::allTargetSet & llvm::omp::loopConstructSet)
- .test(ompDirective)) {
- validDirective = true;
- genTargetOp(converter, eval, semanticsContext, currentLocation,
- loopOpClauseList, ompDirective, /*outerCombined=*/true);
- }
- if ((llvm::omp::allTeamsSet & llvm::omp::loopConstructSet)
- .test(ompDirective)) {
- validDirective = true;
- genTeamsOp(converter, eval, currentLocation, loopOpClauseList,
- /*outerCombined=*/true);
- }
- if (llvm::omp::allDistributeSet.test(ompDirective)) {
- validDirective = true;
- TODO(currentLocation, "Distribute construct");
- }
- if ((llvm::omp::allParallelSet & llvm::omp::loopConstructSet)
- .test(ompDirective)) {
- validDirective = true;
- genParallelOp(converter, eval, currentLocation, loopOpClauseList,
- /*outerCombined=*/true);
- }
+ Fortran::lower::createGlobalInitialization(
+ firOpBuilder, global, [&](fir::FirOpBuilder &b) {
+ mlir::Value undef = b.create<fir::UndefOp>(currentLocation, ty);
+ b.create<fir::HasValueOp>(currentLocation, undef);
+ });
}
- if ((llvm::omp::allDoSet | llvm::omp::allSimdSet).test(ompDirective))
- validDirective = true;
- if (!validDirective) {
- TODO(currentLocation, "Unhandled loop directive (" +
- llvm::omp::getOpenMPDirectiveName(ompDirective) +
- ")");
+ return global;
+}
+
+// This function replicates threadprivate's behaviour of generating
+// an internal fir.GlobalOp for non-global variables in the main program
+// that have the implicit SAVE attribute, to simplifiy LLVM-IR and MLIR
+// generation.
+// SA
+static void
+genDeclareTargetIntGlobal(Fortran::lower::AbstractConverter &converter,
+ const Fortran::lower::pft::Variable &var) {
+ if (!var.isGlobal()) {
+ // A non-global variable which can be in a declare target directive must
+ // be a variable in the main program, and it has the implicit SAVE
+ // attribute. We create a GlobalOp for it to simplify the translation to
+ // LLVM IR.
+ globalInitialization(converter, converter.getFirOpBuilder(),
+ var.getSymbol(), var, converter.getCurrentLocation());
}
+}
- DataSharingProcessor dsp(converter, loopOpClauseList, eval);
- dsp.processStep1();
+// SA
+static void genThreadprivateOp(Fortran::lower::AbstractConverter &converter,
+ const Fortran::lower::pft::Variable &var) {
+ fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+ mlir::Location currentLocation = converter.getCurrentLocation();
- ClauseProcessor cp(converter, loopOpClauseList);
- cp.processCollapse(currentLocation, eval, lowerBound, upperBound, step, iv,
- loopVarTypeSize);
- cp.processScheduleChunk(stmtCtx, scheduleChunkClauseOperand);
- cp.processReduction(currentLocation, reductionVars, reductionDeclSymbols);
- cp.processTODO<Fortran::parser::OmpClause::Linear,
- Fortran::parser::OmpClause::Order>(currentLocation,
- ompDirective);
+ const Fortran::semantics::Symbol &sym = var.getSymbol();
+ mlir::Value symThreadprivateValue;
+ if (const Fortran::semantics::Symbol *common =
+ Fortran::semantics::FindCommonBlockContaining(sym.GetUltimate())) {
+ mlir::Value commonValue = converter.getSymbolAddress(*common);
+ if (mlir::isa<mlir::omp::ThreadprivateOp>(commonValue.getDefiningOp())) {
+ // Generate ThreadprivateOp for a common block instead of its members and
+ // only do it once for a common block.
+ return;
+ }
+ // Generate ThreadprivateOp and rebind the common block.
+ mlir::Value commonThreadprivateValue =
+ firOpBuilder.create<mlir::omp::ThreadprivateOp>(
+ currentLocation, commonValue.getType(), commonValue);
+ converter.bindSymbol(*common, commonThreadprivateValue);
+ // Generate the threadprivate value for the common block member.
+ symThreadprivateValue = genCommonBlockMember(converter, currentLocation,
+ sym, commonThreadprivateValue);
+ } else if (!var.isGlobal()) {
+ // Non-global variable which can be in threadprivate directive must be one
+ // variable in main program, and it has implicit SAVE attribute. Take it as
+ // with SAVE attribute, so to create GlobalOp for it to simplify the
+ // translation to LLVM IR.
+ fir::GlobalOp global = globalInitialization(converter, firOpBuilder, sym,
+ var, currentLocation);
- // The types of lower bound, upper bound, and step are converted into the
- // type of the loop variable if necessary.
- mlir::Type loopVarType = getLoopVarType(converter, loopVarTypeSize);
- for (unsigned it = 0; it < (unsigned)lowerBound.size(); it++) {
- lowerBound[it] = firOpBuilder.createConvert(currentLocation, loopVarType,
- lowerBound[it]);
- upperBound[it] = firOpBuilder.createConvert(currentLocation, loopVarType,
- upperBound[it]);
- step[it] =
- firOpBuilder.createConvert(currentLocation, loopVarType, step[it]);
- }
+ mlir::Value symValue = firOpBuilder.create<fir::AddrOfOp>(
+ currentLocation, global.resultType(), global.getSymbol());
+ symThreadprivateValue = firOpBuilder.create<mlir::omp::ThreadprivateOp>(
+ currentLocation, symValue.getType(), symValue);
+ } else {
+ mlir::Value symValue = converter.getSymbolAddress(sym);
- // 2.9.3.1 SIMD construct
- if (llvm::omp::allSimdSet.test(ompDirective)) {
- llvm::SmallVector<mlir::Value> alignedVars, nontemporalVars;
- mlir::Value ifClauseOperand;
- mlir::IntegerAttr simdlenClauseOperand, safelenClauseOperand;
- cp.processIf(Fortran::parser::OmpIfClause::DirectiveNameModifier::Simd,
- ifClauseOperand);
- cp.processSimdlen(simdlenClauseOperand);
- cp.processSafelen(safelenClauseOperand);
- cp.processTODO<Fortran::parser::OmpClause::Aligned,
- Fortran::parser::OmpClause::Allocate,
- Fortran::parser::OmpClause::Nontemporal>(currentLocation,
- ompDirective);
+ // The symbol may be use-associated multiple times, and nothing needs to be
+ // done after the original symbol is mapped to the threadprivatized value
+ // for the first time. Use the threadprivatized value directly.
+ mlir::Operation *op;
+ if (auto declOp = symValue.getDefiningOp<hlfir::DeclareOp>())
+ op = declOp.getMemref().getDefiningOp();
+ else
+ op = symValue.getDefiningOp();
+ if (mlir::isa<mlir::omp::ThreadprivateOp>(op))
+ return;
- mlir::TypeRange resultType;
- auto simdLoopOp = firOpBuilder.create<mlir::omp::SimdLoopOp>(
- currentLocation, resultType, lowerBound, upperBound, step, alignedVars,
- /*alignment_values=*/nullptr, ifClauseOperand, nontemporalVars,
- orderClauseOperand, simdlenClauseOperand, safelenClauseOperand,
- /*inclusive=*/firOpBuilder.getUnitAttr());
- createBodyOfOp<mlir::omp::SimdLoopOp>(
- simdLoopOp, converter, currentLocation, eval, &loopOpClauseList, iv,
- /*outer=*/false, &dsp);
- return;
+ symThreadprivateValue = firOpBuilder.create<mlir::omp::ThreadprivateOp>(
+ currentLocation, symValue.getType(), symValue);
}
- auto wsLoopOp = firOpBuilder.create<mlir::omp::WsLoopOp>(
- currentLocation, lowerBound, upperBound, step, linearVars, linearStepVars,
- reductionVars,
- reductionDeclSymbols.empty()
- ? nullptr
- : mlir::ArrayAttr::get(firOpBuilder.getContext(),
- reductionDeclSymbols),
- scheduleValClauseOperand, scheduleChunkClauseOperand,
- /*schedule_modifiers=*/nullptr,
- /*simd_modifier=*/nullptr, nowaitClauseOperand, orderedClauseOperand,
- orderClauseOperand,
- /*inclusive=*/firOpBuilder.getUnitAttr());
+ fir::ExtendedValue sexv = converter.getSymbolExtendedValue(sym);
+ fir::ExtendedValue symThreadprivateExv =
+ getExtendedValue(sexv, symThreadprivateValue);
+ converter.bindSymbol(sym, symThreadprivateExv);
+}
- // Handle attribute based clauses.
- if (cp.processOrdered(orderedClauseOperand))
- wsLoopOp.setOrderedValAttr(orderedClauseOperand);
+static void updateReduction(mlir::Operation *op,
+ fir::FirOpBuilder &firOpBuilder,
+ mlir::Value loadVal, mlir::Value reductionVal,
+ fir::ConvertOp *convertOp = nullptr) {
+ mlir::OpBuilder::InsertPoint insertPtDel = firOpBuilder.saveInsertionPoint();
+ firOpBuilder.setInsertionPoint(op);
- 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
- // !$omp do
- // <...>
- // !$omp end do nowait
- if (const auto &endClauseList =
- std::get<std::optional<Fortran::parser::OmpEndLoopDirective>>(
- loopConstruct.t)) {
- const auto &clauseList =
- std::get<Fortran::parser::OmpClauseList>((*endClauseList).t);
- if (ClauseProcessor(converter, clauseList)
- .processNowait(nowaitClauseOperand))
- wsLoopOp.setNowaitAttr(nowaitClauseOperand);
- }
+ mlir::Value reductionOp;
+ if (convertOp)
+ reductionOp = convertOp->getOperand();
+ else if (op->getOperand(0) == loadVal)
+ reductionOp = op->getOperand(1);
+ else
+ reductionOp = op->getOperand(0);
- createBodyOfOp<mlir::omp::WsLoopOp>(wsLoopOp, converter, currentLocation,
- eval, &loopOpClauseList, iv,
- /*outer=*/false, &dsp);
+ firOpBuilder.create<mlir::omp::ReductionOp>(op->getLoc(), reductionOp,
+ reductionVal);
+ firOpBuilder.restoreInsertionPoint(insertPtDel);
}
-static void
-genOMP(Fortran::lower::AbstractConverter &converter,
- Fortran::lower::pft::Evaluation &eval,
- Fortran::semantics::SemanticsContext &semanticsContext,
- const Fortran::parser::OpenMPBlockConstruct &blockConstruct) {
- const auto &beginBlockDirective =
- std::get<Fortran::parser::OmpBeginBlockDirective>(blockConstruct.t);
- const auto &endBlockDirective =
- std::get<Fortran::parser::OmpEndBlockDirective>(blockConstruct.t);
- const auto &directive =
- std::get<Fortran::parser::OmpBlockDirective>(beginBlockDirective.t);
- const auto &beginClauseList =
- std::get<Fortran::parser::OmpClauseList>(beginBlockDirective.t);
- const auto &endClauseList =
- std::get<Fortran::parser::OmpClauseList>(endBlockDirective.t);
-
- for (const Fortran::parser::OmpClause &clause : beginClauseList.v) {
- mlir::Location clauseLocation = converter.genLocation(clause.source);
- 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::Priority>(&clause.u) &&
- !std::get_if<Fortran::parser::OmpClause::Reduction>(&clause.u) &&
- !std::get_if<Fortran::parser::OmpClause::Depend>(&clause.u) &&
- !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) &&
- !std::get_if<Fortran::parser::OmpClause::Shared>(&clause.u) &&
- !std::get_if<Fortran::parser::OmpClause::Threads>(&clause.u) &&
- !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) &&
- !std::get_if<Fortran::parser::OmpClause::NumTeams>(&clause.u)) {
- TODO(clauseLocation, "OpenMP Block construct clause");
+static void removeStoreOp(mlir::Operation *reductionOp, mlir::Value symVal) {
+ for (mlir::Operation *reductionOpUse : reductionOp->getUsers()) {
+ if (auto convertReduction =
+ mlir::dyn_cast<fir::ConvertOp>(reductionOpUse)) {
+ for (mlir::Operation *convertReductionUse :
+ convertReduction.getRes().getUsers()) {
+ if (auto storeOp = mlir::dyn_cast<fir::StoreOp>(convertReductionUse)) {
+ if (storeOp.getMemref() == symVal)
+ storeOp.erase();
+ }
+ if (auto assignOp =
+ mlir::dyn_cast<hlfir::AssignOp>(convertReductionUse)) {
+ if (assignOp.getLhs() == symVal)
+ assignOp.erase();
+ }
+ }
}
}
+}
- for (const auto &clause : endClauseList.v) {
- mlir::Location clauseLocation = converter.genLocation(clause.source);
- if (!std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u))
- TODO(clauseLocation, "OpenMP Block construct clause");
- }
-
- mlir::Location currentLocation = converter.genLocation(directive.source);
- switch (directive.v) {
- case llvm::omp::Directive::OMPD_master:
- genMasterOp(converter, eval, currentLocation);
- break;
- case llvm::omp::Directive::OMPD_ordered:
- genOrderedRegionOp(converter, eval, currentLocation);
- break;
- case llvm::omp::Directive::OMPD_parallel:
- genParallelOp(converter, eval, currentLocation, beginClauseList);
- break;
- case llvm::omp::Directive::OMPD_single:
- genSingleOp(converter, eval, currentLocation, beginClauseList,
- endClauseList);
- break;
- case llvm::omp::Directive::OMPD_target:
- genTargetOp(converter, eval, semanticsContext, currentLocation,
- beginClauseList, directive.v);
- break;
- case llvm::omp::Directive::OMPD_target_data:
- genDataOp(converter, eval, semanticsContext, currentLocation,
- beginClauseList);
- break;
- case llvm::omp::Directive::OMPD_task:
- genTaskOp(converter, eval, currentLocation, beginClauseList);
- break;
- case llvm::omp::Directive::OMPD_taskgroup:
- genTaskGroupOp(converter, eval, currentLocation, beginClauseList);
- break;
- case llvm::omp::Directive::OMPD_teams:
- genTeamsOp(converter, eval, currentLocation, beginClauseList,
- /*outerCombined=*/false);
- break;
- case llvm::omp::Directive::OMPD_workshare:
- TODO(currentLocation, "Workshare construct");
- break;
- default: {
- // Codegen for combined directives
- bool combinedDirective = false;
- if ((llvm::omp::allTargetSet & llvm::omp::blockConstructSet)
- .test(directive.v)) {
- genTargetOp(converter, eval, semanticsContext, currentLocation,
- beginClauseList, directive.v, /*outerCombined=*/true);
- combinedDirective = true;
- }
- if ((llvm::omp::allTeamsSet & llvm::omp::blockConstructSet)
- .test(directive.v)) {
- genTeamsOp(converter, eval, currentLocation, beginClauseList);
- combinedDirective = true;
- }
- if ((llvm::omp::allParallelSet & llvm::omp::blockConstructSet)
- .test(directive.v)) {
- bool outerCombined =
- directive.v != llvm::omp::Directive::OMPD_target_parallel;
- genParallelOp(converter, eval, currentLocation, beginClauseList,
- outerCombined);
- combinedDirective = true;
- }
- if ((llvm::omp::workShareSet & llvm::omp::blockConstructSet)
- .test(directive.v)) {
- TODO(currentLocation, "Workshare construct");
- combinedDirective = true;
+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;
}
- if (!combinedDirective)
- TODO(currentLocation, "Unhandled block directive (" +
- llvm::omp::getOpenMPDirectiveName(directive.v) +
- ")");
- break;
- }
}
+ return nullptr;
}
-static void
-genOMP(Fortran::lower::AbstractConverter &converter,
- Fortran::lower::pft::Evaluation &eval,
- 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);
- if (std::get<std::optional<Fortran::parser::Name>>(cd.t).has_value()) {
- name =
- std::get<std::optional<Fortran::parser::Name>>(cd.t).value().ToString();
- }
-
- const auto &clauseList = std::get<Fortran::parser::OmpClauseList>(cd.t);
- ClauseProcessor(converter, clauseList).processHint(hintClauseOp);
-
- mlir::omp::CriticalOp criticalOp = [&]() {
- if (name.empty()) {
- return firOpBuilder.create<mlir::omp::CriticalOp>(
- currentLocation, mlir::FlatSymbolRefAttr());
+static mlir::Operation *
+findReductionChain(mlir::Value loadVal, mlir::Value *reductionVal = nullptr) {
+ for (mlir::OpOperand &loadOperand : loadVal.getUses()) {
+ if (mlir::Operation *reductionOp = loadOperand.getOwner()) {
+ if (auto convertOp = mlir::dyn_cast<fir::ConvertOp>(reductionOp)) {
+ for (mlir::OpOperand &convertOperand : convertOp.getRes().getUses()) {
+ if (mlir::Operation *reductionOp = convertOperand.getOwner())
+ return reductionOp;
+ }
+ }
+ for (mlir::OpOperand &reductionOperand : reductionOp->getUses()) {
+ if (auto store =
+ mlir::dyn_cast<fir::StoreOp>(reductionOperand.getOwner())) {
+ if (store.getMemref() == *reductionVal) {
+ store.erase();
+ return reductionOp;
+ }
+ }
+ if (auto assign =
+ mlir::dyn_cast<hlfir::AssignOp>(reductionOperand.getOwner())) {
+ if (assign.getLhs() == *reductionVal) {
+ assign.erase();
+ return reductionOp;
+ }
+ }
+ }
}
- 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<mlir::omp::CriticalOp>(criticalOp, converter, currentLocation,
- eval);
+ }
+ return nullptr;
}
-static void
-genOMP(Fortran::lower::AbstractConverter &converter,
- Fortran::lower::pft::Evaluation &eval,
- const Fortran::parser::OpenMPSectionConstruct §ionConstruct) {
- mlir::Location currentLocation = converter.getCurrentLocation();
- const Fortran::parser::OpenMPConstruct *parentOmpConstruct =
- eval.parentConstruct->getIf<Fortran::parser::OpenMPConstruct>();
- assert(parentOmpConstruct &&
- "No enclosing parent OpenMPConstruct on SECTION construct");
- const Fortran::parser::OpenMPSectionsConstruct *sectionsConstruct =
- std::get_if<Fortran::parser::OpenMPSectionsConstruct>(
- &parentOmpConstruct->u);
- assert(sectionsConstruct && "SECTION construct must have parent"
- "SECTIONS construct");
- const Fortran::parser::OmpClauseList §ionsClauseList =
- std::get<Fortran::parser::OmpClauseList>(
- std::get<Fortran::parser::OmpBeginSectionsDirective>(
- sectionsConstruct->t)
- .t);
- // Currently only private/firstprivate clause is handled, and
- // all privatization is done within `omp.section` operations.
- genOpWithBody<mlir::omp::SectionOp>(converter, eval, currentLocation,
- /*outerCombined=*/false,
- §ionsClauseList);
+// 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
+static fir::ConvertOp 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;
}
+// 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
+// operation types.
+// TODO: Generate the reduction operation during lowering instead of creating
+// and removing operations since this is not a robust approach. Also, removing
+// ops in the builder (instead of a rewriter) is probably not the best approach.
static void
-genOMP(Fortran::lower::AbstractConverter &converter,
- Fortran::lower::pft::Evaluation &eval,
- const Fortran::parser::OpenMPSectionsConstruct §ionsConstruct) {
- mlir::Location currentLocation = converter.getCurrentLocation();
- llvm::SmallVector<mlir::Value> allocateOperands, allocatorOperands;
- mlir::UnitAttr nowaitClauseOperand;
- const auto &beginSectionsDirective =
- std::get<Fortran::parser::OmpBeginSectionsDirective>(sectionsConstruct.t);
- const auto §ionsClauseList =
- std::get<Fortran::parser::OmpClauseList>(beginSectionsDirective.t);
-
- // Process clauses before optional omp.parallel, so that new variables are
- // allocated outside of the parallel region
- ClauseProcessor cp(converter, sectionsClauseList);
- cp.processSectionsReduction(currentLocation);
- cp.processAllocate(allocatorOperands, allocateOperands);
-
- llvm::omp::Directive dir =
- std::get<Fortran::parser::OmpSectionsDirective>(beginSectionsDirective.t)
- .v;
-
- // Parallel wrapper of PARALLEL SECTIONS construct
- if (dir == llvm::omp::Directive::OMPD_parallel_sections) {
- genParallelOp(converter, eval, currentLocation, sectionsClauseList,
- /*outerCombined=*/true);
- } else {
- const auto &endSectionsDirective =
- std::get<Fortran::parser::OmpEndSectionsDirective>(sectionsConstruct.t);
- const auto &endSectionsClauseList =
- std::get<Fortran::parser::OmpClauseList>(endSectionsDirective.t);
- ClauseProcessor(converter, endSectionsClauseList)
- .processNowait(nowaitClauseOperand);
- }
+genOpenMPReduction(Fortran::lower::AbstractConverter &converter,
+ const Fortran::parser::OmpClauseList &clauseList) {
+ fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
- // SECTIONS construct
- genOpWithBody<mlir::omp::SectionsOp>(converter, eval, currentLocation,
- /*outerCombined=*/false,
- /*clauseList=*/nullptr,
- /*reduction_vars=*/mlir::ValueRange(),
- /*reductions=*/nullptr, allocateOperands,
- allocatorOperands, nowaitClauseOperand);
-}
-
-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) {
- mlir::Location loc = converter.genLocation(atomicRead.source);
- Fortran::lower::genOmpAccAtomicRead<
- Fortran::parser::OmpAtomicRead,
- Fortran::parser::OmpAtomicClauseList>(converter, atomicRead,
- loc);
- },
- [&](const Fortran::parser::OmpAtomicWrite &atomicWrite) {
- mlir::Location loc = converter.genLocation(atomicWrite.source);
- Fortran::lower::genOmpAccAtomicWrite<
- Fortran::parser::OmpAtomicWrite,
- Fortran::parser::OmpAtomicClauseList>(converter, atomicWrite,
- loc);
- },
- [&](const Fortran::parser::OmpAtomic &atomicConstruct) {
- mlir::Location loc = converter.genLocation(atomicConstruct.source);
- Fortran::lower::genOmpAtomic<Fortran::parser::OmpAtomic,
- Fortran::parser::OmpAtomicClauseList>(
- converter, atomicConstruct, loc);
- },
- [&](const Fortran::parser::OmpAtomicUpdate &atomicUpdate) {
- mlir::Location loc = converter.genLocation(atomicUpdate.source);
- Fortran::lower::genOmpAccAtomicUpdate<
- Fortran::parser::OmpAtomicUpdate,
- Fortran::parser::OmpAtomicClauseList>(converter, atomicUpdate,
- loc);
- },
- [&](const Fortran::parser::OmpAtomicCapture &atomicCapture) {
- mlir::Location loc = converter.genLocation(atomicCapture.source);
- Fortran::lower::genOmpAccAtomicCapture<
- Fortran::parser::OmpAtomicCapture,
- Fortran::parser::OmpAtomicClauseList>(converter, atomicCapture,
- loc);
- },
- },
- 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();
- mlir::omp::DeclareTargetDeviceType deviceType = getDeclareTargetInfo(
- converter, eval, declareTargetConstruct, symbolAndClause);
-
- 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));
- }
-}
-
-//===----------------------------------------------------------------------===//
-// 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::semantics::SemanticsContext &semanticsContext,
- Fortran::lower::pft::Evaluation &eval,
- const Fortran::parser::OpenMPConstruct &ompConstruct) {
- std::visit(
- common::visitors{
- [&](const Fortran::parser::OpenMPStandaloneConstruct
- &standaloneConstruct) {
- genOMP(converter, eval, semanticsContext, standaloneConstruct);
- },
- [&](const Fortran::parser::OpenMPSectionsConstruct
- §ionsConstruct) {
- genOMP(converter, eval, sectionsConstruct);
- },
- [&](const Fortran::parser::OpenMPSectionConstruct §ionConstruct) {
- genOMP(converter, eval, sectionConstruct);
- },
- [&](const Fortran::parser::OpenMPLoopConstruct &loopConstruct) {
- genOMP(converter, eval, semanticsContext, loopConstruct);
- },
- [&](const Fortran::parser::OpenMPDeclarativeAllocate
- &execAllocConstruct) {
- TODO(converter.getCurrentLocation(), "OpenMPDeclarativeAllocate");
- },
- [&](const Fortran::parser::OpenMPExecutableAllocate
- &execAllocConstruct) {
- TODO(converter.getCurrentLocation(), "OpenMPExecutableAllocate");
- },
- [&](const Fortran::parser::OpenMPAllocatorsConstruct
- &allocsConstruct) {
- TODO(converter.getCurrentLocation(), "OpenMPAllocatorsConstruct");
- },
- [&](const Fortran::parser::OpenMPBlockConstruct &blockConstruct) {
- genOMP(converter, eval, semanticsContext, blockConstruct);
- },
- [&](const Fortran::parser::OpenMPAtomicConstruct &atomicConstruct) {
- genOMP(converter, eval, atomicConstruct);
- },
- [&](const Fortran::parser::OpenMPCriticalConstruct
- &criticalConstruct) {
- genOMP(converter, eval, criticalConstruct);
- },
- },
- ompConstruct.u);
-}
-
-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) {
- // Requires directives are gathered and processed in semantics and
- // then combined in the lowering bridge before triggering codegen
- // just once. Hence, there is no need to lower each individual
- // occurrence here.
- },
- [&](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);
-}
-
-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 1;
-}
-
-void Fortran::lower::genThreadprivateOp(
- Fortran::lower::AbstractConverter &converter,
- const Fortran::lower::pft::Variable &var) {
- fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
- mlir::Location currentLocation = converter.getCurrentLocation();
-
- const Fortran::semantics::Symbol &sym = var.getSymbol();
- mlir::Value symThreadprivateValue;
- if (const Fortran::semantics::Symbol *common =
- Fortran::semantics::FindCommonBlockContaining(sym.GetUltimate())) {
- mlir::Value commonValue = converter.getSymbolAddress(*common);
- if (mlir::isa<mlir::omp::ThreadprivateOp>(commonValue.getDefiningOp())) {
- // Generate ThreadprivateOp for a common block instead of its members and
- // only do it once for a common block.
- return;
- }
- // Generate ThreadprivateOp and rebind the common block.
- mlir::Value commonThreadprivateValue =
- firOpBuilder.create<mlir::omp::ThreadprivateOp>(
- currentLocation, commonValue.getType(), commonValue);
- converter.bindSymbol(*common, commonThreadprivateValue);
- // Generate the threadprivate value for the common block member.
- symThreadprivateValue = genCommonBlockMember(converter, currentLocation,
- sym, commonThreadprivateValue);
- } else if (!var.isGlobal()) {
- // Non-global variable which can be in threadprivate directive must be one
- // variable in main program, and it has implicit SAVE attribute. Take it as
- // with SAVE attribute, so to create GlobalOp for it to simplify the
- // translation to LLVM IR.
- fir::GlobalOp global = globalInitialization(converter, firOpBuilder, sym,
- var, currentLocation);
-
- mlir::Value symValue = firOpBuilder.create<fir::AddrOfOp>(
- currentLocation, global.resultType(), global.getSymbol());
- symThreadprivateValue = firOpBuilder.create<mlir::omp::ThreadprivateOp>(
- currentLocation, symValue.getType(), symValue);
- } else {
- mlir::Value symValue = converter.getSymbolAddress(sym);
-
- // The symbol may be use-associated multiple times, and nothing needs to be
- // done after the original symbol is mapped to the threadprivatized value
- // for the first time. Use the threadprivatized value directly.
- mlir::Operation *op;
- if (auto declOp = symValue.getDefiningOp<hlfir::DeclareOp>())
- op = declOp.getMemref().getDefiningOp();
- else
- op = symValue.getDefiningOp();
- if (mlir::isa<mlir::omp::ThreadprivateOp>(op))
- return;
-
- symThreadprivateValue = firOpBuilder.create<mlir::omp::ThreadprivateOp>(
- currentLocation, symValue.getType(), symValue);
- }
-
- fir::ExtendedValue sexv = converter.getSymbolExtendedValue(sym);
- fir::ExtendedValue symThreadprivateExv =
- getExtendedValue(sexv, symThreadprivateValue);
- converter.bindSymbol(sym, symThreadprivateExv);
-}
-
-// This function replicates threadprivate's behaviour of generating
-// an internal fir.GlobalOp for non-global variables in the main program
-// that have the implicit SAVE attribute, to simplifiy LLVM-IR and MLIR
-// generation.
-void Fortran::lower::genDeclareTargetIntGlobal(
- Fortran::lower::AbstractConverter &converter,
- const Fortran::lower::pft::Variable &var) {
- if (!var.isGlobal()) {
- // A non-global variable which can be in a declare target directive must
- // be a variable in the main program, and it has the implicit SAVE
- // attribute. We create a GlobalOp for it to simplify the translation to
- // LLVM IR.
- globalInitialization(converter, converter.getFirOpBuilder(),
- var.getSymbol(), var, converter.getCurrentLocation());
- }
-}
-
-// 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
-// operation types.
-// TODO: Generate the reduction operation during lowering instead of creating
-// and removing operations since this is not a robust approach. Also, removing
-// ops in the builder (instead of a rewriter) is probably not the best approach.
-void Fortran::lower::genOpenMPReduction(
- Fortran::lower::AbstractConverter &converter,
- const Fortran::parser::OmpClauseList &clauseList) {
- fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
-
- 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 (const auto *reductionOp =
- std::get_if<Fortran::parser::DefinedOperator>(&redOperator.u)) {
- const auto &intrinsicOp{
- std::get<Fortran::parser::DefinedOperator::IntrinsicOperator>(
- reductionOp->u)};
+ 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 (const auto *reductionOp =
+ std::get_if<Fortran::parser::DefinedOperator>(&redOperator.u)) {
+ const auto &intrinsicOp{
+ std::get<Fortran::parser::DefinedOperator::IntrinsicOperator>(
+ reductionOp->u)};
switch (intrinsicOp) {
case Fortran::parser::DefinedOperator::IntrinsicOperator::Add:
@@ -3699,199 +3177,769 @@ void Fortran::lower::genOpenMPReduction(
if (reductionOp == nullptr)
continue;
- if (redName == "max" || redName == "min") {
- assert(mlir::isa<mlir::arith::SelectOp>(reductionOp) &&
- "Selection Op not found in reduction intrinsic");
- mlir::Operation *compareOp =
- getCompareFromReductionOp(reductionOp, loadVal);
- updateReduction(compareOp, firOpBuilder, loadVal,
- reductionVal);
- }
- if (redName == "ior" || redName == "ieor" ||
- redName == "iand") {
+ if (redName == "max" || redName == "min") {
+ assert(mlir::isa<mlir::arith::SelectOp>(reductionOp) &&
+ "Selection Op not found in reduction intrinsic");
+ mlir::Operation *compareOp =
+ getCompareFromReductionOp(reductionOp, loadVal);
+ updateReduction(compareOp, firOpBuilder, loadVal,
+ reductionVal);
+ }
+ if (redName == "ior" || redName == "ieor" ||
+ redName == "iand") {
+
+ updateReduction(reductionOp, firOpBuilder, loadVal,
+ reductionVal);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+// FC
+static bool isOpenMPDeviceDeclareTarget(
+ Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ const Fortran::parser::OpenMPDeclarativeConstruct &ompDecl) {
+ return std::visit(
+ Fortran::common::visitors{
+ [&](const Fortran::parser::OpenMPDeclareTargetConstruct &ompReq) {
+ mlir::omp::DeclareTargetDeviceType targetType =
+ getDeclareTargetFunctionDevice(converter, eval, ompReq)
+ .value_or(mlir::omp::DeclareTargetDeviceType::host);
+ return targetType != mlir::omp::DeclareTargetDeviceType::host;
+ },
+ [&](const auto &) { return false; },
+ },
+ ompDecl.u);
+}
+
+// FC
+static void genOpenMPRequires(mlir::Operation *mod,
+ const Fortran::semantics::Symbol *symbol) {
+ using MlirRequires = mlir::omp::ClauseRequires;
+ using SemaRequires = Fortran::semantics::WithOmpDeclarative::RequiresFlag;
+
+ if (auto offloadMod =
+ llvm::dyn_cast<mlir::omp::OffloadModuleInterface>(mod)) {
+ Fortran::semantics::WithOmpDeclarative::RequiresFlags semaFlags;
+ if (symbol) {
+ Fortran::common::visit(
+ [&](const auto &details) {
+ if constexpr (std::is_base_of_v<
+ Fortran::semantics::WithOmpDeclarative,
+ std::decay_t<decltype(details)>>) {
+ if (details.has_ompRequires())
+ semaFlags = *details.ompRequires();
+ }
+ },
+ symbol->details());
+ }
+
+ MlirRequires mlirFlags = MlirRequires::none;
+ if (semaFlags.test(SemaRequires::ReverseOffload))
+ mlirFlags = mlirFlags | MlirRequires::reverse_offload;
+ if (semaFlags.test(SemaRequires::UnifiedAddress))
+ mlirFlags = mlirFlags | MlirRequires::unified_address;
+ if (semaFlags.test(SemaRequires::UnifiedSharedMemory))
+ mlirFlags = mlirFlags | MlirRequires::unified_shared_memory;
+ if (semaFlags.test(SemaRequires::DynamicAllocators))
+ mlirFlags = mlirFlags | MlirRequires::dynamic_allocators;
+
+ offloadMod.setRequires(mlirFlags);
+ }
+}
+
+namespace Fortran::lower {
+
+template <>
+void OpenMPMixin<FirConverter>::genOMP(
+ Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::parser::OpenMPSimpleStandaloneConstruct &simple) {
+ fir::FirOpBuilder &builder = converter.getFirOpBuilder();
+
+ const auto &directive =
+ std::get<Fortran::parser::OmpSimpleStandaloneDirective>(simple.t);
+ const auto &opClauseList = std::get<Fortran::parser::OmpClauseList>(simple.t);
+ mlir::Location currentLocation = converter.genLocation(directive.source);
+
+ switch (directive.v) {
+ default:
+ break;
+ case llvm::omp::Directive::OMPD_barrier:
+ builder.create<mlir::omp::BarrierOp>(currentLocation);
+ break;
+ case llvm::omp::Directive::OMPD_taskwait:
+ ClauseProcessor(converter, opClauseList)
+ .processTODO<Fortran::parser::OmpClause::Depend,
+ Fortran::parser::OmpClause::Nowait>(
+ currentLocation, llvm::omp::Directive::OMPD_taskwait);
+ builder.create<mlir::omp::TaskwaitOp>(currentLocation);
+ break;
+ case llvm::omp::Directive::OMPD_taskyield:
+ builder.create<mlir::omp::TaskyieldOp>(currentLocation);
+ break;
+ case llvm::omp::Directive::OMPD_target_data:
+ genDataOp(converter, eval, semaCtx, currentLocation, opClauseList);
+ break;
+ case llvm::omp::Directive::OMPD_target_enter_data:
+ genEnterExitDataOp<mlir::omp::EnterDataOp>(converter, semaCtx,
+ currentLocation, opClauseList);
+ break;
+ case llvm::omp::Directive::OMPD_target_exit_data:
+ genEnterExitDataOp<mlir::omp::ExitDataOp>(converter, semaCtx,
+ currentLocation, opClauseList);
+ break;
+ case llvm::omp::Directive::OMPD_target_update:
+ TODO(currentLocation, "OMPD_target_update");
+ case llvm::omp::Directive::OMPD_ordered:
+ TODO(currentLocation, "OMPD_ordered");
+ }
+}
+
+template <>
+void OpenMPMixin<FirConverter>::genOMP(
+ Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::parser::OpenMPFlushConstruct &flush) {
+ llvm::SmallVector<mlir::Value, 4> operandRange;
+ if (const auto &ompObjectList =
+ std::get<std::optional<Fortran::parser::OmpObjectList>>(flush.t))
+ genObjectList(*ompObjectList, converter, operandRange);
+
+ const auto &memOrderClause =
+ std::get<std::optional<std::list<Fortran::parser::OmpMemoryOrderClause>>>(
+ flush.t);
+
+ if (memOrderClause && memOrderClause->size() > 0)
+ TODO(converter.getCurrentLocation(), "Handle OmpMemoryOrderClause");
+ converter.getFirOpBuilder().create<mlir::omp::FlushOp>(
+ converter.getCurrentLocation(), operandRange);
+}
+
+template <>
+void OpenMPMixin<FirConverter>::genOMP(
+ Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::parser::OpenMPCancelConstruct &) {
+ TODO(converter.getCurrentLocation(), "OpenMPCancelConstruct");
+}
+
+template <>
+void OpenMPMixin<FirConverter>::genOMP(
+ Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::parser::OpenMPCancellationPointConstruct &) {
+ TODO(converter.getCurrentLocation(), "OpenMPCancellationPointConstruct");
+}
+
+template <>
+void OpenMPMixin<FirConverter>::genOMP(
+ Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::parser::OpenMPStandaloneConstruct &standalone) {
+ // This will instantiate "genOMP" templates, so make sure the actual
+ // implementations for FirConverter (specializations) are placed earlier on.
+ std::visit([&](auto &&constr) { genOMP(*This(), eval, semaCtx, constr); },
+ standalone.u);
+}
+
+template <>
+void OpenMPMixin<FirConverter>::genOMP(
+ Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::parser::OpenMPSectionsConstruct §ions) {
+ mlir::Location currentLocation = converter.getCurrentLocation();
+ llvm::SmallVector<mlir::Value> allocateOperands, allocatorOperands;
+ mlir::UnitAttr nowaitClauseOperand;
+ const auto &beginSectionsDirective =
+ std::get<Fortran::parser::OmpBeginSectionsDirective>(sections.t);
+ const auto §ionsClauseList =
+ std::get<Fortran::parser::OmpClauseList>(beginSectionsDirective.t);
+
+ // Process clauses before optional omp.parallel, so that new variables are
+ // allocated outside of the parallel region
+ ClauseProcessor cp(converter, sectionsClauseList);
+ cp.processSectionsReduction(currentLocation);
+ cp.processAllocate(allocatorOperands, allocateOperands);
+
+ llvm::omp::Directive dir =
+ std::get<Fortran::parser::OmpSectionsDirective>(beginSectionsDirective.t)
+ .v;
+
+ // Parallel wrapper of PARALLEL SECTIONS construct
+ if (dir == llvm::omp::Directive::OMPD_parallel_sections) {
+ genParallelOp(converter, eval, currentLocation, sectionsClauseList,
+ /*outerCombined=*/true);
+ } else {
+ const auto &endSectionsDirective =
+ std::get<Fortran::parser::OmpEndSectionsDirective>(sections.t);
+ const auto &endSectionsClauseList =
+ std::get<Fortran::parser::OmpClauseList>(endSectionsDirective.t);
+ ClauseProcessor(converter, endSectionsClauseList)
+ .processNowait(nowaitClauseOperand);
+ }
+
+ // SECTIONS construct
+ genOpWithBody<mlir::omp::SectionsOp>(converter, eval, currentLocation,
+ /*outerCombined=*/false,
+ /*clauseList=*/nullptr,
+ /*reduction_vars=*/mlir::ValueRange(),
+ /*reductions=*/nullptr, allocateOperands,
+ allocatorOperands, nowaitClauseOperand);
+}
+
+template <>
+void OpenMPMixin<FirConverter>::genOMP(
+ Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::parser::OpenMPSectionConstruct & /*unused*/) {
+ fir::FirOpBuilder &builder = converter.getFirOpBuilder();
+ mlir::Location currentLocation = converter.getCurrentLocation();
+
+ const Fortran::parser::OpenMPConstruct *parentOmpConstruct =
+ eval.parentConstruct->getIf<Fortran::parser::OpenMPConstruct>();
+ assert(parentOmpConstruct &&
+ "No enclosing parent OpenMPConstruct on SECTION construct");
+ const Fortran::parser::OpenMPSectionsConstruct *sectionsConstruct =
+ std::get_if<Fortran::parser::OpenMPSectionsConstruct>(
+ &parentOmpConstruct->u);
+ assert(sectionsConstruct && "SECTION construct must have parent"
+ "SECTIONS construct");
+ const Fortran::parser::OmpClauseList §ionsClauseList =
+ std::get<Fortran::parser::OmpClauseList>(
+ std::get<Fortran::parser::OmpBeginSectionsDirective>(
+ sectionsConstruct->t)
+ .t);
+ // Currently only private/firstprivate clause is handled, and
+ // all privatization is done within `omp.section` operations.
+ mlir::omp::SectionOp sectionOp =
+ builder.create<mlir::omp::SectionOp>(currentLocation);
+ createBodyOfOp<mlir::omp::SectionOp>(sectionOp, converter, currentLocation,
+ eval, §ionsClauseList);
+}
+
+template <>
+void OpenMPMixin<FirConverter>::genOMP(
+ Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::parser::OpenMPLoopConstruct &loop) {
+ fir::FirOpBuilder &builder = converter.getFirOpBuilder();
+
+ llvm::SmallVector<mlir::Value> lowerBound, upperBound, step, linearVars,
+ linearStepVars, reductionVars;
+ mlir::Value scheduleChunkClauseOperand;
+ mlir::IntegerAttr orderedClauseOperand;
+ mlir::omp::ClauseOrderKindAttr orderClauseOperand;
+ mlir::omp::ClauseScheduleKindAttr scheduleValClauseOperand;
+ mlir::omp::ScheduleModifierAttr scheduleModClauseOperand;
+ mlir::UnitAttr nowaitClauseOperand, scheduleSimdClauseOperand;
+ llvm::SmallVector<mlir::Attribute> reductionDeclSymbols;
+ Fortran::lower::StatementContext stmtCtx;
+ std::size_t loopVarTypeSize;
+ llvm::SmallVector<const Fortran::semantics::Symbol *> iv;
+
+ const auto &beginLoopDirective =
+ std::get<Fortran::parser::OmpBeginLoopDirective>(loop.t);
+ const auto &loopOpClauseList =
+ std::get<Fortran::parser::OmpClauseList>(beginLoopDirective.t);
+ mlir::Location currentLocation =
+ converter.genLocation(beginLoopDirective.source);
+ const auto ompDirective =
+ std::get<Fortran::parser::OmpLoopDirective>(beginLoopDirective.t).v;
+
+ bool validDirective = false;
+ if (llvm::omp::topTaskloopSet.test(ompDirective)) {
+ validDirective = true;
+ TODO(currentLocation, "Taskloop construct");
+ } else {
+ // Create omp.{target, teams, distribute, parallel} nested operations
+ if ((llvm::omp::allTargetSet & llvm::omp::loopConstructSet)
+ .test(ompDirective)) {
+ validDirective = true;
+ genTargetOp(converter, eval, semaCtx, currentLocation, loopOpClauseList,
+ ompDirective, /*outerCombined=*/true);
+ }
+ if ((llvm::omp::allTeamsSet & llvm::omp::loopConstructSet)
+ .test(ompDirective)) {
+ validDirective = true;
+ genTeamsOp(converter, eval, currentLocation, loopOpClauseList,
+ /*outerCombined=*/true);
+ }
+ if (llvm::omp::allDistributeSet.test(ompDirective)) {
+ validDirective = true;
+ TODO(currentLocation, "Distribute construct");
+ }
+ if ((llvm::omp::allParallelSet & llvm::omp::loopConstructSet)
+ .test(ompDirective)) {
+ validDirective = true;
+ genParallelOp(converter, eval, currentLocation, loopOpClauseList,
+ /*outerCombined=*/true);
+ }
+ }
+ if ((llvm::omp::allDoSet | llvm::omp::allSimdSet).test(ompDirective))
+ validDirective = true;
+
+ if (!validDirective) {
+ TODO(currentLocation, "Unhandled loop directive (" +
+ llvm::omp::getOpenMPDirectiveName(ompDirective) +
+ ")");
+ }
+
+ DataSharingProcessor dsp(converter, loopOpClauseList, eval);
+ dsp.processStep1();
+
+ ClauseProcessor cp(converter, loopOpClauseList);
+ cp.processCollapse(currentLocation, eval, lowerBound, upperBound, step, iv,
+ loopVarTypeSize);
+ cp.processScheduleChunk(stmtCtx, scheduleChunkClauseOperand);
+ cp.processReduction(currentLocation, reductionVars, reductionDeclSymbols);
+ cp.processTODO<Fortran::parser::OmpClause::Linear,
+ Fortran::parser::OmpClause::Order>(currentLocation,
+ ompDirective);
+
+ // The types of lower bound, upper bound, and step are converted into the
+ // type of the loop variable if necessary.
+ mlir::Type loopVarType = getLoopVarType(converter, loopVarTypeSize);
+ for (unsigned it = 0; it < (unsigned)lowerBound.size(); it++) {
+ lowerBound[it] =
+ builder.createConvert(currentLocation, loopVarType, lowerBound[it]);
+ upperBound[it] =
+ builder.createConvert(currentLocation, loopVarType, upperBound[it]);
+ step[it] = builder.createConvert(currentLocation, loopVarType, step[it]);
+ }
+
+ // 2.9.3.1 SIMD construct
+ if (llvm::omp::allSimdSet.test(ompDirective)) {
+ llvm::SmallVector<mlir::Value> alignedVars, nontemporalVars;
+ mlir::Value ifClauseOperand;
+ mlir::IntegerAttr simdlenClauseOperand, safelenClauseOperand;
+ cp.processIf(Fortran::parser::OmpIfClause::DirectiveNameModifier::Simd,
+ ifClauseOperand);
+ cp.processSimdlen(simdlenClauseOperand);
+ cp.processSafelen(safelenClauseOperand);
+ cp.processTODO<Fortran::parser::OmpClause::Aligned,
+ Fortran::parser::OmpClause::Allocate,
+ Fortran::parser::OmpClause::Nontemporal>(currentLocation,
+ ompDirective);
+
+ mlir::TypeRange resultType;
+ auto simdLoopOp = builder.create<mlir::omp::SimdLoopOp>(
+ currentLocation, resultType, lowerBound, upperBound, step, alignedVars,
+ /*alignment_values=*/nullptr, ifClauseOperand, nontemporalVars,
+ orderClauseOperand, simdlenClauseOperand, safelenClauseOperand,
+ /*inclusive=*/builder.getUnitAttr());
+ createBodyOfOp<mlir::omp::SimdLoopOp>(
+ simdLoopOp, converter, currentLocation, eval, &loopOpClauseList, iv,
+ /*outer=*/false, &dsp);
+ return;
+ }
+
+ auto wsLoopOp = builder.create<mlir::omp::WsLoopOp>(
+ currentLocation, lowerBound, upperBound, step, linearVars, linearStepVars,
+ reductionVars,
+ reductionDeclSymbols.empty()
+ ? nullptr
+ : mlir::ArrayAttr::get(builder.getContext(), reductionDeclSymbols),
+ scheduleValClauseOperand, scheduleChunkClauseOperand,
+ /*schedule_modifiers=*/nullptr,
+ /*simd_modifier=*/nullptr, nowaitClauseOperand, orderedClauseOperand,
+ orderClauseOperand,
+ /*inclusive=*/builder.getUnitAttr());
+
+ // Handle attribute based clauses.
+ 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
+ // !$omp do
+ // <...>
+ // !$omp end do nowait
+ if (const auto &endClauseList =
+ std::get<std::optional<Fortran::parser::OmpEndLoopDirective>>(
+ loop.t)) {
+ const auto &clauseList =
+ std::get<Fortran::parser::OmpClauseList>((*endClauseList).t);
+ if (ClauseProcessor(converter, clauseList)
+ .processNowait(nowaitClauseOperand))
+ wsLoopOp.setNowaitAttr(nowaitClauseOperand);
+ }
+
+ createBodyOfOp<mlir::omp::WsLoopOp>(wsLoopOp, converter, currentLocation,
+ eval, &loopOpClauseList, iv,
+ /*outer=*/false, &dsp);
+}
+
+template <>
+void OpenMPMixin<FirConverter>::genOMP(
+ Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::parser::OpenMPBlockConstruct &block) {
+ const auto &beginBlockDirective =
+ std::get<Fortran::parser::OmpBeginBlockDirective>(block.t);
+ const auto &endBlockDirective =
+ std::get<Fortran::parser::OmpEndBlockDirective>(block.t);
+ const auto &directive =
+ std::get<Fortran::parser::OmpBlockDirective>(beginBlockDirective.t);
+ const auto &beginClauseList =
+ std::get<Fortran::parser::OmpClauseList>(beginBlockDirective.t);
+ const auto &endClauseList =
+ std::get<Fortran::parser::OmpClauseList>(endBlockDirective.t);
+
+ for (const Fortran::parser::OmpClause &clause : beginClauseList.v) {
+ mlir::Location clauseLocation = converter.genLocation(clause.source);
+ 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::Priority>(&clause.u) &&
+ !std::get_if<Fortran::parser::OmpClause::Reduction>(&clause.u) &&
+ !std::get_if<Fortran::parser::OmpClause::Depend>(&clause.u) &&
+ !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) &&
+ !std::get_if<Fortran::parser::OmpClause::Shared>(&clause.u) &&
+ !std::get_if<Fortran::parser::OmpClause::Threads>(&clause.u) &&
+ !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) &&
+ !std::get_if<Fortran::parser::OmpClause::NumTeams>(&clause.u)) {
+ TODO(clauseLocation, "OpenMP Block construct clause");
+ }
+ }
+
+ for (const auto &clause : endClauseList.v) {
+ mlir::Location clauseLocation = converter.genLocation(clause.source);
+ if (!std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u))
+ TODO(clauseLocation, "OpenMP Block construct clause");
+ }
+
+ mlir::Location currentLocation = converter.genLocation(directive.source);
+ switch (directive.v) {
+ case llvm::omp::Directive::OMPD_master:
+ genMasterOp(converter, eval, currentLocation);
+ break;
+ case llvm::omp::Directive::OMPD_ordered:
+ genOrderedRegionOp(converter, eval, currentLocation);
+ break;
+ case llvm::omp::Directive::OMPD_parallel:
+ genParallelOp(converter, eval, currentLocation, beginClauseList);
+ break;
+ case llvm::omp::Directive::OMPD_single:
+ genSingleOp(converter, eval, currentLocation, beginClauseList,
+ endClauseList);
+ break;
+ case llvm::omp::Directive::OMPD_target:
+ genTargetOp(converter, eval, semaCtx, currentLocation, beginClauseList,
+ directive.v);
+ break;
+ case llvm::omp::Directive::OMPD_target_data:
+ genDataOp(converter, eval, semaCtx, currentLocation, beginClauseList);
+ break;
+ case llvm::omp::Directive::OMPD_task:
+ genTaskOp(converter, eval, currentLocation, beginClauseList);
+ break;
+ case llvm::omp::Directive::OMPD_taskgroup:
+ genTaskGroupOp(converter, eval, currentLocation, beginClauseList);
+ break;
+ case llvm::omp::Directive::OMPD_teams:
+ genTeamsOp(converter, eval, currentLocation, beginClauseList,
+ /*outerCombined=*/false);
+ break;
+ case llvm::omp::Directive::OMPD_workshare:
+ TODO(currentLocation, "Workshare construct");
+ break;
+ default: {
+ // Codegen for combined directives
+ bool combinedDirective = false;
+ if ((llvm::omp::allTargetSet & llvm::omp::blockConstructSet)
+ .test(directive.v)) {
+ genTargetOp(converter, eval, semaCtx, currentLocation, beginClauseList,
+ directive.v, /*outerCombined=*/true);
+ combinedDirective = true;
+ }
+ if ((llvm::omp::allTeamsSet & llvm::omp::blockConstructSet)
+ .test(directive.v)) {
+ genTeamsOp(converter, eval, currentLocation, beginClauseList);
+ combinedDirective = true;
+ }
+ if ((llvm::omp::allParallelSet & llvm::omp::blockConstructSet)
+ .test(directive.v)) {
+ bool outerCombined =
+ directive.v != llvm::omp::Directive::OMPD_target_parallel;
+ genParallelOp(converter, eval, currentLocation, beginClauseList,
+ outerCombined);
+ combinedDirective = true;
+ }
+ if ((llvm::omp::workShareSet & llvm::omp::blockConstructSet)
+ .test(directive.v)) {
+ TODO(currentLocation, "Workshare construct");
+ combinedDirective = true;
+ }
+ if (!combinedDirective)
+ TODO(currentLocation, "Unhandled block directive (" +
+ llvm::omp::getOpenMPDirectiveName(directive.v) +
+ ")");
+ break;
+ }
+ }
+}
+
+template <>
+void OpenMPMixin<FirConverter>::genOMP(
+ Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::parser::OpenMPAtomicConstruct &atomic) {
+ std::visit(
+ Fortran::common::visitors{
+ [&](const Fortran::parser::OmpAtomicRead &atomicRead) {
+ mlir::Location loc = converter.genLocation(atomicRead.source);
+ Fortran::lower::genOmpAccAtomicRead<
+ Fortran::parser::OmpAtomicRead,
+ Fortran::parser::OmpAtomicClauseList>(converter, atomicRead,
+ loc);
+ },
+ [&](const Fortran::parser::OmpAtomicWrite &atomicWrite) {
+ mlir::Location loc = converter.genLocation(atomicWrite.source);
+ Fortran::lower::genOmpAccAtomicWrite<
+ Fortran::parser::OmpAtomicWrite,
+ Fortran::parser::OmpAtomicClauseList>(converter, atomicWrite,
+ loc);
+ },
+ [&](const Fortran::parser::OmpAtomic &atomicConstruct) {
+ mlir::Location loc = converter.genLocation(atomicConstruct.source);
+ Fortran::lower::genOmpAtomic<Fortran::parser::OmpAtomic,
+ Fortran::parser::OmpAtomicClauseList>(
+ converter, atomicConstruct, loc);
+ },
+ [&](const Fortran::parser::OmpAtomicUpdate &atomicUpdate) {
+ mlir::Location loc = converter.genLocation(atomicUpdate.source);
+ Fortran::lower::genOmpAccAtomicUpdate<
+ Fortran::parser::OmpAtomicUpdate,
+ Fortran::parser::OmpAtomicClauseList>(converter, atomicUpdate,
+ loc);
+ },
+ [&](const Fortran::parser::OmpAtomicCapture &atomicCapture) {
+ mlir::Location loc = converter.genLocation(atomicCapture.source);
+ Fortran::lower::genOmpAccAtomicCapture<
+ Fortran::parser::OmpAtomicCapture,
+ Fortran::parser::OmpAtomicClauseList>(converter, atomicCapture,
+ loc);
+ },
+ },
+ atomic.u);
+}
+
+template <>
+void OpenMPMixin<FirConverter>::genOMP(
+ Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::parser::OpenMPCriticalConstruct &critical) {
+ fir::FirOpBuilder &builder = converter.getFirOpBuilder();
+ mlir::Location currentLocation = converter.getCurrentLocation();
+ mlir::IntegerAttr hintClauseOp;
+ std::string name;
- updateReduction(reductionOp, firOpBuilder, loadVal,
- reductionVal);
- }
- }
- }
- }
- }
- }
- }
- }
- }
+ const Fortran::parser::OmpCriticalDirective &cd =
+ std::get<Fortran::parser::OmpCriticalDirective>(critical.t);
+ if (std::get<std::optional<Fortran::parser::Name>>(cd.t).has_value()) {
+ name =
+ std::get<std::optional<Fortran::parser::Name>>(cd.t).value().ToString();
}
-}
-mlir::Operation *Fortran::lower::findReductionChain(mlir::Value loadVal,
- mlir::Value *reductionVal) {
- for (mlir::OpOperand &loadOperand : loadVal.getUses()) {
- if (mlir::Operation *reductionOp = loadOperand.getOwner()) {
- if (auto convertOp = mlir::dyn_cast<fir::ConvertOp>(reductionOp)) {
- for (mlir::OpOperand &convertOperand : convertOp.getRes().getUses()) {
- if (mlir::Operation *reductionOp = convertOperand.getOwner())
- return reductionOp;
- }
- }
- for (mlir::OpOperand &reductionOperand : reductionOp->getUses()) {
- if (auto store =
- mlir::dyn_cast<fir::StoreOp>(reductionOperand.getOwner())) {
- if (store.getMemref() == *reductionVal) {
- store.erase();
- return reductionOp;
- }
- }
- if (auto assign =
- mlir::dyn_cast<hlfir::AssignOp>(reductionOperand.getOwner())) {
- if (assign.getLhs() == *reductionVal) {
- assign.erase();
- return reductionOp;
- }
- }
- }
- }
- }
- return nullptr;
-}
+ const auto &clauseList = std::get<Fortran::parser::OmpClauseList>(cd.t);
+ ClauseProcessor(converter, clauseList).processHint(hintClauseOp);
-// 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;
+ mlir::omp::CriticalOp criticalOp = [&]() {
+ if (name.empty()) {
+ return builder.create<mlir::omp::CriticalOp>(currentLocation,
+ mlir::FlatSymbolRefAttr());
}
- }
- return nullptr;
+ mlir::ModuleOp module = builder.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(builder.getContext(), name),
+ hintClauseOp);
+ return builder.create<mlir::omp::CriticalOp>(
+ currentLocation, mlir::FlatSymbolRefAttr::get(builder.getContext(),
+ global.getSymName()));
+ }();
+ createBodyOfOp<mlir::omp::CriticalOp>(criticalOp, converter, currentLocation,
+ eval);
}
-void Fortran::lower::updateReduction(mlir::Operation *op,
- fir::FirOpBuilder &firOpBuilder,
- mlir::Value loadVal,
- mlir::Value reductionVal,
- fir::ConvertOp *convertOp) {
- mlir::OpBuilder::InsertPoint insertPtDel = firOpBuilder.saveInsertionPoint();
- firOpBuilder.setInsertionPoint(op);
-
- mlir::Value reductionOp;
- if (convertOp)
- reductionOp = convertOp->getOperand();
- else if (op->getOperand(0) == loadVal)
- reductionOp = op->getOperand(1);
- else
- reductionOp = op->getOperand(0);
+template <>
+void OpenMPMixin<FirConverter>::genOMP(
+ Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::parser::OpenMPExecutableAllocate &allocate) {
+ TODO(converter.getCurrentLocation(), "OpenMPExecutableAllocate");
+}
- firOpBuilder.create<mlir::omp::ReductionOp>(op->getLoc(), reductionOp,
- reductionVal);
- firOpBuilder.restoreInsertionPoint(insertPtDel);
+template <>
+void OpenMPMixin<FirConverter>::genOMP(
+ Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::parser::OpenMPAllocatorsConstruct &allocators) {
+ TODO(converter.getCurrentLocation(), "OpenMPAllocatorsConstruct");
}
-void Fortran::lower::removeStoreOp(mlir::Operation *reductionOp,
- mlir::Value symVal) {
- for (mlir::Operation *reductionOpUse : reductionOp->getUsers()) {
- if (auto convertReduction =
- mlir::dyn_cast<fir::ConvertOp>(reductionOpUse)) {
- for (mlir::Operation *convertReductionUse :
- convertReduction.getRes().getUsers()) {
- if (auto storeOp = mlir::dyn_cast<fir::StoreOp>(convertReductionUse)) {
- if (storeOp.getMemref() == symVal)
- storeOp.erase();
- }
- if (auto assignOp =
- mlir::dyn_cast<hlfir::AssignOp>(convertReductionUse)) {
- if (assignOp.getLhs() == symVal)
- assignOp.erase();
- }
- }
- }
- }
+template <>
+void OpenMPMixin<FirConverter>::genOMP(
+ Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::parser::OpenMPDeclarativeAllocate &allocate) {
+ TODO(converter.getCurrentLocation(), "OpenMPDeclarativeAllocate");
}
-bool Fortran::lower::isOpenMPTargetConstruct(
- const Fortran::parser::OpenMPConstruct &omp) {
- llvm::omp::Directive dir = llvm::omp::Directive::OMPD_unknown;
- if (const auto *block =
- std::get_if<Fortran::parser::OpenMPBlockConstruct>(&omp.u)) {
- const auto &begin =
- std::get<Fortran::parser::OmpBeginBlockDirective>(block->t);
- dir = std::get<Fortran::parser::OmpBlockDirective>(begin.t).v;
- } else if (const auto *loop =
- std::get_if<Fortran::parser::OpenMPLoopConstruct>(&omp.u)) {
- const auto &begin =
- std::get<Fortran::parser::OmpBeginLoopDirective>(loop->t);
- dir = std::get<Fortran::parser::OmpLoopDirective>(begin.t).v;
- }
- return llvm::omp::allTargetSet.test(dir);
+template <>
+void OpenMPMixin<FirConverter>::genOMP(
+ Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::parser::OpenMPDeclareReductionConstruct &) {
+ TODO(converter.getCurrentLocation(), "OpenMPDeclareReductionConstruct");
}
-bool Fortran::lower::isOpenMPDeviceDeclareTarget(
+template <>
+void OpenMPMixin<FirConverter>::genOMP(
Fortran::lower::AbstractConverter &converter,
Fortran::lower::pft::Evaluation &eval,
- const Fortran::parser::OpenMPDeclarativeConstruct &ompDecl) {
- return std::visit(
- Fortran::common::visitors{
- [&](const Fortran::parser::OpenMPDeclareTargetConstruct &ompReq) {
- mlir::omp::DeclareTargetDeviceType targetType =
- getDeclareTargetFunctionDevice(converter, eval, ompReq)
- .value_or(mlir::omp::DeclareTargetDeviceType::host);
- return targetType != mlir::omp::DeclareTargetDeviceType::host;
- },
- [&](const auto &) { return false; },
- },
- ompDecl.u);
+ Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::parser::OpenMPDeclareSimdConstruct &) {
+ TODO(converter.getCurrentLocation(), "OpenMPDeclareSimdConstruct");
}
-void Fortran::lower::genOpenMPRequires(
- mlir::Operation *mod, const Fortran::semantics::Symbol *symbol) {
- using MlirRequires = mlir::omp::ClauseRequires;
- using SemaRequires = Fortran::semantics::WithOmpDeclarative::RequiresFlag;
+template <>
+void OpenMPMixin<FirConverter>::genOMP(
+ Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::parser::OpenMPDeclareTargetConstruct &declTarget) {
- if (auto offloadMod =
- llvm::dyn_cast<mlir::omp::OffloadModuleInterface>(mod)) {
- Fortran::semantics::WithOmpDeclarative::RequiresFlags semaFlags;
- if (symbol) {
- Fortran::common::visit(
- [&](const auto &details) {
- if constexpr (std::is_base_of_v<
- Fortran::semantics::WithOmpDeclarative,
- std::decay_t<decltype(details)>>) {
- if (details.has_ompRequires())
- semaFlags = *details.ompRequires();
- }
- },
- symbol->details());
+ llvm::SmallVector<DeclareTargetCapturePair, 0> symbolAndClause;
+ mlir::ModuleOp mod = getBuilder().getModule();
+ mlir::omp::DeclareTargetDeviceType deviceType =
+ getDeclareTargetInfo(converter, eval, declTarget, symbolAndClause);
+
+ 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");
}
- MlirRequires mlirFlags = MlirRequires::none;
- if (semaFlags.test(SemaRequires::ReverseOffload))
- mlirFlags = mlirFlags | MlirRequires::reverse_offload;
- if (semaFlags.test(SemaRequires::UnifiedAddress))
- mlirFlags = mlirFlags | MlirRequires::unified_address;
- if (semaFlags.test(SemaRequires::UnifiedSharedMemory))
- mlirFlags = mlirFlags | MlirRequires::unified_shared_memory;
- if (semaFlags.test(SemaRequires::DynamicAllocators))
- mlirFlags = mlirFlags | MlirRequires::dynamic_allocators;
+ // 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;
+ }
- offloadMod.setRequires(mlirFlags);
+ declareTargetOp.setDeclareTarget(
+ deviceType, std::get<mlir::omp::DeclareTargetCaptureClause>(symClause));
}
}
-namespace Fortran::lower {
+template <>
+void OpenMPMixin<FirConverter>::genOMP(
+ Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::parser::OpenMPRequiresConstruct &) {
+ // Requires directives are gathered and processed in semantics and
+ // then combined in the lowering bridge before triggering codegen
+ // just once. Hence, there is no need to lower each individual
+ // occurrence here.
+}
+
+template <>
+void OpenMPMixin<FirConverter>::genOMP(
+ Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::parser::OpenMPThreadprivate &) {
+ // The directive is lowered when instantiating the variable to
+ // support the case of threadprivate variable declared in module.
+}
template <>
void OpenMPMixin<FirConverter>::genFIR(
const Fortran::parser::OpenMPConstruct &omp) {
+ // OpenMP constructs with blocks, or other executable statements:
+ // SECTIONS: Block, indirectly via SECTION
+ // SECTION: Block
+ // Loop: DoConstruct
+ // BLOCK: Block
+ // CRITICAL: Block
+ // ATOMIC: AssignmentStmt
+ // ALLOCATE: AssignmentStmt
+ // ALLOCATORS: AssignmentStmt
mlir::OpBuilder::InsertPoint insertPt = getBuilder().saveInsertionPoint();
getSymTable().pushScope();
- genOpenMPConstruct(*This(), getBridge().getSemanticsContext(), getEval(),
- omp);
+
+ std::visit(
+ [this](auto &&val) {
+ genOMP(*This(), getEval(), getBridge().getSemanticsContext(), val);
+ },
+ omp.u);
const Fortran::parser::OpenMPLoopConstruct *ompLoop =
std::get_if<Fortran::parser::OpenMPLoopConstruct>(&omp.u);
@@ -3909,7 +3957,7 @@ void OpenMPMixin<FirConverter>::genFIR(
if (ompLoop) {
loopOpClauseList = &std::get<Fortran::parser::OmpClauseList>(
std::get<Fortran::parser::OmpBeginLoopDirective>(ompLoop->t).t);
- int64_t collapseValue = Fortran::lower::getCollapseValue(*loopOpClauseList);
+ int64_t collapseValue = getCollapseValue(*loopOpClauseList);
curEval = &curEval->getFirstNestedEvaluation();
for (int64_t i = 1; i < collapseValue; i++) {
@@ -3944,10 +3992,15 @@ void OpenMPMixin<FirConverter>::genFIR(
mlir::OpBuilder::InsertPoint insertPt = getBuilder().saveInsertionPoint();
// Register if a declare target construct intended for a target device was
// found
- ompDeviceCodeFound =
- ompDeviceCodeFound ||
- Fortran::lower::isOpenMPDeviceDeclareTarget(*This(), getEval(), ompDecl);
- genOpenMPDeclarativeConstruct(*This(), getEval(), ompDecl);
+ ompDeviceCodeFound = ompDeviceCodeFound ||
+ isOpenMPDeviceDeclareTarget(*This(), getEval(), ompDecl);
+
+ std::visit(
+ [this](auto &&decl) {
+ genOMP(*This(), getEval(), getBridge().getSemanticsContext(), decl);
+ },
+ ompDecl.u);
+
for (Fortran::lower::pft::Evaluation &e : getEval().getNestedEvaluations())
This()->genFIR(e);
getBuilder().restoreInsertionPoint(insertPt);
diff --git a/flang/lib/Lower/OpenMPMixin.h b/flang/lib/Lower/OpenMPMixin.h
index 7339d9eb4fc61f..74229249233682 100644
--- a/flang/lib/Lower/OpenMPMixin.h
+++ b/flang/lib/Lower/OpenMPMixin.h
@@ -21,8 +21,9 @@ class FirOpBuilder;
}
namespace Fortran::semantics {
+class SemanticsContext;
class Symbol;
-}
+} // namespace Fortran::semantics
namespace Fortran::lower {
@@ -47,6 +48,94 @@ class OpenMPMixin : public ConverterMixinBase<ConverterT> {
const Fortran::lower::pft::Variable &var);
void finalize(const Fortran::semantics::Symbol *globalOmpRequiresSymbol);
+private:
+ void genOMP(Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::parser::OpenMPSimpleStandaloneConstruct &);
+ void genOMP(Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::parser::OpenMPFlushConstruct &);
+ void genOMP(Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::parser::OpenMPCancelConstruct &);
+ void genOMP(Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::parser::OpenMPCancellationPointConstruct &);
+ void genOMP(Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::parser::OpenMPStandaloneConstruct &);
+
+ void genOMP(Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::parser::OpenMPSectionsConstruct &);
+
+ void genOMP(Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::parser::OpenMPSectionConstruct &);
+
+ void genOMP(Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::parser::OpenMPLoopConstruct &);
+
+ void genOMP(Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::parser::OpenMPBlockConstruct &);
+
+ void genOMP(Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::parser::OpenMPAtomicConstruct &);
+
+ void genOMP(Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::parser::OpenMPCriticalConstruct &);
+
+ void genOMP(Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::parser::OpenMPExecutableAllocate &);
+
+ void genOMP(Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::parser::OpenMPAllocatorsConstruct &);
+
+ // Declarative
+ void genOMP(Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::parser::OpenMPDeclarativeAllocate &);
+ void genOMP(Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::parser::OpenMPDeclareReductionConstruct &);
+ void genOMP(Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::parser::OpenMPDeclareSimdConstruct &);
+ void genOMP(Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::parser::OpenMPDeclareTargetConstruct &);
+ void genOMP(Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::parser::OpenMPRequiresConstruct &);
+ void genOMP(Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ Fortran::semantics::SemanticsContext &semaCtx,
+ const Fortran::parser::OpenMPThreadprivate &);
+
private:
// Shortcuts to call ConverterT:: functions. They can't be defined here
// because the definition of ConverterT is not available at this point.
More information about the llvm-branch-commits
mailing list