blob: 49bccc1d46c6e6691d77757f72005af4f0f7e877 [file] [log] [blame]
//===-- OpenACC.cpp -- OpenACC 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/OpenACC.h"
#include "flang/Common/idioms.h"
#include "flang/Lower/Bridge.h"
#include "flang/Lower/FIRBuilder.h"
#include "flang/Lower/PFTBuilder.h"
#include "flang/Lower/Support/BoxValue.h"
#include "flang/Lower/Todo.h"
#include "flang/Parser/parse-tree.h"
#include "flang/Semantics/tools.h"
#include "mlir/Dialect/OpenACC/OpenACC.h"
#include "llvm/Frontend/OpenACC/ACC.h.inc"
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::AccObjectList &objectList,
Fortran::lower::AbstractConverter &converter,
SmallVectorImpl<Value> &operands) {
for (const auto &accObject : 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);
}},
accObject.u);
}
}
template <typename Clause>
static void
genObjectListWithModifier(const Clause *x,
Fortran::lower::AbstractConverter &converter,
Fortran::parser::AccDataModifier::Modifier mod,
SmallVectorImpl<Value> &operandsWithModifier,
SmallVectorImpl<Value> &operands) {
const Fortran::parser::AccObjectListWithModifier &listWithModifier = x->v;
const Fortran::parser::AccObjectList &accObjectList =
std::get<Fortran::parser::AccObjectList>(listWithModifier.t);
const auto &modifier =
std::get<std::optional<Fortran::parser::AccDataModifier>>(
listWithModifier.t);
if (modifier && (*modifier).v == mod) {
genObjectList(accObjectList, converter, operandsWithModifier);
} else {
genObjectList(accObjectList, converter, operands);
}
}
static void addOperands(SmallVectorImpl<Value> &operands,
SmallVectorImpl<int32_t> &operandSegments,
const SmallVectorImpl<Value> &clauseOperands) {
operands.append(clauseOperands.begin(), clauseOperands.end());
operandSegments.push_back(clauseOperands.size());
}
static void addOperand(SmallVectorImpl<Value> &operands,
SmallVectorImpl<int32_t> &operandSegments,
const Value &clauseOperand) {
if (clauseOperand) {
operands.push_back(clauseOperand);
operandSegments.push_back(1);
} else {
operandSegments.push_back(0);
}
}
template <typename Op, typename Terminator>
static Op createRegionOp(Fortran::lower::FirOpBuilder &builder,
mlir::Location loc,
const SmallVectorImpl<Value> &operands,
const SmallVectorImpl<int32_t> &operandSegments) {
llvm::ArrayRef<mlir::Type> argTy;
Op op = builder.create<Op>(loc, argTy, operands);
builder.createBlock(&op.getRegion());
auto &block = op.getRegion().back();
builder.setInsertionPointToStart(&block);
builder.create<Terminator>(loc);
op->setAttr(Op::getOperandSegmentSizeAttr(),
builder.getI32VectorAttr(operandSegments));
// Place the insertion point to the start of the first block.
builder.setInsertionPointToStart(&block);
return op;
}
template <typename Op>
static Op createSimpleOp(Fortran::lower::FirOpBuilder &builder,
mlir::Location loc,
const SmallVectorImpl<Value> &operands,
const SmallVectorImpl<int32_t> &operandSegments) {
llvm::ArrayRef<mlir::Type> argTy;
Op op = builder.create<Op>(loc, argTy, operands);
op->setAttr(Op::getOperandSegmentSizeAttr(),
builder.getI32VectorAttr(operandSegments));
return op;
}
static void genACC(Fortran::lower::AbstractConverter &converter,
Fortran::lower::pft::Evaluation &eval,
const Fortran::parser::OpenACCLoopConstruct &loopConstruct) {
const auto &beginLoopDirective =
std::get<Fortran::parser::AccBeginLoopDirective>(loopConstruct.t);
const auto &loopDirective =
std::get<Fortran::parser::AccLoopDirective>(beginLoopDirective.t);
if (loopDirective.v == llvm::acc::ACCD_loop) {
auto &firOpBuilder = converter.getFirOpBuilder();
auto currentLocation = converter.getCurrentLocation();
// Add attribute extracted from clauses.
const auto &accClauseList =
std::get<Fortran::parser::AccClauseList>(beginLoopDirective.t);
mlir::Value workerNum;
mlir::Value vectorLength;
mlir::Value gangNum;
mlir::Value gangStatic;
SmallVector<Value, 2> tileOperands, privateOperands, reductionOperands;
std::int64_t executionMapping = mlir::acc::OpenACCExecMapping::NONE;
// Lower clauses values mapped to operands.
for (const auto &clause : accClauseList.v) {
if (const auto *gangClause =
std::get_if<Fortran::parser::AccClause::Gang>(&clause.u)) {
if (gangClause->v) {
const Fortran::parser::AccGangArgument &x = *gangClause->v;
if (const auto &gangNumValue =
std::get<std::optional<Fortran::parser::ScalarIntExpr>>(
x.t)) {
gangNum = fir::getBase(converter.genExprValue(
*Fortran::semantics::GetExpr(gangNumValue.value())));
}
if (const auto &gangStaticValue =
std::get<std::optional<Fortran::parser::AccSizeExpr>>(x.t)) {
const auto &expr =
std::get<std::optional<Fortran::parser::ScalarIntExpr>>(
gangStaticValue.value().t);
if (expr) {
gangStatic = fir::getBase(
converter.genExprValue(*Fortran::semantics::GetExpr(*expr)));
} else {
// * was passed as value and will be represented as a -1 constant
// integer.
gangStatic = firOpBuilder.createIntegerConstant(
currentLocation, firOpBuilder.getIntegerType(32),
/* STAR */ -1);
}
}
}
executionMapping |= mlir::acc::OpenACCExecMapping::GANG;
} else if (const auto *workerClause =
std::get_if<Fortran::parser::AccClause::Worker>(
&clause.u)) {
if (workerClause->v) {
workerNum = fir::getBase(converter.genExprValue(
*Fortran::semantics::GetExpr(*workerClause->v)));
}
executionMapping |= mlir::acc::OpenACCExecMapping::WORKER;
} else if (const auto *vectorClause =
std::get_if<Fortran::parser::AccClause::Vector>(
&clause.u)) {
if (vectorClause->v) {
vectorLength = fir::getBase(converter.genExprValue(
*Fortran::semantics::GetExpr(*vectorClause->v)));
}
executionMapping |= mlir::acc::OpenACCExecMapping::VECTOR;
} else if (const auto *tileClause =
std::get_if<Fortran::parser::AccClause::Tile>(&clause.u)) {
const Fortran::parser::AccTileExprList &accTileExprList = tileClause->v;
for (const auto &accTileExpr : accTileExprList.v) {
const auto &expr =
std::get<std::optional<Fortran::parser::ScalarIntConstantExpr>>(
accTileExpr.t);
if (expr) {
tileOperands.push_back(fir::getBase(
converter.genExprValue(*Fortran::semantics::GetExpr(*expr))));
} else {
// * was passed as value and will be represented as a -1 constant
// integer.
mlir::Value tileStar = firOpBuilder.createIntegerConstant(
currentLocation, firOpBuilder.getIntegerType(32),
/* STAR */ -1);
tileOperands.push_back(tileStar);
}
}
} else if (const auto *privateClause =
std::get_if<Fortran::parser::AccClause::Private>(
&clause.u)) {
genObjectList(privateClause->v, converter, privateOperands);
}
// Reduction clause is left out for the moment as the clause will probably
// end up having its own operation.
}
// Prepare the operand segement size attribute and the operands value range.
SmallVector<Value, 8> operands;
SmallVector<int32_t, 8> operandSegments;
addOperand(operands, operandSegments, gangNum);
addOperand(operands, operandSegments, gangStatic);
addOperand(operands, operandSegments, workerNum);
addOperand(operands, operandSegments, vectorLength);
addOperands(operands, operandSegments, tileOperands);
addOperands(operands, operandSegments, privateOperands);
addOperands(operands, operandSegments, reductionOperands);
auto loopOp = createRegionOp<mlir::acc::LoopOp, mlir::acc::YieldOp>(
firOpBuilder, currentLocation, operands, operandSegments);
loopOp->setAttr(mlir::acc::LoopOp::getExecutionMappingAttrName(),
firOpBuilder.getI64IntegerAttr(executionMapping));
// Lower clauses mapped to attributes
for (const auto &clause : accClauseList.v) {
if (const auto *collapseClause =
std::get_if<Fortran::parser::AccClause::Collapse>(&clause.u)) {
const auto *expr = Fortran::semantics::GetExpr(collapseClause->v);
const auto collapseValue = Fortran::evaluate::ToInt64(*expr);
if (collapseValue) {
loopOp->setAttr(mlir::acc::LoopOp::getCollapseAttrName(),
firOpBuilder.getI64IntegerAttr(*collapseValue));
}
} else if (std::get_if<Fortran::parser::AccClause::Seq>(&clause.u)) {
loopOp->setAttr(mlir::acc::LoopOp::getSeqAttrName(),
firOpBuilder.getUnitAttr());
} else if (std::get_if<Fortran::parser::AccClause::Independent>(
&clause.u)) {
loopOp->setAttr(mlir::acc::LoopOp::getIndependentAttrName(),
firOpBuilder.getUnitAttr());
} else if (std::get_if<Fortran::parser::AccClause::Auto>(&clause.u)) {
loopOp->setAttr(mlir::acc::LoopOp::getAutoAttrName(),
firOpBuilder.getUnitAttr());
}
}
}
}
static void
genACCParallelOp(Fortran::lower::AbstractConverter &converter,
const Fortran::parser::AccClauseList &accClauseList) {
mlir::Value async;
mlir::Value numGangs;
mlir::Value numWorkers;
mlir::Value vectorLength;
mlir::Value ifCond;
mlir::Value selfCond;
SmallVector<Value, 2> waitOperands, reductionOperands, copyOperands,
copyinOperands, copyinReadonlyOperands, copyoutOperands,
copyoutZeroOperands, createOperands, createZeroOperands, noCreateOperands,
presentOperands, devicePtrOperands, attachOperands, privateOperands,
firstprivateOperands;
// Async, wait and self clause have optional values but can be present with
// no value as well. When there is no value, the op has an attribute to
// represent the clause.
bool addAsyncAttr = false;
bool addWaitAttr = false;
bool addSelfAttr = false;
auto &firOpBuilder = converter.getFirOpBuilder();
auto currentLocation = converter.getCurrentLocation();
// Lower clauses values mapped to operands.
// Keep track of each group of operands separatly as clauses can appear
// more than once.
for (const auto &clause : accClauseList.v) {
if (const auto *asyncClause =
std::get_if<Fortran::parser::AccClause::Async>(&clause.u)) {
const auto &asyncClauseValue = asyncClause->v;
if (asyncClauseValue) { // async has a value.
async = fir::getBase(converter.genExprValue(
*Fortran::semantics::GetExpr(*asyncClauseValue)));
} else {
addAsyncAttr = true;
}
} else if (const auto *waitClause =
std::get_if<Fortran::parser::AccClause::Wait>(&clause.u)) {
const auto &waitClauseValue = waitClause->v;
if (waitClauseValue) { // wait has a value.
const Fortran::parser::AccWaitArgument &waitArg = *waitClauseValue;
const std::list<Fortran::parser::ScalarIntExpr> &waitList =
std::get<std::list<Fortran::parser::ScalarIntExpr>>(waitArg.t);
for (const Fortran::parser::ScalarIntExpr &value : waitList) {
Value v = fir::getBase(
converter.genExprValue(*Fortran::semantics::GetExpr(value)));
waitOperands.push_back(v);
}
} else {
addWaitAttr = true;
}
} else if (const auto *numGangsClause =
std::get_if<Fortran::parser::AccClause::NumGangs>(
&clause.u)) {
numGangs = fir::getBase(converter.genExprValue(
*Fortran::semantics::GetExpr(numGangsClause->v)));
} else if (const auto *numWorkersClause =
std::get_if<Fortran::parser::AccClause::NumWorkers>(
&clause.u)) {
numWorkers = fir::getBase(converter.genExprValue(
*Fortran::semantics::GetExpr(numWorkersClause->v)));
} else if (const auto *vectorLengthClause =
std::get_if<Fortran::parser::AccClause::VectorLength>(
&clause.u)) {
vectorLength = fir::getBase(converter.genExprValue(
*Fortran::semantics::GetExpr(vectorLengthClause->v)));
} else if (const auto *ifClause =
std::get_if<Fortran::parser::AccClause::If>(&clause.u)) {
Value cond = fir::getBase(
converter.genExprValue(*Fortran::semantics::GetExpr(ifClause->v)));
ifCond = firOpBuilder.createConvert(currentLocation,
firOpBuilder.getI1Type(), cond);
} else if (const auto *selfClause =
std::get_if<Fortran::parser::AccClause::Self>(&clause.u)) {
const Fortran::parser::AccSelfClause &accSelfClause = selfClause->v;
if (const auto *optCondition =
std::get_if<std::optional<Fortran::parser::ScalarLogicalExpr>>(
&accSelfClause.u)) {
if (*optCondition) {
Value cond = fir::getBase(converter.genExprValue(
*Fortran::semantics::GetExpr(*optCondition)));
selfCond = firOpBuilder.createConvert(currentLocation,
firOpBuilder.getI1Type(), cond);
} else {
addSelfAttr = true;
}
}
} else if (const auto *copyClause =
std::get_if<Fortran::parser::AccClause::Copy>(&clause.u)) {
genObjectList(copyClause->v, converter, copyOperands);
} else if (const auto *copyinClause =
std::get_if<Fortran::parser::AccClause::Copyin>(&clause.u)) {
genObjectListWithModifier<Fortran::parser::AccClause::Copyin>(
copyinClause, converter,
Fortran::parser::AccDataModifier::Modifier::ReadOnly,
copyinReadonlyOperands, copyinOperands);
} else if (const auto *copyoutClause =
std::get_if<Fortran::parser::AccClause::Copyout>(
&clause.u)) {
genObjectListWithModifier<Fortran::parser::AccClause::Copyout>(
copyoutClause, converter,
Fortran::parser::AccDataModifier::Modifier::Zero, copyoutZeroOperands,
copyoutOperands);
} else if (const auto *createClause =
std::get_if<Fortran::parser::AccClause::Create>(&clause.u)) {
genObjectListWithModifier<Fortran::parser::AccClause::Create>(
createClause, converter,
Fortran::parser::AccDataModifier::Modifier::Zero, createZeroOperands,
createOperands);
} else if (const auto *noCreateClause =
std::get_if<Fortran::parser::AccClause::NoCreate>(
&clause.u)) {
genObjectList(noCreateClause->v, converter, noCreateOperands);
} else if (const auto *presentClause =
std::get_if<Fortran::parser::AccClause::Present>(
&clause.u)) {
genObjectList(presentClause->v, converter, presentOperands);
} else if (const auto *devicePtrClause =
std::get_if<Fortran::parser::AccClause::Deviceptr>(
&clause.u)) {
genObjectList(devicePtrClause->v, converter, devicePtrOperands);
} else if (const auto *attachClause =
std::get_if<Fortran::parser::AccClause::Attach>(&clause.u)) {
genObjectList(attachClause->v, converter, attachOperands);
} else if (const auto *privateClause =
std::get_if<Fortran::parser::AccClause::Private>(
&clause.u)) {
genObjectList(privateClause->v, converter, privateOperands);
} else if (const auto *firstprivateClause =
std::get_if<Fortran::parser::AccClause::Firstprivate>(
&clause.u)) {
genObjectList(firstprivateClause->v, converter, firstprivateOperands);
}
}
// Prepare the operand segement size attribute and the operands value range.
SmallVector<Value, 8> operands;
SmallVector<int32_t, 8> operandSegments;
addOperand(operands, operandSegments, async);
addOperands(operands, operandSegments, waitOperands);
addOperand(operands, operandSegments, numGangs);
addOperand(operands, operandSegments, numWorkers);
addOperand(operands, operandSegments, vectorLength);
addOperand(operands, operandSegments, ifCond);
addOperand(operands, operandSegments, selfCond);
addOperands(operands, operandSegments, reductionOperands);
addOperands(operands, operandSegments, copyOperands);
addOperands(operands, operandSegments, copyinOperands);
addOperands(operands, operandSegments, copyinReadonlyOperands);
addOperands(operands, operandSegments, copyoutOperands);
addOperands(operands, operandSegments, copyoutZeroOperands);
addOperands(operands, operandSegments, createOperands);
addOperands(operands, operandSegments, createZeroOperands);
addOperands(operands, operandSegments, noCreateOperands);
addOperands(operands, operandSegments, presentOperands);
addOperands(operands, operandSegments, devicePtrOperands);
addOperands(operands, operandSegments, attachOperands);
addOperands(operands, operandSegments, privateOperands);
addOperands(operands, operandSegments, firstprivateOperands);
auto parallelOp = createRegionOp<mlir::acc::ParallelOp, mlir::acc::YieldOp>(
firOpBuilder, currentLocation, operands, operandSegments);
if (addAsyncAttr)
parallelOp->setAttr(mlir::acc::ParallelOp::getAsyncAttrName(),
firOpBuilder.getUnitAttr());
if (addWaitAttr)
parallelOp->setAttr(mlir::acc::ParallelOp::getWaitAttrName(),
firOpBuilder.getUnitAttr());
if (addSelfAttr)
parallelOp->setAttr(mlir::acc::ParallelOp::getSelfAttrName(),
firOpBuilder.getUnitAttr());
}
static void genACCDataOp(Fortran::lower::AbstractConverter &converter,
const Fortran::parser::AccClauseList &accClauseList) {
mlir::Value ifCond;
SmallVector<Value, 2> copyOperands, copyinOperands, copyinReadonlyOperands,
copyoutOperands, copyoutZeroOperands, createOperands, createZeroOperands,
noCreateOperands, presentOperands, deviceptrOperands, attachOperands;
auto &firOpBuilder = converter.getFirOpBuilder();
auto currentLocation = converter.getCurrentLocation();
// Lower clauses values mapped to operands.
// Keep track of each group of operands separatly as clauses can appear
// more than once.
for (const auto &clause : accClauseList.v) {
if (const auto *ifClause =
std::get_if<Fortran::parser::AccClause::If>(&clause.u)) {
Value cond = fir::getBase(
converter.genExprValue(*Fortran::semantics::GetExpr(ifClause->v)));
ifCond = firOpBuilder.createConvert(currentLocation,
firOpBuilder.getI1Type(), cond);
} else if (const auto *copyClause =
std::get_if<Fortran::parser::AccClause::Copy>(&clause.u)) {
genObjectList(copyClause->v, converter, copyOperands);
} else if (const auto *copyinClause =
std::get_if<Fortran::parser::AccClause::Copyin>(&clause.u)) {
genObjectListWithModifier<Fortran::parser::AccClause::Copyin>(
copyinClause, converter,
Fortran::parser::AccDataModifier::Modifier::ReadOnly,
copyinReadonlyOperands, copyinOperands);
} else if (const auto *copyoutClause =
std::get_if<Fortran::parser::AccClause::Copyout>(
&clause.u)) {
genObjectListWithModifier<Fortran::parser::AccClause::Copyout>(
copyoutClause, converter,
Fortran::parser::AccDataModifier::Modifier::Zero, copyoutZeroOperands,
copyoutOperands);
} else if (const auto *createClause =
std::get_if<Fortran::parser::AccClause::Create>(&clause.u)) {
genObjectListWithModifier<Fortran::parser::AccClause::Create>(
createClause, converter,
Fortran::parser::AccDataModifier::Modifier::Zero, createZeroOperands,
createOperands);
} else if (const auto *noCreateClause =
std::get_if<Fortran::parser::AccClause::NoCreate>(
&clause.u)) {
genObjectList(noCreateClause->v, converter, noCreateOperands);
} else if (const auto *presentClause =
std::get_if<Fortran::parser::AccClause::Present>(
&clause.u)) {
genObjectList(presentClause->v, converter, presentOperands);
} else if (const auto *deviceptrClause =
std::get_if<Fortran::parser::AccClause::Deviceptr>(
&clause.u)) {
genObjectList(deviceptrClause->v, converter, deviceptrOperands);
} else if (const auto *attachClause =
std::get_if<Fortran::parser::AccClause::Attach>(&clause.u)) {
genObjectList(attachClause->v, converter, attachOperands);
}
}
// Prepare the operand segement size attribute and the operands value range.
SmallVector<Value, 8> operands;
SmallVector<int32_t, 8> operandSegments;
addOperand(operands, operandSegments, ifCond);
addOperands(operands, operandSegments, copyOperands);
addOperands(operands, operandSegments, copyinOperands);
addOperands(operands, operandSegments, copyinReadonlyOperands);
addOperands(operands, operandSegments, copyoutOperands);
addOperands(operands, operandSegments, copyoutZeroOperands);
addOperands(operands, operandSegments, createOperands);
addOperands(operands, operandSegments, createZeroOperands);
addOperands(operands, operandSegments, noCreateOperands);
addOperands(operands, operandSegments, presentOperands);
addOperands(operands, operandSegments, deviceptrOperands);
addOperands(operands, operandSegments, attachOperands);
createRegionOp<mlir::acc::DataOp, mlir::acc::TerminatorOp>(
firOpBuilder, currentLocation, operands, operandSegments);
}
static void
genACC(Fortran::lower::AbstractConverter &converter,
Fortran::lower::pft::Evaluation &eval,
const Fortran::parser::OpenACCBlockConstruct &blockConstruct) {
const auto &beginBlockDirective =
std::get<Fortran::parser::AccBeginBlockDirective>(blockConstruct.t);
const auto &blockDirective =
std::get<Fortran::parser::AccBlockDirective>(beginBlockDirective.t);
const auto &accClauseList =
std::get<Fortran::parser::AccClauseList>(beginBlockDirective.t);
if (blockDirective.v == llvm::acc::ACCD_parallel) {
genACCParallelOp(converter, accClauseList);
} else if (blockDirective.v == llvm::acc::ACCD_data) {
genACCDataOp(converter, accClauseList);
}
}
static void
genACCEnterDataOp(Fortran::lower::AbstractConverter &converter,
const Fortran::parser::AccClauseList &accClauseList) {
mlir::Value ifCond, async, waitDevnum;
SmallVector<Value, 2> copyinOperands, createOperands, createZeroOperands,
attachOperands, waitOperands;
// Async, wait and self clause have optional values but can be present with
// no value as well. When there is no value, the op has an attribute to
// represent the clause.
bool addAsyncAttr = false;
bool addWaitAttr = false;
auto &firOpBuilder = converter.getFirOpBuilder();
auto currentLocation = converter.getCurrentLocation();
// Lower clauses values mapped to operands.
// Keep track of each group of operands separatly as clauses can appear
// more than once.
for (const auto &clause : accClauseList.v) {
if (const auto *ifClause =
std::get_if<Fortran::parser::AccClause::If>(&clause.u)) {
mlir::Value cond = fir::getBase(
converter.genExprValue(*Fortran::semantics::GetExpr(ifClause->v)));
ifCond = firOpBuilder.createConvert(currentLocation,
firOpBuilder.getI1Type(), cond);
} else if (const auto *asyncClause =
std::get_if<Fortran::parser::AccClause::Async>(&clause.u)) {
const auto &asyncClauseValue = asyncClause->v;
if (asyncClauseValue) { // async has a value.
async = fir::getBase(converter.genExprValue(
*Fortran::semantics::GetExpr(*asyncClauseValue)));
} else {
addAsyncAttr = true;
}
} else if (const auto *waitClause =
std::get_if<Fortran::parser::AccClause::Wait>(&clause.u)) {
const auto &waitClauseValue = waitClause->v;
if (waitClauseValue) { // wait has a value.
const Fortran::parser::AccWaitArgument &waitArg = *waitClauseValue;
const std::list<Fortran::parser::ScalarIntExpr> &waitList =
std::get<std::list<Fortran::parser::ScalarIntExpr>>(waitArg.t);
for (const Fortran::parser::ScalarIntExpr &value : waitList) {
mlir::Value v = fir::getBase(
converter.genExprValue(*Fortran::semantics::GetExpr(value)));
waitOperands.push_back(v);
}
const std::optional<Fortran::parser::ScalarIntExpr> &waitDevnumValue =
std::get<std::optional<Fortran::parser::ScalarIntExpr>>(waitArg.t);
if (waitDevnumValue)
waitDevnum = fir::getBase(converter.genExprValue(
*Fortran::semantics::GetExpr(*waitDevnumValue)));
} else {
addWaitAttr = true;
}
} else if (const auto *copyinClause =
std::get_if<Fortran::parser::AccClause::Copyin>(&clause.u)) {
const Fortran::parser::AccObjectListWithModifier &listWithModifier =
copyinClause->v;
const Fortran::parser::AccObjectList &accObjectList =
std::get<Fortran::parser::AccObjectList>(listWithModifier.t);
genObjectList(accObjectList, converter, copyinOperands);
} else if (const auto *createClause =
std::get_if<Fortran::parser::AccClause::Create>(&clause.u)) {
genObjectListWithModifier<Fortran::parser::AccClause::Create>(
createClause, converter,
Fortran::parser::AccDataModifier::Modifier::Zero, createZeroOperands,
createOperands);
} else if (const auto *attachClause =
std::get_if<Fortran::parser::AccClause::Attach>(&clause.u)) {
genObjectList(attachClause->v, converter, attachOperands);
} else {
llvm::report_fatal_error(
"Unknown clause in ENTER DATA directive lowering");
}
}
// Prepare the operand segement size attribute and the operands value range.
SmallVector<mlir::Value, 16> operands;
SmallVector<int32_t, 8> operandSegments;
addOperand(operands, operandSegments, ifCond);
addOperand(operands, operandSegments, async);
addOperand(operands, operandSegments, waitDevnum);
addOperands(operands, operandSegments, waitOperands);
addOperands(operands, operandSegments, copyinOperands);
addOperands(operands, operandSegments, createOperands);
addOperands(operands, operandSegments, createZeroOperands);
addOperands(operands, operandSegments, attachOperands);
auto enterDataOp = createSimpleOp<mlir::acc::EnterDataOp>(
firOpBuilder, currentLocation, operands, operandSegments);
if (addAsyncAttr)
enterDataOp.asyncAttr(firOpBuilder.getUnitAttr());
if (addWaitAttr)
enterDataOp.waitAttr(firOpBuilder.getUnitAttr());
}
static void
genACCExitDataOp(Fortran::lower::AbstractConverter &converter,
const Fortran::parser::AccClauseList &accClauseList) {
mlir::Value ifCond, async, waitDevnum;
SmallVector<Value, 2> copyoutOperands, deleteOperands, detachOperands,
waitOperands;
// Async and wait clause have optional values but can be present with
// no value as well. When there is no value, the op has an attribute to
// represent the clause.
bool addAsyncAttr = false;
bool addWaitAttr = false;
bool addFinalizeAttr = false;
auto &firOpBuilder = converter.getFirOpBuilder();
auto currentLocation = converter.getCurrentLocation();
// Lower clauses values mapped to operands.
// Keep track of each group of operands separatly as clauses can appear
// more than once.
for (const auto &clause : accClauseList.v) {
if (const auto *ifClause =
std::get_if<Fortran::parser::AccClause::If>(&clause.u)) {
Value cond = fir::getBase(
converter.genExprValue(*Fortran::semantics::GetExpr(ifClause->v)));
ifCond = firOpBuilder.createConvert(currentLocation,
firOpBuilder.getI1Type(), cond);
} else if (const auto *asyncClause =
std::get_if<Fortran::parser::AccClause::Async>(&clause.u)) {
const auto &asyncClauseValue = asyncClause->v;
if (asyncClauseValue) { // async has a value.
async = fir::getBase(converter.genExprValue(
*Fortran::semantics::GetExpr(*asyncClauseValue)));
} else {
addAsyncAttr = true;
}
} else if (const auto *waitClause =
std::get_if<Fortran::parser::AccClause::Wait>(&clause.u)) {
const auto &waitClauseValue = waitClause->v;
if (waitClauseValue) { // wait has a value.
const Fortran::parser::AccWaitArgument &waitArg = *waitClauseValue;
const std::list<Fortran::parser::ScalarIntExpr> &waitList =
std::get<std::list<Fortran::parser::ScalarIntExpr>>(waitArg.t);
for (const Fortran::parser::ScalarIntExpr &value : waitList) {
Value v = fir::getBase(
converter.genExprValue(*Fortran::semantics::GetExpr(value)));
waitOperands.push_back(v);
}
const std::optional<Fortran::parser::ScalarIntExpr> &waitDevnumValue =
std::get<std::optional<Fortran::parser::ScalarIntExpr>>(waitArg.t);
if (waitDevnumValue)
waitDevnum = fir::getBase(converter.genExprValue(
*Fortran::semantics::GetExpr(*waitDevnumValue)));
} else {
addWaitAttr = true;
}
} else if (const auto *copyoutClause =
std::get_if<Fortran::parser::AccClause::Copyout>(
&clause.u)) {
const Fortran::parser::AccObjectListWithModifier &listWithModifier =
copyoutClause->v;
const Fortran::parser::AccObjectList &accObjectList =
std::get<Fortran::parser::AccObjectList>(listWithModifier.t);
genObjectList(accObjectList, converter, copyoutOperands);
} else if (const auto *deleteClause =
std::get_if<Fortran::parser::AccClause::Delete>(&clause.u)) {
genObjectList(deleteClause->v, converter, deleteOperands);
} else if (const auto *detachClause =
std::get_if<Fortran::parser::AccClause::Detach>(&clause.u)) {
genObjectList(detachClause->v, converter, detachOperands);
} else if (std::get_if<Fortran::parser::AccClause::Finalize>(&clause.u)) {
addFinalizeAttr = true;
}
}
// Prepare the operand segement size attribute and the operands value range.
SmallVector<mlir::Value, 14> operands;
SmallVector<int32_t, 7> operandSegments;
addOperand(operands, operandSegments, ifCond);
addOperand(operands, operandSegments, async);
addOperand(operands, operandSegments, waitDevnum);
addOperands(operands, operandSegments, waitOperands);
addOperands(operands, operandSegments, copyoutOperands);
addOperands(operands, operandSegments, deleteOperands);
addOperands(operands, operandSegments, detachOperands);
auto exitDataOp = createSimpleOp<mlir::acc::ExitDataOp>(
firOpBuilder, currentLocation, operands, operandSegments);
if (addAsyncAttr)
exitDataOp.asyncAttr(firOpBuilder.getUnitAttr());
if (addWaitAttr)
exitDataOp.waitAttr(firOpBuilder.getUnitAttr());
if (addFinalizeAttr)
exitDataOp.finalizeAttr(firOpBuilder.getUnitAttr());
}
template <typename Op>
static void
genACCInitShutdownOp(Fortran::lower::AbstractConverter &converter,
const Fortran::parser::AccClauseList &accClauseList) {
mlir::Value ifCond, deviceNum;
SmallVector<Value, 2> deviceTypeOperands;
auto &firOpBuilder = converter.getFirOpBuilder();
auto currentLocation = converter.getCurrentLocation();
// Lower clauses values mapped to operands.
// Keep track of each group of operands separatly as clauses can appear
// more than once.
for (const auto &clause : accClauseList.v) {
if (const auto *ifClause =
std::get_if<Fortran::parser::AccClause::If>(&clause.u)) {
mlir::Value cond = fir::getBase(
converter.genExprValue(*Fortran::semantics::GetExpr(ifClause->v)));
ifCond = firOpBuilder.createConvert(currentLocation,
firOpBuilder.getI1Type(), cond);
} else if (const auto *deviceNumClause =
std::get_if<Fortran::parser::AccClause::DeviceNum>(
&clause.u)) {
deviceNum = fir::getBase(converter.genExprValue(
*Fortran::semantics::GetExpr(deviceNumClause->v)));
} else if (const auto *deviceTypeClause =
std::get_if<Fortran::parser::AccClause::DeviceType>(
&clause.u)) {
const auto &deviceTypeValue = deviceTypeClause->v;
if (deviceTypeValue) {
for (const auto &scalarIntExpr : *deviceTypeValue) {
mlir::Value expr = fir::getBase(converter.genExprValue(
*Fortran::semantics::GetExpr(scalarIntExpr)));
deviceTypeOperands.push_back(expr);
}
} else {
// * was passed as value and will be represented as a -1 constant
// integer.
mlir::Value star = firOpBuilder.createIntegerConstant(
currentLocation, firOpBuilder.getIntegerType(32), /* STAR */ -1);
deviceTypeOperands.push_back(star);
}
}
}
// Prepare the operand segement size attribute and the operands value range.
SmallVector<mlir::Value, 6> operands;
SmallVector<int32_t, 3> operandSegments;
addOperands(operands, operandSegments, deviceTypeOperands);
addOperand(operands, operandSegments, deviceNum);
addOperand(operands, operandSegments, ifCond);
createSimpleOp<Op>(firOpBuilder, currentLocation, operands, operandSegments);
}
static void
genACCUpdateOp(Fortran::lower::AbstractConverter &converter,
const Fortran::parser::AccClauseList &accClauseList) {
mlir::Value ifCond, async, waitDevnum;
SmallVector<Value, 2> hostOperands, deviceOperands, waitOperands,
deviceTypeOperands;
// Async and wait clause have optional values but can be present with
// no value as well. When there is no value, the op has an attribute to
// represent the clause.
bool addAsyncAttr = false;
bool addWaitAttr = false;
bool addIfPresentAttr = false;
auto &firOpBuilder = converter.getFirOpBuilder();
auto currentLocation = converter.getCurrentLocation();
// Lower clauses values mapped to operands.
// Keep track of each group of operands separatly as clauses can appear
// more than once.
for (const auto &clause : accClauseList.v) {
if (const auto *ifClause =
std::get_if<Fortran::parser::AccClause::If>(&clause.u)) {
mlir::Value cond = fir::getBase(
converter.genExprValue(*Fortran::semantics::GetExpr(ifClause->v)));
ifCond = firOpBuilder.createConvert(currentLocation,
firOpBuilder.getI1Type(), cond);
} else if (const auto *asyncClause =
std::get_if<Fortran::parser::AccClause::Async>(&clause.u)) {
const auto &asyncClauseValue = asyncClause->v;
if (asyncClauseValue) { // async has a value.
async = fir::getBase(converter.genExprValue(
*Fortran::semantics::GetExpr(*asyncClauseValue)));
} else {
addAsyncAttr = true;
}
} else if (const auto *waitClause =
std::get_if<Fortran::parser::AccClause::Wait>(&clause.u)) {
const auto &waitClauseValue = waitClause->v;
if (waitClauseValue) { // wait has a value.
const Fortran::parser::AccWaitArgument &waitArg = *waitClauseValue;
const std::list<Fortran::parser::ScalarIntExpr> &waitList =
std::get<std::list<Fortran::parser::ScalarIntExpr>>(waitArg.t);
for (const Fortran::parser::ScalarIntExpr &value : waitList) {
mlir::Value v = fir::getBase(
converter.genExprValue(*Fortran::semantics::GetExpr(value)));
waitOperands.push_back(v);
}
const std::optional<Fortran::parser::ScalarIntExpr> &waitDevnumValue =
std::get<std::optional<Fortran::parser::ScalarIntExpr>>(waitArg.t);
if (waitDevnumValue)
waitDevnum = fir::getBase(converter.genExprValue(
*Fortran::semantics::GetExpr(*waitDevnumValue)));
} else {
addWaitAttr = true;
}
} else if (const auto *deviceTypeClause =
std::get_if<Fortran::parser::AccClause::DeviceType>(
&clause.u)) {
const auto &deviceTypeValue = deviceTypeClause->v;
if (deviceTypeValue) {
for (const auto &scalarIntExpr : *deviceTypeValue) {
mlir::Value expr = fir::getBase(converter.genExprValue(
*Fortran::semantics::GetExpr(scalarIntExpr)));
deviceTypeOperands.push_back(expr);
}
} else {
// * was passed as value and will be represented as a -1 constant
// integer.
mlir::Value star = firOpBuilder.createIntegerConstant(
currentLocation, firOpBuilder.getIntegerType(32), /* STAR */ -1);
deviceTypeOperands.push_back(star);
}
} else if (const auto *hostClause =
std::get_if<Fortran::parser::AccClause::Host>(&clause.u)) {
genObjectList(hostClause->v, converter, hostOperands);
} else if (const auto *deviceClause =
std::get_if<Fortran::parser::AccClause::Device>(&clause.u)) {
genObjectList(deviceClause->v, converter, deviceOperands);
}
}
// Prepare the operand segement size attribute and the operands value range.
SmallVector<mlir::Value, 14> operands;
SmallVector<int32_t, 7> operandSegments;
addOperand(operands, operandSegments, async);
addOperand(operands, operandSegments, waitDevnum);
addOperands(operands, operandSegments, waitOperands);
addOperands(operands, operandSegments, deviceTypeOperands);
addOperand(operands, operandSegments, ifCond);
addOperands(operands, operandSegments, hostOperands);
addOperands(operands, operandSegments, deviceOperands);
auto updateOp = createSimpleOp<mlir::acc::UpdateOp>(
firOpBuilder, currentLocation, operands, operandSegments);
if (addAsyncAttr)
updateOp.asyncAttr(firOpBuilder.getUnitAttr());
if (addWaitAttr)
updateOp.waitAttr(firOpBuilder.getUnitAttr());
if (addIfPresentAttr)
updateOp.ifPresentAttr(firOpBuilder.getUnitAttr());
}
static void
genACC(Fortran::lower::AbstractConverter &converter,
Fortran::lower::pft::Evaluation &eval,
const Fortran::parser::OpenACCStandaloneConstruct &standaloneConstruct) {
const auto &standaloneDirective =
std::get<Fortran::parser::AccStandaloneDirective>(standaloneConstruct.t);
const auto &accClauseList =
std::get<Fortran::parser::AccClauseList>(standaloneConstruct.t);
if (standaloneDirective.v == llvm::acc::Directive::ACCD_enter_data) {
genACCEnterDataOp(converter, accClauseList);
} else if (standaloneDirective.v == llvm::acc::Directive::ACCD_exit_data) {
genACCExitDataOp(converter, accClauseList);
} else if (standaloneDirective.v == llvm::acc::Directive::ACCD_init) {
genACCInitShutdownOp<mlir::acc::InitOp>(converter, accClauseList);
} else if (standaloneDirective.v == llvm::acc::Directive::ACCD_shutdown) {
genACCInitShutdownOp<mlir::acc::ShutdownOp>(converter, accClauseList);
} else if (standaloneDirective.v == llvm::acc::Directive::ACCD_set) {
TODO(converter.genLocation(), "OpenACC set directive not lowered yet!");
} else if (standaloneDirective.v == llvm::acc::Directive::ACCD_update) {
genACCUpdateOp(converter, accClauseList);
}
}
static void genACC(Fortran::lower::AbstractConverter &converter,
Fortran::lower::pft::Evaluation &eval,
const Fortran::parser::OpenACCWaitConstruct &waitConstruct) {
const auto &waitArgument =
std::get<std::optional<Fortran::parser::AccWaitArgument>>(
waitConstruct.t);
const auto &accClauseList =
std::get<Fortran::parser::AccClauseList>(waitConstruct.t);
mlir::Value ifCond, asyncOperand, waitDevnum, async;
SmallVector<mlir::Value, 2> waitOperands;
// Async clause have optional values but can be present with
// no value as well. When there is no value, the op has an attribute to
// represent the clause.
bool addAsyncAttr = false;
auto &firOpBuilder = converter.getFirOpBuilder();
auto currentLocation = converter.getCurrentLocation();
if (waitArgument) { // wait has a value.
const Fortran::parser::AccWaitArgument &waitArg = *waitArgument;
const std::list<Fortran::parser::ScalarIntExpr> &waitList =
std::get<std::list<Fortran::parser::ScalarIntExpr>>(waitArg.t);
for (const Fortran::parser::ScalarIntExpr &value : waitList) {
mlir::Value v = fir::getBase(
converter.genExprValue(*Fortran::semantics::GetExpr(value)));
waitOperands.push_back(v);
}
const std::optional<Fortran::parser::ScalarIntExpr> &waitDevnumValue =
std::get<std::optional<Fortran::parser::ScalarIntExpr>>(waitArg.t);
if (waitDevnumValue)
waitDevnum = fir::getBase(converter.genExprValue(
*Fortran::semantics::GetExpr(*waitDevnumValue)));
}
// Lower clauses values mapped to operands.
// Keep track of each group of operands separatly as clauses can appear
// more than once.
for (const auto &clause : accClauseList.v) {
if (const auto *ifClause =
std::get_if<Fortran::parser::AccClause::If>(&clause.u)) {
mlir::Value cond = fir::getBase(
converter.genExprValue(*Fortran::semantics::GetExpr(ifClause->v)));
ifCond = firOpBuilder.createConvert(currentLocation,
firOpBuilder.getI1Type(), cond);
} else if (const auto *asyncClause =
std::get_if<Fortran::parser::AccClause::Async>(&clause.u)) {
const auto &asyncClauseValue = asyncClause->v;
if (asyncClauseValue) { // async has a value.
async = fir::getBase(converter.genExprValue(
*Fortran::semantics::GetExpr(*asyncClauseValue)));
} else {
addAsyncAttr = true;
}
}
}
// Prepare the operand segement size attribute and the operands value range.
SmallVector<mlir::Value, 8> operands;
SmallVector<int32_t, 4> operandSegments;
addOperands(operands, operandSegments, waitOperands);
addOperand(operands, operandSegments, async);
addOperand(operands, operandSegments, waitDevnum);
addOperand(operands, operandSegments, ifCond);
auto waitOp = createSimpleOp<mlir::acc::WaitOp>(firOpBuilder, currentLocation,
operands, operandSegments);
if (addAsyncAttr)
waitOp.asyncAttr(firOpBuilder.getUnitAttr());
}
void Fortran::lower::genOpenACCConstruct(
Fortran::lower::AbstractConverter &converter,
Fortran::lower::pft::Evaluation &eval,
const Fortran::parser::OpenACCConstruct &accConstruct) {
std::visit(
common::visitors{
[&](const Fortran::parser::OpenACCBlockConstruct &blockConstruct) {
genACC(converter, eval, blockConstruct);
},
[&](const Fortran::parser::OpenACCCombinedConstruct
&combinedConstruct) {
TODO(converter.genLocation(),
"OpenACC Combined construct not lowered yet!");
},
[&](const Fortran::parser::OpenACCLoopConstruct &loopConstruct) {
genACC(converter, eval, loopConstruct);
},
[&](const Fortran::parser::OpenACCStandaloneConstruct
&standaloneConstruct) {
genACC(converter, eval, standaloneConstruct);
},
[&](const Fortran::parser::OpenACCRoutineConstruct
&routineConstruct) {
TODO(converter.genLocation(),
"OpenACC Routine construct not lowered yet!");
},
[&](const Fortran::parser::OpenACCCacheConstruct &cacheConstruct) {
TODO(converter.genLocation(),
"OpenACC Cache construct not lowered yet!");
},
[&](const Fortran::parser::OpenACCWaitConstruct &waitConstruct) {
genACC(converter, eval, waitConstruct);
},
[&](const Fortran::parser::OpenACCAtomicConstruct &atomicConstruct) {
TODO(converter.genLocation(),
"OpenACC Atomic construct not lowered yet!");
},
},
accConstruct.u);
}