[Mlir-commits] [mlir] f9c2117 - [acc] Add an API to make private recipes out of firstprivate recipes (#170588)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Thu Dec 4 14:08:38 PST 2025


Author: Susan Tan (ス-ザン タン)
Date: 2025-12-04T17:08:34-05:00
New Revision: f9c2117b860e4b56f37be3c7c5f736065139eb12

URL: https://github.com/llvm/llvm-project/commit/f9c2117b860e4b56f37be3c7c5f736065139eb12
DIFF: https://github.com/llvm/llvm-project/commit/f9c2117b860e4b56f37be3c7c5f736065139eb12.diff

LOG: [acc] Add an API to make private recipes out of firstprivate recipes  (#170588)

In cases of optimizations promoting firstprivate to private, this API
would be useful.

Added: 
    mlir/test/Dialect/OpenACC/recipe-populate-private-from-firstprivate.mlir

Modified: 
    mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td
    mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp
    mlir/test/lib/Dialect/OpenACC/TestRecipePopulate.cpp

Removed: 
    


################################################################################
diff  --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td b/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td
index 7a727bd7fb838..e968c2b151f5a 100644
--- a/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td
+++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td
@@ -1394,6 +1394,19 @@ def OpenACC_PrivateRecipeOp
         ::mlir::Type varType,
         ::llvm::StringRef varName = "",
         ::mlir::ValueRange bounds = {});
+
+    /// Creates a PrivateRecipeOp using the same variable type as an existing
+    /// FirstprivateRecipeOp. This is a useful in cases where we promote private variables to firstprivate by analysis
+    /// This function reuses the init region from a firstprivate recipe when building a private
+    /// recipe. Callers thus must ensure that this is semantically valid for the language
+    /// lowering (e.g. that private does not perform extra default initialization
+    /// that firstprivate intentionally omits, such as for C++ classes or Fortran
+    /// derived types with default initialization).
+    static std::optional<PrivateRecipeOp> createAndPopulate(
+        ::mlir::OpBuilder &builder,
+        ::mlir::Location loc,
+        ::llvm::StringRef recipeName,
+        ::mlir::acc::FirstprivateRecipeOp firstprivRecipe);
   }];
 }
 

diff  --git a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp
index 029b1eee8e93d..7b91393c0314f 100644
--- a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp
+++ b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp
@@ -15,6 +15,7 @@
 #include "mlir/IR/BuiltinAttributes.h"
 #include "mlir/IR/BuiltinTypes.h"
 #include "mlir/IR/DialectImplementation.h"
+#include "mlir/IR/IRMapping.h"
 #include "mlir/IR/Matchers.h"
 #include "mlir/IR/OpImplementation.h"
 #include "mlir/IR/SymbolTable.h"
@@ -1449,6 +1450,28 @@ PrivateRecipeOp::createAndPopulate(OpBuilder &builder, Location loc,
   return recipe;
 }
 
+std::optional<PrivateRecipeOp>
+PrivateRecipeOp::createAndPopulate(OpBuilder &builder, Location loc,
+                                   StringRef recipeName,
+                                   FirstprivateRecipeOp firstprivRecipe) {
+  // Create the private.recipe op with the same type as the firstprivate.recipe.
+  OpBuilder::InsertionGuard guard(builder);
+  auto varType = firstprivRecipe.getType();
+  auto recipe = PrivateRecipeOp::create(builder, loc, recipeName, varType);
+
+  // Clone the init region
+  IRMapping mapping;
+  firstprivRecipe.getInitRegion().cloneInto(&recipe.getInitRegion(), mapping);
+
+  // Clone destroy region if the firstprivate.recipe has one.
+  if (!firstprivRecipe.getDestroyRegion().empty()) {
+    IRMapping mapping;
+    firstprivRecipe.getDestroyRegion().cloneInto(&recipe.getDestroyRegion(),
+                                                 mapping);
+  }
+  return recipe;
+}
+
 //===----------------------------------------------------------------------===//
 // FirstprivateRecipeOp
 //===----------------------------------------------------------------------===//

diff  --git a/mlir/test/Dialect/OpenACC/recipe-populate-private-from-firstprivate.mlir b/mlir/test/Dialect/OpenACC/recipe-populate-private-from-firstprivate.mlir
new file mode 100644
index 0000000000000..154d44e31d027
--- /dev/null
+++ b/mlir/test/Dialect/OpenACC/recipe-populate-private-from-firstprivate.mlir
@@ -0,0 +1,38 @@
+// RUN: mlir-opt %s --split-input-file --pass-pipeline="builtin.module(test-acc-recipe-populate{recipe-type=private_from_firstprivate})" | FileCheck %s
+
+// Verify that we can create a private recipe using the convenience overload
+// that takes an existing firstprivate recipe as input. For a simple scalar
+// alloca-backed memref, only an init region is expected (no destroy).
+// CHECK: acc.private.recipe @private_from_firstprivate_scalar : memref<f32> init {
+// CHECK: ^bb0(%{{.*}}: memref<f32>):
+// CHECK:   %[[ALLOC:.*]] = memref.alloca() {acc.var_name = #acc.var_name<"scalar">} : memref<f32>
+// CHECK:   acc.yield %[[ALLOC]] : memref<f32>
+// CHECK: }
+
+func.func @test_scalar_from_firstprivate() {
+  %0 = memref.alloca() {test.var = "scalar"} : memref<f32>
+  return
+}
+
+// -----
+
+// Verify that destroy regions are also present when creating a private recipe
+// from a firstprivate recipe that requires dynamic deallocation.
+// CHECK: acc.private.recipe @private_from_firstprivate_dynamic_d2 : memref<?x?xf32> init {
+// CHECK: ^bb0(%[[ARG:.*]]: memref<?x?xf32>):
+// CHECK:   %[[C0:.*]] = arith.constant 0 : index
+// CHECK:   %[[DIM0:.*]] = memref.dim %[[ARG]], %[[C0]] : memref<?x?xf32>
+// CHECK:   %[[C1:.*]] = arith.constant 1 : index
+// CHECK:   %[[DIM1:.*]] = memref.dim %[[ARG]], %[[C1]] : memref<?x?xf32>
+// CHECK:   %[[ALLOC:.*]] = memref.alloc(%[[DIM0]], %[[DIM1]]) {acc.var_name = #acc.var_name<"dynamic_d2">} : memref<?x?xf32>
+// CHECK:   acc.yield %[[ALLOC]] : memref<?x?xf32>
+// CHECK: } destroy {
+// CHECK: ^bb0(%{{.*}}: memref<?x?xf32>, %[[VAL:.*]]: memref<?x?xf32>):
+// CHECK:   memref.dealloc %[[VAL]] : memref<?x?xf32>
+// CHECK:   acc.terminator
+// CHECK: }
+
+func.func @test_dynamic_from_firstprivate(%arg0: index, %arg1: index) {
+  %0 = memref.alloc(%arg0, %arg1) {test.var = "dynamic_d2"} : memref<?x?xf32>
+  return
+}

diff  --git a/mlir/test/lib/Dialect/OpenACC/TestRecipePopulate.cpp b/mlir/test/lib/Dialect/OpenACC/TestRecipePopulate.cpp
index 35f092c2188a6..2506ca4fc39ce 100644
--- a/mlir/test/lib/Dialect/OpenACC/TestRecipePopulate.cpp
+++ b/mlir/test/lib/Dialect/OpenACC/TestRecipePopulate.cpp
@@ -93,6 +93,29 @@ void TestRecipePopulatePass::runOnOperation() {
       if (!recipe) {
         op->emitError("Failed to create firstprivate recipe for ") << varName;
       }
+    } else if (recipeType == "private_from_firstprivate") {
+      // First create a firstprivate recipe, then use it to drive creation of a
+      // matching private recipe via the convenience overload. Give each recipe
+      // a stable, predictable name so tests can check both.
+      std::string firstprivName = "first_firstprivate_" + varName;
+      std::string privName = "private_from_firstprivate_" + varName;
+
+      auto firstpriv = FirstprivateRecipeOp::createAndPopulate(
+          builder, loc, firstprivName, var.getType(), varName, bounds);
+
+      if (!firstpriv) {
+        op->emitError("Failed to create firstprivate recipe for ") << varName;
+        return;
+      }
+
+      auto priv = PrivateRecipeOp::createAndPopulate(builder, loc, privName,
+                                                     *firstpriv);
+
+      if (!priv) {
+        op->emitError(
+            "Failed to create private recipe (from firstprivate) for ")
+            << varName;
+      }
     }
   }
 }


        


More information about the Mlir-commits mailing list