[Mlir-commits] [mlir] 1ada304 - [mlir][OpenACC] Keep ThreadY active for inner-combine-fed worker reductions (#211696)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Fri Jul 24 06:37:40 PDT 2026


Author: Matsu
Date: 2026-07-24T06:37:35-07:00
New Revision: 1ada3044291aa3215b9deb2aeec3e952c13c2813

URL: https://github.com/llvm/llvm-project/commit/1ada3044291aa3215b9deb2aeec3e952c13c2813
DIFF: https://github.com/llvm/llvm-project/commit/1ada3044291aa3215b9deb2aeec3e952c13c2813.diff

LOG: [mlir][OpenACC] Keep ThreadY active for inner-combine-fed worker reductions (#211696)

Example:
```fortran
res = 0
!$cuf kernel do(2) <<< *, (32,8) >>> reduce(+:res)
do j2 = 1, n2
  do j1 = 1, n1
    res = res + a(j1, j2)
  end do
end do
```
In this code the reduction accumulator is per-(block_y, thread_y): each
worker row's shared slot is filled by an inner block-scoped combine, so
the rows hold distinct partials. The final combine into the result was
classified as not "worker-private" (block_y+thread_y into a global
dest), so it fell back to the ThreadY row-zero path and dropped every
worker but row 0 — a 2D SUM returned -1 instead of -4.
Fix: keep ThreadY active for a block_y+thread_y accumulator that is fed
by an inner block-scoped combine (distinct per-worker partials). A plain
worker accumulate still lowers to a worker-wide all_reduce that
broadcasts the total, so it correctly stays row-zero and is not
multiplied by the worker count.

Added: 
    

Modified: 
    mlir/lib/Dialect/OpenACC/Transforms/ACCCGToGPU.cpp

Removed: 
    


################################################################################
diff  --git a/mlir/lib/Dialect/OpenACC/Transforms/ACCCGToGPU.cpp b/mlir/lib/Dialect/OpenACC/Transforms/ACCCGToGPU.cpp
index 3bb78cd834b3e..d50d346ed9204 100644
--- a/mlir/lib/Dialect/OpenACC/Transforms/ACCCGToGPU.cpp
+++ b/mlir/lib/Dialect/OpenACC/Transforms/ACCCGToGPU.cpp
@@ -1276,6 +1276,30 @@ static bool hasUnsafeEffectsWhenBroadening(Operation *op) {
   return !op->hasTrait<OpTrait::HasRecursiveMemoryEffects>();
 }
 
+/// True when \p accumulator is the destination of a separate block-scoped
+/// combine, so it holds a distinct per-worker partial rather than a broadcast.
+static bool isFedByInnerBlockCombine(acc::PrivateLocalOp accumulator,
+                                     Operation *selfCombine) {
+  if (!accumulator)
+    return false;
+  for (Operation *user : accumulator.getResult().getUsers()) {
+    if (user == selfCombine)
+      continue;
+    auto combineOp = dyn_cast<acc::ReductionCombineOp>(user);
+    if (!combineOp ||
+        unwrapMemRefConversion(combineOp.getDestMemref()).getDefiningOp() !=
+            accumulator.getOperation())
+      continue;
+    SmallVector<mlir::acc::GPUParallelDimAttr> parDims =
+        getReductionCombineParDims(combineOp);
+    if (llvm::any_of(parDims, [](mlir::acc::GPUParallelDimAttr d) {
+          return d.isAnyBlock();
+        }))
+      return true;
+  }
+  return false;
+}
+
 /// Records whether \p combineOp requires ThreadY to remain active.
 static void classifyThreadYCombine(ThreadYBroadeningInfo &info,
                                    Operation *combineOp, Value src, Value dest,
@@ -1299,6 +1323,16 @@ static void classifyThreadYCombine(ThreadYBroadeningInfo &info,
     return;
   }
 
+  // A block_y+thread_y accumulator fed by an inner block-scoped combine holds
+  // a distinct partial per worker row, so its combine runs on every row; a
+  // plain worker accumulate broadcasts via all_reduce and stays row-zero.
+  if (hasThreadY && hasBlock &&
+      isThreadYPrivate(srcPrivate, /*allowBlock=*/true, computeRegion) &&
+      isFedByInnerBlockCombine(srcPrivate, combineOp)) {
+    info.hasActiveWorkerCombine = true;
+    return;
+  }
+
   info.hasExplicitInactiveCombine = true;
   if (!info.diagnosticOp)
     info.diagnosticOp = combineOp;


        


More information about the Mlir-commits mailing list