[Mlir-commits] [mlir] [mlir][openacc] Report private arrays placed in local memory (PR #224398)

Yian Su llvmlistbot at llvm.org
Thu Sep 17 12:55:33 PDT 2026


https://github.com/yiansu created https://github.com/llvm/llvm-project/pull/224398

ACCCGToGPU reports private arrays it places in GPU shared memory, but says nothing when a private array is given per-thread stack storage. Collect those names too and emit a matching remark, so -Minfo output accounts for every private array placement.

Rank-0 privatizations are skipped: a private scalar is a register candidate rather than local memory.

>From f87b9248aa42a5685d89e7f0918a01d365eb14ad Mon Sep 17 00:00:00 2001
From: Yian Su <yians at nvidia.com>
Date: Thu, 17 Sep 2026 12:38:02 -0700
Subject: [PATCH] [mlir][openacc] Report private arrays placed in local memory

ACCCGToGPU reports private arrays it places in GPU shared memory, but
says nothing when a private array is given per-thread stack storage.
Collect those names too and emit a matching remark, so -Minfo output
accounts for every private array placement.

Rank-0 privatizations are skipped: a private scalar is a register
candidate rather than local memory.
---
 .../Dialect/OpenACC/Transforms/ACCCGToGPU.cpp | 35 +++++++++++
 ...cg-to-gpu-private-local-memory-remark.mlir | 58 +++++++++++++++++++
 2 files changed, 93 insertions(+)
 create mode 100644 mlir/test/Dialect/OpenACC/acc-cg-to-gpu-private-local-memory-remark.mlir

diff --git a/mlir/lib/Dialect/OpenACC/Transforms/ACCCGToGPU.cpp b/mlir/lib/Dialect/OpenACC/Transforms/ACCCGToGPU.cpp
index b9d634e059a01..96f974a488c3c 100644
--- a/mlir/lib/Dialect/OpenACC/Transforms/ACCCGToGPU.cpp
+++ b/mlir/lib/Dialect/OpenACC/Transforms/ACCCGToGPU.cpp
@@ -529,6 +529,12 @@ class ACCCGToGPULowering {
   /// Reserve \p bytes from the shared-memory budget.
   bool tryAllocateSharedMemory(int64_t bytes);
 
+  /// Record the array behind \p privateLocal as per-thread local memory for
+  /// the closing remark. Rank-0 privatizations are skipped: a scalar is a
+  /// register candidate, not local memory.
+  void recordLocalMemPrivate(acc::PrivateLocalOp privateLocal,
+                             MemRefType baseTy);
+
   /// Element size in bytes for \p elementType .
   int64_t getElementSizeInBytes(Location loc, Type elementType) const;
 
@@ -701,6 +707,7 @@ class ACCCGToGPULowering {
   acc::DefaultACCToGPUMappingPolicy defaultPolicy;
   SharedMemoryBudget sharedMemBudget;
   SmallVector<std::string> sharedMemPrivateVarNames;
+  SmallVector<std::string> localMemPrivateVarNames;
   llvm::SmallVector<Operation *, 4> deferredBarrierSeqLoops;
 
   Value getThreadId(Location loc, gpu::Dimension dim) {
@@ -865,6 +872,20 @@ bool ACCCGToGPULowering::tryAllocateSharedMemory(int64_t bytes) {
   return sharedMemBudget.tryAllocate(bytes);
 }
 
+void ACCCGToGPULowering::recordLocalMemPrivate(acc::PrivateLocalOp privateLocal,
+                                               MemRefType baseTy) {
+  if (baseTy.getRank() == 0)
+    return;
+  std::string varName =
+      privateLocal ? accSupport.getVariableName(privateLocal.getResult())
+                   : std::string();
+  if (varName.empty())
+    varName = "(*)";
+  // The same variable reaches this point once per acc.private_local it has.
+  if (!llvm::is_contained(localMemPrivateVarNames, varName))
+    localMemPrivateVarNames.push_back(varName);
+}
+
 FailureOr<arith::AtomicRMWKind>
 ACCCGToGPULowering::getReductionKind(acc::ReductionOperator redOp, Type type,
                                      Location loc) {
@@ -1250,6 +1271,14 @@ LogicalResult ACCCGToGPULowering::rewrite() {
   if (hasFailed)
     return failure();
 
+  if (!localMemPrivateVarNames.empty()) {
+    accSupport.emitRemark(computeRegion, [&]() {
+      return (llvm::Twine("Local memory used for ") +
+              llvm::join(localMemPrivateVarNames, ","))
+          .str();
+    });
+  }
+
   if (!sharedMemPrivateVarNames.empty()) {
     accSupport.emitRemark(computeRegion, [&]() {
       return (llvm::Twine("GPU shared memory used for ") +
@@ -2578,6 +2607,8 @@ Value ACCCGToGPULowering::processPrivatize(acc::PrivatizeOp privatize) {
   }
   Operation *privatizeUser = getOnlyUser(tracked);
   assert(privatizeUser && "expected PrivateLocalOp user for privatize");
+  acc::PrivateLocalOp privateLocalUser =
+      dyn_cast<acc::PrivateLocalOp>(privatizeUser);
 
   std::pair<SmallVector<mlir::acc::GPUParallelDimAttr>,
             SmallVector<mlir::acc::GPUParallelDimAttr>>
@@ -2609,6 +2640,7 @@ Value ACCCGToGPULowering::processPrivatize(acc::PrivatizeOp privatize) {
   if (threadXActive &&
       canUseStackAlloca(baseTy, loc, options.maxThreadPrivateStack)) {
     auto alloca = memref::AllocaOp::create(rewriter, loc, baseTy);
+    recordLocalMemPrivate(privateLocalUser, baseTy);
     mapping.map(privatize.getResult(), alloca.getResult());
     return alloca.getResult();
   }
@@ -2667,6 +2699,7 @@ Value ACCCGToGPULowering::processPrivatize(acc::PrivatizeOp privatize) {
       // Static sizes: use alloca (stack allocation)
       auto alloca =
           memref::AllocaOp::create(rewriter, privatize->getLoc(), baseTy);
+      recordLocalMemPrivate(privateLocalUser, baseTy);
       mapping.map(privatize.getResult(), alloca.getResult());
       return alloca.getResult();
     }
@@ -2853,6 +2886,7 @@ void ACCCGToGPULowering::processPrivateLocal(
          (arrayAccum && storageHasThreadX(privateLocal, computeRegion))) &&
         canUseStackAlloca(baseTy, loc, options.maxThreadPrivateStack)) {
       Value alloca = memref::AllocaOp::create(rewriter, loc, baseTy);
+      recordLocalMemPrivate(privateLocal, baseTy);
       if (arrayAccum) {
         FailureOr<arith::AtomicRMWKind> kind = getReductionKind(
             arrayAccum.getReductionOperator(), baseTy.getElementType(), loc);
@@ -2943,6 +2977,7 @@ void ACCCGToGPULowering::processPrivateLocal(
          (arrayAccum && storageHasThreadX(privateLocal, computeRegion))) &&
         canUseStackAlloca(baseTy, loc, options.maxThreadPrivateStack)) {
       Value alloca = memref::AllocaOp::create(rewriter, loc, baseTy);
+      recordLocalMemPrivate(privateLocal, baseTy);
       if (arrayAccum) {
         FailureOr<arith::AtomicRMWKind> kind = getReductionKind(
             arrayAccum.getReductionOperator(), baseTy.getElementType(), loc);
diff --git a/mlir/test/Dialect/OpenACC/acc-cg-to-gpu-private-local-memory-remark.mlir b/mlir/test/Dialect/OpenACC/acc-cg-to-gpu-private-local-memory-remark.mlir
new file mode 100644
index 0000000000000..b69cf304d7fec
--- /dev/null
+++ b/mlir/test/Dialect/OpenACC/acc-cg-to-gpu-private-local-memory-remark.mlir
@@ -0,0 +1,58 @@
+// RUN: mlir-opt %s --pass-pipeline="builtin.module(func.func(acc-cg-to-gpu))" \
+// RUN:   --remarks-filter="(open)?acc.*" 2>&1 | FileCheck %s
+
+// CHECK: Function=one_array | Remark="Local memory used for buf"
+func.func @one_array() {
+  %c0 = arith.constant 0 : index
+  %c1 = arith.constant 1 : i32
+  %priv = acc.privatize par_dims(#acc<par_dims[thread_x]>)
+      : () -> !acc.private_type<memref<4xi32>>
+
+  acc.compute_region ins(%priv_in = %priv) : (!acc.private_type<memref<4xi32>>) {
+    %c0_k = arith.constant 0 : index
+    %c1_k = arith.constant 1 : i32
+    %local = acc.private_local %priv_in {acc.var_name = #acc.var_name<"buf">}
+        : (!acc.private_type<memref<4xi32>>) -> memref<4xi32>
+    memref.store %c1_k, %local[%c0_k] : memref<4xi32>
+    acc.yield
+  } <{origin = "acc.parallel"}>
+  return
+}
+
+// CHECK: Function=two_arrays | Remark="Local memory used for a,b"
+func.func @two_arrays() {
+  %priv_a = acc.privatize par_dims(#acc<par_dims[thread_x]>)
+      : () -> !acc.private_type<memref<4xi32>>
+  %priv_b = acc.privatize par_dims(#acc<par_dims[thread_x]>)
+      : () -> !acc.private_type<memref<8xi32>>
+
+  acc.compute_region ins(%a_in = %priv_a, %b_in = %priv_b)
+      : (!acc.private_type<memref<4xi32>>, !acc.private_type<memref<8xi32>>) {
+    %c0_k = arith.constant 0 : index
+    %c1_k = arith.constant 1 : i32
+    %a = acc.private_local %a_in {acc.var_name = #acc.var_name<"a">}
+        : (!acc.private_type<memref<4xi32>>) -> memref<4xi32>
+    %b = acc.private_local %b_in {acc.var_name = #acc.var_name<"b">}
+        : (!acc.private_type<memref<8xi32>>) -> memref<8xi32>
+    memref.store %c1_k, %a[%c0_k] : memref<4xi32>
+    memref.store %c1_k, %b[%c0_k] : memref<8xi32>
+    acc.yield
+  } <{origin = "acc.parallel"}>
+  return
+}
+
+// CHECK: Function=scalar
+// CHECK-NOT: Local memory used for
+func.func @scalar() {
+  %priv = acc.privatize par_dims(#acc<par_dims[thread_x]>)
+      : () -> !acc.private_type<memref<i32>>
+
+  acc.compute_region ins(%priv_in = %priv) : (!acc.private_type<memref<i32>>) {
+    %c1_k = arith.constant 1 : i32
+    %local = acc.private_local %priv_in {acc.var_name = #acc.var_name<"s">}
+        : (!acc.private_type<memref<i32>>) -> memref<i32>
+    memref.store %c1_k, %local[] : memref<i32>
+    acc.yield
+  } <{origin = "acc.parallel"}>
+  return
+}



More information about the Mlir-commits mailing list