[flang-commits] [flang] [flang][acc][NFC] move recipe generation in FIROpenACCUtils (PR #176924)

via flang-commits flang-commits at lists.llvm.org
Tue Jan 20 05:25:13 PST 2026


https://github.com/jeanPerier created https://github.com/llvm/llvm-project/pull/176924

Move the code that generates private, firstprivate, and reduction from Lower/OpenACC.cpp to Optimizer/OpenACC/Support/FIROpenACCUtils.cpp so that it can be used in passes too.

>From 7481c083215d0c1c1716694990075a698dde4a85 Mon Sep 17 00:00:00 2001
From: Jean Perier <jperier at nvidia.com>
Date: Tue, 20 Jan 2026 05:10:28 -0800
Subject: [PATCH] [flang][acc][NFC] move recipe generation in FIROpenACCUtils

---
 .../OpenACC/Support/FIROpenACCUtils.h         |  37 ++
 flang/lib/Lower/OpenACC.cpp                   | 409 +-----------------
 .../OpenACC/Support/FIROpenACCUtils.cpp       | 392 ++++++++++++++++-
 3 files changed, 431 insertions(+), 407 deletions(-)

diff --git a/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCUtils.h b/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCUtils.h
index 5ca0925ea681f..79f5626df4d24 100644
--- a/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCUtils.h
+++ b/flang/include/flang/Optimizer/OpenACC/Support/FIROpenACCUtils.h
@@ -14,6 +14,7 @@
 #define FORTRAN_OPTIMIZER_OPENACC_SUPPORT_FIROPENACCUTILS_H
 
 #include "mlir/Dialect/OpenACC/OpenACC.h"
+#include "mlir/IR/Builders.h"
 #include "mlir/IR/Value.h"
 #include <string>
 
@@ -51,6 +52,42 @@ std::string getRecipeName(mlir::acc::RecipeKind kind, mlir::Type type,
 /// \return true if all bounds have constant lowerbound/upperbound or extent
 bool areAllBoundsConstant(llvm::ArrayRef<mlir::Value> bounds);
 
+/// Create or get a private recipe for the given type and name.
+/// \param builder The FIR builder
+/// \param loc The location
+/// \param ty The type of the variable
+/// \param dataBoundOps Optional bounds for the variable
+/// \return The existing or created PrivateRecipeOp symbol
+mlir::SymbolRefAttr
+createOrGetPrivateRecipe(mlir::OpBuilder &builder, mlir::Location loc,
+                         mlir::Type ty,
+                         llvm::SmallVector<mlir::Value> &dataBoundOps);
+
+/// Create or get a firstprivate recipe for the given type and name.
+/// \param builder The FIR builder
+/// \param loc The location
+/// \param ty The type of the variable
+/// \param dataBoundOps Optional bounds for the variable
+/// \return The existing or created FirstprivateRecipeOp symbol
+mlir::SymbolRefAttr
+createOrGetFirstprivateRecipe(mlir::OpBuilder &builder, mlir::Location loc,
+                              mlir::Type ty,
+                              llvm::SmallVector<mlir::Value> &dataBoundOps);
+
+/// Create or get a reduction recipe for the given type, name and operator.
+/// \param builder The FIR builder
+/// \param loc The location
+/// \param ty The type of the variable
+/// \param op The reduction operator
+/// \param dataBoundOps Optional bounds for the variable
+/// \param fastMathAttr Optional fast math attributes
+/// \return The existing or created ReductionRecipeOp symbol
+mlir::SymbolRefAttr
+createOrGetReductionRecipe(mlir::OpBuilder &builder, mlir::Location loc,
+                           mlir::Type ty, mlir::acc::ReductionOperator op,
+                           llvm::SmallVector<mlir::Value> &dataBoundOps,
+                           mlir::Attribute fastMathAttr = {});
+
 } // namespace acc
 } // namespace fir
 
diff --git a/flang/lib/Lower/OpenACC.cpp b/flang/lib/Lower/OpenACC.cpp
index 1e313b20d464c..ee1c95349c535 100644
--- a/flang/lib/Lower/OpenACC.cpp
+++ b/flang/lib/Lower/OpenACC.cpp
@@ -83,8 +83,6 @@ static constexpr std::int64_t starCst = -1;
 
 static unsigned routineCounter = 0;
 static constexpr llvm::StringRef accRoutinePrefix = "acc_routine_";
