This patch adds translation for allocate clause for parallel and single constructs. Also added tests for block constructs. This patch also adds tests for parallel construct which were not added earlier. Reviewed By: NimishMishra, peixin Differential Revision: https://reviews.llvm.org/D122483 Co-authored-by: Sourabh Singh Tomar <SourabhSingh.Tomar@amd.com>
422 lines
18 KiB
C++
422 lines
18 KiB
C++
//===-- OpenMP.cpp -- Open MP directive lowering --------------------------===//
|
|
//
|
|
// 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 "flang/Lower/OpenMP.h"
|
|
#include "flang/Common/idioms.h"
|
|
#include "flang/Lower/Bridge.h"
|
|
#include "flang/Lower/PFTBuilder.h"
|
|
#include "flang/Lower/StatementContext.h"
|
|
#include "flang/Lower/Todo.h"
|
|
#include "flang/Optimizer/Builder/BoxValue.h"
|
|
#include "flang/Optimizer/Builder/FIRBuilder.h"
|
|
#include "flang/Parser/parse-tree.h"
|
|
#include "flang/Semantics/tools.h"
|
|
#include "mlir/Dialect/OpenMP/OpenMPDialect.h"
|
|
#include "llvm/Frontend/OpenMP/OMPConstants.h"
|
|
|
|
using namespace mlir;
|
|
|
|
static const Fortran::parser::Name *
|
|
getDesignatorNameIfDataRef(const Fortran::parser::Designator &designator) {
|
|
const auto *dataRef = std::get_if<Fortran::parser::DataRef>(&designator.u);
|
|
return dataRef ? std::get_if<Fortran::parser::Name>(&dataRef->u) : nullptr;
|
|
}
|
|
|
|
static void genObjectList(const Fortran::parser::OmpObjectList &objectList,
|
|
Fortran::lower::AbstractConverter &converter,
|
|
SmallVectorImpl<Value> &operands) {
|
|
for (const auto &ompObject : objectList.v) {
|
|
std::visit(
|
|
Fortran::common::visitors{
|
|
[&](const Fortran::parser::Designator &designator) {
|
|
if (const auto *name = getDesignatorNameIfDataRef(designator)) {
|
|
const auto variable = converter.getSymbolAddress(*name->symbol);
|
|
operands.push_back(variable);
|
|
}
|
|
},
|
|
[&](const Fortran::parser::Name &name) {
|
|
const auto variable = converter.getSymbolAddress(*name.symbol);
|
|
operands.push_back(variable);
|
|
}},
|
|
ompObject.u);
|
|
}
|
|
}
|
|
|
|
template <typename Op>
|
|
static void createBodyOfOp(Op &op, fir::FirOpBuilder &firOpBuilder,
|
|
mlir::Location &loc) {
|
|
firOpBuilder.createBlock(&op.getRegion());
|
|
auto &block = op.getRegion().back();
|
|
firOpBuilder.setInsertionPointToStart(&block);
|
|
// Ensure the block is well-formed.
|
|
firOpBuilder.create<mlir::omp::TerminatorOp>(loc);
|
|
// Reset the insertion point to the start of the first block.
|
|
firOpBuilder.setInsertionPointToStart(&block);
|
|
}
|
|
|
|
static void genOMP(Fortran::lower::AbstractConverter &converter,
|
|
Fortran::lower::pft::Evaluation &eval,
|
|
const Fortran::parser::OpenMPSimpleStandaloneConstruct
|
|
&simpleStandaloneConstruct) {
|
|
const auto &directive =
|
|
std::get<Fortran::parser::OmpSimpleStandaloneDirective>(
|
|
simpleStandaloneConstruct.t);
|
|
switch (directive.v) {
|
|
default:
|
|
break;
|
|
case llvm::omp::Directive::OMPD_barrier:
|
|
converter.getFirOpBuilder().create<mlir::omp::BarrierOp>(
|
|
converter.getCurrentLocation());
|
|
break;
|
|
case llvm::omp::Directive::OMPD_taskwait:
|
|
converter.getFirOpBuilder().create<mlir::omp::TaskwaitOp>(
|
|
converter.getCurrentLocation());
|
|
break;
|
|
case llvm::omp::Directive::OMPD_taskyield:
|
|
converter.getFirOpBuilder().create<mlir::omp::TaskyieldOp>(
|
|
converter.getCurrentLocation());
|
|
break;
|
|
case llvm::omp::Directive::OMPD_target_enter_data:
|
|
TODO(converter.getCurrentLocation(), "OMPD_target_enter_data");
|
|
case llvm::omp::Directive::OMPD_target_exit_data:
|
|
TODO(converter.getCurrentLocation(), "OMPD_target_exit_data");
|
|
case llvm::omp::Directive::OMPD_target_update:
|
|
TODO(converter.getCurrentLocation(), "OMPD_target_update");
|
|
case llvm::omp::Directive::OMPD_ordered:
|
|
TODO(converter.getCurrentLocation(), "OMPD_ordered");
|
|
}
|
|
}
|
|
|
|
static void
|
|
genAllocateClause(Fortran::lower::AbstractConverter &converter,
|
|
const Fortran::parser::OmpAllocateClause &ompAllocateClause,
|
|
SmallVector<Value> &allocatorOperands,
|
|
SmallVector<Value> &allocateOperands) {
|
|
auto &firOpBuilder = converter.getFirOpBuilder();
|
|
auto currentLocation = converter.getCurrentLocation();
|
|
Fortran::lower::StatementContext stmtCtx;
|
|
|
|
mlir::Value allocatorOperand;
|
|
const Fortran::parser::OmpObjectList &ompObjectList =
|
|
std::get<Fortran::parser::OmpObjectList>(ompAllocateClause.t);
|
|
const auto &allocatorValue =
|
|
std::get<std::optional<Fortran::parser::OmpAllocateClause::Allocator>>(
|
|
ompAllocateClause.t);
|
|
// Check if allocate clause has allocator specified. If so, add it
|
|
// to list of allocators, otherwise, add default allocator to
|
|
// list of allocators.
|
|
if (allocatorValue) {
|
|
allocatorOperand = fir::getBase(converter.genExprValue(
|
|
*Fortran::semantics::GetExpr(allocatorValue->v), stmtCtx));
|
|
allocatorOperands.insert(allocatorOperands.end(), ompObjectList.v.size(),
|
|
allocatorOperand);
|
|
} else {
|
|
allocatorOperand = firOpBuilder.createIntegerConstant(
|
|
currentLocation, firOpBuilder.getI32Type(), 1);
|
|
allocatorOperands.insert(allocatorOperands.end(), ompObjectList.v.size(),
|
|
allocatorOperand);
|
|
}
|
|
genObjectList(ompObjectList, converter, allocateOperands);
|
|
}
|
|
|
|
static void
|
|
genOMP(Fortran::lower::AbstractConverter &converter,
|
|
Fortran::lower::pft::Evaluation &eval,
|
|
const Fortran::parser::OpenMPStandaloneConstruct &standaloneConstruct) {
|
|
std::visit(
|
|
Fortran::common::visitors{
|
|
[&](const Fortran::parser::OpenMPSimpleStandaloneConstruct
|
|
&simpleStandaloneConstruct) {
|
|
genOMP(converter, eval, simpleStandaloneConstruct);
|
|
},
|
|
[&](const Fortran::parser::OpenMPFlushConstruct &flushConstruct) {
|
|
SmallVector<Value, 4> operandRange;
|
|
if (const auto &ompObjectList =
|
|
std::get<std::optional<Fortran::parser::OmpObjectList>>(
|
|
flushConstruct.t))
|
|
genObjectList(*ompObjectList, converter, operandRange);
|
|
const auto &memOrderClause = std::get<std::optional<
|
|
std::list<Fortran::parser::OmpMemoryOrderClause>>>(
|
|
flushConstruct.t);
|
|
if (memOrderClause.has_value() && memOrderClause->size() > 0)
|
|
TODO(converter.getCurrentLocation(),
|
|
"Handle OmpMemoryOrderClause");
|
|
converter.getFirOpBuilder().create<mlir::omp::FlushOp>(
|
|
converter.getCurrentLocation(), operandRange);
|
|
},
|
|
[&](const Fortran::parser::OpenMPCancelConstruct &cancelConstruct) {
|
|
TODO(converter.getCurrentLocation(), "OpenMPCancelConstruct");
|
|
},
|
|
[&](const Fortran::parser::OpenMPCancellationPointConstruct
|
|
&cancellationPointConstruct) {
|
|
TODO(converter.getCurrentLocation(), "OpenMPCancelConstruct");
|
|
},
|
|
},
|
|
standaloneConstruct.u);
|
|
}
|
|
|
|
static void
|
|
genOMP(Fortran::lower::AbstractConverter &converter,
|
|
Fortran::lower::pft::Evaluation &eval,
|
|
const Fortran::parser::OpenMPBlockConstruct &blockConstruct) {
|
|
const auto &beginBlockDirective =
|
|
std::get<Fortran::parser::OmpBeginBlockDirective>(blockConstruct.t);
|
|
const auto &blockDirective =
|
|
std::get<Fortran::parser::OmpBlockDirective>(beginBlockDirective.t);
|
|
const auto &endBlockDirective =
|
|
std::get<Fortran::parser::OmpEndBlockDirective>(blockConstruct.t);
|
|
|
|
auto &firOpBuilder = converter.getFirOpBuilder();
|
|
auto currentLocation = converter.getCurrentLocation();
|
|
Fortran::lower::StatementContext stmtCtx;
|
|
llvm::ArrayRef<mlir::Type> argTy;
|
|
mlir::Value ifClauseOperand, numThreadsClauseOperand;
|
|
mlir::omp::ClauseProcBindKindAttr procBindKindAttr;
|
|
SmallVector<Value> allocateOperands, allocatorOperands;
|
|
mlir::UnitAttr nowaitAttr;
|
|
|
|
for (const auto &clause :
|
|
std::get<Fortran::parser::OmpClauseList>(beginBlockDirective.t).v) {
|
|
if (const auto &ifClause =
|
|
std::get_if<Fortran::parser::OmpClause::If>(&clause.u)) {
|
|
auto &expr = std::get<Fortran::parser::ScalarLogicalExpr>(ifClause->v.t);
|
|
ifClauseOperand = fir::getBase(
|
|
converter.genExprValue(*Fortran::semantics::GetExpr(expr), stmtCtx));
|
|
} else if (const auto &numThreadsClause =
|
|
std::get_if<Fortran::parser::OmpClause::NumThreads>(
|
|
&clause.u)) {
|
|
// OMPIRBuilder expects `NUM_THREAD` clause as a `Value`.
|
|
numThreadsClauseOperand = fir::getBase(converter.genExprValue(
|
|
*Fortran::semantics::GetExpr(numThreadsClause->v), stmtCtx));
|
|
} else if (const auto &procBindClause =
|
|
std::get_if<Fortran::parser::OmpClause::ProcBind>(
|
|
&clause.u)) {
|
|
omp::ClauseProcBindKind pbKind;
|
|
switch (procBindClause->v.v) {
|
|
case Fortran::parser::OmpProcBindClause::Type::Master:
|
|
pbKind = omp::ClauseProcBindKind::Master;
|
|
break;
|
|
case Fortran::parser::OmpProcBindClause::Type::Close:
|
|
pbKind = omp::ClauseProcBindKind::Close;
|
|
break;
|
|
case Fortran::parser::OmpProcBindClause::Type::Spread:
|
|
pbKind = omp::ClauseProcBindKind::Spread;
|
|
break;
|
|
case Fortran::parser::OmpProcBindClause::Type::Primary:
|
|
pbKind = omp::ClauseProcBindKind::Primary;
|
|
break;
|
|
}
|
|
procBindKindAttr =
|
|
omp::ClauseProcBindKindAttr::get(firOpBuilder.getContext(), pbKind);
|
|
} else if (const auto &allocateClause =
|
|
std::get_if<Fortran::parser::OmpClause::Allocate>(
|
|
&clause.u)) {
|
|
genAllocateClause(converter, allocateClause->v, allocatorOperands,
|
|
allocateOperands);
|
|
} else if (const auto &privateClause =
|
|
std::get_if<Fortran::parser::OmpClause::Private>(
|
|
&clause.u)) {
|
|
// TODO: Handle private. This cannot be a hard TODO because testing for
|
|
// allocate clause requires private variables.
|
|
} else {
|
|
TODO(currentLocation, "OpenMP Block construct clauses");
|
|
}
|
|
}
|
|
|
|
for (const auto &clause :
|
|
std::get<Fortran::parser::OmpClauseList>(endBlockDirective.t).v) {
|
|
if (std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u))
|
|
nowaitAttr = firOpBuilder.getUnitAttr();
|
|
}
|
|
|
|
if (blockDirective.v == llvm::omp::OMPD_parallel) {
|
|
// Create and insert the operation.
|
|
auto parallelOp = firOpBuilder.create<mlir::omp::ParallelOp>(
|
|
currentLocation, argTy, ifClauseOperand, numThreadsClauseOperand,
|
|
allocateOperands, allocatorOperands, /*reduction_vars=*/ValueRange(),
|
|
/*reductions=*/nullptr, procBindKindAttr);
|
|
createBodyOfOp<omp::ParallelOp>(parallelOp, firOpBuilder, currentLocation);
|
|
} else if (blockDirective.v == llvm::omp::OMPD_master) {
|
|
auto masterOp =
|
|
firOpBuilder.create<mlir::omp::MasterOp>(currentLocation, argTy);
|
|
createBodyOfOp<omp::MasterOp>(masterOp, firOpBuilder, currentLocation);
|
|
} else if (blockDirective.v == llvm::omp::OMPD_single) {
|
|
auto singleOp = firOpBuilder.create<mlir::omp::SingleOp>(
|
|
currentLocation, allocateOperands, allocatorOperands, nowaitAttr);
|
|
createBodyOfOp(singleOp, firOpBuilder, currentLocation);
|
|
}
|
|
}
|
|
|
|
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();
|
|
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();
|
|
}
|
|
|
|
uint64_t hint = 0;
|
|
const auto &clauseList = std::get<Fortran::parser::OmpClauseList>(cd.t);
|
|
for (const Fortran::parser::OmpClause &clause : clauseList.v)
|
|
if (auto hintClause =
|
|
std::get_if<Fortran::parser::OmpClause::Hint>(&clause.u)) {
|
|
const auto *expr = Fortran::semantics::GetExpr(hintClause->v);
|
|
hint = *Fortran::evaluate::ToInt64(*expr);
|
|
break;
|
|
}
|
|
|
|
mlir::omp::CriticalOp criticalOp = [&]() {
|
|
if (name.empty()) {
|
|
return firOpBuilder.create<mlir::omp::CriticalOp>(currentLocation,
|
|
FlatSymbolRefAttr());
|
|
} else {
|
|
mlir::ModuleOp module = firOpBuilder.getModule();
|
|
mlir::OpBuilder modBuilder(module.getBodyRegion());
|
|
auto global = module.lookupSymbol<mlir::omp::CriticalDeclareOp>(name);
|
|
if (!global)
|
|
global = modBuilder.create<mlir::omp::CriticalDeclareOp>(
|
|
currentLocation, name, hint);
|
|
return firOpBuilder.create<mlir::omp::CriticalOp>(
|
|
currentLocation, mlir::FlatSymbolRefAttr::get(
|
|
firOpBuilder.getContext(), global.sym_name()));
|
|
}
|
|
}();
|
|
createBodyOfOp<omp::CriticalOp>(criticalOp, firOpBuilder, currentLocation);
|
|
}
|
|
|
|
static void
|
|
genOMP(Fortran::lower::AbstractConverter &converter,
|
|
Fortran::lower::pft::Evaluation &eval,
|
|
const Fortran::parser::OpenMPSectionConstruct §ionConstruct) {
|
|
|
|
auto &firOpBuilder = converter.getFirOpBuilder();
|
|
auto currentLocation = converter.getCurrentLocation();
|
|
mlir::omp::SectionOp sectionOp =
|
|
firOpBuilder.create<mlir::omp::SectionOp>(currentLocation);
|
|
createBodyOfOp<omp::SectionOp>(sectionOp, firOpBuilder, currentLocation);
|
|
}
|
|
|
|
// TODO: Add support for reduction
|
|
static void
|
|
genOMP(Fortran::lower::AbstractConverter &converter,
|
|
Fortran::lower::pft::Evaluation &eval,
|
|
const Fortran::parser::OpenMPSectionsConstruct §ionsConstruct) {
|
|
auto &firOpBuilder = converter.getFirOpBuilder();
|
|
auto currentLocation = converter.getCurrentLocation();
|
|
SmallVector<Value> reductionVars, allocateOperands, allocatorOperands;
|
|
mlir::UnitAttr noWaitClauseOperand;
|
|
const auto §ionsClauseList = std::get<Fortran::parser::OmpClauseList>(
|
|
std::get<Fortran::parser::OmpBeginSectionsDirective>(sectionsConstruct.t)
|
|
.t);
|
|
for (const Fortran::parser::OmpClause &clause : sectionsClauseList.v) {
|
|
|
|
// Reduction Clause
|
|
if (std::get_if<Fortran::parser::OmpClause::Reduction>(&clause.u)) {
|
|
TODO(currentLocation, "OMPC_Reduction");
|
|
|
|
// Allocate clause
|
|
} else if (const auto &allocateClause =
|
|
std::get_if<Fortran::parser::OmpClause::Allocate>(
|
|
&clause.u)) {
|
|
genAllocateClause(converter, allocateClause->v, allocatorOperands,
|
|
allocateOperands);
|
|
}
|
|
}
|
|
const auto &endSectionsClauseList =
|
|
std::get<Fortran::parser::OmpEndSectionsDirective>(sectionsConstruct.t);
|
|
const auto &clauseList =
|
|
std::get<Fortran::parser::OmpClauseList>(endSectionsClauseList.t);
|
|
for (const auto &clause : clauseList.v) {
|
|
// Nowait clause
|
|
if (std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u)) {
|
|
noWaitClauseOperand = firOpBuilder.getUnitAttr();
|
|
}
|
|
}
|
|
|
|
llvm::omp::Directive dir =
|
|
std::get<Fortran::parser::OmpSectionsDirective>(
|
|
std::get<Fortran::parser::OmpBeginSectionsDirective>(
|
|
sectionsConstruct.t)
|
|
.t)
|
|
.v;
|
|
|
|
// Parallel Sections Construct
|
|
if (dir == llvm::omp::Directive::OMPD_parallel_sections) {
|
|
auto parallelOp = firOpBuilder.create<mlir::omp::ParallelOp>(
|
|
currentLocation, /*if_expr_var*/ nullptr, /*num_threads_var*/ nullptr,
|
|
allocateOperands, allocatorOperands, /*reduction_vars=*/ValueRange(),
|
|
/*reductions=*/nullptr, /*proc_bind_val*/ nullptr);
|
|
createBodyOfOp(parallelOp, firOpBuilder, currentLocation);
|
|
auto sectionsOp = firOpBuilder.create<mlir::omp::SectionsOp>(
|
|
currentLocation, /*reduction_vars*/ ValueRange(),
|
|
/*reductions=*/nullptr, /*allocate_vars*/ ValueRange(),
|
|
/*allocators_vars*/ ValueRange(), /*nowait=*/nullptr);
|
|
createBodyOfOp(sectionsOp, firOpBuilder, currentLocation);
|
|
|
|
// Sections Construct
|
|
} else if (dir == llvm::omp::Directive::OMPD_sections) {
|
|
auto sectionsOp = firOpBuilder.create<mlir::omp::SectionsOp>(
|
|
currentLocation, reductionVars, /*reductions = */ nullptr,
|
|
allocateOperands, allocatorOperands, noWaitClauseOperand);
|
|
createBodyOfOp<omp::SectionsOp>(sectionsOp, firOpBuilder, currentLocation);
|
|
}
|
|
}
|
|
|
|
void Fortran::lower::genOpenMPConstruct(
|
|
Fortran::lower::AbstractConverter &converter,
|
|
Fortran::lower::pft::Evaluation &eval,
|
|
const Fortran::parser::OpenMPConstruct &ompConstruct) {
|
|
|
|
std::visit(
|
|
common::visitors{
|
|
[&](const Fortran::parser::OpenMPStandaloneConstruct
|
|
&standaloneConstruct) {
|
|
genOMP(converter, eval, standaloneConstruct);
|
|
},
|
|
[&](const Fortran::parser::OpenMPSectionsConstruct
|
|
§ionsConstruct) {
|
|
genOMP(converter, eval, sectionsConstruct);
|
|
},
|
|
[&](const Fortran::parser::OpenMPSectionConstruct §ionConstruct) {
|
|
genOMP(converter, eval, sectionConstruct);
|
|
},
|
|
[&](const Fortran::parser::OpenMPLoopConstruct &loopConstruct) {
|
|
TODO(converter.getCurrentLocation(), "OpenMPLoopConstruct");
|
|
},
|
|
[&](const Fortran::parser::OpenMPDeclarativeAllocate
|
|
&execAllocConstruct) {
|
|
TODO(converter.getCurrentLocation(), "OpenMPDeclarativeAllocate");
|
|
},
|
|
[&](const Fortran::parser::OpenMPExecutableAllocate
|
|
&execAllocConstruct) {
|
|
TODO(converter.getCurrentLocation(), "OpenMPExecutableAllocate");
|
|
},
|
|
[&](const Fortran::parser::OpenMPBlockConstruct &blockConstruct) {
|
|
genOMP(converter, eval, blockConstruct);
|
|
},
|
|
[&](const Fortran::parser::OpenMPAtomicConstruct &atomicConstruct) {
|
|
TODO(converter.getCurrentLocation(), "OpenMPAtomicConstruct");
|
|
},
|
|
[&](const Fortran::parser::OpenMPCriticalConstruct
|
|
&criticalConstruct) {
|
|
genOMP(converter, eval, criticalConstruct);
|
|
},
|
|
},
|
|
ompConstruct.u);
|
|
}
|