//===-- 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 &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 &statOrErrList = std::get>(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(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 &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(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 &statOrErrList = std::get>(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 &statOrErrList = std::get>(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`. 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 &coarrayAssocList = std::get>(stmt.t); if (coarrayAssocList.size()) TODO(loc, "Coarrays provided in the association list."); // Handle TEAM-VALUE const auto *teamExpr = Fortran::semantics::GetExpr(std::get(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 &statOrErrList = std::get>(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 statOrErrList{}; Fortran::lower::StatementContext stmtCtx; const auto &formSpecList = std::get>(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(stmt.t)); teamNumber = fir::getBase(converter.genExprValue(loc, *teamNumberExpr, stmtCtx)); // Handle TEAM-VARIABLE const auto *teamExpr = Fortran::semantics::GetExpr( std::get(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()) { size_t corank = object->coshape().size(); mlir::Type arrayType = fir::SequenceType::get( {static_cast(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()) { size_t corank = object->coshape().size(); // PRIF take an array of size corank-1 for ucobound. mlir::Type arrayType = fir::SequenceType::get( {static_cast(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 genCoBounds(Fortran::lower::AbstractConverter &converter, mlir::Location loc, const Fortran::parser::AllocateCoarraySpec &allocSpec) { fir::FirOpBuilder &builder = converter.getFirOpBuilder(); Fortran::lower::StatementContext stmtCtx; llvm::SmallVector lcbs, ucbs; mlir::Type i64Ty = builder.getI64Type(); mlir::Type addrType = builder.getRefType(i64Ty); mlir::Value one = builder.createIntegerConstant(loc, i64Ty, 1); const std::list &coshapeSpecs = std::get<0>(allocSpec.t); size_t corank = coshapeSpecs.size() + 1; mlir::Type arrayType = fir::SequenceType::get( {static_cast(corank)}, i64Ty); mlir::Value lcobounds = builder.createTemporary(loc, arrayType); mlir::Type arrayType2 = fir::SequenceType::get( {static_cast(corank - 1)}, i64Ty); mlir::Value ucobounds = builder.createTemporary(loc, arrayType2); size_t i = 0; for (const Fortran::parser::AllocateCoshapeSpec &coshapeSpec : coshapeSpecs) { const std::optional &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 &lastCobound = std::get<1>(allocSpec.t)) { auto expr = Fortran::semantics::GetExpr(*lastCobound); lb = fir::getBase(converter.genExprValue(loc, expr, stmtCtx)); lb = fir::ConvertOp::create(builder, loc, i64Ty, lb); } 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 &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"); }