[Mlir-commits] [mlir] [mlir][acc] Add acc.predicate_region for redundant/single semantics (PR #203011)
Razvan Lupusoru
llvmlistbot at llvm.org
Wed Jun 10 08:14:47 PDT 2026
https://github.com/razvanlupusoru updated https://github.com/llvm/llvm-project/pull/203011
>From d7315affb33b9ec1e14fb497ea60ca67d961b3db Mon Sep 17 00:00:00 2001
From: Razvan Lupusoru <rlupusoru at nvidia.com>
Date: Wed, 10 Jun 2026 08:09:41 -0700
Subject: [PATCH] [mlir][acc] Add acc.predicate_region for redundant/single
semantics
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.
---
.../mlir/Dialect/OpenACC/OpenACCCGOps.td | 55 ++++++++++++
mlir/lib/Dialect/OpenACC/IR/OpenACCCG.cpp | 14 +++
mlir/test/Dialect/OpenACC/invalid-cg.mlir | 36 ++++++++
mlir/test/Dialect/OpenACC/ops-cg.mlir | 86 +++++++++++++++++++
4 files changed, 191 insertions(+)
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: }
More information about the Mlir-commits
mailing list