-static constexpr llvm::StringRef accPrivateInitName = "acc.private.init";
-static constexpr llvm::StringRef accReductionInitName = "acc.reduction.init";
 
 static mlir::Location
 genOperandLocation(Fortran::lower::AbstractConverter &converter,
@@ -649,32 +647,6 @@ void genAtomicCapture(Fortran::lower::AbstractConverter &converter,
   firOpBuilder.setInsertionPointAfter(atomicCaptureOp);
 }
 
-static mlir::SymbolRefAttr
-createOrGetRecipe(fir::FirOpBuilder &builder, mlir::Location loc,
-                  mlir::acc::RecipeKind kind, mlir::Value addr,
-                  llvm::SmallVector<mlir::Value> &bounds) {
-  mlir::Type ty = addr.getType();
-  // Compute the canonical recipe name for the given kind, type, address and
-  // bounds so that recipes are shared wherever possible.
-  std::string recipeName = fir::acc::getRecipeName(kind, ty, addr, bounds);
-
-  switch (kind) {
-  case mlir::acc::RecipeKind::private_recipe: {
-    auto recipe = Fortran::lower::createOrGetPrivateRecipe(builder, recipeName,
-                                                           loc, ty, bounds);
-    return mlir::SymbolRefAttr::get(builder.getContext(), recipe.getSymName());
-  }
-  case mlir::acc::RecipeKind::firstprivate_recipe: {
-    auto recipe = Fortran::lower::createOrGetFirstprivateRecipe(
-        builder, recipeName, loc, ty, bounds);
-    return mlir::SymbolRefAttr::get(builder.getContext(), recipe.getSymName());
-  }
-  default:
-    llvm::report_fatal_error(
-        "createOrGetRecipe only supports private and firstprivate recipes");
-  }
-}
-
 namespace {
 // Helper class to keep track of designators that appear in data clauses of
 // structured constructs so that they can be remapped to the data operation
@@ -828,14 +800,12 @@ genDataOperandOperations(const Fortran::parser::AccObjectList &objectList,
 
     // For private/firstprivate, attach (and optionally record) the recipe.
     if constexpr (std::is_same_v<Op, mlir::acc::PrivateOp>) {
-      mlir::SymbolRefAttr recipeAttr = createOrGetRecipe(
-          builder, operandLocation, mlir::acc::RecipeKind::private_recipe,
-          info.addr, bounds);
+      mlir::SymbolRefAttr recipeAttr = fir::acc::createOrGetPrivateRecipe(
+          builder, operandLocation, baseAddr.getType(), bounds);
       op.setRecipeAttr(recipeAttr);
     } else if constexpr (std::is_same_v<Op, mlir::acc::FirstprivateOp>) {
-      mlir::SymbolRefAttr recipeAttr = createOrGetRecipe(
-          builder, operandLocation, mlir::acc::RecipeKind::firstprivate_recipe,
-          info.addr, bounds);
+      mlir::SymbolRefAttr recipeAttr = fir::acc::createOrGetFirstprivateRecipe(
+          builder, operandLocation, baseAddr.getType(), bounds);
       op.setRecipeAttr(recipeAttr);
     }
   }
@@ -1100,323 +1070,6 @@ genDataExitOperations(fir::FirOpBuilder &builder,
   }
 }
 
-/// Get the initial value for reduction operator.
-template <typename R>
-static R getReductionInitValue(mlir::acc::ReductionOperator op, mlir::Type ty) {
-  if (op == mlir::acc::ReductionOperator::AccMin) {
-    // min init value -> largest
-    if constexpr (std::is_same_v<R, llvm::APInt>) {
-      assert(ty.isIntOrIndex() && "expect integer or index type");
-      return llvm::APInt::getSignedMaxValue(ty.getIntOrFloatBitWidth());
-    }
-    if constexpr (std::is_same_v<R, llvm::APFloat>) {
-      auto floatTy = mlir::dyn_cast_or_null<mlir::FloatType>(ty);
-      assert(floatTy && "expect float type");
-      return llvm::APFloat::getLargest(floatTy.getFloatSemantics(),
-                                       /*negative=*/false);
-    }
-  } else if (op == mlir::acc::ReductionOperator::AccMax) {
-    // max init value -> smallest
-    if constexpr (std::is_same_v<R, llvm::APInt>) {
-      assert(ty.isIntOrIndex() && "expect integer or index type");
-      return llvm::APInt::getSignedMinValue(ty.getIntOrFloatBitWidth());
-    }
-    if constexpr (std::is_same_v<R, llvm::APFloat>) {
-      auto floatTy = mlir::dyn_cast_or_null<mlir::FloatType>(ty);
-      assert(floatTy && "expect float type");
-      return llvm::APFloat::getSmallest(floatTy.getFloatSemantics(),
-                                        /*negative=*/true);
-    }
-  } else if (op == mlir::acc::ReductionOperator::AccIand) {
-    if constexpr (std::is_same_v<R, llvm::APInt>) {
-      assert(ty.isIntOrIndex() && "expect integer type");
-      unsigned bits = ty.getIntOrFloatBitWidth();
-      return llvm::APInt::getAllOnes(bits);
-    }
-  } else {
-    assert(op != mlir::acc::ReductionOperator::AccNone);
-    // +, ior, ieor init value -> 0
-    // * init value -> 1
-    int64_t value = (op == mlir::acc::ReductionOperator::AccMul) ? 1 : 0;
-    if constexpr (std::is_same_v<R, llvm::APInt>) {
-      assert(ty.isIntOrIndex() && "expect integer or index type");
-      return llvm::APInt(ty.getIntOrFloatBitWidth(), value, true);
-    }
-
-    if constexpr (std::is_same_v<R, llvm::APFloat>) {
-      assert(mlir::isa<mlir::FloatType>(ty) && "expect float type");
-      auto floatTy = mlir::dyn_cast<mlir::FloatType>(ty);
-      return llvm::APFloat(floatTy.getFloatSemantics(), value);
-    }
-
-    if constexpr (std::is_same_v<R, int64_t>)
-      return value;
-  }
-  llvm_unreachable("OpenACC reduction unsupported type");
-}
-
-/// Return a constant with the initial value for the reduction operator and
-/// type combination.
-static mlir::Value getReductionInitValue(fir::FirOpBuilder &builder,
-                                         mlir::Location loc, mlir::Type ty,
-                                         mlir::acc::ReductionOperator op) {
-  if (op == mlir::acc::ReductionOperator::AccLand ||
-      op == mlir::acc::ReductionOperator::AccLor ||
-      op == mlir::acc::ReductionOperator::AccEqv ||
-      op == mlir::acc::ReductionOperator::AccNeqv) {
-    assert(mlir::isa<fir::LogicalType>(ty) && "expect fir.logical type");
-    bool value = true; // .true. for .and. and .eqv.
-    if (op == mlir::acc::ReductionOperator::AccLor ||
-        op == mlir::acc::ReductionOperator::AccNeqv)
-      value = false; // .false. for .or. and .neqv.
-    return builder.createBool(loc, value);
-  }
-  if (ty.isIntOrIndex())
-    return mlir::arith::ConstantOp::create(
-        builder, loc, ty,
-        builder.getIntegerAttr(ty, getReductionInitValue<llvm::APInt>(op, ty)));
-  if (op == mlir::acc::ReductionOperator::AccMin ||
-      op == mlir::acc::ReductionOperator::AccMax) {
-    if (mlir::isa<mlir::ComplexType>(ty))
-      llvm::report_fatal_error(
-          "min/max reduction not supported for complex type");
-    if (auto floatTy = mlir::dyn_cast_or_null<mlir::FloatType>(ty))
-      return mlir::arith::ConstantOp::create(
-          builder, loc, ty,
-          builder.getFloatAttr(ty,
-                               getReductionInitValue<llvm::APFloat>(op, ty)));
-  } else if (auto floatTy = mlir::dyn_cast_or_null<mlir::FloatType>(ty)) {
-    return mlir::arith::ConstantOp::create(
-        builder, loc, ty,
-        builder.getFloatAttr(ty, getReductionInitValue<int64_t>(op, ty)));
-  } else if (auto cmplxTy = mlir::dyn_cast_or_null<mlir::ComplexType>(ty)) {
-    mlir::Type floatTy = cmplxTy.getElementType();
-    mlir::Value realInit = builder.createRealConstant(
-        loc, floatTy, getReductionInitValue<int64_t>(op, cmplxTy));
-    mlir::Value imagInit = builder.createRealConstant(loc, floatTy, 0.0);
-    return fir::factory::Complex{builder, loc}.createComplex(cmplxTy, realInit,
-                                                             imagInit);
-  }
-
-  if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(ty))
-    return getReductionInitValue(builder, loc, seqTy.getEleTy(), op);
-
-  if (auto boxTy = mlir::dyn_cast<fir::BaseBoxType>(ty))
-    return getReductionInitValue(builder, loc, boxTy.getEleTy(), op);
-
-  if (auto heapTy = mlir::dyn_cast<fir::HeapType>(ty))
-    return getReductionInitValue(builder, loc, heapTy.getEleTy(), op);
-
-  if (auto ptrTy = mlir::dyn_cast<fir::PointerType>(ty))
-    return getReductionInitValue(builder, loc, ptrTy.getEleTy(), op);
-
-  llvm::report_fatal_error("Unsupported OpenACC reduction type");
-}
-
-static llvm::SmallVector<mlir::Value>
-getRecipeBounds(fir::FirOpBuilder &builder, mlir::Location loc,
-                mlir::ValueRange dataBoundOps,
-                mlir::ValueRange blockBoundArgs) {
-  if (dataBoundOps.empty())
-    return {};
-  mlir::Type idxTy = builder.getIndexType();
-  mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
-  llvm::SmallVector<mlir::Value> bounds;
-  if (!blockBoundArgs.empty()) {
-    for (unsigned i = 0; i + 2 < blockBoundArgs.size(); i += 3) {
-      bounds.push_back(blockBoundArgs[i]);
-      bounds.push_back(blockBoundArgs[i + 1]);
-      // acc data bound strides is the inner size in bytes or elements, but
-      // sections are always 1-based, so there is no need to try to compute
-      // that back from the acc bounds.
-      bounds.push_back(one);
-    }
-    return bounds;
-  }
-  for (auto bound : dataBoundOps) {
-    auto dataBound = llvm::dyn_cast_if_present<mlir::acc::DataBoundsOp>(
-        bound.getDefiningOp());
-    assert(dataBound && "expect acc bounds to be produced by DataBoundsOp");
-    assert(
-        dataBound.getLowerbound() && dataBound.getUpperbound() &&
-        "expect acc bounds for Fortran to always have lower and upper bounds");
-    std::optional<std::int64_t> lb =
-        fir::getIntIfConstant(dataBound.getLowerbound());
-    std::optional<std::int64_t> ub =
-        fir::getIntIfConstant(dataBound.getUpperbound());
-    assert(lb.has_value() && ub.has_value() &&
-           "must get constant bounds when there are no bound block arguments");
-    bounds.push_back(builder.createIntegerConstant(loc, idxTy, *lb));
-    bounds.push_back(builder.createIntegerConstant(loc, idxTy, *ub));
-    bounds.push_back(one);
-  }
-  return bounds;
-}
-
-static void addRecipeBoundsArgs(llvm::SmallVector<mlir::Value> &bounds,
-                                bool allConstantBound,
-                                llvm::SmallVector<mlir::Type> &argsTy,
-                                llvm::SmallVector<mlir::Location> &argsLoc) {
-  if (!allConstantBound) {
-    for (mlir::Value bound : llvm::reverse(bounds)) {
-      auto dataBound =
-          mlir::dyn_cast<mlir::acc::DataBoundsOp>(bound.getDefiningOp());
-      argsTy.push_back(dataBound.getLowerbound().getType());
-      argsLoc.push_back(dataBound.getLowerbound().getLoc());
-      argsTy.push_back(dataBound.getUpperbound().getType());
-      argsLoc.push_back(dataBound.getUpperbound().getLoc());
-      argsTy.push_back(dataBound.getStartIdx().getType());
-      argsLoc.push_back(dataBound.getStartIdx().getLoc());
-    }
-  }
-}
-
-using MappableValue = mlir::TypedValue<mlir::acc::MappableType>;
-
-template <typename RecipeOp>
-static RecipeOp genRecipeOp(
-    fir::FirOpBuilder &builder, mlir::ModuleOp mod, llvm::StringRef recipeName,
-    mlir::Location loc, mlir::Type ty,
-    llvm::SmallVector<mlir::Value> &dataOperationBounds, bool allConstantBound,
-    mlir::acc::ReductionOperator op = mlir::acc::ReductionOperator::AccNone) {
-  mlir::OpBuilder modBuilder(mod.getBodyRegion());
-  RecipeOp recipe;
-  if constexpr (std::is_same_v<RecipeOp, mlir::acc::ReductionRecipeOp>) {
-    recipe = mlir::acc::ReductionRecipeOp::create(modBuilder, loc, recipeName,
-                                                  ty, op);
-  } else {
-    recipe = RecipeOp::create(modBuilder, loc, recipeName, ty);
-  }
-
-  assert(hlfir::isFortranVariableType(ty) && "expect Fortran variable type");
-
-  llvm::SmallVector<mlir::Type> argsTy{ty};
-  llvm::SmallVector<mlir::Location> argsLoc{loc};
-  if (!dataOperationBounds.empty())
-    addRecipeBoundsArgs(dataOperationBounds, allConstantBound, argsTy, argsLoc);
-
-  auto initBlock = builder.createBlock(
-      &recipe.getInitRegion(), recipe.getInitRegion().end(), argsTy, argsLoc);
-  builder.setInsertionPointToEnd(&recipe.getInitRegion().back());
-  mlir::Value initValue;
-  if constexpr (std::is_same_v<RecipeOp, mlir::acc::ReductionRecipeOp>) {
-    assert(op != mlir::acc::ReductionOperator::AccNone);
-    initValue = getReductionInitValue(builder, loc, fir::unwrapRefType(ty), op);
-  }
-
-  // Since we reuse the same recipe for all variables of the same type - we
-  // cannot use the actual variable name. Thus use a temporary name.
-  llvm::StringRef initName;
-  if constexpr (std::is_same_v<RecipeOp, mlir::acc::ReductionRecipeOp>)
-    initName = accReductionInitName;
-  else
-    initName = accPrivateInitName;
-
-  auto mappableTy = mlir::dyn_cast<mlir::acc::MappableType>(ty);
-  assert(mappableTy &&
-         "Expected that all variable types are considered mappable");
-  bool needsDestroy = false;
-  llvm::SmallVector<mlir::Value> initBounds =
-      getRecipeBounds(builder, loc, dataOperationBounds,
-                      initBlock->getArguments().drop_front(1));
-  mlir::Value retVal = mappableTy.generatePrivateInit(
-      builder, loc, mlir::cast<MappableValue>(initBlock->getArgument(0)),
-      initName, initBounds, initValue, needsDestroy);
-  mlir::acc::YieldOp::create(builder, loc, retVal);
-  // Create destroy region and generate destruction if requested.
-  if (needsDestroy) {
-    llvm::SmallVector<mlir::Type> destroyArgsTy;
-    llvm::SmallVector<mlir::Location> destroyArgsLoc;
-    // original and privatized/reduction value
-    destroyArgsTy.push_back(ty);
-    destroyArgsTy.push_back(ty);
-    destroyArgsLoc.push_back(loc);
-    destroyArgsLoc.push_back(loc);
-    // Append bounds arguments (if any) in the same order as init region
-    if (argsTy.size() > 1) {
-      destroyArgsTy.append(argsTy.begin() + 1, argsTy.end());
-      destroyArgsLoc.insert(destroyArgsLoc.end(), argsTy.size() - 1, loc);
-    }
-
-    mlir::Block *destroyBlock = builder.createBlock(
-        &recipe.getDestroyRegion(), recipe.getDestroyRegion().end(),
-        destroyArgsTy, destroyArgsLoc);
-    builder.setInsertionPointToEnd(destroyBlock);
-
-    llvm::SmallVector<mlir::Value> destroyBounds =
-        getRecipeBounds(builder, loc, dataOperationBounds,
-                        destroyBlock->getArguments().drop_front(2));
-    [[maybe_unused]] bool success = mappableTy.generatePrivateDestroy(
-        builder, loc, destroyBlock->getArgument(1), destroyBounds);
-    assert(success && "failed to generate destroy region");
-    mlir::acc::TerminatorOp::create(builder, loc);
-  }
-  return recipe;
-}
-
-mlir::acc::PrivateRecipeOp Fortran::lower::createOrGetPrivateRecipe(
-    fir::FirOpBuilder &builder, llvm::StringRef recipeName, mlir::Location loc,
-    mlir::Type ty, llvm::SmallVector<mlir::Value> &bounds) {
-  mlir::ModuleOp mod =
-      builder.getBlock()->getParent()->getParentOfType<mlir::ModuleOp>();
-  if (auto recipe = mod.lookupSymbol<mlir::acc::PrivateRecipeOp>(recipeName))
-    return recipe;
-
-  auto ip = builder.saveInsertionPoint();
-  bool allConstantBound = fir::acc::areAllBoundsConstant(bounds);
-  auto recipe = genRecipeOp<mlir::acc::PrivateRecipeOp>(
-      builder, mod, recipeName, loc, ty, bounds, allConstantBound);
-  builder.restoreInsertionPoint(ip);
-  return recipe;
-}
-
-// Generate the combiner or copy region block and block arguments and return the
-// source and destination entities.
-static std::pair<MappableValue, MappableValue>
-genRecipeCombinerOrCopyRegion(fir::FirOpBuilder &builder, mlir::Location loc,
-                              mlir::Type ty, mlir::Region &region,
-                              llvm::SmallVector<mlir::Value> &bounds,
-                              bool allConstantBound) {
-  llvm::SmallVector<mlir::Type> argsTy{ty, ty};
-  llvm::SmallVector<mlir::Location> argsLoc{loc, loc};
-  addRecipeBoundsArgs(bounds, allConstantBound, argsTy, argsLoc);
-  mlir::Block *block =
-      builder.createBlock(&region, region.end(), argsTy, argsLoc);
-  builder.setInsertionPointToEnd(&region.back());
-  auto firstArg = mlir::cast<MappableValue>(block->getArgument(0));
-  auto secondArg = mlir::cast<MappableValue>(block->getArgument(1));
-  return {firstArg, secondArg};
-}
-
-mlir::acc::FirstprivateRecipeOp Fortran::lower::createOrGetFirstprivateRecipe(
-    fir::FirOpBuilder &builder, llvm::StringRef recipeName, mlir::Location loc,
-    mlir::Type ty, llvm::SmallVector<mlir::Value> &dataBoundOps) {
-  mlir::ModuleOp mod =
-      builder.getBlock()->getParent()->getParentOfType<mlir::ModuleOp>();
-  if (auto recipe =
-          mod.lookupSymbol<mlir::acc::FirstprivateRecipeOp>(recipeName))
-    return recipe;
-
-  mlir::OpBuilder::InsertionGuard guard(builder);
-  bool allConstantBound = fir::acc::areAllBoundsConstant(dataBoundOps);
-  auto recipe = genRecipeOp<mlir::acc::FirstprivateRecipeOp>(
-      builder, mod, recipeName, loc, ty, dataBoundOps, allConstantBound);
-  auto [source, destination] = genRecipeCombinerOrCopyRegion(
-      builder, loc, ty, recipe.getCopyRegion(), dataBoundOps, allConstantBound);
-  llvm::SmallVector<mlir::Value> copyBounds =
-      getRecipeBounds(builder, loc, dataBoundOps,
-                      recipe.getCopyRegion().getArguments().drop_front(2));
-
-  auto mappableTy = mlir::dyn_cast<mlir::acc::MappableType>(ty);
-  assert(mappableTy &&
-         "Expected that all variable types are considered mappable");
-  [[maybe_unused]] bool success =
-      mappableTy.generateCopy(builder, loc, source, destination, copyBounds);
-  assert(success && "failed to generate copy");
-  mlir::acc::TerminatorOp::create(builder, loc);
-  return recipe;
-}
-
 /// Return the corresponding enum value for the mlir::acc::ReductionOperator
 /// from the parser representation.
 static mlir::acc::ReductionOperator
