Skip to content

Conversation

JDPailleux
Copy link
Contributor

In relation to the approval and merge of the #76088 specification about multi-image features in Flang.
Here is a PR on adding support of the collectives CO_BROADCAST, CO_SUM, CO_MIN and CO_MAX in conformance with the PRIF specification.

@JDPailleux JDPailleux requested review from clementval and ktras August 21, 2025 14:11
@llvmbot llvmbot added flang Flang issues not falling into any other category flang:fir-hlfir labels Aug 21, 2025
@llvmbot
Copy link
Member

llvmbot commented Aug 21, 2025

@llvm/pr-subscribers-flang-fir-hlfir

Author: Jean-Didier PAILLEUX (JDPailleux)

Changes

In relation to the approval and merge of the #76088 specification about multi-image features in Flang.
Here is a PR on adding support of the collectives CO_BROADCAST, CO_SUM, CO_MIN and CO_MAX in conformance with the PRIF specification.


Patch is 46.15 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/154770.diff

8 Files Affected:

  • (modified) flang/include/flang/Optimizer/Builder/IntrinsicCall.h (+4)
  • (modified) flang/include/flang/Optimizer/Builder/Runtime/Coarray.h (+22)
  • (modified) flang/lib/Optimizer/Builder/IntrinsicCall.cpp (+108)
  • (modified) flang/lib/Optimizer/Builder/Runtime/Coarray.cpp (+81)
  • (added) flang/test/Lower/Coarray/co_broadcast.f90 (+92)
  • (added) flang/test/Lower/Coarray/co_max.f90 (+112)
  • (added) flang/test/Lower/Coarray/co_min.f90 (+112)
  • (added) flang/test/Lower/Coarray/co_sum.f90 (+122)
