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 for THIS_IMAGE and NUM_IMAGES in conformance with the PRIF specification.
For THIS_IMAGE, the lowering to the subroutine containing the coarray argument is not present in this PR, and will be in a future one.

@llvmbot llvmbot added flang Flang issues not falling into any other category flang:fir-hlfir labels Aug 18, 2025
@JDPailleux JDPailleux requested a review from ktras August 18, 2025 09:17
@llvmbot
Copy link
Member

llvmbot commented Aug 18, 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 for THIS_IMAGE and NUM_IMAGES in conformance with the PRIF specification.
For THIS_IMAGE, the lowering to the subroutine containing the coarray argument is not present in this PR, and will be in a future one.


Full diff: https://github.com/llvm/llvm-project/pull/154081.diff

6 Files Affected:

  • (modified) flang/include/flang/Optimizer/Builder/IntrinsicCall.h (+13)
  • (modified) flang/include/flang/Optimizer/Builder/Runtime/Coarray.h (+12)
  • (modified) flang/lib/Optimizer/Builder/IntrinsicCall.cpp (+47)
  • (modified) flang/lib/Optimizer/Builder/Runtime/Coarray.cpp (+57)
  • (added) flang/test/Lower/Coarray/num_images.f90 (+18)
  • (added) flang/test/Lower/Coarray/this_image.f90 (+14)
diff --git a/flang/include/flang/Optimizer/Builder/IntrinsicCall.h b/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
index 2afd50410ae82..88c3ada3ff64f 100644
--- a/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
+++ b/flang/include/flang/Optimizer/Builder/IntrinsicCall.h
@@ -378,6 +378,8 @@ struct IntrinsicLibrary {
   fir::ExtendedValue genNorm2(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
   mlir::Value genNot(mlir::Type, llvm::ArrayRef<mlir::Value>);
   fir::ExtendedValue genNull(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
+  fir::ExtendedValue genNumImages(mlir::Type,
+                                  llvm::ArrayRef<fir::ExtendedValue>);
   template <typename OpTy>
   mlir::Value genNVVMTime(mlir::Type, llvm::ArrayRef<mlir::Value>);
   fir::ExtendedValue genPack(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
@@ -449,6 +451,8 @@ struct IntrinsicLibrary {
   fir::ExtendedValue genTranspose(mlir::Type,
                                   llvm::ArrayRef<fir::ExtendedValue>);
   mlir::Value genThisGrid(mlir::Type, llvm::ArrayRef<mlir::Value>);
+  fir::ExtendedValue genThisImage(mlir::Type,
+                                  llvm::ArrayRef<fir::ExtendedValue>);
   mlir::Value genThisThreadBlock(mlir::Type, llvm::ArrayRef<mlir::Value>);
   mlir::Value genThisWarp(mlir::Type, llvm::ArrayRef<mlir::Value>);
   void genThreadFence(llvm::ArrayRef<fir::ExtendedValue>);
@@ -563,6 +567,15 @@ struct IntrinsicLibrary {
 
   void setResultMustBeFreed() { resultMustBeFreed = true; }
 
+  // Check support of coarray features
+  void checkCoarrayEnabled() {
+    if (converter &&
+        !converter->getFoldingContext().languageFeatures().IsEnabled(
+            Fortran::common::LanguageFeature::Coarray))
+      fir::emitFatalError(loc, "Coarrays disabled, use '-fcoarray' to enable.",
+                          false);
+  }
+
   fir::FirOpBuilder &builder;
   mlir::Location loc;
   bool resultMustBeFreed = false;
diff --git a/flang/include/flang/Optimizer/Builder/Runtime/Coarray.h b/flang/include/flang/Optimizer/Builder/Runtime/Coarray.h
index f2c76c9e8d978..23bb378c30838 100644
--- a/flang/include/flang/Optimizer/Builder/Runtime/Coarray.h
+++ b/flang/include/flang/Optimizer/Builder/Runtime/Coarray.h
@@ -37,5 +37,17 @@ namespace fir::runtime {
 /// Generate Call to runtime prif_init
 mlir::Value genInitCoarray(fir::FirOpBuilder &builder, mlir::Location loc);
 
+/// Generate Call to runtime prif_num_images
+mlir::Value getNumImages(fir::FirOpBuilder &builder, mlir::Location loc);
+
+/// Generate Call to runtime prif_num_images_with_team or
+/// prif_num_images_with_team_number
+mlir::Value getNumImagesWithTeam(fir::FirOpBuilder &builder, mlir::Location loc,
+                                 mlir::Value team);
+
+/// Generate Call to runtime prif_this_image_no_coarray
+mlir::Value getThisImage(fir::FirOpBuilder &builder, mlir::Location loc,
+                         mlir::Value team = {});
+
 } // 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 319ab1912cd3d..1964ff3a387a5 100644
--- a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
+++ b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
@@ -25,6 +25,7 @@
 #include "flang/Optimizer/Builder/Runtime/Allocatable.h"
 #include "flang/Optimizer/Builder/Runtime/CUDA/Descriptor.h"
 #include "flang/Optimizer/Builder/Runtime/Character.h"
+#include "flang/Optimizer/Builder/Runtime/Coarray.h"
 #include "flang/Optimizer/Builder/Runtime/Command.h"
 #include "flang/Optimizer/Builder/Runtime/Derived.h"
 #include "flang/Optimizer/Builder/Runtime/Exceptions.h"
@@ -778,6 +779,10 @@ static constexpr IntrinsicHandler handlers[]{
      /*isElemental=*/false},
     {"not", &I::genNot},
     {"null", &I::genNull, {{{"mold", asInquired}}}, /*isElemental=*/false},
+    {"num_images",
+     &I::genNumImages,
+     {{{"team", asAddr}, {"team_number", asAddr}}},
+     /*isElemental*/ false},
     {"pack",
      &I::genPack,
      {{{"array", asBox},
@@ -947,6 +952,12 @@ static constexpr IntrinsicHandler handlers[]{
     {"tand", &I::genTand},
     {"tanpi", &I::genTanpi},
     {"this_grid", &I::genThisGrid, {}, /*isElemental=*/false},
+    {"this_image",
+     &I::genThisImage,
+     {{{"coarray", asBox},
+       {"dim", asAddr},
+       {"team", asBox, handleDynamicOptional}}},
+     /*isElemental=*/false},
     {"this_thread_block", &I::genThisThreadBlock, {}, /*isElemental=*/false},
     {"this_warp", &I::genThisWarp, {}, /*isElemental=*/false},
     {"threadfence", &I::genThreadFence, {}, /*isElemental=*/false},
@@ -7277,6 +7288,20 @@ IntrinsicLibrary::genNull(mlir::Type, llvm::ArrayRef<fir::ExtendedValue> args) {
   return fir::MutableBoxValue(boxStorage, mold->nonDeferredLenParams(), {});
 }
 
+// NUM_IMAGES
+fir::ExtendedValue
+IntrinsicLibrary::genNumImages(mlir::Type resultType,
+                               llvm::ArrayRef<fir::ExtendedValue> args) {
+  checkCoarrayEnabled();
+  assert(args.size() == 0 || args.size() == 1);
+
+  if (args.size()) {
+    return fir::runtime::getNumImagesWithTeam(builder, loc,
+                                              fir::getBase(args[0]));
+  }
+  return fir::runtime::getNumImages(builder, loc);
+}
+
 // CLOCK, CLOCK64, GLOBALTIMER
 template <typename OpTy>
 mlir::Value IntrinsicLibrary::genNVVMTime(mlir::Type resultType,
@@ -8327,6 +8352,28 @@ mlir::Value IntrinsicLibrary::genThisGrid(mlir::Type resultType,
   return res;
 }
 
+// THIS_IMAGE
+fir::ExtendedValue
+IntrinsicLibrary::genThisImage(mlir::Type resultType,
+                               llvm::ArrayRef<fir::ExtendedValue> args) {
+  checkCoarrayEnabled();
+  assert(args.size() >= 1 && args.size() <= 3);
+  const bool coarrayIsAbsent = args.size() == 1;
+  mlir::Value team =
+      !isStaticallyAbsent(args, args.size() - 1)
+          ? fir::getBase(args[args.size() - 1])
+          : builder
+                .create<fir::AbsentOp>(loc,
+                                       fir::BoxType::get(builder.getNoneType()))
+                .getResult();
+
+  if (!coarrayIsAbsent) {
+    TODO(loc, "this_image with coarray argument.");
+  }
+  mlir::Value res = fir::runtime::getThisImage(builder, loc, team);
+  return builder.createConvert(loc, resultType, res);
+}
+
 // THIS_THREAD_BLOCK
 mlir::Value
 IntrinsicLibrary::genThisThreadBlock(mlir::Type resultType,
diff --git a/flang/lib/Optimizer/Builder/Runtime/Coarray.cpp b/flang/lib/Optimizer/Builder/Runtime/Coarray.cpp
index eaff6c37ecdbf..fb72fc2089e23 100644
--- a/flang/lib/Optimizer/Builder/Runtime/Coarray.cpp
+++ b/flang/lib/Optimizer/Builder/Runtime/Coarray.cpp
@@ -27,3 +27,60 @@ mlir::Value fir::runtime::genInitCoarray(fir::FirOpBuilder &builder,
   builder.create<fir::CallOp>(loc, funcOp, args);
   return builder.create<fir::LoadOp>(loc, result);
 }
+
+/// Generate Call to runtime prif_num_images
+mlir::Value fir::runtime::getNumImages(fir::FirOpBuilder &builder,
+                                       mlir::Location loc) {
+  mlir::Value result = builder.createTemporary(loc, builder.getI32Type());
+  mlir::FunctionType ftype =
+      PRIF_FUNCTYPE(builder.getRefType(builder.getI32Type()));
+  mlir::func::FuncOp funcOp =
+      builder.createFunction(loc, PRIFNAME_SUB("num_images"), ftype);
+  llvm::SmallVector<mlir::Value> args =
+      fir::runtime::createArguments(builder, loc, ftype, result);
+  builder.create<fir::CallOp>(loc, funcOp, args);
+  return builder.create<fir::LoadOp>(loc, result);
+}
+
+/// Generate Call to runtime prif_num_images_with_{team|team_number}
+mlir::Value fir::runtime::getNumImagesWithTeam(fir::FirOpBuilder &builder,
+                                               mlir::Location loc,
+                                               mlir::Value team) {
+  bool isTeamNumber = fir::unwrapPassByRefType(team.getType()).isInteger();
+  std::string numImagesName = isTeamNumber
+                                  ? PRIFNAME_SUB("num_images_with_team_number")
+                                  : PRIFNAME_SUB("num_images_with_team");
+
+  mlir::Value result = builder.createTemporary(loc, builder.getI32Type());
+  mlir::Type refTy = builder.getRefType(builder.getI32Type());
+  mlir::FunctionType ftype =
+      isTeamNumber
+          ? PRIF_FUNCTYPE(builder.getRefType(builder.getI64Type()), refTy)
+          : PRIF_FUNCTYPE(fir::BoxType::get(builder.getNoneType()), refTy);
+  mlir::func::FuncOp funcOp = builder.createFunction(loc, numImagesName, ftype);
+
+  if (!isTeamNumber)
+    team = builder.createBox(loc, team);
+  llvm::SmallVector<mlir::Value> args =
+      fir::runtime::createArguments(builder, loc, ftype, team, result);
+  builder.create<fir::CallOp>(loc, funcOp, args);
+  return builder.create<fir::LoadOp>(loc, result);
+}
+
+/// Generate Call to runtime prif_this_image_no_coarray
+mlir::Value fir::runtime::getThisImage(fir::FirOpBuilder &builder,
+                                       mlir::Location loc, mlir::Value team) {
+  mlir::Type refTy = builder.getRefType(builder.getI32Type());
+  mlir::Type boxTy = fir::BoxType::get(builder.getNoneType());
+  mlir::FunctionType ftype = PRIF_FUNCTYPE(boxTy, refTy);
+  mlir::func::FuncOp funcOp =
+      builder.createFunction(loc, PRIFNAME_SUB("this_image_no_coarray"), ftype);
+
+  mlir::Value result = builder.createTemporary(loc, builder.getI32Type());
+  mlir::Value teamArg =
+      !team ? builder.create<fir::AbsentOp>(loc, boxTy) : team;
+  llvm::SmallVector<mlir::Value> args =
+      fir::runtime::createArguments(builder, loc, ftype, teamArg, result);
+  builder.create<fir::CallOp>(loc, funcOp, args);
+  return builder.create<fir::LoadOp>(loc, result);
+}
diff --git a/flang/test/Lower/Coarray/num_images.f90 b/flang/test/Lower/Coarray/num_images.f90
new file mode 100644
index 0000000000000..ebfce5db0dbfb
--- /dev/null
+++ b/flang/test/Lower/Coarray/num_images.f90
@@ -0,0 +1,18 @@
+! RUN: %flang_fc1 -emit-hlfir -fcoarray %s -o - | FileCheck %s
+
+program test
+  use iso_fortran_env
+  integer :: i
+  integer :: team_number 
+  type(team_type) :: team
+
+  ! CHECK: fir.call @_QMprifPprif_num_images
+  i = num_images()
+
+  ! CHECK: fir.call @_QMprifPprif_num_images_with_team_number
+  i = num_images(TEAM_NUMBER=team_number)
+
+  ! CHECK: fir.call @_QMprifPprif_num_images_with_team
+  i = num_images(TEAM=team)
+
+end program
diff --git a/flang/test/Lower/Coarray/this_image.f90 b/flang/test/Lower/Coarray/this_image.f90
new file mode 100644
index 0000000000000..967b5b6727759
--- /dev/null
+++ b/flang/test/Lower/Coarray/this_image.f90
@@ -0,0 +1,14 @@
+! RUN: %flang_fc1 -emit-hlfir -fcoarray %s -o - | FileCheck %s
+
+program test
+  use iso_fortran_env
+  integer :: i
+  type(team_type) :: team
+
+  ! CHECK: fir.call @_QMprifPprif_this_image
+  i = this_image()
+
+  ! CHECK: fir.call @_QMprifPprif_this_image_no_coarray
+  i = this_image(TEAM=team)
+
+end program

@JDPailleux JDPailleux requested a review from clementval August 18, 2025 09:17
Copy link
Contributor

@clementval clementval left a comment

Choose a reason for hiding this comment

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

No braces on simple ifs. Otherwise LGTM

@JDPailleux JDPailleux force-pushed the jdp/flang/prif_this_image_num_images branch from 437018d to 4ceb28b Compare August 18, 2025 17:52
@JDPailleux
Copy link
Contributor Author

@clementval Ok for braces and thanks for the review.

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.

Looks great! And I can confirm that flang built from this branch can build Caffeine and link against it when compiling a program with num_images and this_image calls. Here is some output I was able to get:

 Compiler version is flang version 22.0.0 ([email protected]:SiPearl/llvm-project.
 git 4ceb28b8fea237b281d5f831ec791897d7741c23)
 Hello world from image  3  of  4
 Hello world from image  1  of  4
 Hello world from image  2  of  4
 Hello world from image  4  of  4

I had a question on one line of the test, so please check it out.

@clementval
Copy link
Contributor

@ktras @JDPailleux We probably talked about this in one of the flang meetings but what is the plan for Caffeine? Is this gonna be proposed to join llvm upstream at some point or is the plan to have a different implementation of PRIF upstream?

@bonachea
Copy link
Contributor

@clementval asks:

what is the plan for Caffeine? Is this gonna be proposed to join llvm upstream at some point or is the plan to have a different implementation of PRIF upstream?

Thanks for the great question!

The short answer is: the question of when/if to upstream a parallel runtime library has not yet been decided.

The main idea with PRIF is to have an open runtime interface for the multi-image features. We hope there can eventually be several PRIF library implementations (e.g. potentially optimized in vendor-specific or even proprietary ways) and we want to allow the flang user to mix-and-match parallel runtime libraries to suit their needs.
We also hope that Caffeine will eventually support several PRIF-enabled Fortran compilers (ie more than just LLVM flang).

Here's a paper with more details about PRIF:
https://dx.doi.org/10.25344/S4N017
and we've just submitted a new paper about our recent work to this year's LLVM-HPC workshop.

@clementval
Copy link
Contributor

Ok I see. Thanks for the refresher. The only problem I see without an implementation upstream in LLVM, is that we cannot really test the end to end pipeline. is there any plan to have a CI with caffeine and flang somewhere?

@JDPailleux JDPailleux force-pushed the jdp/flang/prif_this_image_num_images branch from 4ceb28b to 14560be Compare August 19, 2025 06:04
@ktras
Copy link
Contributor

ktras commented Aug 19, 2025

Ok I see. Thanks for the refresher. The only problem I see without an implementation upstream in LLVM, is that we cannot really test the end to end pipeline. is there any plan to have a CI with caffeine and flang somewhere?

@clementval Yes, we definitely agree that end to end testing is important. We are discussing how we can best achieve this in a follow-up pull request.

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.

LGTM. Thanks @JDPailleux for addressing my comment.

@JDPailleux JDPailleux merged commit e4334af into llvm:main Aug 20, 2025
9 checks passed
if (converter &&
!converter->getFoldingContext().languageFeatures().IsEnabled(
Fortran::common::LanguageFeature::Coarray))
fir::emitFatalError(loc, "Coarrays disabled, use '-fcoarray' to enable.",
Copy link
Contributor

Choose a reason for hiding this comment

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

If you could, please change this to something like:

not yet implemented: coarrays are experimental, use '-fcoarray' to enable
to keep the "not yet implemented" pattern in place. Thank you!

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.

6 participants