This patch is one in a series of four patches that seeks to refactor
slightly and extend the current record type map support that was
put in place for Fortran's descriptor types to handle explicit
member mapping for record types at a single level of depth.
For example, the below case where two members of a Fortran
derived type are mapped explicitly:
''''
type :: scalar_and_array
real(4) :: real
integer(4) :: array(10)
integer(4) :: int
end type scalar_and_array
type(scalar_and_array) :: scalar_arr
!$omp target map(tofrom: scalar_arr%int, scalar_arr%real)
''''
Current cases of derived type mapping left for future work are:
> explicit member mapping of nested members (e.g. two layers of
record types where we explicitly map a member from the internal
record type)
> Fortran's automagical mapping of all elements and nested elements
of a derived type
> explicit member mapping of a derived type and then constituient members
(redundant in Fortran due to former case but still legal as far as I am aware)
> explicit member mapping of a record type (may be handled reasonably, just
not fully tested in this iteration)
> explicit member mapping for Fortran allocatable types (a variation of nested
record types)
This patch seeks to support this by extending the Flang-new OpenMP lowering to
support generation of this newly required information, creating the neccessary
parent <-to-> member map_info links, calculating the member indices and
setting if it's a partial map.
The OMPDescriptorMapInfoGen pass has also been generalized into a map
finalization phase, now named OMPMapInfoFinalization. This pass was extended
to support the insertion of member maps into the BlockArg and MapOperands of
relevant map carrying operations. Similar to the method in which descriptor types
are expanded and constituient members inserted.
Pull Request: https://github.com/llvm/llvm-project/pull/82853
1025 lines
42 KiB
C++
1025 lines
42 KiB
C++
//===-- ClauseProcessor.cpp -------------------------------------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "ClauseProcessor.h"
|
|
#include "Clauses.h"
|
|
|
|
#include "flang/Lower/PFTBuilder.h"
|
|
#include "flang/Parser/tools.h"
|
|
#include "flang/Semantics/tools.h"
|
|
|
|
namespace Fortran {
|
|
namespace lower {
|
|
namespace omp {
|
|
|
|
/// Check for unsupported map operand types.
|
|
static void checkMapType(mlir::Location location, mlir::Type type) {
|
|
if (auto refType = mlir::dyn_cast<fir::ReferenceType>(type))
|
|
type = refType.getElementType();
|
|
if (auto boxType = mlir::dyn_cast_or_null<fir::BoxType>(type))
|
|
if (!mlir::isa<fir::PointerType>(boxType.getElementType()))
|
|
TODO(location, "OMPD_target_data MapOperand BoxType");
|
|
}
|
|
|
|
static mlir::omp::ScheduleModifier
|
|
translateScheduleModifier(const omp::clause::Schedule::OrderingModifier &m) {
|
|
switch (m) {
|
|
case omp::clause::Schedule::OrderingModifier::Monotonic:
|
|
return mlir::omp::ScheduleModifier::monotonic;
|
|
case omp::clause::Schedule::OrderingModifier::Nonmonotonic:
|
|
return mlir::omp::ScheduleModifier::nonmonotonic;
|
|
}
|
|
return mlir::omp::ScheduleModifier::none;
|
|
}
|
|
|
|
static mlir::omp::ScheduleModifier
|
|
getScheduleModifier(const omp::clause::Schedule &clause) {
|
|
using Schedule = omp::clause::Schedule;
|
|
const auto &modifier =
|
|
std::get<std::optional<Schedule::OrderingModifier>>(clause.t);
|
|
if (modifier)
|
|
return translateScheduleModifier(*modifier);
|
|
return mlir::omp::ScheduleModifier::none;
|
|
}
|
|
|
|
static mlir::omp::ScheduleModifier
|
|
getSimdModifier(const omp::clause::Schedule &clause) {
|
|
using Schedule = omp::clause::Schedule;
|
|
const auto &modifier =
|
|
std::get<std::optional<Schedule::ChunkModifier>>(clause.t);
|
|
if (modifier && *modifier == Schedule::ChunkModifier::Simd)
|
|
return mlir::omp::ScheduleModifier::simd;
|
|
return mlir::omp::ScheduleModifier::none;
|
|
}
|
|
|
|
static void
|
|
genAllocateClause(Fortran::lower::AbstractConverter &converter,
|
|
const omp::clause::Allocate &clause,
|
|
llvm::SmallVectorImpl<mlir::Value> &allocatorOperands,
|
|
llvm::SmallVectorImpl<mlir::Value> &allocateOperands) {
|
|
fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
|
|
mlir::Location currentLocation = converter.getCurrentLocation();
|
|
Fortran::lower::StatementContext stmtCtx;
|
|
|
|
auto &objects = std::get<omp::ObjectList>(clause.t);
|
|
|
|
using Allocate = omp::clause::Allocate;
|
|
// ALIGN in this context is unimplemented
|
|
if (std::get<std::optional<Allocate::AlignModifier>>(clause.t))
|
|
TODO(currentLocation, "OmpAllocateClause ALIGN modifier");
|
|
|
|
// Check if allocate clause has allocator specified. If so, add it
|
|
// to list of allocators, otherwise, add default allocator to
|
|
// list of allocators.
|
|
using SimpleModifier = Allocate::AllocatorSimpleModifier;
|
|
using ComplexModifier = Allocate::AllocatorComplexModifier;
|
|
if (auto &mod = std::get<std::optional<SimpleModifier>>(clause.t)) {
|
|
mlir::Value operand = fir::getBase(converter.genExprValue(*mod, stmtCtx));
|
|
allocatorOperands.append(objects.size(), operand);
|
|
} else if (auto &mod = std::get<std::optional<ComplexModifier>>(clause.t)) {
|
|
mlir::Value operand = fir::getBase(converter.genExprValue(mod->v, stmtCtx));
|
|
allocatorOperands.append(objects.size(), operand);
|
|
} else {
|
|
mlir::Value operand = firOpBuilder.createIntegerConstant(
|
|
currentLocation, firOpBuilder.getI32Type(), 1);
|
|
allocatorOperands.append(objects.size(), operand);
|
|
}
|
|
|
|
genObjectList(objects, converter, allocateOperands);
|
|
}
|
|
|
|
static mlir::omp::ClauseProcBindKindAttr
|
|
genProcBindKindAttr(fir::FirOpBuilder &firOpBuilder,
|
|
const omp::clause::ProcBind &clause) {
|
|
mlir::omp::ClauseProcBindKind procBindKind;
|
|
switch (clause.v) {
|
|
case omp::clause::ProcBind::AffinityPolicy::Master:
|
|
procBindKind = mlir::omp::ClauseProcBindKind::Master;
|
|
break;
|
|
case omp::clause::ProcBind::AffinityPolicy::Close:
|
|
procBindKind = mlir::omp::ClauseProcBindKind::Close;
|
|
break;
|
|
case omp::clause::ProcBind::AffinityPolicy::Spread:
|
|
procBindKind = mlir::omp::ClauseProcBindKind::Spread;
|
|
break;
|
|
case omp::clause::ProcBind::AffinityPolicy::Primary:
|
|
procBindKind = mlir::omp::ClauseProcBindKind::Primary;
|
|
break;
|
|
}
|
|
return mlir::omp::ClauseProcBindKindAttr::get(firOpBuilder.getContext(),
|
|
procBindKind);
|
|
}
|
|
|
|
static mlir::omp::ClauseTaskDependAttr
|
|
genDependKindAttr(fir::FirOpBuilder &firOpBuilder,
|
|
const omp::clause::Depend::TaskDependenceType kind) {
|
|
mlir::omp::ClauseTaskDepend pbKind;
|
|
switch (kind) {
|
|
case omp::clause::Depend::TaskDependenceType::In:
|
|
pbKind = mlir::omp::ClauseTaskDepend::taskdependin;
|
|
break;
|
|
case omp::clause::Depend::TaskDependenceType::Out:
|
|
pbKind = mlir::omp::ClauseTaskDepend::taskdependout;
|
|
break;
|
|
case omp::clause::Depend::TaskDependenceType::Inout:
|
|
pbKind = mlir::omp::ClauseTaskDepend::taskdependinout;
|
|
break;
|
|
case omp::clause::Depend::TaskDependenceType::Mutexinoutset:
|
|
case omp::clause::Depend::TaskDependenceType::Inoutset:
|
|
case omp::clause::Depend::TaskDependenceType::Depobj:
|
|
llvm_unreachable("unhandled parser task dependence type");
|
|
break;
|
|
}
|
|
return mlir::omp::ClauseTaskDependAttr::get(firOpBuilder.getContext(),
|
|
pbKind);
|
|
}
|
|
|
|
static mlir::Value
|
|
getIfClauseOperand(Fortran::lower::AbstractConverter &converter,
|
|
const omp::clause::If &clause,
|
|
omp::clause::If::DirectiveNameModifier directiveName,
|
|
mlir::Location clauseLocation) {
|
|
// Only consider the clause if it's intended for the given directive.
|
|
auto &directive =
|
|
std::get<std::optional<omp::clause::If::DirectiveNameModifier>>(clause.t);
|
|
if (directive && directive.value() != directiveName)
|
|
return nullptr;
|
|
|
|
Fortran::lower::StatementContext stmtCtx;
|
|
fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
|
|
mlir::Value ifVal = fir::getBase(
|
|
converter.genExprValue(std::get<omp::SomeExpr>(clause.t), stmtCtx));
|
|
return firOpBuilder.createConvert(clauseLocation, firOpBuilder.getI1Type(),
|
|
ifVal);
|
|
}
|
|
|
|
static void addUseDeviceClause(
|
|
Fortran::lower::AbstractConverter &converter,
|
|
const omp::ObjectList &objects,
|
|
llvm::SmallVectorImpl<mlir::Value> &operands,
|
|
llvm::SmallVectorImpl<mlir::Type> &useDeviceTypes,
|
|
llvm::SmallVectorImpl<mlir::Location> &useDeviceLocs,
|
|
llvm::SmallVectorImpl<const Fortran::semantics::Symbol *> &useDeviceSyms) {
|
|
genObjectList(objects, converter, operands);
|
|
for (mlir::Value &operand : operands) {
|
|
checkMapType(operand.getLoc(), operand.getType());
|
|
useDeviceTypes.push_back(operand.getType());
|
|
useDeviceLocs.push_back(operand.getLoc());
|
|
}
|
|
for (const omp::Object &object : objects)
|
|
useDeviceSyms.push_back(object.id());
|
|
}
|
|
|
|
static void convertLoopBounds(Fortran::lower::AbstractConverter &converter,
|
|
mlir::Location loc,
|
|
mlir::omp::CollapseClauseOps &result,
|
|
std::size_t loopVarTypeSize) {
|
|
fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
|
|
// 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)result.loopLBVar.size(); it++) {
|
|
result.loopLBVar[it] =
|
|
firOpBuilder.createConvert(loc, loopVarType, result.loopLBVar[it]);
|
|
result.loopUBVar[it] =
|
|
firOpBuilder.createConvert(loc, loopVarType, result.loopUBVar[it]);
|
|
result.loopStepVar[it] =
|
|
firOpBuilder.createConvert(loc, loopVarType, result.loopStepVar[it]);
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// ClauseProcessor unique clauses
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
bool ClauseProcessor::processCollapse(
|
|
mlir::Location currentLocation, Fortran::lower::pft::Evaluation &eval,
|
|
mlir::omp::CollapseClauseOps &result,
|
|
llvm::SmallVectorImpl<const Fortran::semantics::Symbol *> &iv) const {
|
|
bool found = false;
|
|
fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
|
|
|
|
// Collect the loops to collapse.
|
|
Fortran::lower::pft::Evaluation *doConstructEval =
|
|
&eval.getFirstNestedEvaluation();
|
|
if (doConstructEval->getIf<Fortran::parser::DoConstruct>()
|
|
->IsDoConcurrent()) {
|
|
TODO(currentLocation, "Do Concurrent in Worksharing loop construct");
|
|
}
|
|
|
|
std::int64_t collapseValue = 1l;
|
|
if (auto *clause = findUniqueClause<omp::clause::Collapse>()) {
|
|
collapseValue = Fortran::evaluate::ToInt64(clause->v).value();
|
|
found = true;
|
|
}
|
|
|
|
std::size_t loopVarTypeSize = 0;
|
|
do {
|
|
Fortran::lower::pft::Evaluation *doLoop =
|
|
&doConstructEval->getFirstNestedEvaluation();
|
|
auto *doStmt = doLoop->getIf<Fortran::parser::NonLabelDoStmt>();
|
|
assert(doStmt && "Expected do loop to be in the nested evaluation");
|
|
const auto &loopControl =
|
|
std::get<std::optional<Fortran::parser::LoopControl>>(doStmt->t);
|
|
const Fortran::parser::LoopControl::Bounds *bounds =
|
|
std::get_if<Fortran::parser::LoopControl::Bounds>(&loopControl->u);
|
|
assert(bounds && "Expected bounds for worksharing do loop");
|
|
Fortran::lower::StatementContext stmtCtx;
|
|
result.loopLBVar.push_back(fir::getBase(converter.genExprValue(
|
|
*Fortran::semantics::GetExpr(bounds->lower), stmtCtx)));
|
|
result.loopUBVar.push_back(fir::getBase(converter.genExprValue(
|
|
*Fortran::semantics::GetExpr(bounds->upper), stmtCtx)));
|
|
if (bounds->step) {
|
|
result.loopStepVar.push_back(fir::getBase(converter.genExprValue(
|
|
*Fortran::semantics::GetExpr(bounds->step), stmtCtx)));
|
|
} else { // If `step` is not present, assume it as `1`.
|
|
result.loopStepVar.push_back(firOpBuilder.createIntegerConstant(
|
|
currentLocation, firOpBuilder.getIntegerType(32), 1));
|
|
}
|
|
iv.push_back(bounds->name.thing.symbol);
|
|
loopVarTypeSize = std::max(loopVarTypeSize,
|
|
bounds->name.thing.symbol->GetUltimate().size());
|
|
collapseValue--;
|
|
doConstructEval =
|
|
&*std::next(doConstructEval->getNestedEvaluations().begin());
|
|
} while (collapseValue > 0);
|
|
|
|
convertLoopBounds(converter, currentLocation, result, loopVarTypeSize);
|
|
|
|
return found;
|
|
}
|
|
|
|
bool ClauseProcessor::processDefault() const {
|
|
if (auto *clause = findUniqueClause<omp::clause::Default>()) {
|
|
// Private, Firstprivate, Shared, None
|
|
switch (clause->v) {
|
|
case omp::clause::Default::DataSharingAttribute::Shared:
|
|
case omp::clause::Default::DataSharingAttribute::None:
|
|
// Default clause with shared or none do not require any handling since
|
|
// Shared is the default behavior in the IR and None is only required
|
|
// for semantic checks.
|
|
break;
|
|
case omp::clause::Default::DataSharingAttribute::Private:
|
|
// TODO Support default(private)
|
|
break;
|
|
case omp::clause::Default::DataSharingAttribute::Firstprivate:
|
|
// TODO Support default(firstprivate)
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ClauseProcessor::processDevice(Fortran::lower::StatementContext &stmtCtx,
|
|
mlir::omp::DeviceClauseOps &result) const {
|
|
const Fortran::parser::CharBlock *source = nullptr;
|
|
if (auto *clause = findUniqueClause<omp::clause::Device>(&source)) {
|
|
mlir::Location clauseLocation = converter.genLocation(*source);
|
|
if (auto deviceModifier =
|
|
std::get<std::optional<omp::clause::Device::DeviceModifier>>(
|
|
clause->t)) {
|
|
if (deviceModifier == omp::clause::Device::DeviceModifier::Ancestor) {
|
|
TODO(clauseLocation, "OMPD_target Device Modifier Ancestor");
|
|
}
|
|
}
|
|
const auto &deviceExpr = std::get<omp::SomeExpr>(clause->t);
|
|
result.deviceVar =
|
|
fir::getBase(converter.genExprValue(deviceExpr, stmtCtx));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ClauseProcessor::processDeviceType(
|
|
mlir::omp::DeviceTypeClauseOps &result) const {
|
|
if (auto *clause = findUniqueClause<omp::clause::DeviceType>()) {
|
|
// Case: declare target ... device_type(any | host | nohost)
|
|
switch (clause->v) {
|
|
case omp::clause::DeviceType::DeviceTypeDescription::Nohost:
|
|
result.deviceType = mlir::omp::DeclareTargetDeviceType::nohost;
|
|
break;
|
|
case omp::clause::DeviceType::DeviceTypeDescription::Host:
|
|
result.deviceType = mlir::omp::DeclareTargetDeviceType::host;
|
|
break;
|
|
case omp::clause::DeviceType::DeviceTypeDescription::Any:
|
|
result.deviceType = mlir::omp::DeclareTargetDeviceType::any;
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ClauseProcessor::processFinal(Fortran::lower::StatementContext &stmtCtx,
|
|
mlir::omp::FinalClauseOps &result) const {
|
|
const Fortran::parser::CharBlock *source = nullptr;
|
|
if (auto *clause = findUniqueClause<omp::clause::Final>(&source)) {
|
|
fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
|
|
mlir::Location clauseLocation = converter.genLocation(*source);
|
|
|
|
mlir::Value finalVal =
|
|
fir::getBase(converter.genExprValue(clause->v, stmtCtx));
|
|
result.finalVar = firOpBuilder.createConvert(
|
|
clauseLocation, firOpBuilder.getI1Type(), finalVal);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ClauseProcessor::processHint(mlir::omp::HintClauseOps &result) const {
|
|
if (auto *clause = findUniqueClause<omp::clause::Hint>()) {
|
|
fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
|
|
int64_t hintValue = *Fortran::evaluate::ToInt64(clause->v);
|
|
result.hintAttr = firOpBuilder.getI64IntegerAttr(hintValue);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ClauseProcessor::processMergeable(
|
|
mlir::omp::MergeableClauseOps &result) const {
|
|
return markClauseOccurrence<omp::clause::Mergeable>(result.mergeableAttr);
|
|
}
|
|
|
|
bool ClauseProcessor::processNowait(mlir::omp::NowaitClauseOps &result) const {
|
|
return markClauseOccurrence<omp::clause::Nowait>(result.nowaitAttr);
|
|
}
|
|
|
|
bool ClauseProcessor::processNumTeams(
|
|
Fortran::lower::StatementContext &stmtCtx,
|
|
mlir::omp::NumTeamsClauseOps &result) const {
|
|
// TODO Get lower and upper bounds for num_teams when parser is updated to
|
|
// accept both.
|
|
if (auto *clause = findUniqueClause<omp::clause::NumTeams>()) {
|
|
// auto lowerBound = std::get<std::optional<ExprTy>>(clause->t);
|
|
auto &upperBound = std::get<ExprTy>(clause->t);
|
|
result.numTeamsUpperVar =
|
|
fir::getBase(converter.genExprValue(upperBound, stmtCtx));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ClauseProcessor::processNumThreads(
|
|
Fortran::lower::StatementContext &stmtCtx,
|
|
mlir::omp::NumThreadsClauseOps &result) const {
|
|
if (auto *clause = findUniqueClause<omp::clause::NumThreads>()) {
|
|
// OMPIRBuilder expects `NUM_THREADS` clause as a `Value`.
|
|
result.numThreadsVar =
|
|
fir::getBase(converter.genExprValue(clause->v, stmtCtx));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ClauseProcessor::processOrdered(
|
|
mlir::omp::OrderedClauseOps &result) const {
|
|
if (auto *clause = findUniqueClause<omp::clause::Ordered>()) {
|
|
fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
|
|
int64_t orderedClauseValue = 0l;
|
|
if (clause->v.has_value())
|
|
orderedClauseValue = *Fortran::evaluate::ToInt64(*clause->v);
|
|
result.orderedAttr = firOpBuilder.getI64IntegerAttr(orderedClauseValue);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ClauseProcessor::processPriority(
|
|
Fortran::lower::StatementContext &stmtCtx,
|
|
mlir::omp::PriorityClauseOps &result) const {
|
|
if (auto *clause = findUniqueClause<omp::clause::Priority>()) {
|
|
result.priorityVar =
|
|
fir::getBase(converter.genExprValue(clause->v, stmtCtx));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ClauseProcessor::processProcBind(
|
|
mlir::omp::ProcBindClauseOps &result) const {
|
|
if (auto *clause = findUniqueClause<omp::clause::ProcBind>()) {
|
|
fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
|
|
result.procBindKindAttr = genProcBindKindAttr(firOpBuilder, *clause);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ClauseProcessor::processSafelen(
|
|
mlir::omp::SafelenClauseOps &result) const {
|
|
if (auto *clause = findUniqueClause<omp::clause::Safelen>()) {
|
|
fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
|
|
const std::optional<std::int64_t> safelenVal =
|
|
Fortran::evaluate::ToInt64(clause->v);
|
|
result.safelenAttr = firOpBuilder.getI64IntegerAttr(*safelenVal);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ClauseProcessor::processSchedule(
|
|
Fortran::lower::StatementContext &stmtCtx,
|
|
mlir::omp::ScheduleClauseOps &result) const {
|
|
if (auto *clause = findUniqueClause<omp::clause::Schedule>()) {
|
|
fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
|
|
mlir::MLIRContext *context = firOpBuilder.getContext();
|
|
const auto &scheduleType = std::get<omp::clause::Schedule::Kind>(clause->t);
|
|
|
|
mlir::omp::ClauseScheduleKind scheduleKind;
|
|
switch (scheduleType) {
|
|
case omp::clause::Schedule::Kind::Static:
|
|
scheduleKind = mlir::omp::ClauseScheduleKind::Static;
|
|
break;
|
|
case omp::clause::Schedule::Kind::Dynamic:
|
|
scheduleKind = mlir::omp::ClauseScheduleKind::Dynamic;
|
|
break;
|
|
case omp::clause::Schedule::Kind::Guided:
|
|
scheduleKind = mlir::omp::ClauseScheduleKind::Guided;
|
|
break;
|
|
case omp::clause::Schedule::Kind::Auto:
|
|
scheduleKind = mlir::omp::ClauseScheduleKind::Auto;
|
|
break;
|
|
case omp::clause::Schedule::Kind::Runtime:
|
|
scheduleKind = mlir::omp::ClauseScheduleKind::Runtime;
|
|
break;
|
|
}
|
|
|
|
result.scheduleValAttr =
|
|
mlir::omp::ClauseScheduleKindAttr::get(context, scheduleKind);
|
|
|
|
mlir::omp::ScheduleModifier scheduleModifier = getScheduleModifier(*clause);
|
|
if (scheduleModifier != mlir::omp::ScheduleModifier::none)
|
|
result.scheduleModAttr =
|
|
mlir::omp::ScheduleModifierAttr::get(context, scheduleModifier);
|
|
|
|
if (getSimdModifier(*clause) != mlir::omp::ScheduleModifier::none)
|
|
result.scheduleSimdAttr = firOpBuilder.getUnitAttr();
|
|
|
|
if (const auto &chunkExpr = std::get<omp::MaybeExpr>(clause->t))
|
|
result.scheduleChunkVar =
|
|
fir::getBase(converter.genExprValue(*chunkExpr, stmtCtx));
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ClauseProcessor::processSimdlen(
|
|
mlir::omp::SimdlenClauseOps &result) const {
|
|
if (auto *clause = findUniqueClause<omp::clause::Simdlen>()) {
|
|
fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
|
|
const std::optional<std::int64_t> simdlenVal =
|
|
Fortran::evaluate::ToInt64(clause->v);
|
|
result.simdlenAttr = firOpBuilder.getI64IntegerAttr(*simdlenVal);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ClauseProcessor::processThreadLimit(
|
|
Fortran::lower::StatementContext &stmtCtx,
|
|
mlir::omp::ThreadLimitClauseOps &result) const {
|
|
if (auto *clause = findUniqueClause<omp::clause::ThreadLimit>()) {
|
|
result.threadLimitVar =
|
|
fir::getBase(converter.genExprValue(clause->v, stmtCtx));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ClauseProcessor::processUntied(mlir::omp::UntiedClauseOps &result) const {
|
|
return markClauseOccurrence<omp::clause::Untied>(result.untiedAttr);
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// ClauseProcessor repeatable clauses
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
bool ClauseProcessor::processAllocate(
|
|
mlir::omp::AllocateClauseOps &result) const {
|
|
return findRepeatableClause<omp::clause::Allocate>(
|
|
[&](const omp::clause::Allocate &clause,
|
|
const Fortran::parser::CharBlock &) {
|
|
genAllocateClause(converter, clause, result.allocatorVars,
|
|
result.allocateVars);
|
|
});
|
|
}
|
|
|
|
bool ClauseProcessor::processCopyin() const {
|
|
fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
|
|
mlir::OpBuilder::InsertPoint insPt = firOpBuilder.saveInsertionPoint();
|
|
firOpBuilder.setInsertionPointToStart(firOpBuilder.getAllocaBlock());
|
|
auto checkAndCopyHostAssociateVar =
|
|
[&](Fortran::semantics::Symbol *sym,
|
|
mlir::OpBuilder::InsertPoint *copyAssignIP = nullptr) {
|
|
assert(sym->has<Fortran::semantics::HostAssocDetails>() &&
|
|
"No host-association found");
|
|
if (converter.isPresentShallowLookup(*sym))
|
|
converter.copyHostAssociateVar(*sym, copyAssignIP);
|
|
};
|
|
bool hasCopyin = findRepeatableClause<omp::clause::Copyin>(
|
|
[&](const omp::clause::Copyin &clause,
|
|
const Fortran::parser::CharBlock &) {
|
|
for (const omp::Object &object : clause.v) {
|
|
Fortran::semantics::Symbol *sym = object.id();
|
|
assert(sym && "Expecting symbol");
|
|
if (const auto *commonDetails =
|
|
sym->detailsIf<Fortran::semantics::CommonBlockDetails>()) {
|
|
for (const auto &mem : commonDetails->objects())
|
|
checkAndCopyHostAssociateVar(&*mem, &insPt);
|
|
break;
|
|
}
|
|
if (Fortran::semantics::IsAllocatableOrObjectPointer(
|
|
&sym->GetUltimate()))
|
|
TODO(converter.getCurrentLocation(),
|
|
"pointer or allocatable variables in Copyin clause");
|
|
assert(sym->has<Fortran::semantics::HostAssocDetails>() &&
|
|
"No host-association found");
|
|
checkAndCopyHostAssociateVar(sym);
|
|
}
|
|
});
|
|
|
|
// [OMP 5.0, 2.19.6.1] The copy is done after the team is formed and prior to
|
|
// the execution of the associated structured block. Emit implicit barrier to
|
|
// synchronize threads and avoid data races on propagation master's thread
|
|
// values of threadprivate variables to local instances of that variables of
|
|
// all other implicit threads.
|
|
|
|
// All copies are inserted at either "insPt" (i.e. immediately before it),
|
|
// or at some earlier point (as determined by "copyHostAssociateVar").
|
|
// Unless the insertion point is given to "copyHostAssociateVar" explicitly,
|
|
// it will not restore the builder's insertion point. Since the copies may be
|
|
// inserted in any order (not following the execution order), make sure the
|
|
// barrier is inserted following all of them.
|
|
firOpBuilder.restoreInsertionPoint(insPt);
|
|
if (hasCopyin)
|
|
firOpBuilder.create<mlir::omp::BarrierOp>(converter.getCurrentLocation());
|
|
return hasCopyin;
|
|
}
|
|
|
|
/// Class that extracts information from the specified type.
|
|
class TypeInfo {
|
|
public:
|
|
TypeInfo(mlir::Type ty) { typeScan(ty); }
|
|
|
|
// Returns the length of character types.
|
|
std::optional<fir::CharacterType::LenType> getCharLength() const {
|
|
return charLen;
|
|
}
|
|
|
|
// Returns the shape of array types.
|
|
llvm::ArrayRef<int64_t> getShape() const { return shape; }
|
|
|
|
// Is the type inside a box?
|
|
bool isBox() const { return inBox; }
|
|
|
|
private:
|
|
void typeScan(mlir::Type type);
|
|
|
|
std::optional<fir::CharacterType::LenType> charLen;
|
|
llvm::SmallVector<int64_t> shape;
|
|
bool inBox = false;
|
|
};
|
|
|
|
void TypeInfo::typeScan(mlir::Type ty) {
|
|
if (auto sty = mlir::dyn_cast<fir::SequenceType>(ty)) {
|
|
assert(shape.empty() && !sty.getShape().empty());
|
|
shape = llvm::SmallVector<int64_t>(sty.getShape());
|
|
typeScan(sty.getEleTy());
|
|
} else if (auto bty = mlir::dyn_cast<fir::BoxType>(ty)) {
|
|
inBox = true;
|
|
typeScan(bty.getEleTy());
|
|
} else if (auto cty = mlir::dyn_cast<fir::CharacterType>(ty)) {
|
|
charLen = cty.getLen();
|
|
} else if (auto hty = mlir::dyn_cast<fir::HeapType>(ty)) {
|
|
typeScan(hty.getEleTy());
|
|
} else if (auto pty = mlir::dyn_cast<fir::PointerType>(ty)) {
|
|
typeScan(pty.getEleTy());
|
|
} else {
|
|
// The scan ends when reaching any built-in or record type.
|
|
assert(ty.isIntOrIndexOrFloat() || mlir::isa<fir::ComplexType>(ty) ||
|
|
mlir::isa<fir::LogicalType>(ty) || mlir::isa<fir::RecordType>(ty));
|
|
}
|
|
}
|
|
|
|
// Create a function that performs a copy between two variables, compatible
|
|
// with their types and attributes.
|
|
static mlir::func::FuncOp
|
|
createCopyFunc(mlir::Location loc, Fortran::lower::AbstractConverter &converter,
|
|
mlir::Type varType, fir::FortranVariableFlagsEnum varAttrs) {
|
|
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
|
|
mlir::ModuleOp module = builder.getModule();
|
|
mlir::Type eleTy = mlir::cast<fir::ReferenceType>(varType).getEleTy();
|
|
TypeInfo typeInfo(eleTy);
|
|
std::string copyFuncName =
|
|
fir::getTypeAsString(eleTy, builder.getKindMap(), "_copy");
|
|
|
|
if (auto decl = module.lookupSymbol<mlir::func::FuncOp>(copyFuncName))
|
|
return decl;
|
|
|
|
// create function
|
|
mlir::OpBuilder::InsertionGuard guard(builder);
|
|
mlir::OpBuilder modBuilder(module.getBodyRegion());
|
|
llvm::SmallVector<mlir::Type> argsTy = {varType, varType};
|
|
auto funcType = mlir::FunctionType::get(builder.getContext(), argsTy, {});
|
|
mlir::func::FuncOp funcOp =
|
|
modBuilder.create<mlir::func::FuncOp>(loc, copyFuncName, funcType);
|
|
funcOp.setVisibility(mlir::SymbolTable::Visibility::Private);
|
|
builder.createBlock(&funcOp.getRegion(), funcOp.getRegion().end(), argsTy,
|
|
{loc, loc});
|
|
builder.setInsertionPointToStart(&funcOp.getRegion().back());
|
|
// generate body
|
|
fir::FortranVariableFlagsAttr attrs;
|
|
if (varAttrs != fir::FortranVariableFlagsEnum::None)
|
|
attrs = fir::FortranVariableFlagsAttr::get(builder.getContext(), varAttrs);
|
|
llvm::SmallVector<mlir::Value> typeparams;
|
|
if (typeInfo.getCharLength().has_value()) {
|
|
mlir::Value charLen = builder.createIntegerConstant(
|
|
loc, builder.getCharacterLengthType(), *typeInfo.getCharLength());
|
|
typeparams.push_back(charLen);
|
|
}
|
|
mlir::Value shape;
|
|
if (!typeInfo.isBox() && !typeInfo.getShape().empty()) {
|
|
llvm::SmallVector<mlir::Value> extents;
|
|
for (auto extent : typeInfo.getShape())
|
|
extents.push_back(
|
|
builder.createIntegerConstant(loc, builder.getIndexType(), extent));
|
|
shape = builder.create<fir::ShapeOp>(loc, extents);
|
|
}
|
|
auto declDst = builder.create<hlfir::DeclareOp>(
|
|
loc, funcOp.getArgument(0), copyFuncName + "_dst", shape, typeparams,
|
|
/*dummy_scope=*/nullptr, attrs);
|
|
auto declSrc = builder.create<hlfir::DeclareOp>(
|
|
loc, funcOp.getArgument(1), copyFuncName + "_src", shape, typeparams,
|
|
/*dummy_scope=*/nullptr, attrs);
|
|
converter.copyVar(loc, declDst.getBase(), declSrc.getBase());
|
|
builder.create<mlir::func::ReturnOp>(loc);
|
|
return funcOp;
|
|
}
|
|
|
|
bool ClauseProcessor::processCopyprivate(
|
|
mlir::Location currentLocation,
|
|
mlir::omp::CopyprivateClauseOps &result) const {
|
|
auto addCopyPrivateVar = [&](Fortran::semantics::Symbol *sym) {
|
|
mlir::Value symVal = converter.getSymbolAddress(*sym);
|
|
auto declOp = symVal.getDefiningOp<hlfir::DeclareOp>();
|
|
if (!declOp)
|
|
fir::emitFatalError(currentLocation,
|
|
"COPYPRIVATE is supported only in HLFIR mode");
|
|
symVal = declOp.getBase();
|
|
mlir::Type symType = symVal.getType();
|
|
fir::FortranVariableFlagsEnum attrs =
|
|
declOp.getFortranAttrs().has_value()
|
|
? *declOp.getFortranAttrs()
|
|
: fir::FortranVariableFlagsEnum::None;
|
|
mlir::Value cpVar = symVal;
|
|
|
|
// CopyPrivate variables must be passed by reference. However, in the case
|
|
// of assumed shapes/vla the type is not a !fir.ref, but a !fir.box.
|
|
// In these cases to retrieve the appropriate !fir.ref<!fir.box<...>> to
|
|
// access the data we need we must perform an alloca and then store to it
|
|
// and retrieve the data from the new alloca.
|
|
if (mlir::isa<fir::BaseBoxType>(symType)) {
|
|
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
|
|
auto alloca = builder.create<fir::AllocaOp>(currentLocation, symType);
|
|
builder.create<fir::StoreOp>(currentLocation, symVal, alloca);
|
|
cpVar = alloca;
|
|
}
|
|
|
|
result.copyprivateVars.push_back(cpVar);
|
|
mlir::func::FuncOp funcOp =
|
|
createCopyFunc(currentLocation, converter, cpVar.getType(), attrs);
|
|
result.copyprivateFuncs.push_back(mlir::SymbolRefAttr::get(funcOp));
|
|
};
|
|
|
|
bool hasCopyPrivate = findRepeatableClause<clause::Copyprivate>(
|
|
[&](const clause::Copyprivate &clause,
|
|
const Fortran::parser::CharBlock &) {
|
|
for (const Object &object : clause.v) {
|
|
Fortran::semantics::Symbol *sym = object.id();
|
|
if (const auto *commonDetails =
|
|
sym->detailsIf<Fortran::semantics::CommonBlockDetails>()) {
|
|
for (const auto &mem : commonDetails->objects())
|
|
addCopyPrivateVar(&*mem);
|
|
break;
|
|
}
|
|
addCopyPrivateVar(sym);
|
|
}
|
|
});
|
|
|
|
return hasCopyPrivate;
|
|
}
|
|
|
|
bool ClauseProcessor::processDepend(mlir::omp::DependClauseOps &result) const {
|
|
fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
|
|
|
|
return findRepeatableClause<omp::clause::Depend>(
|
|
[&](const omp::clause::Depend &clause,
|
|
const Fortran::parser::CharBlock &) {
|
|
using Depend = omp::clause::Depend;
|
|
assert(std::holds_alternative<Depend::WithLocators>(clause.u) &&
|
|
"Only the modern form is handled at the moment");
|
|
auto &modern = std::get<Depend::WithLocators>(clause.u);
|
|
auto kind = std::get<Depend::TaskDependenceType>(modern.t);
|
|
auto &objects = std::get<omp::ObjectList>(modern.t);
|
|
|
|
mlir::omp::ClauseTaskDependAttr dependTypeOperand =
|
|
genDependKindAttr(firOpBuilder, kind);
|
|
result.dependTypeAttrs.append(objects.size(), dependTypeOperand);
|
|
|
|
for (const omp::Object &object : objects) {
|
|
assert(object.ref() && "Expecting designator");
|
|
|
|
if (Fortran::evaluate::ExtractSubstring(*object.ref())) {
|
|
TODO(converter.getCurrentLocation(),
|
|
"substring not supported for task depend");
|
|
} else if (Fortran::evaluate::IsArrayElement(*object.ref())) {
|
|
TODO(converter.getCurrentLocation(),
|
|
"array sections not supported for task depend");
|
|
}
|
|
|
|
Fortran::semantics::Symbol *sym = object.id();
|
|
const mlir::Value variable = converter.getSymbolAddress(*sym);
|
|
result.dependVars.push_back(variable);
|
|
}
|
|
});
|
|
}
|
|
|
|
bool ClauseProcessor::processHasDeviceAddr(
|
|
mlir::omp::HasDeviceAddrClauseOps &result,
|
|
llvm::SmallVectorImpl<mlir::Type> &isDeviceTypes,
|
|
llvm::SmallVectorImpl<mlir::Location> &isDeviceLocs,
|
|
llvm::SmallVectorImpl<const Fortran::semantics::Symbol *> &isDeviceSymbols)
|
|
const {
|
|
return findRepeatableClause<omp::clause::HasDeviceAddr>(
|
|
[&](const omp::clause::HasDeviceAddr &devAddrClause,
|
|
const Fortran::parser::CharBlock &) {
|
|
addUseDeviceClause(converter, devAddrClause.v, result.hasDeviceAddrVars,
|
|
isDeviceTypes, isDeviceLocs, isDeviceSymbols);
|
|
});
|
|
}
|
|
|
|
bool ClauseProcessor::processIf(
|
|
omp::clause::If::DirectiveNameModifier directiveName,
|
|
mlir::omp::IfClauseOps &result) const {
|
|
bool found = false;
|
|
findRepeatableClause<omp::clause::If>(
|
|
[&](const omp::clause::If &clause,
|
|
const Fortran::parser::CharBlock &source) {
|
|
mlir::Location clauseLocation = converter.genLocation(source);
|
|
mlir::Value operand = getIfClauseOperand(converter, clause,
|
|
directiveName, clauseLocation);
|
|
// Assume that, at most, a single 'if' clause will be applicable to the
|
|
// given directive.
|
|
if (operand) {
|
|
result.ifVar = operand;
|
|
found = true;
|
|
}
|
|
});
|
|
return found;
|
|
}
|
|
|
|
bool ClauseProcessor::processIsDevicePtr(
|
|
mlir::omp::IsDevicePtrClauseOps &result,
|
|
llvm::SmallVectorImpl<mlir::Type> &isDeviceTypes,
|
|
llvm::SmallVectorImpl<mlir::Location> &isDeviceLocs,
|
|
llvm::SmallVectorImpl<const Fortran::semantics::Symbol *> &isDeviceSymbols)
|
|
const {
|
|
return findRepeatableClause<omp::clause::IsDevicePtr>(
|
|
[&](const omp::clause::IsDevicePtr &devPtrClause,
|
|
const Fortran::parser::CharBlock &) {
|
|
addUseDeviceClause(converter, devPtrClause.v, result.isDevicePtrVars,
|
|
isDeviceTypes, isDeviceLocs, isDeviceSymbols);
|
|
});
|
|
}
|
|
|
|
bool ClauseProcessor::processLink(
|
|
llvm::SmallVectorImpl<DeclareTargetCapturePair> &result) const {
|
|
return findRepeatableClause<omp::clause::Link>(
|
|
[&](const omp::clause::Link &clause, const Fortran::parser::CharBlock &) {
|
|
// Case: declare target link(var1, var2)...
|
|
gatherFuncAndVarSyms(
|
|
clause.v, mlir::omp::DeclareTargetCaptureClause::link, result);
|
|
});
|
|
}
|
|
|
|
bool ClauseProcessor::processMap(
|
|
mlir::Location currentLocation, Fortran::lower::StatementContext &stmtCtx,
|
|
mlir::omp::MapClauseOps &result,
|
|
llvm::SmallVectorImpl<const Fortran::semantics::Symbol *> *mapSyms,
|
|
llvm::SmallVectorImpl<mlir::Location> *mapSymLocs,
|
|
llvm::SmallVectorImpl<mlir::Type> *mapSymTypes) const {
|
|
fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
|
|
// We always require tracking of symbols, even if the caller does not,
|
|
// so we create an optionally used local set of symbols when the mapSyms
|
|
// argument is not present.
|
|
llvm::SmallVector<const Fortran::semantics::Symbol *> localMapSyms;
|
|
llvm::SmallVectorImpl<const Fortran::semantics::Symbol *> *ptrMapSyms =
|
|
mapSyms ? mapSyms : &localMapSyms;
|
|
std::map<const Fortran::semantics::Symbol *,
|
|
llvm::SmallVector<OmpMapMemberIndicesData>>
|
|
parentMemberIndices;
|
|
|
|
bool clauseFound = findRepeatableClause<omp::clause::Map>(
|
|
[&](const omp::clause::Map &clause,
|
|
const Fortran::parser::CharBlock &source) {
|
|
using Map = omp::clause::Map;
|
|
mlir::Location clauseLocation = converter.genLocation(source);
|
|
const auto &mapType = std::get<std::optional<Map::MapType>>(clause.t);
|
|
llvm::omp::OpenMPOffloadMappingFlags mapTypeBits =
|
|
llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_NONE;
|
|
// If the map type is specified, then process it else Tofrom is the
|
|
// default.
|
|
if (mapType) {
|
|
switch (*mapType) {
|
|
case Map::MapType::To:
|
|
mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TO;
|
|
break;
|
|
case Map::MapType::From:
|
|
mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_FROM;
|
|
break;
|
|
case Map::MapType::Tofrom:
|
|
mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TO |
|
|
llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_FROM;
|
|
break;
|
|
case Map::MapType::Alloc:
|
|
case Map::MapType::Release:
|
|
// alloc and release is the default map_type for the Target Data
|
|
// Ops, i.e. if no bits for map_type is supplied then alloc/release
|
|
// is implicitly assumed based on the target directive. Default
|
|
// value for Target Data and Enter Data is alloc and for Exit Data
|
|
// it is release.
|
|
break;
|
|
case Map::MapType::Delete:
|
|
mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_DELETE;
|
|
}
|
|
|
|
auto &modTypeMods =
|
|
std::get<std::optional<Map::MapTypeModifiers>>(clause.t);
|
|
if (modTypeMods) {
|
|
if (llvm::is_contained(*modTypeMods, Map::MapTypeModifier::Always))
|
|
mapTypeBits |=
|
|
llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_ALWAYS;
|
|
}
|
|
} else {
|
|
mapTypeBits |= llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_TO |
|
|
llvm::omp::OpenMPOffloadMappingFlags::OMP_MAP_FROM;
|
|
}
|
|
|
|
for (const omp::Object &object : std::get<omp::ObjectList>(clause.t)) {
|
|
llvm::SmallVector<mlir::Value> bounds;
|
|
std::stringstream asFortran;
|
|
|
|
Fortran::lower::AddrAndBoundsInfo info =
|
|
Fortran::lower::gatherDataOperandAddrAndBounds<
|
|
mlir::omp::MapBoundsOp, mlir::omp::MapBoundsType>(
|
|
converter, firOpBuilder, semaCtx, stmtCtx, *object.id(),
|
|
object.ref(), clauseLocation, asFortran, bounds,
|
|
treatIndexAsSection);
|
|
|
|
auto origSymbol = converter.getSymbolAddress(*object.id());
|
|
mlir::Value symAddr = info.addr;
|
|
if (origSymbol && fir::isTypeWithDescriptor(origSymbol.getType()))
|
|
symAddr = origSymbol;
|
|
|
|
// Explicit map captures are captured ByRef by default,
|
|
// optimisation passes may alter this to ByCopy or other capture
|
|
// types to optimise
|
|
mlir::omp::MapInfoOp mapOp = createMapInfoOp(
|
|
firOpBuilder, clauseLocation, symAddr,
|
|
/*varPtrPtr=*/mlir::Value{}, asFortran.str(), bounds,
|
|
/*members=*/{}, /*membersIndex=*/mlir::DenseIntElementsAttr{},
|
|
static_cast<
|
|
std::underlying_type_t<llvm::omp::OpenMPOffloadMappingFlags>>(
|
|
mapTypeBits),
|
|
mlir::omp::VariableCaptureKind::ByRef, symAddr.getType());
|
|
|
|
if (object.id()->owner().IsDerivedType()) {
|
|
addChildIndexAndMapToParent(object, parentMemberIndices, mapOp,
|
|
semaCtx);
|
|
} else {
|
|
result.mapVars.push_back(mapOp);
|
|
ptrMapSyms->push_back(object.id());
|
|
if (mapSymTypes)
|
|
mapSymTypes->push_back(symAddr.getType());
|
|
if (mapSymLocs)
|
|
mapSymLocs->push_back(symAddr.getLoc());
|
|
}
|
|
}
|
|
});
|
|
|
|
insertChildMapInfoIntoParent(converter, parentMemberIndices, result.mapVars,
|
|
*ptrMapSyms, mapSymTypes, mapSymLocs);
|
|
|
|
return clauseFound;
|
|
}
|
|
|
|
bool ClauseProcessor::processReduction(
|
|
mlir::Location currentLocation, mlir::omp::ReductionClauseOps &result,
|
|
llvm::SmallVectorImpl<mlir::Type> *outReductionTypes,
|
|
llvm::SmallVectorImpl<const Fortran::semantics::Symbol *> *outReductionSyms)
|
|
const {
|
|
return findRepeatableClause<omp::clause::Reduction>(
|
|
[&](const omp::clause::Reduction &clause,
|
|
const Fortran::parser::CharBlock &) {
|
|
// Use local lists of reductions to prevent variables from other
|
|
// already-processed reduction clauses from impacting this reduction.
|
|
// For example, the whole `reductionVars` array is queried to decide
|
|
// whether to do the reduction byref.
|
|
llvm::SmallVector<mlir::Value> reductionVars;
|
|
llvm::SmallVector<mlir::Attribute> reductionDeclSymbols;
|
|
llvm::SmallVector<const Fortran::semantics::Symbol *> reductionSyms;
|
|
ReductionProcessor rp;
|
|
rp.addDeclareReduction(currentLocation, converter, clause,
|
|
reductionVars, reductionDeclSymbols,
|
|
outReductionSyms ? &reductionSyms : nullptr);
|
|
|
|
// Copy local lists into the output.
|
|
llvm::copy(reductionVars, std::back_inserter(result.reductionVars));
|
|
llvm::copy(reductionDeclSymbols,
|
|
std::back_inserter(result.reductionDeclSymbols));
|
|
|
|
if (outReductionTypes) {
|
|
outReductionTypes->reserve(outReductionTypes->size() +
|
|
reductionVars.size());
|
|
llvm::transform(reductionVars, std::back_inserter(*outReductionTypes),
|
|
[](mlir::Value v) { return v.getType(); });
|
|
}
|
|
|
|
if (outReductionSyms)
|
|
llvm::copy(reductionSyms, std::back_inserter(*outReductionSyms));
|
|
});
|
|
}
|
|
|
|
bool ClauseProcessor::processSectionsReduction(
|
|
mlir::Location currentLocation, mlir::omp::ReductionClauseOps &) const {
|
|
return findRepeatableClause<omp::clause::Reduction>(
|
|
[&](const omp::clause::Reduction &, const Fortran::parser::CharBlock &) {
|
|
TODO(currentLocation, "OMPC_Reduction");
|
|
});
|
|
}
|
|
|
|
bool ClauseProcessor::processTo(
|
|
llvm::SmallVectorImpl<DeclareTargetCapturePair> &result) const {
|
|
return findRepeatableClause<omp::clause::To>(
|
|
[&](const omp::clause::To &clause, const Fortran::parser::CharBlock &) {
|
|
// Case: declare target to(func, var1, var2)...
|
|
gatherFuncAndVarSyms(std::get<ObjectList>(clause.t),
|
|
mlir::omp::DeclareTargetCaptureClause::to, result);
|
|
});
|
|
}
|
|
|
|
bool ClauseProcessor::processEnter(
|
|
llvm::SmallVectorImpl<DeclareTargetCapturePair> &result) const {
|
|
return findRepeatableClause<omp::clause::Enter>(
|
|
[&](const omp::clause::Enter &clause,
|
|
const Fortran::parser::CharBlock &) {
|
|
// Case: declare target enter(func, var1, var2)...
|
|
gatherFuncAndVarSyms(
|
|
clause.v, mlir::omp::DeclareTargetCaptureClause::enter, result);
|
|
});
|
|
}
|
|
|
|
bool ClauseProcessor::processUseDeviceAddr(
|
|
mlir::omp::UseDeviceClauseOps &result,
|
|
llvm::SmallVectorImpl<mlir::Type> &useDeviceTypes,
|
|
llvm::SmallVectorImpl<mlir::Location> &useDeviceLocs,
|
|
llvm::SmallVectorImpl<const Fortran::semantics::Symbol *> &useDeviceSyms)
|
|
const {
|
|
return findRepeatableClause<omp::clause::UseDeviceAddr>(
|
|
[&](const omp::clause::UseDeviceAddr &clause,
|
|
const Fortran::parser::CharBlock &) {
|
|
addUseDeviceClause(converter, clause.v, result.useDeviceAddrVars,
|
|
useDeviceTypes, useDeviceLocs, useDeviceSyms);
|
|
});
|
|
}
|
|
|
|
bool ClauseProcessor::processUseDevicePtr(
|
|
mlir::omp::UseDeviceClauseOps &result,
|
|
llvm::SmallVectorImpl<mlir::Type> &useDeviceTypes,
|
|
llvm::SmallVectorImpl<mlir::Location> &useDeviceLocs,
|
|
llvm::SmallVectorImpl<const Fortran::semantics::Symbol *> &useDeviceSyms)
|
|
const {
|
|
return findRepeatableClause<omp::clause::UseDevicePtr>(
|
|
[&](const omp::clause::UseDevicePtr &clause,
|
|
const Fortran::parser::CharBlock &) {
|
|
addUseDeviceClause(converter, clause.v, result.useDevicePtrVars,
|
|
useDeviceTypes, useDeviceLocs, useDeviceSyms);
|
|
});
|
|
}
|
|
|
|
} // namespace omp
|
|
} // namespace lower
|
|
} // namespace Fortran
|