diff --git a/flang/include/flang/Optimizer/Builder/IntrinsicCall.h b/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
index 88c3ada3ff64f..84ed16c088e00 100644
--- a/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
+++ b/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
@@ -225,6 +225,10 @@ struct IntrinsicLibrary {
   fir::ExtendedValue genCharacterCompare(mlir::Type,
                                          llvm::ArrayRef<fir::ExtendedValue>);
   mlir::Value genCmplx(mlir::Type, llvm::ArrayRef<mlir::Value>);
+  void genCoBroadcast(llvm::ArrayRef<fir::ExtendedValue>);
+  void genCoMax(llvm::ArrayRef<fir::ExtendedValue>);
+  void genCoMin(llvm::ArrayRef<fir::ExtendedValue>);
+  void genCoSum(llvm::ArrayRef<fir::ExtendedValue>);
   mlir::Value genConjg(mlir::Type, llvm::ArrayRef<mlir::Value>);
   fir::ExtendedValue genCount(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
   void genCpuTime(llvm::ArrayRef<fir::ExtendedValue>);
diff --git a/flang/include/flang/Optimizer/Builder/Runtime/Coarray.h b/flang/include/flang/Optimizer/Builder/Runtime/Coarray.h
index 23bb378c30838..10ed503a485a3 100644
--- a/flang/include/flang/Optimizer/Builder/Runtime/Coarray.h
+++ b/flang/include/flang/Optimizer/Builder/Runtime/Coarray.h
@@ -34,6 +34,11 @@ namespace fir::runtime {
     return fir::NameUniquer::doProcedure({"prif"}, {}, oss.str());             \
   }()
 
+#define PRIF_STAT_TYPE builder.getRefType(builder.getI32Type())
+#define PRIF_ERRMSG_TYPE                                                       \
+  fir::BoxType::get(fir::CharacterType::get(builder.getContext(), 1,           \
+                                            fir::CharacterType::unknownLen()))
+
 /// Generate Call to runtime prif_init
 mlir::Value genInitCoarray(fir::FirOpBuilder &builder, mlir::Location loc);
 
@@ -49,5 +54,22 @@ mlir::Value getNumImagesWithTeam(fir::FirOpBuilder &builder, mlir::Location loc,
 mlir::Value getThisImage(fir::FirOpBuilder &builder, mlir::Location loc,
                          mlir::Value team = {});
 
+/// Generate call to runtime subroutine prif_co_broadcast
+void genCoBroadcast(fir::FirOpBuilder &builder, mlir::Location loc,
+                    mlir::Value A, mlir::Value sourceImage, mlir::Value stat,
+                    mlir::Value errmsg);
+
+/// Generate call to runtime subroutine prif_co_max and prif_co_max_character
+void genCoMax(fir::FirOpBuilder &builder, mlir::Location loc, mlir::Value A,
+              mlir::Value resultImage, mlir::Value stat, mlir::Value errmsg);
+
+/// Generate call to runtime subroutine prif_co_min or prif_co_min_character
+void genCoMin(fir::FirOpBuilder &builder, mlir::Location loc, mlir::Value A,
+              mlir::Value resultImage, mlir::Value stat, mlir::Value errmsg);
+
+/// Generate call to runtime subroutine prif_co_sum
+void genCoSum(fir::FirOpBuilder &builder, mlir::Location loc, mlir::Value A,
+              mlir::Value resultImage, mlir::Value stat, mlir::Value errmsg);
+
 } // namespace fir::runtime
 #endif // FORTRAN_OPTIMIZER_BUILDER_RUNTIME_COARRAY_H
diff --git a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
index 8aacdb1815e3b..d5b4b77f8cb98 100644
--- a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
+++ b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
@@ -397,6 +397,34 @@ static constexpr IntrinsicHandler handlers[]{
     {"cmplx",
      &I::genCmplx,
      {{{"x", asValue}, {"y", asValue, handleDynamicOptional}}}},
+    {"co_broadcast",
+     &I::genCoBroadcast,
+     {{{"a", asBox},
+       {"source_image", asAddr, handleDynamicOptional},
+       {"stat", asAddr, handleDynamicOptional},
+       {"errmsg", asBox, handleDynamicOptional}}},
+     /*isElemental*/ false},
+    {"co_max",
+     &I::genCoMax,
+     {{{"a", asBox},
+       {"result_image", asAddr, handleDynamicOptional},
+       {"stat", asAddr, handleDynamicOptional},
+       {"errmsg", asBox, handleDynamicOptional}}},
+     /*isElemental*/ false},
+    {"co_min",
+     &I::genCoMin,
+     {{{"a", asBox},
+       {"result_image", asAddr, handleDynamicOptional},
+       {"stat", asAddr, handleDynamicOptional},
+       {"errmsg", asBox, handleDynamicOptional}}},
+     /*isElemental*/ false},
+    {"co_sum",
+     &I::genCoSum,
+     {{{"a", asBox},
+       {"result_image", asAddr, handleDynamicOptional},
+       {"stat", asAddr, handleDynamicOptional},
+       {"errmsg", asBox, handleDynamicOptional}}},
+     /*isElemental*/ false},
     {"command_argument_count", &I::genCommandArgumentCount},
     {"conjg", &I::genConjg},
     {"cosd", &I::genCosd},
@@ -3649,6 +3677,86 @@ mlir::Value IntrinsicLibrary::genCmplx(mlir::Type resultType,
                                                            imag);
 }
 
+// CO_BROADCAST
+void IntrinsicLibrary::genCoBroadcast(llvm::ArrayRef<fir::ExtendedValue> args) {
+  checkCoarrayEnabled();
+  assert(args.size() == 4);
+  mlir::Value refNone =
+      builder
+          .create<fir::AbsentOp>(loc, builder.getRefType(builder.getI32Type()))
+          .getResult();
+  mlir::Value sourceImage =
+      isStaticallyAbsent(args[1]) ? refNone : fir::getBase(args[1]);
+  mlir::Value status =
+      isStaticallyAbsent(args[2]) ? refNone : fir::getBase(args[2]);
+  mlir::Value errmsg =
+      isStaticallyAbsent(args[3])
+          ? builder.create<fir::AbsentOp>(loc, PRIF_ERRMSG_TYPE).getResult()
+          : fir::getBase(args[3]);
+  fir::runtime::genCoBroadcast(builder, loc, fir::getBase(args[0]), sourceImage,
+                               status, errmsg);
+}
+
+// CO_MAX
+void IntrinsicLibrary::genCoMax(llvm::ArrayRef<fir::ExtendedValue> args) {
+  checkCoarrayEnabled();
+  assert(args.size() == 4);
+  mlir::Value refNone =
+      builder
+          .create<fir::AbsentOp>(loc, builder.getRefType(builder.getI32Type()))
+          .getResult();
+  mlir::Value remoteImage =
+      isStaticallyAbsent(args[1]) ? refNone : fir::getBase(args[1]);
+  mlir::Value status =
+      isStaticallyAbsent(args[2]) ? refNone : fir::getBase(args[2]);
+  mlir::Value errmsg =
+      isStaticallyAbsent(args[3])
+          ? builder.create<fir::AbsentOp>(loc, PRIF_ERRMSG_TYPE).getResult()
+          : fir::getBase(args[3]);
+  fir::runtime::genCoMax(builder, loc, fir::getBase(args[0]), remoteImage,
+                         status, errmsg);
+}
+
+// CO_MIN
+void IntrinsicLibrary::genCoMin(llvm::ArrayRef<fir::ExtendedValue> args) {
+  checkCoarrayEnabled();
+  assert(args.size() == 4);
+  mlir::Value refNone =
+      builder
+          .create<fir::AbsentOp>(loc, builder.getRefType(builder.getI32Type()))
+          .getResult();
+  mlir::Value remoteImage =
+      isStaticallyAbsent(args[1]) ? refNone : fir::getBase(args[1]);
+  mlir::Value status =
+      isStaticallyAbsent(args[2]) ? refNone : fir::getBase(args[2]);
+  mlir::Value errmsg =
+      isStaticallyAbsent(args[3])
+          ? builder.create<fir::AbsentOp>(loc, PRIF_ERRMSG_TYPE).getResult()
+          : fir::getBase(args[3]);
+  fir::runtime::genCoMin(builder, loc, fir::getBase(args[0]), remoteImage,
+                         status, errmsg);
+}
+
+// CO_SUM
+void IntrinsicLibrary::genCoSum(llvm::ArrayRef<fir::ExtendedValue> args) {
+  checkCoarrayEnabled();
+  assert(args.size() == 4);
+  mlir::Value absentInt =
+      builder
+          .create<fir::AbsentOp>(loc, builder.getRefType(builder.getI32Type()))
+          .getResult();
+  mlir::Value remoteImage =
+      isStaticallyAbsent(args[1]) ? absentInt : fir::getBase(args[1]);
+  mlir::Value status =
+      isStaticallyAbsent(args[2]) ? absentInt : fir::getBase(args[2]);
+  mlir::Value errmsg =
+      isStaticallyAbsent(args[3])
+          ? builder.create<fir::AbsentOp>(loc, PRIF_ERRMSG_TYPE).getResult()
+          : fir::getBase(args[3]);
+  fir::runtime::genCoSum(builder, loc, fir::getBase(args[0]), remoteImage,
+                         status, errmsg);
+}
+
 // COMMAND_ARGUMENT_COUNT
 fir::ExtendedValue IntrinsicLibrary::genCommandArgumentCount(
     mlir::Type resultType, llvm::ArrayRef<fir::ExtendedValue> args) {
diff --git a/flang/lib/Optimizer/Builder/Runtime/Coarray.cpp b/flang/lib/Optimizer/Builder/Runtime/Coarray.cpp
index fb72fc2089e23..ca3052d9206a5 100644
--- a/flang/lib/Optimizer/Builder/Runtime/Coarray.cpp
+++ b/flang/lib/Optimizer/Builder/Runtime/Coarray.cpp
@@ -14,6 +14,24 @@
 using namespace Fortran::runtime;
 using namespace Fortran::semantics;
 
+// Most PRIF functions take `errmsg` and `errmsg_alloc` as two optional
+// arguments of intent (out). One is allocatable, the other is not.
+// It is the responsibility of the compiler to ensure that the appropriate
+// optional argument is passed, and at most one must be provided in a given
+// call.
+// Depending on the type of `errmsg`, this function will return the pair
+// corresponding to (`errmsg`, `errmsg_alloc`).
+static std::pair<mlir::Value, mlir::Value>
+genErrmsgPRIF(fir::FirOpBuilder &builder, mlir::Location loc,
+              mlir::Value errmsg) {
+  bool isAllocatableErrmsg = fir::isAllocatableType(errmsg.getType());
+
+  mlir::Value absent = builder.create<fir::AbsentOp>(loc, PRIF_ERRMSG_TYPE);
+  mlir::Value errMsg = isAllocatableErrmsg ? absent : errmsg;
+  mlir::Value errMsgAlloc = isAllocatableErrmsg ? errmsg : absent;
+  return {errMsg, errMsgAlloc};
+}
+
 /// Generate Call to runtime prif_init
 mlir::Value fir::runtime::genInitCoarray(fir::FirOpBuilder &builder,
                                          mlir::Location loc) {
@@ -84,3 +102,66 @@ mlir::Value fir::runtime::getThisImage(fir::FirOpBuilder &builder,
   builder.create<fir::CallOp>(loc, funcOp, args);
   return builder.create<fir::LoadOp>(loc, result);
 }
+
+/// Generate call to collective subroutines except co_reduce
+/// A must be lowered as a box
+void genCollectiveSubroutine(fir::FirOpBuilder &builder, mlir::Location loc,
+                             mlir::Value A, mlir::Value sourceImage,
+                             mlir::Value stat, mlir::Value errmsg,
+                             std::string coName) {
+  mlir::Type boxTy = fir::BoxType::get(builder.getNoneType());
+  mlir::FunctionType ftype =
+      PRIF_FUNCTYPE(boxTy, builder.getRefType(builder.getI32Type()),
+                    PRIF_STAT_TYPE, PRIF_ERRMSG_TYPE, PRIF_ERRMSG_TYPE);
+  mlir::func::FuncOp funcOp = builder.createFunction(loc, coName, ftype);
+
+  auto [errmsgArg, errmsgAllocArg] = genErrmsgPRIF(builder, loc, errmsg);
+  llvm::SmallVector<mlir::Value> args = fir::runtime::createArguments(
+      builder, loc, ftype, A, sourceImage, stat, errmsgArg, errmsgAllocArg);
+  builder.create<fir::CallOp>(loc, funcOp, args);
+}
+
+/// Generate call to runtime subroutine prif_co_broadcast
+void fir::runtime::genCoBroadcast(fir::FirOpBuilder &builder,
+                                  mlir::Location loc, mlir::Value A,
+                                  mlir::Value sourceImage, mlir::Value stat,
+                                  mlir::Value errmsg) {
+  genCollectiveSubroutine(builder, loc, A, sourceImage, stat, errmsg,
+                          PRIFNAME_SUB("co_broadcast"));
+}
+
+/// Generate call to runtime subroutine prif_co_max or prif_co_max_character
+void fir::runtime::genCoMax(fir::FirOpBuilder &builder, mlir::Location loc,
+                            mlir::Value A, mlir::Value resultImage,
+                            mlir::Value stat, mlir::Value errmsg) {
+  mlir::Type argTy =
+      fir::unwrapSequenceType(fir::unwrapPassByRefType(A.getType()));
+  if (mlir::isa<fir::CharacterType>(argTy))
+    genCollectiveSubroutine(builder, loc, A, resultImage, stat, errmsg,
+                            PRIFNAME_SUB("co_max_character"));
+  else
+    genCollectiveSubroutine(builder, loc, A, resultImage, stat, errmsg,
+                            PRIFNAME_SUB("co_max"));
+}
+
+/// Generate call to runtime subroutine prif_co_min or prif_co_min_character
+void fir::runtime::genCoMin(fir::FirOpBuilder &builder, mlir::Location loc,
+                            mlir::Value A, mlir::Value resultImage,
+                            mlir::Value stat, mlir::Value errmsg) {
+  mlir::Type argTy =
+      fir::unwrapSequenceType(fir::unwrapPassByRefType(A.getType()));
+  if (mlir::isa<fir::CharacterType>(argTy))
+    genCollectiveSubroutine(builder, loc, A, resultImage, stat, errmsg,
+                            PRIFNAME_SUB("co_min_character"));
+  else
+    genCollectiveSubroutine(builder, loc, A, resultImage, stat, errmsg,
+                            PRIFNAME_SUB("co_min"));
+}
+
+/// Generate call to runtime subroutine prif_co_sum
+void fir::runtime::genCoSum(fir::FirOpBuilder &builder, mlir::Location loc,
+                            mlir::Value A, mlir::Value resultImage,
+                            mlir::Value stat, mlir::Value errmsg) {
+  genCollectiveSubroutine(builder, loc, A, resultImage, stat, errmsg,
+                          PRIFNAME_SUB("co_sum"));
+}
diff --git a/flang/test/Lower/Coarray/co_broadcast.f90 b/flang/test/Lower/Coarray/co_broadcast.f90
new file mode 100644
index 0000000000000..be7fdcb99252c
--- /dev/null
+++ b/flang/test/Lower/Coarray/co_broadcast.f90
@@ -0,0 +1,92 @@
+! RUN: %flang_fc1 -emit-hlfir -fcoarray %s -o - | FileCheck %s
+
+program test_co_broadcast
+  integer :: i, array_i(2), status
+  real :: r, array_r(2)
+  double precision :: d, array_d(2)
+  complex :: c, array_c(2)
+  character(len=1) :: message
+
+  ! CHECK: %[[C1_i32:.*]] = arith.constant 1 : i32
+  ! CHECK: %[[V1:.*]] = fir.embox %[[VAR_I:.*]]#0 : (!fir.ref<i32>) -> !fir.box<i32>
+  ! CHECK: fir.store %[[C1_i32]] to %[[IMAGE_RESULT:.*]] : !fir.ref<i32>
+  ! CHECK: %[[V2:.*]] = fir.absent !fir.ref<i32>
+  ! CHECK: %[[V3:.*]] = fir.absent !fir.box<!fir.char<1,?>>
+  ! CHECK: %[[V4:.*]] = fir.absent !fir.box<!fir.char<1,?>>
+  ! CHECK: %[[V5:.*]] = fir.convert %[[V1]] : (!fir.box<i32>) -> !fir.box<none>
+  ! CHECK: fir.call @_QMprifPprif_co_broadcast(%[[V5]], %[[IMAGE_RESULT]], %[[V2]], %[[V3]], %[[V4]]) fastmath<contract> : (!fir.box<none>, !fir.ref<i32>, !fir.ref<i32>, !fir.box<!fir.char<1,?>>, !fir.box<!fir.char<1,?>>) -> ()
+  call co_broadcast(i,       source_image=1)
+
+  ! CHECK: %[[C1_i32:.*]] = arith.constant 1 : i32
+  ! CHECK: %[[V1:.*]] = fir.embox %[[VAR_C:.*]]#0 : (!fir.ref<complex<f32>>) -> !fir.box<complex<f32>>
+  ! CHECK: fir.store %[[C1_i32]] to %[[IMAGE_RESULT:.*]] : !fir.ref<i32>
+  ! CHECK: %[[V2:.*]] = fir.absent !fir.box<!fir.char<1,?>>
+  ! CHECK: %[[V3:.*]] = fir.absent !fir.box<!fir.char<1,?>>
+  ! CHECK: %[[V4:.*]] = fir.convert %[[V1]] : (!fir.box<complex<f32>>) -> !fir.box<none>
+  ! CHECK: fir.call @_QMprifPprif_co_broadcast(%[[V4]], %[[IMAGE_RESULT]], %[[STATUS:.*]], %[[V2]], %[[V3]]) fastmath<contract> : (!fir.box<none>, !fir.ref<i32>, !fir.ref<i32>, !fir.box<!fir.char<1,?>>, !fir.box<!fir.char<1,?>>) -> ()
+  call co_broadcast(c,       source_image=1, stat=status)
+
+  ! CHECK: %[[C1_i32:.*]] = arith.constant 1 : i32
+  ! CHECK: %[[V1:.*]] = fir.embox %[[VAR_D:.*]]#0 : (!fir.ref<f64>) -> !fir.box<f64>
+  ! CHECK: fir.store %[[C1_i32]] to %[[IMAGE_RESULT:.*]] : !fir.ref<i32>
+  ! CHECK: %[[V2:.*]] = fir.embox %[[MESSAGE:.*]]#0 : (!fir.ref<!fir.char<1>>) -> !fir.box<!fir.char<1>>
+  ! CHECK: %[[V3:.*]] = fir.absent !fir.box<!fir.char<1,?>>
+  ! CHECK: %[[V4:.*]] = fir.convert %[[V1]] : (!fir.box<f64>) -> !fir.box<none>
+  ! CHECK: %[[V5:.*]] = fir.convert %[[V2]] : (!fir.box<!fir.char<1>>) -> !fir.box<!fir.char<1,?>>
+  ! CHECK: fir.call @_QMprifPprif_co_broadcast(%[[V4]], %[[IMAGE_RESULT]], %[[STATUS]], %[[V5]], %[[V3]]) fastmath<contract> : (!fir.box<none>, !fir.ref<i32>, !fir.ref<i32>, !fir.box<!fir.char<1,?>>, !fir.box<!fir.char<1,?>>) -> ()
+  call co_broadcast(d,       source_image=1, stat=status, errmsg=message)
+
+  ! CHECK: %[[C1_i32:.*]] = arith.constant 1 : i32
+  ! CHECK: %[[V1:.*]] = fir.embox %[[VAR_R:.*]]#0 : (!fir.ref<f32>) -> !fir.box<f32>
+  ! CHECK: fir.store %[[C1_i32]] to %[[IMAGE_RESULT:.*]] : !fir.ref<i32>
+  ! CHECK: %[[V2:.*]] = fir.embox %[[MESSAGE]]#0 : (!fir.ref<!fir.char<1>>) -> !fir.box<!fir.char<1>>
+  ! CHECK: %[[V3:.*]] = fir.absent !fir.box<!fir.char<1,?>>
+  ! CHECK: %[[V4:.*]] = fir.convert %[[V1]] : (!fir.box<f32>) -> !fir.box<none>
+  ! CHECK: %[[V5:.*]] = fir.convert %[[V2]] : (!fir.box<!fir.char<1>>) -> !fir.box<!fir.char<1,?>>
+  ! CHECK: fir.call @_QMprifPprif_co_broadcast(%[[V4]], %[[IMAGE_RESULT]], %[[STATUS]], %[[V5]], %[[V3]]) fastmath<contract> : (!fir.box<none>, !fir.ref<i32>, !fir.ref<i32>, !fir.box<!fir.char<1,?>>, !fir.box<!fir.char<1,?>>) -> ()
+  call co_broadcast(r,       source_image=1, stat=status, errmsg=message)
+
+  ! CHECK: %[[C1_i32:.*]] = arith.constant 1 : i32
+  ! CHECK: %[[SHAPE_2:.*]] = fir.shape %[[C2_2:.*]] : (index) -> !fir.shape<1>
+  ! CHECK: %[[V1:.*]] = fir.embox %[[ARRAY_I:.*]]#0(%[[SHAPE_2]]) : (!fir.ref<!fir.array<2xi32>>, !fir.shape<1>) -> !fir.box<!fir.array<2xi32>>
+  ! CHECK: fir.store %[[C1_i32]] to %[[IMAGE_RESULT:.*]] : !fir.ref<i32>
+  ! CHECK: %[[V2:.*]] = fir.absent !fir.ref<i32>
+  ! CHECK: %[[V3:.*]] = fir.absent !fir.box<!fir.char<1,?>>
+  ! CHECK: %[[V4:.*]] = fir.absent !fir.box<!fir.char<1,?>>
+  ! CHECK: %[[V5:.*]] = fir.convert %[[V1]] : (!fir.box<!fir.array<2xi32>>) -> !fir.box<none>
+  ! CHECK: fir.call @_QMprifPprif_co_broadcast(%[[V5]], %[[IMAGE_RESULT]], %[[V2]], %[[V3]], %[[V4]]) fastmath<contract> : (!fir.box<none>, !fir.ref<i32>, !fir.ref<i32>, !fir.box<!fir.char<1,?>>, !fir.box<!fir.char<1,?>>) -> ()
+  call co_broadcast(array_i, source_image=1)
+  
+  ! CHECK: %[[C1_i32:.*]] = arith.constant 1 : i32
+  ! CHECK: %[[SHAPE_2:.*]] = fir.shape %[[C2_2:.*]] : (index) -> !fir.shape<1>
+  ! CHECK: %[[V1:.*]] = fir.embox %[[ARRAY_C:.*]]#0(%[[SHAPE_2]]) : (!fir.ref<!fir.array<2xcomplex<f32>>>, !fir.shape<1>) -> !fir.box<!fir.array<2xcomplex<f32>>>
+  ! CHECK: fir.store %[[C1_i32]] to %[[IMAGE_RESULT:.*]] : !fir.ref<i32>
+  ! CHECK: %[[V2:.*]] = fir.absent !fir.ref<i32>
+  ! CHECK: %[[V3:.*]] = fir.absent !fir.box<!fir.char<1,?>>
+  ! CHECK: %[[V4:.*]] = fir.absent !fir.box<!fir.char<1,?>>
+  ! CHECK: %[[V5:.*]] = fir.convert %[[V1]] : (!fir.box<!fir.array<2xcomplex<f32>>>) -> !fir.box<none>
+  ! CHECK: fir.call @_QMprifPprif_co_broadcast(%[[V5]], %[[IMAGE_RESULT]], %[[V2]], %[[V3]], %[[V4]]) fastmath<contract> : (!fir.box<none>, !fir.ref<i32>, !fir.ref<i32>, !fir.box<!fir.char<1,?>>, !fir.box<!fir.char<1,?>>) -> ()
+  call co_broadcast(array_c, source_image=1)
+  
+  ! CHECK: %[[C1_i32:.*]] = arith.constant 1 : i32
+  ! CHECK: %[[SHAPE_2:.*]] = fir.shape %[[C2_2:.*]] : (index) -> !fir.shape<1>
+  ! CHECK: %[[V1:.*]] = fir.embox %[[ARRAY_D:.*]]#0(%[[SHAPE_2]]) : (!fir.ref<!fir.array<2xf64>>, !fir.shape<1>) -> !fir.box<!fir.array<2xf64>>
+  ! CHECK: fir.store %[[C1_i32]] to %[[IMAGE_RESULT:.*]] : !fir.ref<i32>
+  ! CHECK: %[[V2:.*]] = fir.absent !fir.box<!fir.char<1,?>>
+  ! CHECK: %[[V3:.*]] = fir.absent !fir.box<!fir.char<1,?>>
+  ! CHECK: %[[V4:.*]] = fir.convert %[[V1]] : (!fir.box<!fir.array<2xf64>>) -> !fir.box<none>
+  ! CHECK: fir.call @_QMprifPprif_co_broadcast(%[[V4]], %[[IMAGE_RESULT]], %[[STATUS]], %[[V2]], %[[V3]]) fastmath<contract> : (!fir.box<none>, !fir.ref<i32>, !fir.ref<i32>, !fir.box<!fir.char<1,?>>, !fir.box<!fir.char<1,?>>) -> ()
+  call co_broadcast(array_d, source_image=1, stat=status)
+
+  ! CHECK: %[[C1_i32:.*]] = arith.constant 1 : i32
+  ! CHECK: %[[SHAPE_2:.*]] = fir.shape %[[C2_2:.*]] : (index) -> !fir.shape<1>
+  ! CHECK: %[[V1:.*]] = fir.embox %[[ARRAY_C:.*]]#0(%[[SHAPE_2]]) : (!fir.ref<!fir.array<2xf32>>, !fir.shape<1>) -> !fir.box<!fir.array<2xf32>>
+  ! CHECK: fir.store %[[C1_i32]] to %[[IMAGE_RESULT:.*]] : !fir.ref<i32>
+  ! CHECK: %[[V2:.*]] = fir.embox %[[MESSAGE]]#0 : (!fir.ref<!fir.char<1>>) -> !fir.box<!fir.char<1>>
+  ! CHECK: %[[V3:.*]] = fir.absent !fir.box<!fir.char<1,?>>
+  ! CHECK: %[[V4:.*]] = fir.convert %[[V1]] : (!fir.box<!fir.array<2xf32>>) -> !fir.box<none>
+  ! CHECK: %[[V5:.*]] = fir.convert %[[V2]] : (!fir.box<!fir.char<1>>) -> !fir.box<!fir.char<1,?>>
+  ! CHECK: fir.call @_QMprifPprif_co_broadcast(%[[V4]], %[[IMAGE_RESULT]], %[[STATUS]], %[[V5]], %[[V3]]) fastmath<contract> : (!fir.box<none>, !fir.ref<i32>, !fir.ref<i32>, !fir.box<!fir.char<1,?>>, !fir.box<!fir.char<1,?>>) -> ()
+  call co_broadcast(array_r, source_image=1, stat= status, errmsg=message)
+
+end program
diff --git a/flang/test/Lower/Coarray/co_max.f90 b/flang/test/Lower/Coarray/co_max.f90
new file mode 100644
index 0000000000000..56d863389d02b
--- /dev/null...
[truncated]

@JDPailleux JDPailleux changed the title [flang][Lower] Add Lowering for CO_{BROADCAST, MAX, MIN, MAX} to PRIF [flang][Lower] Add Lowering for CO_{BROADCAST, MAX, MIN, SUM} to PRIF Aug 21, 2025
@sscalpone
Copy link
Contributor

@JDPailleux Hi! I'm really looking forward to this coarray work but the implementation is surprising me a little. @jeanPerier told me to expect an intermediate lowering of coarray calls to "prif" MLIR instead of directly to PRIF runtime calls. Also, where can we find an implementation of PRIF to test out this work. We can take this discussion to discourse or slack.

@ktras
Copy link
Contributor

ktras commented Aug 21, 2025

Hello @sscalpone, yes we have had discussions about a dialect. To facilitate this broader discussion around PRIF lowering in Flang, let’s take this conversation to Slack, as you suggested. The #prif-implementation channel in the flang Slack workspace seems appropriate and Jean-Didier and others are already in this channel.

Copy link
Contributor

@bonachea bonachea left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @JDPailleux for this PR!

The code seems well-structured, and the tests provide good coverage to exercise the valid use cases.

I've added a few suggestions for minor improvements.

Copy link
Contributor

@ktras ktras left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the updates. LGTM.

@JDPailleux JDPailleux merged commit 60dc68d into llvm:main Sep 10, 2025
9 checks passed
JDPailleux added a commit to SiPearl/llvm-project that referenced this pull request Sep 10, 2025
…llvm#154770)

In relation to the approval and merge of the
llvm#76088 specification about
multi-image features in Flang.
Here is a PR on adding support of the collectives CO_BROADCAST, CO_SUM,
CO_MIN and CO_MAX in conformance with the PRIF specification.

---------

Co-authored-by: Dan Bonachea <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
flang:fir-hlfir flang Flang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants