Skip to content

Commit bb9731d

Browse files
SusanTanHoney Goyal
authored andcommitted
[acc] Add an API to make private recipes out of firstprivate recipes (llvm#170588)
In cases of optimizations promoting firstprivate to private, this API would be useful.
1 parent da3ae56 commit bb9731d

File tree

4 files changed

+97
-0
lines changed

4 files changed

+97
-0
lines changed

mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1394,6 +1394,19 @@ def OpenACC_PrivateRecipeOp
13941394
::mlir::Type varType,
13951395
::llvm::StringRef varName = "",
13961396
::mlir::ValueRange bounds = {});
1397+
1398+
/// Creates a PrivateRecipeOp using the same variable type as an existing
1399+
/// FirstprivateRecipeOp. This is a useful in cases where we promote private variables to firstprivate by analysis
1400+
/// This function reuses the init region from a firstprivate recipe when building a private
1401+
/// recipe. Callers thus must ensure that this is semantically valid for the language
1402+
/// lowering (e.g. that private does not perform extra default initialization
1403+
/// that firstprivate intentionally omits, such as for C++ classes or Fortran
1404+
/// derived types with default initialization).
1405+
static std::optional<PrivateRecipeOp> createAndPopulate(
1406+
::mlir::OpBuilder &builder,
1407+
::mlir::Location loc,
1408+
::llvm::StringRef recipeName,
1409+
::mlir::acc::FirstprivateRecipeOp firstprivRecipe);
13971410
}];
13981411
}
13991412

mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "mlir/IR/BuiltinAttributes.h"
1616
#include "mlir/IR/BuiltinTypes.h"
1717
#include "mlir/IR/DialectImplementation.h"
18+
#include "mlir/IR/IRMapping.h"
1819
#include "mlir/IR/Matchers.h"
1920
#include "mlir/IR/OpImplementation.h"
2021
#include "mlir/IR/SymbolTable.h"
@@ -1449,6 +1450,28 @@ PrivateRecipeOp::createAndPopulate(OpBuilder &builder, Location loc,
14491450
return recipe;
14501451
}
14511452

1453+
std::optional<PrivateRecipeOp>
1454+
PrivateRecipeOp::createAndPopulate(OpBuilder &builder, Location loc,
1455+
StringRef recipeName,
1456+
FirstprivateRecipeOp firstprivRecipe) {
1457+
// Create the private.recipe op with the same type as the firstprivate.recipe.
1458+
OpBuilder::InsertionGuard guard(builder);
1459+
auto varType = firstprivRecipe.getType();
1460+
auto recipe = PrivateRecipeOp::create(builder, loc, recipeName, varType);
1461+
1462+
// Clone the init region
1463+
IRMapping mapping;
1464+
firstprivRecipe.getInitRegion().cloneInto(&recipe.getInitRegion(), mapping);
1465+
1466+
// Clone destroy region if the firstprivate.recipe has one.
1467+
if (!firstprivRecipe.getDestroyRegion().empty()) {
1468+
IRMapping mapping;
1469+
firstprivRecipe.getDestroyRegion().cloneInto(&recipe.getDestroyRegion(),
1470+
mapping);
1471+
}
1472+
return recipe;
1473+
}
1474+
14521475
//===----------------------------------------------------------------------===//
14531476
// FirstprivateRecipeOp
14541477
//===----------------------------------------------------------------------===//
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// RUN: mlir-opt %s --split-input-file --pass-pipeline="builtin.module(test-acc-recipe-populate{recipe-type=private_from_firstprivate})" | FileCheck %s
2+
3+
// Verify that we can create a private recipe using the convenience overload
4+
// that takes an existing firstprivate recipe as input. For a simple scalar
5+
// alloca-backed memref, only an init region is expected (no destroy).
6+
// CHECK: acc.private.recipe @private_from_firstprivate_scalar : memref<f32> init {
7+
// CHECK: ^bb0(%{{.*}}: memref<f32>):
8+
// CHECK: %[[ALLOC:.*]] = memref.alloca() {acc.var_name = #acc.var_name<"scalar">} : memref<f32>
9+
// CHECK: acc.yield %[[ALLOC]] : memref<f32>
10+
// CHECK: }
11+
12+
func.func @test_scalar_from_firstprivate() {
13+
%0 = memref.alloca() {test.var = "scalar"} : memref<f32>
14+
return
15+
}
16+
17+
// -----
18+
19+
// Verify that destroy regions are also present when creating a private recipe
20+
// from a firstprivate recipe that requires dynamic deallocation.
21+
// CHECK: acc.private.recipe @private_from_firstprivate_dynamic_d2 : memref<?x?xf32> init {
22+
// CHECK: ^bb0(%[[ARG:.*]]: memref<?x?xf32>):
23+
// CHECK: %[[C0:.*]] = arith.constant 0 : index
24+
// CHECK: %[[DIM0:.*]] = memref.dim %[[ARG]], %[[C0]] : memref<?x?xf32>
25+
// CHECK: %[[C1:.*]] = arith.constant 1 : index
26+
// CHECK: %[[DIM1:.*]] = memref.dim %[[ARG]], %[[C1]] : memref<?x?xf32>
27+
// CHECK: %[[ALLOC:.*]] = memref.alloc(%[[DIM0]], %[[DIM1]]) {acc.var_name = #acc.var_name<"dynamic_d2">} : memref<?x?xf32>
28+
// CHECK: acc.yield %[[ALLOC]] : memref<?x?xf32>
29+
// CHECK: } destroy {
30+
// CHECK: ^bb0(%{{.*}}: memref<?x?xf32>, %[[VAL:.*]]: memref<?x?xf32>):
31+
// CHECK: memref.dealloc %[[VAL]] : memref<?x?xf32>
32+
// CHECK: acc.terminator
33+
// CHECK: }
34+
35+
func.func @test_dynamic_from_firstprivate(%arg0: index, %arg1: index) {
36+
%0 = memref.alloc(%arg0, %arg1) {test.var = "dynamic_d2"} : memref<?x?xf32>
37+
return
38+
}

mlir/test/lib/Dialect/OpenACC/TestRecipePopulate.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,29 @@ void TestRecipePopulatePass::runOnOperation() {
9393
if (!recipe) {
9494
op->emitError("Failed to create firstprivate recipe for ") << varName;
9595
}
96+
} else if (recipeType == "private_from_firstprivate") {
97+
// First create a firstprivate recipe, then use it to drive creation of a
98+
// matching private recipe via the convenience overload. Give each recipe
99+
// a stable, predictable name so tests can check both.
100+
std::string firstprivName = "first_firstprivate_" + varName;
101+
std::string privName = "private_from_firstprivate_" + varName;
102+
103+
auto firstpriv = FirstprivateRecipeOp::createAndPopulate(
104+
builder, loc, firstprivName, var.getType(), varName, bounds);
105+
106+
if (!firstpriv) {
107+
op->emitError("Failed to create firstprivate recipe for ") << varName;
108+
return;
109+
}
110+
111+
auto priv = PrivateRecipeOp::createAndPopulate(builder, loc, privName,
112+
*firstpriv);
113+
114+
if (!priv) {
115+
op->emitError(
116+
"Failed to create private recipe (from firstprivate) for ")
117+
<< varName;
118+
}
96119
}
97120
}
98121
}

0 commit comments

Comments
 (0)