-
Notifications
You must be signed in to change notification settings - Fork 14.9k
[mlir][acc] Add destroy region to reduction recipes #155480
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Reduction recipes capture how a private copy is created. In some languages, like C++ class variables with destructors - that private copy also must be properly destroyed. Thus update the reduction recipe to contain a `destroy` region similarly to the private recipes.
@llvm/pr-subscribers-mlir-openacc Author: Razvan Lupusoru (razvanlupusoru) ChangesReduction recipes capture how a private copy is created. In some languages, like C++ class variables with destructors - that private copy also must be properly destroyed. Thus update the reduction recipe to contain a Full diff: https://github.com/llvm/llvm-project/pull/155480.diff 2 Files Affected:
diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td b/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td
index 0db11aa9af683..173429b1a0329 100644
--- a/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td
+++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td
@@ -1302,7 +1302,7 @@ def OpenACC_ReductionRecipeOp
let description = [{
Declares an OpenACC reduction recipe. The operation requires two
- mandatory regions.
+ mandatory regions and one optional region.
1. The initializer region specifies how to initialize the local reduction
value. The region has a first argument that contains the value of the
@@ -1313,6 +1313,9 @@ def OpenACC_ReductionRecipeOp
values of the reduction type into one. It has at least two arguments
and it is expected to `acc.yield` the combined value. Extra arguments
can be added to deal with dynamic arrays.
+ 3. The destroy region specifies how to destruct the value when it reaches
+ its end of life. It takes the reduction value as argument. It is
+ optional.
Example:
@@ -1329,6 +1332,10 @@ def OpenACC_ReductionRecipeOp
// two values into one.
%2 = arith.addi %0, %1 : i64
acc.yield %2 : i64
+ } destroy {
+ ^bb0(%0: i64)
+ // destroy region contains a sequence of operations to destruct the
+ // created copy.
}
// The reduction symbol is then used in the corresponding operation.
@@ -1362,12 +1369,14 @@ def OpenACC_ReductionRecipeOp
OpenACC_ReductionOperatorAttr:$reductionOperator);
let regions = (region AnyRegion:$initRegion,
- AnyRegion:$combinerRegion);
+ AnyRegion:$combinerRegion,
+ AnyRegion:$destroyRegion);
let assemblyFormat = [{
$sym_name `:` $type attr-dict-with-keyword
`reduction_operator` $reductionOperator
`init` $initRegion `combiner` $combinerRegion
+ (`destroy` $destroyRegion^)?
}];
let hasRegionVerifier = 1;
diff --git a/mlir/test/Dialect/OpenACC/ops.mlir b/mlir/test/Dialect/OpenACC/ops.mlir
index 7bb6cf43e49a7..5a3bbaf4252db 100644
--- a/mlir/test/Dialect/OpenACC/ops.mlir
+++ b/mlir/test/Dialect/OpenACC/ops.mlir
@@ -1954,6 +1954,70 @@ acc.reduction.recipe @reduction_add_memref_i32 : memref<i32> reduction_operator
// CHECK-LABEL: acc.reduction.recipe @reduction_add_memref_i32
// CHECK: memref.alloca
+// -----
+
+// Test reduction recipe with destroy region using dynamic memory allocation
+acc.reduction.recipe @reduction_add_with_destroy : memref<?xf32> reduction_operator<add> init {
+^bb0(%arg0: memref<?xf32>):
+ %cst = arith.constant 0.000000e+00 : f32
+ %c0 = arith.constant 0 : index
+ %size = memref.dim %arg0, %c0 : memref<?xf32>
+ %alloc = memref.alloc(%size) : memref<?xf32>
+ %c1 = arith.constant 1 : index
+ scf.for %i = %c0 to %size step %c1 {
+ memref.store %cst, %alloc[%i] : memref<?xf32>
+ }
+ acc.yield %alloc : memref<?xf32>
+} combiner {
+^bb0(%arg0: memref<?xf32>, %arg1: memref<?xf32>):
+ %c0 = arith.constant 0 : index
+ %c1 = arith.constant 1 : index
+ %size = memref.dim %arg0, %c0 : memref<?xf32>
+ scf.for %i = %c0 to %size step %c1 {
+ %val0 = memref.load %arg0[%i] : memref<?xf32>
+ %val1 = memref.load %arg1[%i] : memref<?xf32>
+ %sum = arith.addf %val0, %val1 : f32
+ memref.store %sum, %arg0[%i] : memref<?xf32>
+ }
+ acc.yield %arg0 : memref<?xf32>
+} destroy {
+^bb0(%arg0: memref<?xf32>):
+ // destroy region to deallocate dynamically allocated memory
+ memref.dealloc %arg0 : memref<?xf32>
+ acc.yield
+}
+
+// CHECK-LABEL: acc.reduction.recipe @reduction_add_with_destroy : memref<?xf32> reduction_operator <add> init {
+// CHECK: ^bb0(%[[ARG:.*]]: memref<?xf32>):
+// CHECK: %[[CST:.*]] = arith.constant 0.000000e+00 : f32
+// CHECK: %[[C0:.*]] = arith.constant 0 : index
+// CHECK: %[[SIZE:.*]] = memref.dim %[[ARG]], %[[C0]] : memref<?xf32>
+// CHECK: %[[ALLOC:.*]] = memref.alloc(%[[SIZE]]) : memref<?xf32>
+// CHECK: %[[C1:.*]] = arith.constant 1 : index
+// CHECK: scf.for %[[I:.*]] = %[[C0]] to %[[SIZE]] step %[[C1]] {
+// CHECK: memref.store %[[CST]], %[[ALLOC]][%[[I]]] : memref<?xf32>
+// CHECK: }
+// CHECK: acc.yield %[[ALLOC]] : memref<?xf32>
+// CHECK: } combiner {
+// CHECK: ^bb0(%[[ARG0:.*]]: memref<?xf32>, %[[ARG1:.*]]: memref<?xf32>):
+// CHECK: %[[C0_1:.*]] = arith.constant 0 : index
+// CHECK: %[[C1_1:.*]] = arith.constant 1 : index
+// CHECK: %[[SIZE_1:.*]] = memref.dim %[[ARG0]], %[[C0_1]] : memref<?xf32>
+// CHECK: scf.for %[[I_1:.*]] = %[[C0_1]] to %[[SIZE_1]] step %[[C1_1]] {
+// CHECK: %{{.*}} = memref.load %[[ARG0]][%[[I_1]]] : memref<?xf32>
+// CHECK: %{{.*}} = memref.load %[[ARG1]][%[[I_1]]] : memref<?xf32>
+// CHECK: %[[SUM:.*]] = arith.addf %{{.*}}, %{{.*}} : f32
+// CHECK: memref.store %[[SUM]], %[[ARG0]][%[[I_1]]] : memref<?xf32>
+// CHECK: }
+// CHECK: acc.yield %[[ARG0]] : memref<?xf32>
+// CHECK: } destroy {
+// CHECK: ^bb0(%[[ARG_DESTROY:.*]]: memref<?xf32>):
+// CHECK: memref.dealloc %[[ARG_DESTROY]] : memref<?xf32>
+// CHECK: acc.yield
+// CHECK: }
+
+// -----
+
acc.private.recipe @privatization_memref_i32 : memref<i32> init {
^bb0(%arg0: memref<i32>):
%alloca = memref.alloca() : memref<i32>
|
@llvm/pr-subscribers-openacc Author: Razvan Lupusoru (razvanlupusoru) ChangesReduction recipes capture how a private copy is created. In some languages, like C++ class variables with destructors - that private copy also must be properly destroyed. Thus update the reduction recipe to contain a Full diff: https://github.com/llvm/llvm-project/pull/155480.diff 2 Files Affected:
diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td b/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td
index 0db11aa9af683..173429b1a0329 100644
--- a/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td
+++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td
@@ -1302,7 +1302,7 @@ def OpenACC_ReductionRecipeOp
let description = [{
Declares an OpenACC reduction recipe. The operation requires two
- mandatory regions.
+ mandatory regions and one optional region.
1. The initializer region specifies how to initialize the local reduction
value. The region has a first argument that contains the value of the
@@ -1313,6 +1313,9 @@ def OpenACC_ReductionRecipeOp
values of the reduction type into one. It has at least two arguments
and it is expected to `acc.yield` the combined value. Extra arguments
can be added to deal with dynamic arrays.
+ 3. The destroy region specifies how to destruct the value when it reaches
+ its end of life. It takes the reduction value as argument. It is
+ optional.
Example:
@@ -1329,6 +1332,10 @@ def OpenACC_ReductionRecipeOp
// two values into one.
%2 = arith.addi %0, %1 : i64
acc.yield %2 : i64
+ } destroy {
+ ^bb0(%0: i64)
+ // destroy region contains a sequence of operations to destruct the
+ // created copy.
}
// The reduction symbol is then used in the corresponding operation.
@@ -1362,12 +1369,14 @@ def OpenACC_ReductionRecipeOp
OpenACC_ReductionOperatorAttr:$reductionOperator);
let regions = (region AnyRegion:$initRegion,
- AnyRegion:$combinerRegion);
+ AnyRegion:$combinerRegion,
+ AnyRegion:$destroyRegion);
let assemblyFormat = [{
$sym_name `:` $type attr-dict-with-keyword
`reduction_operator` $reductionOperator
`init` $initRegion `combiner` $combinerRegion
+ (`destroy` $destroyRegion^)?
}];
let hasRegionVerifier = 1;
diff --git a/mlir/test/Dialect/OpenACC/ops.mlir b/mlir/test/Dialect/OpenACC/ops.mlir
index 7bb6cf43e49a7..5a3bbaf4252db 100644
--- a/mlir/test/Dialect/OpenACC/ops.mlir
+++ b/mlir/test/Dialect/OpenACC/ops.mlir
@@ -1954,6 +1954,70 @@ acc.reduction.recipe @reduction_add_memref_i32 : memref<i32> reduction_operator
// CHECK-LABEL: acc.reduction.recipe @reduction_add_memref_i32
// CHECK: memref.alloca
+// -----
+
+// Test reduction recipe with destroy region using dynamic memory allocation
+acc.reduction.recipe @reduction_add_with_destroy : memref<?xf32> reduction_operator<add> init {
+^bb0(%arg0: memref<?xf32>):
+ %cst = arith.constant 0.000000e+00 : f32
+ %c0 = arith.constant 0 : index
+ %size = memref.dim %arg0, %c0 : memref<?xf32>
+ %alloc = memref.alloc(%size) : memref<?xf32>
+ %c1 = arith.constant 1 : index
+ scf.for %i = %c0 to %size step %c1 {
+ memref.store %cst, %alloc[%i] : memref<?xf32>
+ }
+ acc.yield %alloc : memref<?xf32>
+} combiner {
+^bb0(%arg0: memref<?xf32>, %arg1: memref<?xf32>):
+ %c0 = arith.constant 0 : index
+ %c1 = arith.constant 1 : index
+ %size = memref.dim %arg0, %c0 : memref<?xf32>
+ scf.for %i = %c0 to %size step %c1 {
+ %val0 = memref.load %arg0[%i] : memref<?xf32>
+ %val1 = memref.load %arg1[%i] : memref<?xf32>
+ %sum = arith.addf %val0, %val1 : f32
+ memref.store %sum, %arg0[%i] : memref<?xf32>
+ }
+ acc.yield %arg0 : memref<?xf32>
+} destroy {
+^bb0(%arg0: memref<?xf32>):
+ // destroy region to deallocate dynamically allocated memory
+ memref.dealloc %arg0 : memref<?xf32>
+ acc.yield
+}
+
+// CHECK-LABEL: acc.reduction.recipe @reduction_add_with_destroy : memref<?xf32> reduction_operator <add> init {
+// CHECK: ^bb0(%[[ARG:.*]]: memref<?xf32>):
+// CHECK: %[[CST:.*]] = arith.constant 0.000000e+00 : f32
+// CHECK: %[[C0:.*]] = arith.constant 0 : index
+// CHECK: %[[SIZE:.*]] = memref.dim %[[ARG]], %[[C0]] : memref<?xf32>
+// CHECK: %[[ALLOC:.*]] = memref.alloc(%[[SIZE]]) : memref<?xf32>
+// CHECK: %[[C1:.*]] = arith.constant 1 : index
+// CHECK: scf.for %[[I:.*]] = %[[C0]] to %[[SIZE]] step %[[C1]] {
+// CHECK: memref.store %[[CST]], %[[ALLOC]][%[[I]]] : memref<?xf32>
+// CHECK: }
+// CHECK: acc.yield %[[ALLOC]] : memref<?xf32>
+// CHECK: } combiner {
+// CHECK: ^bb0(%[[ARG0:.*]]: memref<?xf32>, %[[ARG1:.*]]: memref<?xf32>):
+// CHECK: %[[C0_1:.*]] = arith.constant 0 : index
+// CHECK: %[[C1_1:.*]] = arith.constant 1 : index
+// CHECK: %[[SIZE_1:.*]] = memref.dim %[[ARG0]], %[[C0_1]] : memref<?xf32>
+// CHECK: scf.for %[[I_1:.*]] = %[[C0_1]] to %[[SIZE_1]] step %[[C1_1]] {
+// CHECK: %{{.*}} = memref.load %[[ARG0]][%[[I_1]]] : memref<?xf32>
+// CHECK: %{{.*}} = memref.load %[[ARG1]][%[[I_1]]] : memref<?xf32>
+// CHECK: %[[SUM:.*]] = arith.addf %{{.*}}, %{{.*}} : f32
+// CHECK: memref.store %[[SUM]], %[[ARG0]][%[[I_1]]] : memref<?xf32>
+// CHECK: }
+// CHECK: acc.yield %[[ARG0]] : memref<?xf32>
+// CHECK: } destroy {
+// CHECK: ^bb0(%[[ARG_DESTROY:.*]]: memref<?xf32>):
+// CHECK: memref.dealloc %[[ARG_DESTROY]] : memref<?xf32>
+// CHECK: acc.yield
+// CHECK: }
+
+// -----
+
acc.private.recipe @privatization_memref_i32 : memref<i32> init {
^bb0(%arg0: memref<i32>):
%alloca = memref.alloca() : memref<i32>
|
@llvm/pr-subscribers-mlir Author: Razvan Lupusoru (razvanlupusoru) ChangesReduction recipes capture how a private copy is created. In some languages, like C++ class variables with destructors - that private copy also must be properly destroyed. Thus update the reduction recipe to contain a Full diff: https://github.com/llvm/llvm-project/pull/155480.diff 2 Files Affected:
diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td b/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td
index 0db11aa9af683..173429b1a0329 100644
--- a/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td
+++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCOps.td
@@ -1302,7 +1302,7 @@ def OpenACC_ReductionRecipeOp
let description = [{
Declares an OpenACC reduction recipe. The operation requires two
- mandatory regions.
+ mandatory regions and one optional region.
1. The initializer region specifies how to initialize the local reduction
value. The region has a first argument that contains the value of the
@@ -1313,6 +1313,9 @@ def OpenACC_ReductionRecipeOp
values of the reduction type into one. It has at least two arguments
and it is expected to `acc.yield` the combined value. Extra arguments
can be added to deal with dynamic arrays.
+ 3. The destroy region specifies how to destruct the value when it reaches
+ its end of life. It takes the reduction value as argument. It is
+ optional.
Example:
@@ -1329,6 +1332,10 @@ def OpenACC_ReductionRecipeOp
// two values into one.
%2 = arith.addi %0, %1 : i64
acc.yield %2 : i64
+ } destroy {
+ ^bb0(%0: i64)
+ // destroy region contains a sequence of operations to destruct the
+ // created copy.
}
// The reduction symbol is then used in the corresponding operation.
@@ -1362,12 +1369,14 @@ def OpenACC_ReductionRecipeOp
OpenACC_ReductionOperatorAttr:$reductionOperator);
let regions = (region AnyRegion:$initRegion,
- AnyRegion:$combinerRegion);
+ AnyRegion:$combinerRegion,
+ AnyRegion:$destroyRegion);
let assemblyFormat = [{
$sym_name `:` $type attr-dict-with-keyword
`reduction_operator` $reductionOperator
`init` $initRegion `combiner` $combinerRegion
+ (`destroy` $destroyRegion^)?
}];
let hasRegionVerifier = 1;
diff --git a/mlir/test/Dialect/OpenACC/ops.mlir b/mlir/test/Dialect/OpenACC/ops.mlir
index 7bb6cf43e49a7..5a3bbaf4252db 100644
--- a/mlir/test/Dialect/OpenACC/ops.mlir
+++ b/mlir/test/Dialect/OpenACC/ops.mlir
@@ -1954,6 +1954,70 @@ acc.reduction.recipe @reduction_add_memref_i32 : memref<i32> reduction_operator
// CHECK-LABEL: acc.reduction.recipe @reduction_add_memref_i32
// CHECK: memref.alloca
+// -----
+
+// Test reduction recipe with destroy region using dynamic memory allocation
+acc.reduction.recipe @reduction_add_with_destroy : memref<?xf32> reduction_operator<add> init {
+^bb0(%arg0: memref<?xf32>):
+ %cst = arith.constant 0.000000e+00 : f32
+ %c0 = arith.constant 0 : index
+ %size = memref.dim %arg0, %c0 : memref<?xf32>
+ %alloc = memref.alloc(%size) : memref<?xf32>
+ %c1 = arith.constant 1 : index
+ scf.for %i = %c0 to %size step %c1 {
+ memref.store %cst, %alloc[%i] : memref<?xf32>
+ }
+ acc.yield %alloc : memref<?xf32>
+} combiner {
+^bb0(%arg0: memref<?xf32>, %arg1: memref<?xf32>):
+ %c0 = arith.constant 0 : index
+ %c1 = arith.constant 1 : index
+ %size = memref.dim %arg0, %c0 : memref<?xf32>
+ scf.for %i = %c0 to %size step %c1 {
+ %val0 = memref.load %arg0[%i] : memref<?xf32>
+ %val1 = memref.load %arg1[%i] : memref<?xf32>
+ %sum = arith.addf %val0, %val1 : f32
+ memref.store %sum, %arg0[%i] : memref<?xf32>
+ }
+ acc.yield %arg0 : memref<?xf32>
+} destroy {
+^bb0(%arg0: memref<?xf32>):
+ // destroy region to deallocate dynamically allocated memory
+ memref.dealloc %arg0 : memref<?xf32>
+ acc.yield
+}
+
+// CHECK-LABEL: acc.reduction.recipe @reduction_add_with_destroy : memref<?xf32> reduction_operator <add> init {
+// CHECK: ^bb0(%[[ARG:.*]]: memref<?xf32>):
+// CHECK: %[[CST:.*]] = arith.constant 0.000000e+00 : f32
+// CHECK: %[[C0:.*]] = arith.constant 0 : index
+// CHECK: %[[SIZE:.*]] = memref.dim %[[ARG]], %[[C0]] : memref<?xf32>
+// CHECK: %[[ALLOC:.*]] = memref.alloc(%[[SIZE]]) : memref<?xf32>
+// CHECK: %[[C1:.*]] = arith.constant 1 : index
+// CHECK: scf.for %[[I:.*]] = %[[C0]] to %[[SIZE]] step %[[C1]] {
+// CHECK: memref.store %[[CST]], %[[ALLOC]][%[[I]]] : memref<?xf32>
+// CHECK: }
+// CHECK: acc.yield %[[ALLOC]] : memref<?xf32>
+// CHECK: } combiner {
+// CHECK: ^bb0(%[[ARG0:.*]]: memref<?xf32>, %[[ARG1:.*]]: memref<?xf32>):
+// CHECK: %[[C0_1:.*]] = arith.constant 0 : index
+// CHECK: %[[C1_1:.*]] = arith.constant 1 : index
+// CHECK: %[[SIZE_1:.*]] = memref.dim %[[ARG0]], %[[C0_1]] : memref<?xf32>
+// CHECK: scf.for %[[I_1:.*]] = %[[C0_1]] to %[[SIZE_1]] step %[[C1_1]] {
+// CHECK: %{{.*}} = memref.load %[[ARG0]][%[[I_1]]] : memref<?xf32>
+// CHECK: %{{.*}} = memref.load %[[ARG1]][%[[I_1]]] : memref<?xf32>
+// CHECK: %[[SUM:.*]] = arith.addf %{{.*}}, %{{.*}} : f32
+// CHECK: memref.store %[[SUM]], %[[ARG0]][%[[I_1]]] : memref<?xf32>
+// CHECK: }
+// CHECK: acc.yield %[[ARG0]] : memref<?xf32>
+// CHECK: } destroy {
+// CHECK: ^bb0(%[[ARG_DESTROY:.*]]: memref<?xf32>):
+// CHECK: memref.dealloc %[[ARG_DESTROY]] : memref<?xf32>
+// CHECK: acc.yield
+// CHECK: }
+
+// -----
+
acc.private.recipe @privatization_memref_i32 : memref<i32> init {
^bb0(%arg0: memref<i32>):
%alloca = memref.alloca() : memref<i32>
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1 suggestion, else LGTM.
@@ -1313,6 +1313,9 @@ def OpenACC_ReductionRecipeOp | |||
values of the reduction type into one. It has at least two arguments | |||
and it is expected to `acc.yield` the combined value. Extra arguments | |||
can be added to deal with dynamic arrays. | |||
3. The destroy region specifies how to destruct the value when it reaches |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps leave off the 'it is optional' sentence, and say "The optional destroy region..."?
WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like it! Thanks! :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Thanks Razvan!
Reduction recipes capture how a private copy is created. In some languages, like C++ class variables with destructors - that private copy also must be properly destroyed. Thus update the reduction recipe to contain a
destroy
region similarly to the private recipes.