@@ -1448,41 +1101,6 @@ getReductionOperator(const Fortran::parser::ReductionOperator &op) {
   llvm_unreachable("unexpected reduction operator");
 }
 
-mlir::acc::ReductionRecipeOp Fortran::lower::createOrGetReductionRecipe(
-    fir::FirOpBuilder &builder, llvm::StringRef recipeName, mlir::Location loc,
-    mlir::Type ty, mlir::acc::ReductionOperator op,
-    llvm::SmallVector<mlir::Value> &dataBoundOps) {
-  mlir::ModuleOp mod =
-      builder.getBlock()->getParent()->getParentOfType<mlir::ModuleOp>();
-  if (auto recipe = mod.lookupSymbol<mlir::acc::ReductionRecipeOp>(recipeName))
-    return recipe;
-
-  mlir::OpBuilder::InsertionGuard guard(builder);
-  bool allConstantBound = fir::acc::areAllBoundsConstant(dataBoundOps);
-  auto recipe = genRecipeOp<mlir::acc::ReductionRecipeOp>(
-      builder, mod, recipeName, loc, ty, dataBoundOps, allConstantBound, op);
-
-  auto [dest, source] = genRecipeCombinerOrCopyRegion(
-      builder, loc, ty, recipe.getCombinerRegion(), dataBoundOps,
-      allConstantBound);
-  llvm::SmallVector<mlir::Value> combinerBounds =
-      getRecipeBounds(builder, loc, dataBoundOps,
-                      recipe.getCombinerRegion().getArguments().drop_front(2));
-
-  auto mappableTy = mlir::dyn_cast<mlir::acc::MappableType>(ty);
-  assert(mappableTy &&
-         "Expected that all variable types are considered mappable");
-  mlir::Attribute fastMathAttr;
-  if (builder.getFastMathFlags() != mlir::arith::FastMathFlags::none)
-    fastMathAttr = mlir::arith::FastMathFlagsAttr::get(
-        builder.getContext(), builder.getFastMathFlags());
-  [[maybe_unused]] bool success = mappableTy.generateCombiner(
-      builder, loc, dest, source, combinerBounds, op, fastMathAttr);
-  assert(success && "failed to generate combiner");
-  mlir::acc::YieldOp::create(builder, loc, dest);
-  return recipe;
-}
-
 static bool isSupportedReductionType(mlir::Type ty) {
   ty = fir::unwrapRefType(ty);
   if (auto boxTy = mlir::dyn_cast<fir::BaseBoxType>(ty))
@@ -1553,14 +1171,13 @@ genReductions(const Fortran::parser::AccObjectListWithReduction &objectList,
         mlir::acc::DataClause::acc_reduction, info.addr.getType(), async,
         asyncDeviceTypes, asyncOnlyDeviceTypes, /*unwrapBoxAddr=*/true);
     mlir::Type ty = op.getAccVar().getType();
-    std::string recipeName = fir::acc::getRecipeName(
-        mlir::acc::RecipeKind::reduction_recipe, ty, info.addr, bounds, mlirOp);
-
-    mlir::acc::ReductionRecipeOp recipe =
-        Fortran::lower::createOrGetReductionRecipe(
-            builder, recipeName, operandLocation, ty, mlirOp, bounds);
-    op.setRecipeAttr(
-        mlir::SymbolRefAttr::get(builder.getContext(), recipe.getSymName()));
+    mlir::Attribute fastMathAttr;
+    if (builder.getFastMathFlags() != mlir::arith::FastMathFlags::none)
+      fastMathAttr = mlir::arith::FastMathFlagsAttr::get(
+          builder.getContext(), builder.getFastMathFlags());
+    mlir::SymbolRefAttr recipe = fir::acc::createOrGetReductionRecipe(
+        builder, operandLocation, ty, mlirOp, bounds, fastMathAttr);
+    op.setRecipeAttr(recipe);
     reductionOperands.push_back(op.getAccVar());
     // Track the symbol and its corresponding mlir::Value if requested so that
     // accesses inside the compute/loop regions use the acc.reduction variable.
@@ -1804,8 +1421,8 @@ static void privatizeIv(
 
   if (privateOp == nullptr) {
     llvm::SmallVector<mlir::Value> noBounds;
-    mlir::SymbolRefAttr recipe = createOrGetRecipe(
-        builder, loc, mlir::acc::RecipeKind::private_recipe, ivValue, noBounds);
+    mlir::SymbolRefAttr recipe = fir::acc::createOrGetPrivateRecipe(
+        builder, loc, ivValue.getType(), noBounds);
 
     std::stringstream asFortran;
     asFortran << Fortran::lower::mangle::demangleName(toStringRef(sym.name()));
diff --git a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp
index b88936426657d..322577f8014fc 100644
--- a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp
+++ b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp
@@ -11,6 +11,9 @@
 //===----------------------------------------------------------------------===//
 
 #include "flang/Optimizer/OpenACC/Support/FIROpenACCUtils.h"
+#include "flang/Optimizer/Builder/BoxValue.h"
+#include "flang/Optimizer/Builder/Complex.h"
+#include "flang/Optimizer/Builder/FIRBuilder.h"
 #include "flang/Optimizer/Dialect/FIROps.h"
 #include "flang/Optimizer/Dialect/FIROpsSupport.h"
 #include "flang/Optimizer/Dialect/FIRType.h"
@@ -18,7 +21,9 @@
 #include "flang/Optimizer/Dialect/Support/KindMapping.h"
 #include "flang/Optimizer/HLFIR/HLFIROps.h"
 #include "flang/Optimizer/Support/InternalNames.h"
+#include "mlir/Dialect/Arith/IR/Arith.h"
 #include "mlir/Dialect/OpenACC/OpenACC.h"
+#include "mlir/Dialect/OpenACC/OpenACCUtils.h"
 #include "mlir/IR/Matchers.h"
 #include "mlir/Interfaces/ViewLikeInterface.h"
 #include "llvm/ADT/TypeSwitch.h"
@@ -26,10 +31,10 @@
 
 using namespace mlir;
 
-namespace fir {
-namespace acc {
+static constexpr llvm::StringRef accPrivateInitName = "acc.private.init";
+static constexpr llvm::StringRef accReductionInitName = "acc.reduction.init";
 
-std::string getVariableName(Value v, bool preferDemangledName) {
+std::string fir::acc::getVariableName(Value v, bool preferDemangledName) {
   std::string srcName;
   std::string prefix;
   llvm::SmallVector<std::string, 4> arrayIndices;
@@ -149,7 +154,7 @@ std::string getVariableName(Value v, bool preferDemangledName) {
 
   // Fallback to the default implementation.
   if (srcName.empty())
-    return acc::getVariableName(v);
+    return mlir::acc::getVariableName(v);
 
   // Build array index suffix if present
   std::string suffix;
@@ -171,7 +176,7 @@ std::string getVariableName(Value v, bool preferDemangledName) {
   return prefix + srcName + suffix;
 }
 
-bool areAllBoundsConstant(llvm::ArrayRef<Value> bounds) {
+bool fir::acc::areAllBoundsConstant(llvm::ArrayRef<Value> bounds) {
   for (auto bound : bounds) {
     auto dataBound =
         mlir::dyn_cast<mlir::acc::DataBoundsOp>(bound.getDefiningOp());
@@ -224,9 +229,11 @@ static std::string getBoundsString(llvm::ArrayRef<Value> bounds) {
   return os.str();
 }
 
-std::string getRecipeName(mlir::acc::RecipeKind kind, Type type, Value var,
-                          llvm::ArrayRef<Value> bounds,
-                          mlir::acc::ReductionOperator reductionOp) {
+static std::string getRecipeName(mlir::acc::RecipeKind kind, Type type,
+                                 const fir::KindMapping &kindMap,
+                                 llvm::ArrayRef<Value> bounds,
+                                 mlir::acc::ReductionOperator reductionOp =
+                                     mlir::acc::ReductionOperator::AccNone) {
   assert(fir::isa_fir_type(type) && "getRecipeName expects a FIR type");
 
   // Build the complete prefix with all components before calling
@@ -253,11 +260,374 @@ std::string getRecipeName(mlir::acc::RecipeKind kind, Type type, Value var,
   if (!bounds.empty())
     prefixOS << getBoundsString(bounds);
 
+  return fir::getTypeAsString(type, kindMap, prefixOS.str());
+}
+
+std::string fir::acc::getRecipeName(mlir::acc::RecipeKind kind, Type type,
+                                    Value var, llvm::ArrayRef<Value> bounds,
+                                    mlir::acc::ReductionOperator reductionOp) {
   auto kindMap = var && var.getDefiningOp()
                      ? fir::getKindMapping(var.getDefiningOp())
                      : fir::KindMapping(type.getContext());
-  return fir::getTypeAsString(type, kindMap, prefixOS.str());
+  return ::getRecipeName(kind, type, kindMap, bounds, reductionOp);
+}
+
+/// Get the initial value for reduction operator.
+template <typename R>
+static R getReductionInitValue(mlir::acc::ReductionOperator op, mlir::Type ty) {
+  if (op == mlir::acc::ReductionOperator::AccMin) {
+    // min init value -> largest
+    if constexpr (std::is_same_v<R, llvm::APInt>) {
+      assert(ty.isIntOrIndex() && "expect integer or index type");
+      return llvm::APInt::getSignedMaxValue(ty.getIntOrFloatBitWidth());
+    }
+    if constexpr (std::is_same_v<R, llvm::APFloat>) {
+      auto floatTy = mlir::dyn_cast_or_null<mlir::FloatType>(ty);
+      assert(floatTy && "expect float type");
+      return llvm::APFloat::getLargest(floatTy.getFloatSemantics(),
+                                       /*negative=*/false);
+    }
+  } else if (op == mlir::acc::ReductionOperator::AccMax) {
+    // max init value -> smallest
+    if constexpr (std::is_same_v<R, llvm::APInt>) {
+      assert(ty.isIntOrIndex() && "expect integer or index type");
+      return llvm::APInt::getSignedMinValue(ty.getIntOrFloatBitWidth());
+    }
+    if constexpr (std::is_same_v<R, llvm::APFloat>) {
+      auto floatTy = mlir::dyn_cast_or_null<mlir::FloatType>(ty);
+      assert(floatTy && "expect float type");
+      return llvm::APFloat::getSmallest(floatTy.getFloatSemantics(),
+                                        /*negative=*/true);
+    }
+  } else if (op == mlir::acc::ReductionOperator::AccIand) {
+    if constexpr (std::is_same_v<R, llvm::APInt>) {
+      assert(ty.isIntOrIndex() && "expect integer type");
+      unsigned bits = ty.getIntOrFloatBitWidth();
+      return llvm::APInt::getAllOnes(bits);
+    }
+  } else {
+    assert(op != mlir::acc::ReductionOperator::AccNone);
+    // +, ior, ieor init value -> 0
+    // * init value -> 1
+    int64_t value = (op == mlir::acc::ReductionOperator::AccMul) ? 1 : 0;
+    if constexpr (std::is_same_v<R, llvm::APInt>) {
+      assert(ty.isIntOrIndex() && "expect integer or index type");
+      return llvm::APInt(ty.getIntOrFloatBitWidth(), value, true);
+    }
+
+    if constexpr (std::is_same_v<R, llvm::APFloat>) {
+      assert(mlir::isa<mlir::FloatType>(ty) && "expect float type");
+      auto floatTy = mlir::dyn_cast<mlir::FloatType>(ty);
+      return llvm::APFloat(floatTy.getFloatSemantics(), value);
+    }
+
+    if constexpr (std::is_same_v<R, int64_t>)
+      return value;
+  }
+  llvm_unreachable("OpenACC reduction unsupported type");
 }
 
-} // namespace acc
-} // namespace fir
+/// Return a constant with the initial value for the reduction operator and
+/// type combination.
+static mlir::Value getReductionInitValue(fir::FirOpBuilder &builder,
+                                         mlir::Location loc, mlir::Type ty,
+                                         mlir::acc::ReductionOperator op) {
+  if (op == mlir::acc::ReductionOperator::AccLand ||
+      op == mlir::acc::ReductionOperator::AccLor ||
+      op == mlir::acc::ReductionOperator::AccEqv ||
+      op == mlir::acc::ReductionOperator::AccNeqv) {
+    assert(mlir::isa<fir::LogicalType>(ty) && "expect fir.logical type");
+    bool value = true; // .true. for .and. and .eqv.
+    if (op == mlir::acc::ReductionOperator::AccLor ||
+        op == mlir::acc::ReductionOperator::AccNeqv)
+      value = false; // .false. for .or. and .neqv.
+    return builder.createBool(loc, value);
+  }
+  if (ty.isIntOrIndex())
+    return mlir::arith::ConstantOp::create(
+        builder, loc, ty,
+        builder.getIntegerAttr(ty, getReductionInitValue<llvm::APInt>(op, ty)));
+  if (op == mlir::acc::ReductionOperator::AccMin ||
+      op == mlir::acc::ReductionOperator::AccMax) {
+    if (mlir::isa<mlir::ComplexType>(ty))
+      llvm::report_fatal_error(
+          "min/max reduction not supported for complex type");
+    if (auto floatTy = mlir::dyn_cast_or_null<mlir::FloatType>(ty))
+      return mlir::arith::ConstantOp::create(
+          builder, loc, ty,
+          builder.getFloatAttr(ty,
+                               getReductionInitValue<llvm::APFloat>(op, ty)));
+  } else if (auto floatTy = mlir::dyn_cast_or_null<mlir::FloatType>(ty)) {
+    return mlir::arith::ConstantOp::create(
+        builder, loc, ty,
+        builder.getFloatAttr(ty, getReductionInitValue<int64_t>(op, ty)));
+  } else if (auto cmplxTy = mlir::dyn_cast_or_null<mlir::ComplexType>(ty)) {
+    mlir::Type floatTy = cmplxTy.getElementType();
+    mlir::Value realInit = builder.createRealConstant(
+        loc, floatTy, getReductionInitValue<int64_t>(op, cmplxTy));
+    mlir::Value imagInit = builder.createRealConstant(loc, floatTy, 0.0);
+    return fir::factory::Complex{builder, loc}.createComplex(cmplxTy, realInit,
+                                                             imagInit);
+  }
+
+  if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(ty))
+    return getReductionInitValue(builder, loc, seqTy.getEleTy(), op);
+
+  if (auto boxTy = mlir::dyn_cast<fir::BaseBoxType>(ty))
+    return getReductionInitValue(builder, loc, boxTy.getEleTy(), op);
+
+  if (auto heapTy = mlir::dyn_cast<fir::HeapType>(ty))
+    return getReductionInitValue(builder, loc, heapTy.getEleTy(), op);
+
+  if (auto ptrTy = mlir::dyn_cast<fir::PointerType>(ty))
+    return getReductionInitValue(builder, loc, ptrTy.getEleTy(), op);
+
+  llvm::report_fatal_error("Unsupported OpenACC reduction type");
+}
+
+static llvm::SmallVector<mlir::Value>
+getRecipeBounds(fir::FirOpBuilder &builder, mlir::Location loc,
+                mlir::ValueRange dataBoundOps,
+                mlir::ValueRange blockBoundArgs) {
+  if (dataBoundOps.empty())
+    return {};
+  mlir::Type idxTy = builder.getIndexType();
+  mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
+  llvm::SmallVector<mlir::Value> bounds;
+  if (!blockBoundArgs.empty()) {
+    for (unsigned i = 0; i + 2 < blockBoundArgs.size(); i += 3) {
+      bounds.push_back(blockBoundArgs[i]);
+      bounds.push_back(blockBoundArgs[i + 1]);
+      // acc data bound strides is the inner size in bytes or elements, but
+      // sections are always 1-based, so there is no need to try to compute
+      // that back from the acc bounds.
+      bounds.push_back(one);
+    }
+    return bounds;
+  }
+  for (auto bound : dataBoundOps) {
+    auto dataBound = llvm::dyn_cast_if_present<mlir::acc::DataBoundsOp>(
+        bound.getDefiningOp());
+    assert(dataBound && "expect acc bounds to be produced by DataBoundsOp");
+    assert(
+        dataBound.getLowerbound() && dataBound.getUpperbound() &&
+        "expect acc bounds for Fortran to always have lower and upper bounds");
+    std::optional<std::int64_t> lb =
+        fir::getIntIfConstant(dataBound.getLowerbound());
+    std::optional<std::int64_t> ub =
+        fir::getIntIfConstant(dataBound.getUpperbound());
+    assert(lb.has_value() && ub.has_value() &&
+           "must get constant bounds when there are no bound block arguments");
+    bounds.push_back(builder.createIntegerConstant(loc, idxTy, *lb));
+    bounds.push_back(builder.createIntegerConstant(loc, idxTy, *ub));
+    bounds.push_back(one);
+  }
+  return bounds;
+}
+
+static void addRecipeBoundsArgs(llvm::SmallVector<mlir::Value> &bounds,
+                                bool allConstantBound,
+                                llvm::SmallVector<mlir::Type> &argsTy,
+                                llvm::SmallVector<mlir::Location> &argsLoc) {
+  if (!allConstantBound) {
+    for (mlir::Value bound : llvm::reverse(bounds)) {
+      auto dataBound =
+          mlir::dyn_cast<mlir::acc::DataBoundsOp>(bound.getDefiningOp());
+      argsTy.push_back(dataBound.getLowerbound().getType());
+      argsLoc.push_back(dataBound.getLowerbound().getLoc());
+      argsTy.push_back(dataBound.getUpperbound().getType());
+      argsLoc.push_back(dataBound.getUpperbound().getLoc());
+      argsTy.push_back(dataBound.getStartIdx().getType());
+      argsLoc.push_back(dataBound.getStartIdx().getLoc());
+    }
+  }
+}
+
+using MappableValue = mlir::TypedValue<mlir::acc::MappableType>;
+
+// Generate the combiner or copy region block and block arguments and return the
+// source and destination entities.
+static std::pair<MappableValue, MappableValue>
+genRecipeCombinerOrCopyRegion(fir::FirOpBuilder &builder, mlir::Location loc,
+                              mlir::Type ty, mlir::Region &region,
+                              llvm::SmallVector<mlir::Value> &bounds,
+                              bool allConstantBound) {
+  llvm::SmallVector<mlir::Type> argsTy{ty, ty};
+  llvm::SmallVector<mlir::Location> argsLoc{loc, loc};
+  addRecipeBoundsArgs(bounds, allConstantBound, argsTy, argsLoc);
+  mlir::Block *block =
+      builder.createBlock(&region, region.end(), argsTy, argsLoc);
+  builder.setInsertionPointToEnd(&region.back());
+  auto firstArg = mlir::cast<MappableValue>(block->getArgument(0));
+  auto secondArg = mlir::cast<MappableValue>(block->getArgument(1));
+  return {firstArg, secondArg};
+}
+
+template <typename RecipeOp>
+static RecipeOp genRecipeOp(
+    fir::FirOpBuilder &builder, mlir::ModuleOp mod, llvm::StringRef recipeName,
+    mlir::Location loc, mlir::Type ty,
+    llvm::SmallVector<mlir::Value> &dataOperationBounds, bool allConstantBound,
+    mlir::acc::ReductionOperator op = mlir::acc::ReductionOperator::AccNone) {
+  mlir::OpBuilder modBuilder(mod.getBodyRegion());
+  RecipeOp recipe;
+  if constexpr (std::is_same_v<RecipeOp, mlir::acc::ReductionRecipeOp>) {
+    recipe = mlir::acc::ReductionRecipeOp::create(modBuilder, loc, recipeName,
+                                                  ty, op);
+  } else {
+    recipe = RecipeOp::create(modBuilder, loc, recipeName, ty);
+  }
+
+  assert(hlfir::isFortranVariableType(ty) && "expect Fortran variable type");
+
+  llvm::SmallVector<mlir::Type> argsTy{ty};
+  llvm::SmallVector<mlir::Location> argsLoc{loc};
+  if (!dataOperationBounds.empty())
+    addRecipeBoundsArgs(dataOperationBounds, allConstantBound, argsTy, argsLoc);
+
+  auto initBlock = builder.createBlock(
+      &recipe.getInitRegion(), recipe.getInitRegion().end(), argsTy, argsLoc);
+  builder.setInsertionPointToEnd(&recipe.getInitRegion().back());
+  mlir::Value initValue;
+  if constexpr (std::is_same_v<RecipeOp, mlir::acc::ReductionRecipeOp>) {
+    assert(op != mlir::acc::ReductionOperator::AccNone);
+    initValue = getReductionInitValue(builder, loc, fir::unwrapRefType(ty), op);
+  }
+
+  // Since we reuse the same recipe for all variables of the same type - we
+  // cannot use the actual variable name. Thus use a temporary name.
+  llvm::StringRef initName;
+  if constexpr (std::is_same_v<RecipeOp, mlir::acc::ReductionRecipeOp>)
+    initName = accReductionInitName;
+  else
+    initName = accPrivateInitName;
+
+  auto mappableTy = mlir::dyn_cast<mlir::acc::MappableType>(ty);
+  assert(mappableTy &&
+         "Expected that all variable types are considered mappable");
+  bool needsDestroy = false;
+  llvm::SmallVector<mlir::Value> initBounds =
+      getRecipeBounds(builder, loc, dataOperationBounds,
+                      initBlock->getArguments().drop_front(1));
+  mlir::Value retVal = mappableTy.generatePrivateInit(
+      builder, loc, mlir::cast<MappableValue>(initBlock->getArgument(0)),
+      initName, initBounds, initValue, needsDestroy);
+  mlir::acc::YieldOp::create(builder, loc, retVal);
+  // Create destroy region and generate destruction if requested.
+  if (needsDestroy) {
+    llvm::SmallVector<mlir::Type> destroyArgsTy;
+    llvm::SmallVector<mlir::Location> destroyArgsLoc;
+    // original and privatized/reduction value
+    destroyArgsTy.push_back(ty);
+    destroyArgsTy.push_back(ty);
+    destroyArgsLoc.push_back(loc);
+    destroyArgsLoc.push_back(loc);
+    // Append bounds arguments (if any) in the same order as init region
+    if (argsTy.size() > 1) {
+      destroyArgsTy.append(argsTy.begin() + 1, argsTy.end());
+      destroyArgsLoc.insert(destroyArgsLoc.end(), argsTy.size() - 1, loc);
+    }
+
+    mlir::Block *destroyBlock = builder.createBlock(
+        &recipe.getDestroyRegion(), recipe.getDestroyRegion().end(),
+        destroyArgsTy, destroyArgsLoc);
+    builder.setInsertionPointToEnd(destroyBlock);
+
+    llvm::SmallVector<mlir::Value> destroyBounds =
+        getRecipeBounds(builder, loc, dataOperationBounds,
+                        destroyBlock->getArguments().drop_front(2));
+    [[maybe_unused]] bool success = mappableTy.generatePrivateDestroy(
+        builder, loc, destroyBlock->getArgument(1), destroyBounds);
+    assert(success && "failed to generate destroy region");
+    mlir::acc::TerminatorOp::create(builder, loc);
+  }
+  return recipe;
+}
+
+mlir::SymbolRefAttr
+fir::acc::createOrGetPrivateRecipe(mlir::OpBuilder &mlirBuilder,
+                                   mlir::Location loc, mlir::Type ty,
+                                   llvm::SmallVector<mlir::Value> &bounds) {
+  mlir::ModuleOp mod =
+      mlirBuilder.getBlock()->getParent()->getParentOfType<mlir::ModuleOp>();
+  fir::FirOpBuilder builder(mlirBuilder, mod);
+  std::string recipeName = ::getRecipeName(
+      mlir::acc::RecipeKind::private_recipe, ty, builder.getKindMap(), bounds);
+  if (auto recipe = mod.lookupSymbol<mlir::acc::PrivateRecipeOp>(recipeName))
+    return mlir::SymbolRefAttr::get(builder.getContext(), recipe.getSymName());
+
+  mlir::OpBuilder::InsertionGuard guard(builder);
+  bool allConstantBound = fir::acc::areAllBoundsConstant(bounds);
+  auto recipe = genRecipeOp<mlir::acc::PrivateRecipeOp>(
+      builder, mod, recipeName, loc, ty, bounds, allConstantBound);
+  return mlir::SymbolRefAttr::get(builder.getContext(), recipe.getSymName());
+}
+
+mlir::SymbolRefAttr fir::acc::createOrGetFirstprivateRecipe(
+    mlir::OpBuilder &mlirBuilder, mlir::Location loc, mlir::Type ty,
+    llvm::SmallVector<mlir::Value> &dataBoundOps) {
+  mlir::ModuleOp mod =
+      mlirBuilder.getBlock()->getParent()->getParentOfType<mlir::ModuleOp>();
+  fir::FirOpBuilder builder(mlirBuilder, mod);
+  std::string recipeName =
+      ::getRecipeName(mlir::acc::RecipeKind::firstprivate_recipe, ty,
+                      builder.getKindMap(), dataBoundOps);
+  if (auto recipe =
+          mod.lookupSymbol<mlir::acc::FirstprivateRecipeOp>(recipeName))
+    return mlir::SymbolRefAttr::get(builder.getContext(), recipe.getSymName());
+
+  mlir::OpBuilder::InsertionGuard guard(builder);
+  bool allConstantBound = fir::acc::areAllBoundsConstant(dataBoundOps);
+  auto recipe = genRecipeOp<mlir::acc::FirstprivateRecipeOp>(
+      builder, mod, recipeName, loc, ty, dataBoundOps, allConstantBound);
+  auto [source, destination] = genRecipeCombinerOrCopyRegion(
+      builder, loc, ty, recipe.getCopyRegion(), dataBoundOps, allConstantBound);
+  llvm::SmallVector<mlir::Value> copyBounds =
+      getRecipeBounds(builder, loc, dataBoundOps,
+                      recipe.getCopyRegion().getArguments().drop_front(2));
+
+  auto mappableTy = mlir::dyn_cast<mlir::acc::MappableType>(ty);
+  assert(mappableTy &&
+         "Expected that all variable types are considered mappable");
+  [[maybe_unused]] bool success =
+      mappableTy.generateCopy(builder, loc, source, destination, copyBounds);
+  assert(success && "failed to generate copy");
+  mlir::acc::TerminatorOp::create(builder, loc);
+  return mlir::SymbolRefAttr::get(builder.getContext(), recipe.getSymName());
+}
+
+mlir::SymbolRefAttr fir::acc::createOrGetReductionRecipe(
+    mlir::OpBuilder &mlirBuilder, mlir::Location loc, mlir::Type ty,
+    mlir::acc::ReductionOperator op,
+    llvm::SmallVector<mlir::Value> &dataBoundOps,
+    mlir::Attribute fastMathAttr) {
+  mlir::ModuleOp mod =
+      mlirBuilder.getBlock()->getParent()->getParentOfType<mlir::ModuleOp>();
+  fir::FirOpBuilder builder(mlirBuilder, mod);
+  std::string recipeName =
+      ::getRecipeName(mlir::acc::RecipeKind::reduction_recipe, ty,
+                      builder.getKindMap(), dataBoundOps, op);
+  if (auto recipe = mod.lookupSymbol<mlir::acc::ReductionRecipeOp>(recipeName))
+    return mlir::SymbolRefAttr::get(builder.getContext(), recipe.getSymName());
+
+  mlir::OpBuilder::InsertionGuard guard(builder);
+  bool allConstantBound = fir::acc::areAllBoundsConstant(dataBoundOps);
+  auto recipe = genRecipeOp<mlir::acc::ReductionRecipeOp>(
+      builder, mod, recipeName, loc, ty, dataBoundOps, allConstantBound, op);
+
+  auto [dest, source] = genRecipeCombinerOrCopyRegion(
+      builder, loc, ty, recipe.getCombinerRegion(), dataBoundOps,
+      allConstantBound);
+  llvm::SmallVector<mlir::Value> combinerBounds =
+      getRecipeBounds(builder, loc, dataBoundOps,
+                      recipe.getCombinerRegion().getArguments().drop_front(2));
+
+  auto mappableTy = mlir::dyn_cast<mlir::acc::MappableType>(ty);
+  assert(mappableTy &&
+         "Expected that all variable types are considered mappable");
+  [[maybe_unused]] bool success = mappableTy.generateCombiner(
+      builder, loc, dest, source, combinerBounds, op, fastMathAttr);
+  assert(success && "failed to generate combiner");
+  mlir::acc::YieldOp::create(builder, loc, dest);
+  return mlir::SymbolRefAttr::get(builder.getContext(), recipe.getSymName());
+}



More information about the flang-commits mailing list