[Flang][MIF] Allocation of NON-ALLOCATABLE SAVE coarray (#205847)

The goal of this PR is to add support for allocating NON-allocatable
coarrays that are not in the `Main` program.
All allocations will be performed in a function called
`__mif_save_coarrays_allocate`. If this function exists, then
LLVM::GlobalCtors is created (or modified).
The initialization of the MIF with `mif.init` is moved too in a
GlobalCtors with a priority of 0 to ensure that the initialization takes
place before any MIF allocations.

---------

Co-authored-by: Dan Bonachea <dobonachea@lbl.gov>
This commit is contained in:
Jean-Didier PAILLEUXandDan Bonachea authored and GitHub committed 2026-07-13 14:39:06 +02:00
1 parent 16bd5ce118
commit 5595710093
14 files changed
+283 -21

No files matched your search

@@ -76,6 +76,11 @@ mlir::Value genAllocateCoarray(
std::nullopt,
mlir::Value errMsg = {}, bool hasStat = false);
void genAllocateNonAllocatableSaveCoarray(AbstractConverter &converter,
mlir::Location loc,
const semantics::Symbol &sym,
mlir::Value addr);
//===----------------------------------------------------------------------===//
// COARRAY expressions
//===----------------------------------------------------------------------===//
@@ -13,6 +13,8 @@
#include "flang/Optimizer/Dialect/MIF/MIFOps.h"
static constexpr llvm::StringRef coarrayHandleSuffix = "_coarray_handle";
static constexpr llvm::StringRef mifSaveCoarraysAllocName =
"__mif_save_coarrays_allocate";
namespace mif {
@@ -22,6 +24,10 @@ mlir::Value genImageIndex(fir::FirOpBuilder &, mlir::Location loc,
mlir::Value coarray, mlir::Value sub,
mlir::Value team);
mlir::func::FuncOp getOrCreateInitFunc(fir::FirOpBuilder &builder,
mlir::ModuleOp mod,
llvm::StringRef name);
} // namespace mif
#endif // FORTRAN_OPTIMIZER_TRANSFORMS_MIFCOMMON_H_
@@ -32,7 +32,12 @@ class region_Op<string mnemonic, list<Trait> traits = []>
def mif_InitOp : mif_Op<"init", []> {
let summary = "Initialize the parallel environment";
let description = [{This operation will initialize the parallel environment}];
let description = [{
This operation will initialize the parallel environment and shall be called
prior to any other MIF operations.
Any second or subsequent call to mif.init is guaranteed to return
immediately with no effect.
}];
let results = (outs I32:$stat);
let assemblyFormat = "`->` type($stat) attr-dict";
@@ -691,6 +691,11 @@ def MIFOpConversion : Pass<"mif-convert", "mlir::ModuleOp"> {
"mlir::cf::ControlFlowDialect", "mlir::DLTIDialect"];
}
def EmitMIFGlobalCtors : Pass<"emit-mif-global-ctors", "mlir::ModuleOp"> {
let summary = "Generate GlobalCtorOp to init MIF and allocate coarrays.";
let dependentDialects = ["fir::FIROpsDialect", "mlir::LLVM::LLVMDialect"];
}
def LoopInvariantCodeMotion : Pass<"flang-licm", "::mlir::func::FuncOp"> {
let summary = "Hoist invariants from loops";
let description = [{
-9
View File
@@ -6634,15 +6634,6 @@ private:
if (sym.name() == "numeric_storage_size" && owner.IsModule() &&
DEREF(owner.symbol()).name() == "iso_fortran_env")
continue;
if (Fortran::evaluate::IsCoarray(sym) &&
!Fortran::semantics::IsAllocatable(sym) &&
Fortran::semantics::IsSaved(sym)) {
mlir::Location loc = toLocation();
TODO(
loc,
"coarray: non-ALLOCATABLE SAVE coarray outside the main program");
}
}
Fortran::lower::defineModuleVariable(*this, var);
}
+12 -6
View File
@@ -698,10 +698,15 @@ static void instantiateGlobal(Fortran::lower::AbstractConverter &converter,
mlir::StringAttr linkage = getLinkageAttribute(converter, var);
fir::GlobalOp global;
if (Fortran::evaluate::IsCoarray(sym))
if (Fortran::evaluate::IsCoarray(sym)) {
if (hasFinalization(sym) || hasAllocatableDirectComponent(sym))
TODO(loc, "coarray: coarray with an allocatable direct component and/or "
"requiring finalization");
const auto *details =
sym.detailsIf<Fortran::semantics::ObjectEntityDetails>();
if (details && details->init())
TODO(loc, "coarray: initialization");
}
if (var.isModuleOrSubmoduleVariable()) {
// A non-intrinsic module global is defined when lowering the module.
@@ -2634,11 +2639,12 @@ void Fortran::lower::mapSymbolAttributes(
assert(!Fortran::semantics::IsAllocatable(sym) &&
"must be a non-ALLOCATABLE coarray");
if (Fortran::semantics::IsSaved(sym) &&
sym.owner().kind() != Fortran::semantics::Scope::Kind::MainProgram)
TODO(loc,
"coarray: non-ALLOCATABLE SAVE coarray outside the main program");
;
Fortran::lower::genAllocateCoarray(converter, loc, sym, addr);
(sym.owner().kind() != Fortran::semantics::Scope::Kind::MainProgram ||
var.isModuleOrSubmoduleVariable()))
Fortran::lower::genAllocateNonAllocatableSaveCoarray(converter, loc, sym,
addr);
else
Fortran::lower::genAllocateCoarray(converter, loc, sym, addr);
::genDeclareSymbol(converter, symMap, sym, addr, len, extents, lbounds,
replace);
return;
+29
View File
@@ -18,6 +18,8 @@
#include "flang/Optimizer/Builder/MIFCommon.h"
#include "flang/Optimizer/Builder/Todo.h"
#include "flang/Parser/parse-tree.h"
#include "flang/Semantics/expression.h"
#include "mlir/IR/IRMapping.h"
//===----------------------------------------------------------------------===//
// Synchronization statements
@@ -449,6 +451,33 @@ mlir::Value Fortran::lower::genAllocateCoarray(
return stat;
}
void Fortran::lower::genAllocateNonAllocatableSaveCoarray(
Fortran::lower::AbstractConverter &converter, mlir::Location loc,
const semantics::Symbol &sym, mlir::Value addr) {
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
mlir::ModuleOp mod = builder.getModule();
mlir::IRMapping mapping;
auto func = mif::getOrCreateInitFunc(builder, mod, mifSaveCoarraysAllocName);
bool funcIsEmpty = func.empty();
mlir::Block &entry = func.getBody().front();
auto returnOp = entry.getTerminator();
mlir::OpBuilder::InsertionGuard guard(builder);
builder.setInsertionPoint(returnOp);
// If the function is empty, then we add the MIF initialization operation
// at the beginning.
if (funcIsEmpty)
mif::InitOp::create(builder, loc);
mlir::Operation &op = *addr.getDefiningOp();
mlir::Operation *localAddrOp = builder.clone(op, mapping);
Fortran::lower::genAllocateCoarray(converter, loc, sym,
localAddrOp->getResult(0));
}
//===----------------------------------------------------------------------===//
// COARRAY expressions
//===----------------------------------------------------------------------===//
+22
View File
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "flang/Optimizer/Builder/MIFCommon.h"
#include "flang/Lower/MultiImageFortran.h"
#include "flang/Optimizer/Builder/FIRBuilder.h"
#include "flang/Optimizer/Builder/HLFIRTools.h"
#include "flang/Optimizer/Dialect/MIF/MIFOps.h"
@@ -80,3 +81,24 @@ mlir::Value mif::genImageIndex(fir::FirOpBuilder &builder, mlir::Location loc,
(*subCastAndCleanup.second)();
return imageIndex;
}
mlir::func::FuncOp mif::getOrCreateInitFunc(fir::FirOpBuilder &builder,
mlir::ModuleOp mod,
llvm::StringRef name) {
mlir::Location loc = mod.getLoc();
auto funcType = builder.getFunctionType({}, {});
auto func = builder.createFunction(loc, name, funcType);
if (!func.empty())
return func;
func.setPublic();
mlir::OpBuilder::InsertionGuard guard(builder);
builder.setInsertionPointToEnd(mod.getBody());
func.addEntryBlock();
builder.setInsertionPointToEnd(&func.getBody().front());
mif::InitOp::create(builder, loc);
mlir::func::ReturnOp::create(builder, loc);
return func;
}
+2
View File
@@ -10,6 +10,7 @@
/// common to flang and the test tools.
#include "flang/Optimizer/Passes/Pipelines.h"
#include "flang/Optimizer/Builder/MIFCommon.h"
#include "flang/Optimizer/Dialect/FIROps.h"
#include "flang/Optimizer/OpenACC/Passes.h"
#include "mlir/Conversion/Passes.h"
@@ -460,6 +461,7 @@ void createDefaultFIRCodeGenPassPipeline(mlir::PassManager &pm,
}
fir::addFIRToLLVMPass(pm, config);
pm.addPass(fir::createEmitMIFGlobalCtors());
if (config.EnableOpenMP && !config.EnableOpenMPSimd) {
// Remove all non target-related operations from host functions still
@@ -28,6 +28,7 @@ add_flang_library(FIRTransforms
ControlFlowConverter.cpp
ConvertComplexPow.cpp
DebugTypeGenerator.cpp
EmitMIFGlobalCtors.cpp
ExternalNameConversion.cpp
FIRToSCF.cpp
FIRToMemRef.cpp
@@ -0,0 +1,100 @@
//===-- EmitMIFGlobalCtors.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
//
//===----------------------------------------------------------------------===//
#include "flang/Optimizer/Builder/MIFCommon.h"
#include "flang/Optimizer/Builder/Runtime/Inquiry.h"
#include "flang/Optimizer/Builder/Runtime/RTBuilder.h"
#include "flang/Optimizer/Dialect/FIRDialect.h"
#include "flang/Optimizer/Dialect/FIROps.h"
#include "flang/Optimizer/Dialect/MIF/MIFOps.h"
#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/BuiltinOps.h"
#include "mlir/Pass/Pass.h"
namespace fir {
#define GEN_PASS_DEF_EMITMIFGLOBALCTORS
#include "flang/Optimizer/Transforms/Passes.h.inc"
} // namespace fir
using namespace mlir;
using namespace Fortran::runtime;
namespace {
class EmitMIFGlobalCtors
: public fir::impl::EmitMIFGlobalCtorsBase<EmitMIFGlobalCtors> {
public:
void runOnOperation() override {
mlir::ModuleOp mod = getOperation();
mlir::MLIRContext *ctx = mod.getContext();
mlir::OpBuilder builder(ctx);
builder.setInsertionPointToEnd(mod.getBody());
mlir::Location loc = builder.getUnknownLoc();
llvm::SmallVector<mlir::Attribute> funcs, priorities, data;
mlir::Type i32Ty = mlir::IntegerType::get(ctx, 32);
mlir::Attribute zeroAttr = mlir::LLVM::ZeroAttr::get(builder.getContext());
// Setting priority 0 for the initialization of mif.
if (mod.lookupSymbol<mlir::LLVM::LLVMFuncOp>(mifSaveCoarraysAllocName)) {
funcs.push_back(
mlir::FlatSymbolRefAttr::get(ctx, mifSaveCoarraysAllocName));
priorities.push_back(mlir::IntegerAttr::get(i32Ty, 0));
data.push_back(zeroAttr);
}
if (funcs.empty())
return;
// We check whether a GlobalCtorsOp already exists to avoid having
// multiple instances.
mlir::LLVM::GlobalCtorsOp globalCtors;
mod.walk([&](mlir::LLVM::GlobalCtorsOp op) { globalCtors = op; });
if (globalCtors) {
llvm::SmallVector<mlir::Attribute> mergedFuncs(
globalCtors.getCtors().begin(), globalCtors.getCtors().end());
llvm::SmallVector<mlir::Attribute> mergedPriorities(
globalCtors.getPriorities().begin(),
globalCtors.getPriorities().end());
llvm::SmallVector<mlir::Attribute> mergedData(
globalCtors.getData().begin(), globalCtors.getData().end());
llvm::SmallDenseSet<llvm::StringRef> existingNames;
for (auto attr : globalCtors.getCtors())
existingNames.insert(
llvm::cast<mlir::FlatSymbolRefAttr>(attr).getValue());
for (auto [func, priority, d] : llvm::zip(funcs, priorities, data)) {
auto name = llvm::cast<mlir::FlatSymbolRefAttr>(func).getValue();
if (!existingNames.contains(name)) {
mergedFuncs.push_back(func);
mergedPriorities.push_back(priority);
mergedData.push_back(d);
}
}
// Deleting the existing GlobalCtorsOp and creating a new one that
// contains the merged attributes.
builder.setInsertionPoint(globalCtors);
mlir::LLVM::GlobalCtorsOp::create(builder, globalCtors.getLoc(),
builder.getArrayAttr(mergedFuncs),
builder.getArrayAttr(mergedPriorities),
builder.getArrayAttr(mergedData));
globalCtors.erase();
} else {
builder.setInsertionPointToEnd(mod.getBody());
mlir::LLVM::GlobalCtorsOp::create(
builder, loc, builder.getArrayAttr(funcs),
builder.getArrayAttr(priorities), builder.getArrayAttr(data));
}
}
};
} // namespace
+1
View File
@@ -184,6 +184,7 @@ func.func @_QQmain() {
// PASSES-NEXT: LowerNontemporalPass
// PASSES-NEXT: FIRToLLVMLowering
// PASSES-NEXT: ReconcileUnrealizedCasts
// PASSES-NEXT: EmitMIFGlobalCtors
// PASSES-NEXT: HostOpFilteringPass
// PASSES-NEXT: 'llvm.func' Pipeline
// PASSES-NEXT: StackToSharedPass
+70 -2
View File
@@ -1,8 +1,76 @@
! RUN: not %flang_fc1 -emit-hlfir -fcoarray %s -o - 2>&1 | FileCheck %s
! RUN: %flang_fc1 -emit-hlfir -fcoarray %s -o - 2>&1 | FileCheck %s
! RUN: %flang_fc1 -emit-llvm -fcoarray %s -o - 2>&1 | FileCheck %s --check-prefix=LLVM
!CHECK: not yet implemented: coarray: non-ALLOCATABLE SAVE coarray outside the main program
! LLVM: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 0, ptr @__mif_save_coarrays_allocate, ptr null }]
subroutine test_coarray_save()
implicit none
real, SAVE :: n[*]
real, SAVE :: m[*]
real, SAVE :: p[*]
end subroutine test_coarray_save
program main
call test_coarray_save()
end program
! CHECK-LABEL: func.func @_QPtest_coarray_save()
! CHECK: %0 = fir.dummy_scope : !fir.dscope
! CHECK: %1 = fir.address_of(@_QFtest_coarray_saveEm) : !fir.ref<f32>
! CHECK: %2:2 = hlfir.declare %1 {uniq_name = "_QFtest_coarray_saveEm"} : (!fir.ref<f32>) -> (!fir.ref<f32>, !fir.ref<f32>)
! CHECK: %3 = fir.address_of(@_QFtest_coarray_saveEn) : !fir.ref<f32>
! CHECK: %4:2 = hlfir.declare %3 {uniq_name = "_QFtest_coarray_saveEn"} : (!fir.ref<f32>) -> (!fir.ref<f32>, !fir.ref<f32>)
! CHECK: %5 = fir.address_of(@_QFtest_coarray_saveEp) : !fir.ref<f32>
! CHECK: %6:2 = hlfir.declare %5 {uniq_name = "_QFtest_coarray_saveEp"} : (!fir.ref<f32>) -> (!fir.ref<f32>, !fir.ref<f32>)
! CHECK: return
! CHECK-LABEL: func.func @_QQmain() attributes {fir.bindc_name = "MAIN"}
! CHECK: %0 = fir.dummy_scope : !fir.dscope
! CHECK: fir.call @_QPtest_coarray_save() fastmath<contract> : () -> ()
! CHECK: return
! CHECK: fir.global internal @_QFtest_coarray_saveEm : f32
! CHECK-LABEL: func.func @__mif_save_coarrays_allocate()
! CHECK: %[[VAL_0:.*]] = fir.alloca !fir.array<0xi64>
! CHECK: %[[VAL_1:.*]] = fir.alloca !fir.array<1xi64>
! CHECK: %[[VAL_2:.*]] = fir.alloca !fir.array<0xi64>
! CHECK: %[[VAL_3:.*]] = fir.alloca !fir.array<1xi64>
! CHECK: %[[VAL_4:.*]] = fir.alloca !fir.array<0xi64>
! CHECK: %[[VAL_5:.*]] = fir.alloca !fir.array<1xi64>
! CHECK: %[[INIT_STAT:.*]] = mif.init -> i32
! CHECK: %[[VAL_6:.*]] = fir.address_of(@_QFtest_coarray_saveEm) : !fir.ref<f32>
! CHECK: %[[C1_I64:.*]] = arith.constant 1 : i64
! CHECK: %[[C1_I64_0:.*]] = arith.constant 1 : i64
! CHECK: %[[C0:.*]] = arith.constant 0 : index
! CHECK: %[[VAL_7:.*]] = fir.coordinate_of %[[VAL_5]], %[[C0]] : (!fir.ref<!fir.array<1xi64>>, index) -> !fir.ref<i64>
! CHECK: fir.store %[[C1_I64_0]] to %[[VAL_7]] : !fir.ref<i64>
! CHECK: %[[VAL_8:.*]] = fir.embox %[[VAL_5]] : (!fir.ref<!fir.array<1xi64>>) -> !fir.box<!fir.array<1xi64>>
! CHECK: %[[C1_I64_1:.*]] = arith.constant 1 : i64
! CHECK: %[[VAL_9:.*]] = fir.embox %[[VAL_4]] : (!fir.ref<!fir.array<0xi64>>) -> !fir.box<!fir.array<0xi64>>
! CHECK: mif.alloc_coarray %[[VAL_6]] lcobounds %[[VAL_8]] ucobounds %[[VAL_9]] {uniq_name = "_QFtest_coarray_saveEm"} : (!fir.ref<f32>, !fir.box<!fir.array<1xi64>>, !fir.box<!fir.array<0xi64>>) -> ()
! CHECK: %[[VAL_10:.*]] = fir.address_of(@_QFtest_coarray_saveEn) : !fir.ref<f32>
! CHECK: %[[C1_I64_2:.*]] = arith.constant 1 : i64
! CHECK: %[[C1_I64_3:.*]] = arith.constant 1 : i64
! CHECK: %[[C0_4:.*]] = arith.constant 0 : index
! CHECK: %[[VAL_11:.*]] = fir.coordinate_of %[[VAL_3]], %[[C0_4]] : (!fir.ref<!fir.array<1xi64>>, index) -> !fir.ref<i64>
! CHECK: fir.store %[[C1_I64_3]] to %[[VAL_11]] : !fir.ref<i64>
! CHECK: %[[VAL_12:.*]] = fir.embox %[[VAL_3]] : (!fir.ref<!fir.array<1xi64>>) -> !fir.box<!fir.array<1xi64>>
! CHECK: %[[C1_I64_5:.*]] = arith.constant 1 : i64
! CHECK: %[[VAL_13:.*]] = fir.embox %[[VAL_2]] : (!fir.ref<!fir.array<0xi64>>) -> !fir.box<!fir.array<0xi64>>
! CHECK: mif.alloc_coarray %[[VAL_10]] lcobounds %[[VAL_12]] ucobounds %[[VAL_13]] {uniq_name = "_QFtest_coarray_saveEn"} : (!fir.ref<f32>, !fir.box<!fir.array<1xi64>>, !fir.box<!fir.array<0xi64>>) -> ()
! CHECK: %[[VAL_14:.*]] = fir.address_of(@_QFtest_coarray_saveEp) : !fir.ref<f32>
! CHECK: %[[C1_I64_6:.*]] = arith.constant 1 : i64
! CHECK: %[[C1_I64_7:.*]] = arith.constant 1 : i64
! CHECK: %[[C0_8:.*]] = arith.constant 0 : index
! CHECK: %[[VAL_15:.*]] = fir.coordinate_of %[[VAL_1]], %[[C0_8]] : (!fir.ref<!fir.array<1xi64>>, index) -> !fir.ref<i64>
! CHECK: fir.store %[[C1_I64_7]] to %[[VAL_15]] : !fir.ref<i64>
! CHECK: %[[VAL_16:.*]] = fir.embox %[[VAL_1]] : (!fir.ref<!fir.array<1xi64>>) -> !fir.box<!fir.array<1xi64>>
! CHECK: %[[VAL_C1_I64_9:.*]] = arith.constant 1 : i64
! CHECK: %[[VAL_17:.*]] = fir.embox %[[VAL_0]] : (!fir.ref<!fir.array<0xi64>>) -> !fir.box<!fir.array<0xi64>>
! CHECK: mif.alloc_coarray %[[VAL_14:.*]] lcobounds %[[VAL_16]] ucobounds %[[VAL_17]] {uniq_name = "_QFtest_coarray_saveEp"} : (!fir.ref<f32>, !fir.box<!fir.array<1xi64>>, !fir.box<!fir.array<0xi64>>) -> ()
! CHECK: return
! CHECK: fir.global internal @_QFtest_coarray_saveEn : f32
! CHECK: fir.global internal @_QFtest_coarray_saveEp : f32
+24 -3
View File
@@ -1,8 +1,29 @@
! RUN: not %flang_fc1 -emit-hlfir -fcoarray %s -o - 2>&1 | FileCheck %s
!CHECK: not yet implemented: coarray: non-ALLOCATABLE SAVE coarray outside the main program
! RUN: %flang_fc1 -emit-hlfir -fcoarray %s -o - | FileCheck %s
! RUN: %flang_fc1 -emit-llvm -fcoarray %s -o - 2>&1 | FileCheck %s --check-prefix=LLVM
module m_coarray_test
implicit none
real :: module_coarray[*]
end module m_coarray_test
program test
use m_coarray_test
end program
! LLVM: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 0, ptr @__mif_save_coarrays_allocate, ptr null }]
! CHECK-LABEL: func.func @__mif_save_coarrays_allocate()
! CHECK: %[[VAL_0:.*]] = fir.alloca !fir.array<0xi64>
! CHECK: %[[VAL_1:.*]] = fir.alloca !fir.array<1xi64>
! CHECK: %[[INIT_STAT:.*]] = mif.init -> i32
! CHECK: %[[VAL_2:.*]] = fir.address_of(@_QMm_coarray_testEmodule_coarray) : !fir.ref<f32>
! CHECK: %c1_i64 = arith.constant 1 : i64
! CHECK: %c1_i64_0 = arith.constant 1 : i64
! CHECK: %c0 = arith.constant 0 : index
! CHECK: %[[VAL_3:.*]] = fir.coordinate_of %[[VAL_1]], %c0 : (!fir.ref<!fir.array<1xi64>>, index) -> !fir.ref<i64>
! CHECK: fir.store %c1_i64_0 to %[[VAL_3]] : !fir.ref<i64>
! CHECK: %[[VAL_4:.*]] = fir.embox %[[VAL_1]] : (!fir.ref<!fir.array<1xi64>>) -> !fir.box<!fir.array<1xi64>>
! CHECK: %c1_i64_1 = arith.constant 1 : i64
! CHECK: %[[VAL_5:.*]] = fir.embox %[[VAL_0]] : (!fir.ref<!fir.array<0xi64>>) -> !fir.box<!fir.array<0xi64>>
! CHECK: mif.alloc_coarray %[[VAL_2]] lcobounds %[[VAL_4]] ucobounds %[[VAL_5]] {uniq_name = "_QMm_coarray_testEmodule_coarray"} : (!fir.ref<f32>, !fir.box<!fir.array<1xi64>>, !fir.box<!fir.array<0xi64>>) -> ()
! CHECK: return