[Mlir-commits] [mlir] [mlir][OpenACC] Lower single-block thread-only array reductions (PR #212369)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Mon Jul 27 15:57:45 PDT 2026


https://github.com/khaki3 created https://github.com/llvm/llvm-project/pull/212369

Example:
```fortran
!$acc parallel loop vector reduction(+:b)
do i = 1, n
  b(i) = i
end do
```

In this code the array reduction is thread(vector)-only with no gang/block launch dim, so a within-block `all_reduce` is a complete reduction. ACCCGToGPU still rejected it as NYI because the accumulate had no block context.

Fix: keep the NYI only when the region actually launches block dimensions; allow the existing per-element `all_reduce` path for single-block thread-only array accumulates.


>From 453cf9156e9d2a9a70ec18825255d2cfe3a55a22 Mon Sep 17 00:00:00 2001
From: Kazuaki Matsumura <kmatsumura at nvidia.com>
Date: Mon, 27 Jul 2026 14:26:37 -0700
Subject: [PATCH] [mlir][OpenACC] Lower single-block thread-only array
 reductions

A within-block all_reduce is a complete reduction when the region
launches no block dimension, so allow thread-only array accumulate in
that case instead of NYI.
---
 .../Dialect/OpenACC/Transforms/ACCCGToGPU.cpp | 10 +++++--
 .../acc-cg-to-gpu-reduction-array.mlir        | 30 +++++++++++++++++++
 2 files changed, 37 insertions(+), 3 deletions(-)

diff --git a/mlir/lib/Dialect/OpenACC/Transforms/ACCCGToGPU.cpp b/mlir/lib/Dialect/OpenACC/Transforms/ACCCGToGPU.cpp
index d50d346ed9204..0d30f54f12447 100644
--- a/mlir/lib/Dialect/OpenACC/Transforms/ACCCGToGPU.cpp
+++ b/mlir/lib/Dialect/OpenACC/Transforms/ACCCGToGPU.cpp
@@ -3512,9 +3512,13 @@ void ACCCGToGPULowering::processAccumulateArrayOp(
     return;
   }
 
-  // A thread-level reduction with no block owner for its elements cannot merge
-  // the cross-thread partials, so report NYI.
-  if (!reductionHasBlockContext(op)) {
+  // A thread-only accumulate merges with a within-block all_reduce, which is
+  // complete only in one block: block context, or a launch with no block dim.
+  // Multi-block thread-only still grid-strides across blocks, so stays NYI.
+  bool regionLaunchesBlocks = llvm::any_of(
+      computeRegion.getLaunchParDims(),
+      [](mlir::acc::GPUParallelDimAttr d) { return d.isAnyBlock(); });
+  if (!reductionHasBlockContext(op) && regionLaunchesBlocks) {
     (void)accSupport.emitNYI(
         loc, "reduction: thread-only array reduction accumulate");
     return;
diff --git a/mlir/test/Dialect/OpenACC/acc-cg-to-gpu-reduction-array.mlir b/mlir/test/Dialect/OpenACC/acc-cg-to-gpu-reduction-array.mlir
index 93354321865a7..64eaec673dd1c 100644
--- a/mlir/test/Dialect/OpenACC/acc-cg-to-gpu-reduction-array.mlir
+++ b/mlir/test/Dialect/OpenACC/acc-cg-to-gpu-reduction-array.mlir
@@ -347,3 +347,33 @@ func.func @thread_x_reduction_with_thread_z_width() {
   } {origin = "acc.parallel"}
   return
 }
+
+// Thread-only array accumulate is well-defined when the region launches no
+// block dim (single block): a within-block all_reduce is a complete reduction.
+// CHECK-LABEL: func.func @thread_only_array_reduction_single_block
+// CHECK: gpu.launch
+// CHECK-NOT: acc.reduction_accumulate_array
+// CHECK: scf.for %[[IV:.*]] = %{{.*}} to %{{.*}} step %{{.*}} {
+// CHECK:   %[[ELT:.*]] = memref.load %[[ALLOCA:.*]][%[[IV]]] : memref<8xi32>
+// CHECK:   %[[RED:.*]] = gpu.all_reduce add %[[ELT]]
+// CHECK:   memref.store %[[RED]], %[[ALLOCA]][%[[IV]]] : memref<8xi32>
+// CHECK: }
+func.func @thread_only_array_reduction_single_block() {
+  %c128 = arith.constant 128 : index
+  %tx = acc.par_width %c128 {par_dim = #acc.par_dim<thread_x>}
+  acc.compute_region launch(%ktx = %tx) {
+    %c0 = arith.constant 0 : index
+    %c1 = arith.constant 1 : index
+    %c8 = arith.constant 8 : index
+    %c0_i32 = arith.constant 0 : i32
+    %local = memref.alloca() : memref<8xi32>
+    scf.for %i = %c0 to %c8 step %c1 {
+      memref.store %c0_i32, %local[%i] : memref<8xi32>
+    }
+    %bounds = acc.bounds extent(%c8 : index)
+    acc.reduction_accumulate_array %local bounds(%bounds) <add>
+        : memref<8xi32> {par_dims = #acc<par_dims[thread_x]>}
+    acc.yield
+  } {origin = "acc.parallel"}
+  return
+}



More information about the Mlir-commits mailing list