[Mlir-commits] [mlir] [mlir][acc] Add acc.predicate_region for redundant/single semantics (PR #203011)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Wed Jun 10 08:12:23 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-mlir
Author: Razvan Lupusoru (razvanlupusoru)
<details>
<summary>Changes</summary>
Add acc.predicate_region, an intermediate codegen operation that groups statements at intermediate points in a loop nest within acc.compute_region. OpenACC distinguishes partitioned loop execution from single and redundant execution at nest transitions: for example, gang-redundant code runs on all gangs but not as partitioned gang-loop iterations, and worker-single or vector-single code runs on one worker or vector lane rather than across the full worker or vector partition. This grouping marks code whose execution scope differs from surrounding partitioned loops, so predication and synchronization can be applied correctly during lowering.
---
Full diff: https://github.com/llvm/llvm-project/pull/203011.diff
4 Files Affected:
- (modified) mlir/include/mlir/Dialect/OpenACC/OpenACCCGOps.td (+55)
- (modified) mlir/lib/Dialect/OpenACC/IR/OpenACCCG.cpp (+14)
- (modified) mlir/test/Dialect/OpenACC/invalid-cg.mlir (+36)
- (modified) mlir/test/Dialect/OpenACC/ops-cg.mlir (+86)
``````````diff
diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACCCGOps.td b/mlir/include/mlir/Dialect/OpenACC/OpenACCCGOps.td
index 71c764aa5675b..26fb6c10e51e6 100644
--- a/mlir/include/mlir/Dialect/OpenACC/OpenACCCGOps.td
+++ b/mlir/include/mlir/Dialect/OpenACC/OpenACCCGOps.td
@@ -527,4 +527,59 @@ def OpenACC_ComputeRegionOp
let hasCustomAssemblyFormat = 1;
}
+//===----------------------------------------------------------------------===//
+// acc.predicate_region
+//===----------------------------------------------------------------------===//
+
+def OpenACC_PredicateRegionOp
+ : OpenACC_Op<"predicate_region", [RecursiveMemoryEffects, NoTerminator]> {
+ let summary = "Groups operations at intermediate loop-nest points";
+ let description = [{
+ Groups statements within an `acc.compute_region` that sit at an
+ intermediate point in a loop nest (outside a partitioned loop body,
+ between or around nested loops). This grouping marks code whose execution
+ scope differs from that of surrounding partitioned loops, so predication
+ and synchronization can be applied correctly during lowering. Corresponds
+ to OpenACC single or redundant execution at nest transitions.
+
+ Example:
+ ```mlir
+ // !$acc parallel num_gangs(NG) vector_length(VL)
+ // !$acc loop gang
+ // !$acc atomic update
+ // !$acc loop vector
+ // !$acc atomic update
+ %w_gang = acc.par_width %cNG {par_dim = #acc.par_dim<block_x>}
+ %w_vector = acc.par_width %cVL {par_dim = #acc.par_dim<thread_x>}
+ acc.compute_region launch(%ng = %w_gang, %vl = %w_vector)
+ ins(%arg_c1 = %c1, %arg_c2 = %c2) : (memref<i32>, memref<i32>) {
+ scf.parallel (%i) = (%c1) to (%cN) step (%c1) {
+ acc.predicate_region {
+ acc.atomic.update %arg_c1 : memref<i32> {
+ ^bb0(%old: i32):
+ %one = arith.constant 1 : i32
+ %sum = arith.addi %old, %one : i32
+ acc.yield %sum : i32
+ }
+ }
+ scf.parallel (%j) = (%c1) to (%cN) step (%c1) {
+ acc.atomic.update %arg_c2 : memref<i32> {
+ ^bb0(%old: i32):
+ %one = arith.constant 1 : i32
+ %sum = arith.addi %old, %one : i32
+ acc.yield %sum : i32
+ }
+ scf.reduce
+ } {acc.par_dims = #acc<par_dims[thread_x]>}
+ scf.reduce
+ } {acc.par_dims = #acc<par_dims[block_x]>}
+ acc.yield
+ } {origin = "acc.parallel"}
+ ```
+ }];
+ let regions = (region AnyRegion:$region);
+ let assemblyFormat = "$region attr-dict";
+ let hasVerifier = 1;
+}
+
#endif // OPENACC_CG_OPS
diff --git a/mlir/lib/Dialect/OpenACC/IR/OpenACCCG.cpp b/mlir/lib/Dialect/OpenACC/IR/OpenACCCG.cpp
index c0c2bb7654830..060680681299b 100644
--- a/mlir/lib/Dialect/OpenACC/IR/OpenACCCG.cpp
+++ b/mlir/lib/Dialect/OpenACC/IR/OpenACCCG.cpp
@@ -782,6 +782,20 @@ ParseResult ComputeRegionOp::parse(OpAsmParser &parser,
return success();
}
+//===----------------------------------------------------------------------===//
+// PredicateRegionOp
+//===----------------------------------------------------------------------===//
+
+LogicalResult PredicateRegionOp::verify() {
+ if (getRegion().empty())
+ return emitOpError("region needs to have at least one block");
+ if (getRegion().front().getNumArguments() > 0)
+ return emitOpError("region cannot have any arguments");
+ if (!getOperation()->getParentOfType<ComputeRegionOp>())
+ return emitOpError("must be nested within an acc.compute_region operation");
+ return success();
+}
+
//===----------------------------------------------------------------------===//
// GPUParallelDimAttr
//===----------------------------------------------------------------------===//
diff --git a/mlir/test/Dialect/OpenACC/invalid-cg.mlir b/mlir/test/Dialect/OpenACC/invalid-cg.mlir
index 16393fd77bfc9..da280bda8ea91 100644
--- a/mlir/test/Dialect/OpenACC/invalid-cg.mlir
+++ b/mlir/test/Dialect/OpenACC/invalid-cg.mlir
@@ -72,3 +72,39 @@ func.func @reduction_accumulate_empty_par_dims() {
: i32 -> memref<i32> {par_dims = #acc<par_dims[]>}
return
}
+
+// -----
+
+func.func @predicate_region_empty() {
+ acc.compute_region {
+ // expected-error at +1 {{region needs to have at least one block}}
+ acc.predicate_region {
+ }
+ acc.yield
+ } {origin = "acc.parallel"}
+ return
+}
+
+// -----
+
+func.func @predicate_region_with_args() {
+ acc.compute_region {
+ // expected-error at +1 {{region cannot have any arguments}}
+ acc.predicate_region {
+ ^bb0(%arg0: index):
+ %c0 = arith.constant 0 : index
+ }
+ acc.yield
+ } {origin = "acc.parallel"}
+ return
+}
+
+// -----
+
+func.func @predicate_region_outside_compute_region() {
+ // expected-error at +1 {{must be nested within an acc.compute_region operation}}
+ acc.predicate_region {
+ %c0 = arith.constant 0 : i32
+ }
+ return
+}
diff --git a/mlir/test/Dialect/OpenACC/ops-cg.mlir b/mlir/test/Dialect/OpenACC/ops-cg.mlir
index e424bf7bd5eab..52ca1a38ebb73 100644
--- a/mlir/test/Dialect/OpenACC/ops-cg.mlir
+++ b/mlir/test/Dialect/OpenACC/ops-cg.mlir
@@ -333,3 +333,89 @@ func.func @compute_region_with_results() -> i32 {
// CHECK: {{.*}} = acc.compute_region launch(%{{.*}} = %[[W]]) -> i32 {
// CHECK: acc.yield
// CHECK: } {origin = "acc.parallel"}
+
+// -----
+
+// CHECK-LABEL: func @predicate_region_gang_vector_atomics
+func.func @predicate_region_gang_vector_atomics(%c1: memref<i32>, %c2: memref<i32>) {
+ %c3 = arith.constant 3 : index
+ %c16 = arith.constant 16 : index
+ %copy_c1 = acc.copyin varPtr(%c1 : memref<i32>) -> memref<i32> {dataClause = #acc<data_clause acc_copy>}
+ %copy_c2 = acc.copyin varPtr(%c2 : memref<i32>) -> memref<i32> {dataClause = #acc<data_clause acc_copy>}
+ acc.kernel_environment dataOperands(%copy_c1, %copy_c2 : memref<i32>, memref<i32>) {
+ %w_gang = acc.par_width %c3 {par_dim = #acc.par_dim<block_x>}
+ %w_vector = acc.par_width %c16 {par_dim = #acc.par_dim<thread_x>}
+ acc.compute_region launch(%arg0 = %w_gang, %arg1 = %w_vector)
+ ins(%arg2 = %copy_c1, %arg3 = %copy_c2) : (memref<i32>, memref<i32>) {
+ %c1_idx = arith.constant 1 : index
+ %c10 = arith.constant 10 : index
+ scf.parallel (%i) = (%c1_idx) to (%c10) step (%c1_idx) {
+ acc.predicate_region {
+ acc.atomic.update %arg2 : memref<i32> {
+ ^bb0(%old: i32):
+ %one = arith.constant 1 : i32
+ %sum = arith.addi %old, %one : i32
+ acc.yield %sum : i32
+ }
+ }
+ scf.parallel (%j) = (%c1_idx) to (%c10) step (%c1_idx) {
+ acc.atomic.update %arg3 : memref<i32> {
+ ^bb0(%old: i32):
+ %one = arith.constant 1 : i32
+ %sum = arith.addi %old, %one : i32
+ acc.yield %sum : i32
+ }
+ scf.reduce
+ } {acc.par_dims = #acc<par_dims[thread_x]>}
+ scf.reduce
+ } {acc.par_dims = #acc<par_dims[block_x]>}
+ acc.yield
+ } {origin = "acc.parallel"}
+ }
+ acc.copyout accPtr(%copy_c1 : memref<i32>) to varPtr(%c1 : memref<i32>) {dataClause = #acc<data_clause acc_copy>}
+ acc.copyout accPtr(%copy_c2 : memref<i32>) to varPtr(%c2 : memref<i32>) {dataClause = #acc<data_clause acc_copy>}
+ return
+}
+// CHECK: acc.predicate_region {
+// CHECK: acc.atomic.update
+// CHECK: }
+
+// -----
+
+// CHECK-LABEL: func @predicate_region_gang_redundant_setup
+func.func @predicate_region_gang_redundant_setup(%idx: memref<i32>, %table: memref<10xi32>) {
+ %c3 = arith.constant 3 : index
+ %c16 = arith.constant 16 : index
+ %copy_idx = acc.copyin varPtr(%idx : memref<i32>) -> memref<i32> {dataClause = #acc<data_clause acc_copy>}
+ %copy_table = acc.copyin varPtr(%table : memref<10xi32>) -> memref<10xi32>
+ acc.kernel_environment dataOperands(%copy_idx, %copy_table : memref<i32>, memref<10xi32>) {
+ %w_gang = acc.par_width %c3 {par_dim = #acc.par_dim<block_x>}
+ %w_vector = acc.par_width %c16 {par_dim = #acc.par_dim<thread_x>}
+ acc.compute_region launch(%arg0 = %w_gang, %arg1 = %w_vector)
+ ins(%arg2 = %copy_idx, %arg3 = %copy_table) : (memref<i32>, memref<10xi32>) {
+ %c0 = arith.constant 0 : index
+ %c1 = arith.constant 1 : index
+ %c10 = arith.constant 10 : index
+ %v = memref.load %arg3[%c0] : memref<10xi32>
+ acc.predicate_region {
+ memref.store %v, %arg2[] : memref<i32>
+ }
+ scf.parallel (%i) = (%c1) to (%c10) step (%c1) {
+ %i_val = memref.load %arg2[] : memref<i32>
+ %twice = arith.addi %i_val, %i_val : i32
+ memref.store %twice, %arg2[] : memref<i32>
+ scf.parallel (%j) = (%c1) to (%c10) step (%c1) {
+ scf.reduce
+ } {acc.par_dims = #acc<par_dims[thread_x]>}
+ scf.reduce
+ } {acc.par_dims = #acc<par_dims[block_x]>}
+ acc.yield
+ } {origin = "acc.kernels"}
+ }
+ acc.copyout accPtr(%copy_idx : memref<i32>) to varPtr(%idx : memref<i32>) {dataClause = #acc<data_clause acc_copy>}
+ acc.delete accPtr(%copy_table : memref<10xi32>)
+ return
+}
+// CHECK: acc.predicate_region {
+// CHECK: memref.store
+// CHECK: }
``````````
</details>
https://github.com/llvm/llvm-project/pull/203011
More information about the Mlir-commits
mailing list