Files
llvm-project/flang/lib/Lower/MultiImageFortran.cpp
Jean-Didier PAILLEUX 6b0d268fe5 [Flang] Adding first lowering for the allocation and deallocation of coarrays (#182110)
This PR add support of coarray allocation and deallocation in Flang and
adds two new operations to MIF:
- `mif::AllocaCoarrayOp` : Allocates a coarray
using `prif_allocate_coarray` PRIF procedure.
- `mif::DeallocaCoarrayOp` : Deallocates a coarray
using `prif_deallocate_coarray` PRIF procedure

This PR does not yet handle allocation for the following cases (which
will be added in future PRs):
- Coarrays with ALLOCATABLE and/or POINTER components (PRIF has
procedures (`prif_(de)allocate`) for this).
- Coarray dummy arguments (PRIF has also procedures for this)
- Finalization of coarrays
- non-ALLOCATABLE SAVE coarrays outside the scoping unit of the main
program (e.g. non-ALLOCATABLE coarrays declared in a module or a
procedure)

Another PR which add support of some basic intrinsics with a coarray as
an argument will follow after this one.

---------

Co-authored-by: Dan Bonachea <dobonachea@lbl.gov>
2026-04-17 10:16:56 +02:00

467 lines
19 KiB
C++

//===-- MultiImageFortran.cpp ---------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
///
/// Implementation of the lowering of image related constructs and expressions.
/// Fortran images can form teams, communicate via coarrays, etc.
///
//===----------------------------------------------------------------------===//
#include "flang/Lower/MultiImageFortran.h"
#include "flang/Lower/AbstractConverter.h"
#include "flang/Lower/Support/Utils.h"
#include "flang/Lower/SymbolMap.h"
#include "flang/Optimizer/Builder/FIRBuilder.h"
#include "flang/Optimizer/Builder/MIFCommon.h"
#include "flang/Optimizer/Builder/Todo.h"
#include "flang/Parser/parse-tree.h"
#include "flang/Semantics/expression.h"
//===----------------------------------------------------------------------===//
// Synchronization statements
//===----------------------------------------------------------------------===//
void Fortran::lower::genSyncAllStatement(
Fortran::lower::AbstractConverter &converter,
const Fortran::parser::SyncAllStmt &stmt) {
mlir::Location loc = converter.getCurrentLocation();
converter.checkCoarrayEnabled();
// Handle STAT and ERRMSG values
const std::list<Fortran::parser::StatOrErrmsg> &statOrErrList = stmt.v;
auto [statAddr, errMsgAddr] = converter.genStatAndErrmsg(loc, statOrErrList);
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
mif::SyncAllOp::create(builder, loc, statAddr, errMsgAddr);
}
void Fortran::lower::genSyncImagesStatement(
Fortran::lower::AbstractConverter &converter,
const Fortran::parser::SyncImagesStmt &stmt) {
mlir::Location loc = converter.getCurrentLocation();
converter.checkCoarrayEnabled();
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
// Handle STAT and ERRMSG values
const std::list<Fortran::parser::StatOrErrmsg> &statOrErrList =
std::get<std::list<Fortran::parser::StatOrErrmsg>>(stmt.t);
auto [statAddr, errMsgAddr] = converter.genStatAndErrmsg(loc, statOrErrList);
// SYNC_IMAGES(*) is passed as count == -1 while SYNC IMAGES([]) has count
// == 0. Note further that SYNC IMAGES(*) is not semantically equivalent to
// SYNC ALL.
Fortran::lower::StatementContext stmtCtx;
mlir::Value imageSet;
const Fortran::parser::SyncImagesStmt::ImageSet &imgSet =
std::get<Fortran::parser::SyncImagesStmt::ImageSet>(stmt.t);
std::visit(Fortran::common::visitors{
[&](const Fortran::parser::IntExpr &intExpr) {
const SomeExpr *expr = Fortran::semantics::GetExpr(intExpr);
imageSet =
fir::getBase(converter.genExprBox(loc, *expr, stmtCtx));
},
[&](const Fortran::parser::Star &) {
// Image set is not set.
imageSet = mlir::Value{};
}},
imgSet.u);
mif::SyncImagesOp::create(builder, loc, imageSet, statAddr, errMsgAddr);
}
void Fortran::lower::genSyncMemoryStatement(
Fortran::lower::AbstractConverter &converter,
const Fortran::parser::SyncMemoryStmt &stmt) {
mlir::Location loc = converter.getCurrentLocation();
converter.checkCoarrayEnabled();
// Handle STAT and ERRMSG values
const std::list<Fortran::parser::StatOrErrmsg> &statOrErrList = stmt.v;
auto [statAddr, errMsgAddr] = converter.genStatAndErrmsg(loc, statOrErrList);
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
mif::SyncMemoryOp::create(builder, loc, statAddr, errMsgAddr);
}
void Fortran::lower::genSyncTeamStatement(
Fortran::lower::AbstractConverter &converter,
const Fortran::parser::SyncTeamStmt &stmt) {
mlir::Location loc = converter.getCurrentLocation();
converter.checkCoarrayEnabled();
// Handle TEAM
Fortran::lower::StatementContext stmtCtx;
const Fortran::parser::TeamValue &teamValue =
std::get<Fortran::parser::TeamValue>(stmt.t);
const SomeExpr *teamExpr = Fortran::semantics::GetExpr(teamValue);
mlir::Value team =
fir::getBase(converter.genExprBox(loc, *teamExpr, stmtCtx));
// Handle STAT and ERRMSG values
const std::list<Fortran::parser::StatOrErrmsg> &statOrErrList =
std::get<std::list<Fortran::parser::StatOrErrmsg>>(stmt.t);
auto [statAddr, errMsgAddr] = converter.genStatAndErrmsg(loc, statOrErrList);
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
mif::SyncTeamOp::create(builder, loc, team, statAddr, errMsgAddr);
}
//===----------------------------------------------------------------------===//
// TEAM statements and constructs
//===----------------------------------------------------------------------===//
void Fortran::lower::genChangeTeamConstruct(
Fortran::lower::AbstractConverter &converter,
Fortran::lower::pft::Evaluation &,
const Fortran::parser::ChangeTeamConstruct &) {
TODO(converter.getCurrentLocation(), "coarray: CHANGE TEAM construct");
}
mif::ChangeTeamOp
Fortran::lower::genChangeTeamStmt(Fortran::lower::AbstractConverter &converter,
Fortran::lower::pft::Evaluation &,
const Fortran::parser::ChangeTeamStmt &stmt) {
mlir::Location loc = converter.getCurrentLocation();
converter.checkCoarrayEnabled();
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
mlir::Value errMsgAddr, statAddr, team;
// Handle STAT and ERRMSG values
Fortran::lower::StatementContext stmtCtx;
const std::list<Fortran::parser::StatOrErrmsg> &statOrErrList =
std::get<std::list<Fortran::parser::StatOrErrmsg>>(stmt.t);
for (const Fortran::parser::StatOrErrmsg &statOrErr : statOrErrList) {
std::visit(Fortran::common::visitors{
[&](const Fortran::parser::StatVariable &statVar) {
const auto *expr = Fortran::semantics::GetExpr(statVar);
statAddr = fir::getBase(
converter.genExprAddr(loc, *expr, stmtCtx));
},
[&](const Fortran::parser::MsgVariable &errMsgVar) {
const auto *expr = Fortran::semantics::GetExpr(errMsgVar);
errMsgAddr = fir::getBase(
converter.genExprBox(loc, *expr, stmtCtx));
},
},
statOrErr.u);
}
// TODO: Manage the list of coarrays associated in
// `std::list<CoarrayAssociation>`. According to the PRIF specification, it is
// necessary to call `prif_alias_{create|destroy}` for each coarray defined in
// this list. Support will be added once lowering to this procedure is
// possible.
const std::list<Fortran::parser::CoarrayAssociation> &coarrayAssocList =
std::get<std::list<Fortran::parser::CoarrayAssociation>>(stmt.t);
if (coarrayAssocList.size())
TODO(loc, "Coarrays provided in the association list.");
// Handle TEAM-VALUE
const auto *teamExpr =
Fortran::semantics::GetExpr(std::get<Fortran::parser::TeamValue>(stmt.t));
team = fir::getBase(converter.genExprBox(loc, *teamExpr, stmtCtx));
return mif::ChangeTeamOp::create(builder, loc, team, statAddr, errMsgAddr);
}
void Fortran::lower::genEndChangeTeamStmt(
Fortran::lower::AbstractConverter &converter,
Fortran::lower::pft::Evaluation &,
const Fortran::parser::EndChangeTeamStmt &stmt) {
converter.checkCoarrayEnabled();
mlir::Location loc = converter.getCurrentLocation();
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
mlir::Value errMsgAddr, statAddr;
// Handle STAT and ERRMSG values
Fortran::lower::StatementContext stmtCtx;
const std::list<Fortran::parser::StatOrErrmsg> &statOrErrList =
std::get<std::list<Fortran::parser::StatOrErrmsg>>(stmt.t);
for (const Fortran::parser::StatOrErrmsg &statOrErr : statOrErrList) {
std::visit(Fortran::common::visitors{
[&](const Fortran::parser::StatVariable &statVar) {
const auto *expr = Fortran::semantics::GetExpr(statVar);
statAddr = fir::getBase(
converter.genExprAddr(loc, *expr, stmtCtx));
},
[&](const Fortran::parser::MsgVariable &errMsgVar) {
const auto *expr = Fortran::semantics::GetExpr(errMsgVar);
errMsgAddr = fir::getBase(
converter.genExprBox(loc, *expr, stmtCtx));
},
},
statOrErr.u);
}
mif::EndTeamOp::create(builder, loc, statAddr, errMsgAddr);
}
void Fortran::lower::genFormTeamStatement(
Fortran::lower::AbstractConverter &converter,
Fortran::lower::pft::Evaluation &,
const Fortran::parser::FormTeamStmt &stmt) {
converter.checkCoarrayEnabled();
mlir::Location loc = converter.getCurrentLocation();
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
mlir::Value errMsgAddr, statAddr, newIndex, teamNumber, team;
// Handle NEW_INDEX, STAT and ERRMSG
std::list<Fortran::parser::StatOrErrmsg> statOrErrList{};
Fortran::lower::StatementContext stmtCtx;
const auto &formSpecList =
std::get<std::list<Fortran::parser::FormTeamStmt::FormTeamSpec>>(stmt.t);
for (const Fortran::parser::FormTeamStmt::FormTeamSpec &formSpec :
formSpecList) {
std::visit(
Fortran::common::visitors{
[&](const Fortran::parser::StatOrErrmsg &statOrErr) {
std::visit(
Fortran::common::visitors{
[&](const Fortran::parser::StatVariable &statVar) {
const auto *expr = Fortran::semantics::GetExpr(statVar);
statAddr = fir::getBase(
converter.genExprAddr(loc, *expr, stmtCtx));
},
[&](const Fortran::parser::MsgVariable &errMsgVar) {
const auto *expr =
Fortran::semantics::GetExpr(errMsgVar);
errMsgAddr = fir::getBase(
converter.genExprBox(loc, *expr, stmtCtx));
},
},
statOrErr.u);
},
[&](const Fortran::parser::ScalarIntExpr &intExpr) {
fir::ExtendedValue newIndexExpr = converter.genExprValue(
loc, Fortran::semantics::GetExpr(intExpr), stmtCtx);
newIndex = fir::getBase(newIndexExpr);
},
},
formSpec.u);
}
// Handle TEAM-NUMBER
const auto *teamNumberExpr = Fortran::semantics::GetExpr(
std::get<Fortran::parser::ScalarIntExpr>(stmt.t));
teamNumber =
fir::getBase(converter.genExprValue(loc, *teamNumberExpr, stmtCtx));
// Handle TEAM-VARIABLE
const auto *teamExpr = Fortran::semantics::GetExpr(
std::get<Fortran::parser::TeamVariable>(stmt.t));
team = fir::getBase(converter.genExprBox(loc, *teamExpr, stmtCtx));
mif::FormTeamOp::create(builder, loc, teamNumber, team, newIndex, statAddr,
errMsgAddr);
}
//===----------------------------------------------------------------------===//
// COARRAY utils
//===----------------------------------------------------------------------===//
mlir::Value
Fortran::lower::genLowerCoBounds(Fortran::lower::AbstractConverter &converter,
mlir::Location loc,
const Fortran::semantics::Symbol &sym) {
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
Fortran::lower::StatementContext stmtCtx;
mlir::Type i64Ty = builder.getI64Type();
mlir::Type addrType = builder.getRefType(i64Ty);
mlir::Value one = builder.createIntegerConstant(loc, i64Ty, 1);
mlir::Value lcobounds;
if (Fortran::semantics::IsAllocatableOrObjectPointer(&sym))
mlir::emitError(
loc,
"Unable to use genLowerCoBounds on ALLOCATABLE and POINTER symbol");
if (const auto *object =
sym.GetUltimate()
.detailsIf<Fortran::semantics::ObjectEntityDetails>()) {
size_t corank = object->coshape().size();
mlir::Type arrayType = fir::SequenceType::get(
{static_cast<fir::SequenceType::Extent>(corank)}, i64Ty);
lcobounds = builder.createTemporary(loc, arrayType);
mlir::Value lcovalue = one; // default lcobounds
for (size_t i = 0; i < corank; i++) {
if (auto lb = object->coshape()[i].lbound().GetExplicit()) {
auto lbExpr = ignoreEvConvert(*lb);
lcovalue = fir::getBase(converter.genExprValue(loc, lbExpr, stmtCtx));
}
if (lcovalue.getType() != i64Ty)
lcovalue = fir::ConvertOp::create(builder, loc, i64Ty, lcovalue);
mlir::Value index =
builder.createIntegerConstant(loc, builder.getIndexType(), i);
mlir::Value lcoaddr =
fir::CoordinateOp::create(builder, loc, addrType, lcobounds, index);
fir::StoreOp::create(builder, loc, lcovalue, lcoaddr);
}
lcobounds = builder.createBox(loc, lcobounds);
}
return lcobounds;
}
mlir::Value
Fortran::lower::genUpperCoBounds(Fortran::lower::AbstractConverter &converter,
mlir::Location loc,
const Fortran::semantics::Symbol &sym) {
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
Fortran::lower::StatementContext stmtCtx;
mlir::Type i64Ty = builder.getI64Type();
mlir::Type addrType = builder.getRefType(i64Ty);
mlir::Value one = builder.createIntegerConstant(loc, i64Ty, 1);
mlir::Value ucobounds;
if (Fortran::semantics::IsAllocatableOrObjectPointer(&sym))
mlir::emitError(
loc,
"Unable to use genUpperCoBounds on ALLOCATABLE and POINTER symbol");
if (const auto *object =
sym.GetUltimate()
.detailsIf<Fortran::semantics::ObjectEntityDetails>()) {
size_t corank = object->coshape().size();
// PRIF take an array of size corank-1 for ucobound.
mlir::Type arrayType = fir::SequenceType::get(
{static_cast<fir::SequenceType::Extent>(corank - 1)}, i64Ty);
ucobounds = builder.createTemporary(loc, arrayType);
mlir::Value ucovalue;
for (size_t i = 0; i < corank - 1; i++) {
if (auto ub = object->coshape()[i].lbound().GetExplicit()) {
auto ubExpr = ignoreEvConvert(*ub);
ucovalue = fir::getBase(converter.genExprValue(loc, ubExpr, stmtCtx));
} else {
if (auto lb = object->coshape()[i].lbound().GetExplicit()) {
auto lbExpr = ignoreEvConvert(*lb);
ucovalue = fir::getBase(converter.genExprValue(
loc, lbExpr, stmtCtx)); // default value from lcobound
} else
ucovalue = one;
}
if (ucovalue.getType() != i64Ty)
ucovalue = fir::ConvertOp::create(builder, loc, i64Ty, ucovalue);
mlir::Value index =
builder.createIntegerConstant(loc, builder.getIndexType(), i);
mlir::Value ucoaddr =
fir::CoordinateOp::create(builder, loc, addrType, ucobounds, index);
fir::StoreOp::create(builder, loc, ucovalue, ucoaddr);
}
ucobounds = builder.createBox(loc, ucobounds);
}
return ucobounds;
}
static std::tuple<mlir::Value, mlir::Value>
genCoBounds(Fortran::lower::AbstractConverter &converter, mlir::Location loc,
const Fortran::parser::AllocateCoarraySpec &allocSpec) {
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
Fortran::lower::StatementContext stmtCtx;
llvm::SmallVector<std::int64_t> lcbs, ucbs;
mlir::Type i64Ty = builder.getI64Type();
mlir::Type addrType = builder.getRefType(i64Ty);
mlir::Value one = builder.createIntegerConstant(loc, i64Ty, 1);
const std::list<Fortran::parser::AllocateCoshapeSpec> &coshapeSpecs =
std::get<0>(allocSpec.t);
size_t corank = coshapeSpecs.size() + 1;
mlir::Type arrayType = fir::SequenceType::get(
{static_cast<fir::SequenceType::Extent>(corank)}, i64Ty);
mlir::Value lcobounds = builder.createTemporary(loc, arrayType);
mlir::Type arrayType2 = fir::SequenceType::get(
{static_cast<fir::SequenceType::Extent>(corank - 1)}, i64Ty);
mlir::Value ucobounds = builder.createTemporary(loc, arrayType2);
size_t i = 0;
for (const Fortran::parser::AllocateCoshapeSpec &coshapeSpec : coshapeSpecs) {
const std::optional<Fortran::parser::BoundExpr> &lbExpr =
std::get<0>(coshapeSpec.t);
const Fortran::parser::BoundExpr &ubExpr = std::get<1>(coshapeSpec.t);
mlir::Value lb = one;
if (lbExpr.has_value()) {
auto expr = Fortran::semantics::GetExpr(*lbExpr);
lb = fir::getBase(converter.genExprValue(loc, expr, stmtCtx));
if (lb.getType() != i64Ty)
lb = fir::ConvertOp::create(builder, loc, i64Ty, lb);
}
auto ube = Fortran::semantics::GetExpr(ubExpr);
mlir::Value ub = fir::getBase(converter.genExprValue(loc, ube, stmtCtx));
if (ub.getType() != i64Ty)
ub = fir::ConvertOp::create(builder, loc, i64Ty, ub);
mlir::Value index =
builder.createIntegerConstant(loc, builder.getIndexType(), i);
// Lcobound
mlir::Value lcoaddr =
fir::CoordinateOp::create(builder, loc, addrType, lcobounds, index);
fir::StoreOp::create(builder, loc, lb, lcoaddr);
// Ucobound
mlir::Value ucoaddr =
fir::CoordinateOp::create(builder, loc, addrType, ucobounds, index);
fir::StoreOp::create(builder, loc, ub, ucoaddr);
i++;
}
// Last lcobound;
{
mlir::Value lb = one;
if (const std::optional<Fortran::parser::BoundExpr> &lastCobound =
std::get<1>(allocSpec.t)) {
auto expr = Fortran::semantics::GetExpr(*lastCobound);
lb = fir::getBase(converter.genExprValue(loc, expr, stmtCtx));
}
mlir::Value index =
builder.createIntegerConstant(loc, builder.getIndexType(), i);
mlir::Value lcoaddr =
fir::CoordinateOp::create(builder, loc, addrType, lcobounds, index);
fir::StoreOp::create(builder, loc, lb, lcoaddr);
}
lcobounds = builder.createBox(loc, lcobounds);
ucobounds = builder.createBox(loc, ucobounds);
return {lcobounds, ucobounds};
}
mlir::Value Fortran::lower::genAllocateCoarray(
Fortran::lower::AbstractConverter &converter, mlir::Location loc,
const Fortran::semantics::Symbol &sym, mlir::Value addr,
const std::optional<Fortran::parser::AllocateCoarraySpec> &allocSpec,
mlir::Value errmsg, bool hasStat) {
converter.checkCoarrayEnabled();
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
mlir::Value stat;
if (hasStat)
stat = builder.createTemporary(loc, builder.getI32Type());
mlir::Value lcobounds, ucobounds;
if (allocSpec.has_value()) {
std::tie(lcobounds, ucobounds) = genCoBounds(converter, loc, *allocSpec);
} else {
lcobounds = Fortran::lower::genLowerCoBounds(converter, loc, sym);
ucobounds = Fortran::lower::genUpperCoBounds(converter, loc, sym);
}
std::string uniqName = mif::getFullUniqName(addr);
if (uniqName.empty())
uniqName = converter.mangleName(sym);
mif::AllocCoarrayOp::create(builder, loc, addr, uniqName, lcobounds,
ucobounds, stat, errmsg);
return stat;
}
//===----------------------------------------------------------------------===//
// COARRAY expressions
//===----------------------------------------------------------------------===//
fir::ExtendedValue Fortran::lower::CoarrayExprHelper::genAddr(
const Fortran::evaluate::CoarrayRef &expr) {
(void)symMap;
TODO(converter.getCurrentLocation(), "co-array address");
}
fir::ExtendedValue Fortran::lower::CoarrayExprHelper::genValue(
const Fortran::evaluate::CoarrayRef &expr) {
TODO(converter.getCurrentLocation(), "co-array value");
}