[flang-commits] [flang] [flang][OpenACC] Avoid allocation actions for unified memory. (PR #217146)

Slava Zakharin via flang-commits flang-commits at lists.llvm.org
Tue Aug 18 14:50:02 PDT 2026


https://github.com/vzakhari created https://github.com/llvm/llvm-project/pull/217146

Under `-gpu unified`, the dynamic allocations are accessible
on host and device, so there is no need generate pre/post-alloc/dealloc
actions for `acc declare` variables' allocations.


>From 67a64f7a8808da8228e922e0886c034e25777962 Mon Sep 17 00:00:00 2001
From: Slava Zakharin <szakharin at nvidia.com>
Date: Tue, 18 Aug 2026 14:46:31 -0700
Subject: [PATCH] [flang][OpenACC] Avoid allocation actions for unified memory.

Under `-gpu unified`, the dynamic allocations are accessible
on host and device, so there is no need generate pre/post-alloc/dealloc
actions for `acc declare` variables' allocations.
---
 flang/lib/Lower/OpenACC.cpp                   | 39 ++++++++--
 .../Lower/OpenACC/acc-declare-unified.f90     | 77 +++++++++++++++++++
 2 files changed, 110 insertions(+), 6 deletions(-)
 create mode 100644 flang/test/Lower/OpenACC/acc-declare-unified.f90

diff --git a/flang/lib/Lower/OpenACC.cpp b/flang/lib/Lower/OpenACC.cpp
index 6237b461e506c..ca736113e8136 100644
--- a/flang/lib/Lower/OpenACC.cpp
+++ b/flang/lib/Lower/OpenACC.cpp
@@ -38,6 +38,7 @@
 #include "flang/Semantics/expression.h"
 #include "flang/Semantics/scope.h"
 #include "flang/Semantics/tools.h"
+#include "flang/Support/Fortran-features.h"
 #include "mlir/Dialect/Arith/IR/Arith.h"
 #include "mlir/Dialect/OpenACC/OpenACC.h"
 #include "mlir/Dialect/OpenACC/OpenACCUtils.h"
@@ -173,6 +174,16 @@ createDataEntryOp(fir::FirOpBuilder &builder, mlir::Location loc,
   return op;
 }
 
+/// Return true when allocatables and pointers are backed by memory that is
+/// addressable from both the host and the device (`-gpu unified`). Device code
+/// then reaches a `declare` variable through its host address, so mirroring the
+/// data on the device would create a second copy that the kernels never read;
+/// the allocation and deallocation actions of section 2.13.2 are skipped.
+static bool isUnifiedMemoryMode(Fortran::lower::AbstractConverter &converter) {
+  return converter.getFoldingContext().languageFeatures().IsEnabled(
+      Fortran::common::LanguageFeature::CudaUnified);
+}
+
 static void addDeclareAttr(fir::FirOpBuilder &builder, mlir::Operation *op,
                            mlir::acc::DataClause clause) {
   if (!op)
@@ -1077,7 +1088,8 @@ static void genDeclareDataOperandOperations(
         /*async=*/{}, /*asyncDeviceTypes=*/{}, /*asyncOnlyDeviceTypes=*/{});
     dataOperands.push_back(op.getAccVar());
     addDeclareAttr(builder, op.getVar().getDefiningOp(), dataClause);
-    if (mlir::isa<fir::BaseBoxType>(fir::unwrapRefType(info.addr.getType()))) {
+    if (mlir::isa<fir::BaseBoxType>(fir::unwrapRefType(info.addr.getType())) &&
+        !isUnifiedMemoryMode(converter)) {
       mlir::OpBuilder modBuilder(builder.getModule().getBodyRegion());
       modBuilder.setInsertionPointAfter(builder.getFunction());
       std::string prefix = converter.mangleName(symbol);
@@ -4139,11 +4151,15 @@ genGlobalCtors(Fortran::lower::AbstractConverter &converter,
                             mlir::acc::DeclareEnterOp, ExitOp>(
           modBuilder, builder, operandLocation, globalOp, clause,
           declareGlobalCtorName.str(), /*implicit=*/true, asFortran);
-      createDeclareAllocFunc<EntryOp>(modBuilder, builder, operandLocation,
-                                      globalOp, clause);
-      if constexpr (!std::is_same_v<EntryOp, ExitOp>)
-        createDeclareDeallocFunc<ExitOp>(modBuilder, builder, operandLocation,
-                                         globalOp, clause);
+      // The constructor and destructor are kept in unified memory mode: they
+      // map the host global onto the symbol that device code references.
+      if (!isUnifiedMemoryMode(converter)) {
+        createDeclareAllocFunc<EntryOp>(modBuilder, builder, operandLocation,
+                                        globalOp, clause);
+        if constexpr (!std::is_same_v<EntryOp, ExitOp>)
+          createDeclareDeallocFunc<ExitOp>(modBuilder, builder, operandLocation,
+                                           globalOp, clause);
+      }
     } else {
       createDeclareGlobalOp<mlir::acc::GlobalConstructorOp, EntryOp,
                             mlir::acc::DeclareEnterOp, ExitOp>(
@@ -5179,6 +5195,8 @@ void Fortran::lower::declareExternalAccModuleDeclareActionRecipes(
   const Fortran::semantics::Symbol &ultimate = sym.GetUltimate();
   if (!ultimate.test(Flag::AccDeclareAction))
     return;
+  if (isUnifiedMemoryMode(converter))
+    return;
 
   mlir::ModuleOp module = builder.getModule();
   mlir::Location loc = converter.genLocation(sym.name());
@@ -5205,6 +5223,9 @@ void Fortran::lower::declareExternalAccModuleDeclareActionRecipes(
 void Fortran::lower::attachDeclarePostAllocAction(
     AbstractConverter &converter, fir::FirOpBuilder &builder,
     const Fortran::semantics::Symbol &sym) {
+  if (isUnifiedMemoryMode(converter))
+    return;
+
   std::stringstream fctName;
   fctName << converter.mangleName(sym) << declarePostAllocSuffix.str();
   mlir::Operation *op = &builder.getInsertionBlock()->back();
@@ -5238,6 +5259,9 @@ void Fortran::lower::attachDeclarePostAllocAction(
 void Fortran::lower::attachDeclarePreDeallocAction(
     AbstractConverter &converter, fir::FirOpBuilder &builder,
     mlir::Value beginOpValue, const Fortran::semantics::Symbol &sym) {
+  if (isUnifiedMemoryMode(converter))
+    return;
+
   if (!sym.test(Fortran::semantics::Symbol::Flag::AccCreate) &&
       !sym.test(Fortran::semantics::Symbol::Flag::AccCopyIn) &&
       !sym.test(Fortran::semantics::Symbol::Flag::AccCopyInReadOnly) &&
@@ -5272,6 +5296,9 @@ void Fortran::lower::attachDeclarePreDeallocAction(
 void Fortran::lower::attachDeclarePostDeallocAction(
     AbstractConverter &converter, fir::FirOpBuilder &builder,
     const Fortran::semantics::Symbol &sym) {
+  if (isUnifiedMemoryMode(converter))
+    return;
+
   if (!sym.test(Fortran::semantics::Symbol::Flag::AccCreate) &&
       !sym.test(Fortran::semantics::Symbol::Flag::AccCopyIn) &&
       !sym.test(Fortran::semantics::Symbol::Flag::AccCopyInReadOnly) &&
diff --git a/flang/test/Lower/OpenACC/acc-declare-unified.f90 b/flang/test/Lower/OpenACC/acc-declare-unified.f90
new file mode 100644
index 0000000000000..ab9b618fa70b0
--- /dev/null
+++ b/flang/test/Lower/OpenACC/acc-declare-unified.f90
@@ -0,0 +1,77 @@
+! Test !$acc declare create on allocatables under '-gpu unified'. The variable
+! is reachable from device code through its host address, so the allocation and
+! deallocation actions of section 2.13.2 must not be generated: mirroring the
+! data on the device would create a second copy that the kernels never read.
+! The global constructor and destructor are still generated because they map the
+! host global onto the symbol that device code references.
+!
+! The runs without '-gpu unified' are the control: they show the same source
+! does generate the actions in discrete memory.
+
+! RUN: split-file %s %t
+
+! RUN: bbc -fopenacc -gpu unified -emit-hlfir %t/mod.f90 -o %t/mod-unified.mlir --module=%t
+! RUN: bbc -fopenacc -gpu unified -emit-hlfir %t/use.f90 -o %t/use-unified.mlir -I %t
+! RUN: FileCheck %s --check-prefix=UNIFIED-MOD --implicit-check-not=acc.declare_action \
+! RUN:   --implicit-check-not=_acc_declare_post_alloc \
+! RUN:   --implicit-check-not=_acc_declare_pre_dealloc \
+! RUN:   --implicit-check-not=_acc_declare_post_dealloc < %t/mod-unified.mlir
+! RUN: FileCheck %s --check-prefix=UNIFIED-USE --implicit-check-not=acc.declare_action \
+! RUN:   --implicit-check-not=_acc_declare_post_alloc \
+! RUN:   --implicit-check-not=_acc_declare_pre_dealloc \
+! RUN:   --implicit-check-not=_acc_declare_post_dealloc < %t/use-unified.mlir
+
+! RUN: bbc -fopenacc -emit-hlfir %t/mod.f90 -o %t/mod-discrete.mlir --module=%t
+! RUN: bbc -fopenacc -emit-hlfir %t/use.f90 -o %t/use-discrete.mlir -I %t
+! RUN: FileCheck %s --check-prefix=DISCRETE-MOD < %t/mod-discrete.mlir
+! RUN: FileCheck %s --check-prefix=DISCRETE-USE < %t/use-discrete.mlir
+
+//--- mod.f90
+module acc_declare_unified_mod
+  real, allocatable :: garr(:)
+  !$acc declare create(garr)
+contains
+  subroutine alloc_global()
+    allocate(garr(10))
+    deallocate(garr)
+  end subroutine
+  subroutine alloc_local()
+    real, allocatable :: larr(:)
+    !$acc declare create(larr)
+    allocate(larr(10))
+    deallocate(larr)
+  end subroutine
+end module
+
+//--- use.f90
+subroutine use_mod()
+  use acc_declare_unified_mod
+  implicit none
+  allocate(garr(100))
+end subroutine
+
+! The constructor and destructor that map the host global are still emitted.
+! UNIFIED-MOD: acc.global_ctor @_QMacc_declare_unified_modEgarr_acc_ctor
+! UNIFIED-MOD: acc.copyin varPtr(%{{.*}} : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>)
+! UNIFIED-MOD: acc.declare_enter
+! UNIFIED-MOD: acc.global_dtor @_QMacc_declare_unified_modEgarr_acc_dtor
+! UNIFIED-MOD: acc.declare_exit
+
+! The using unit neither annotates the allocation nor declares external recipes.
+! UNIFIED-USE: func.func @_QPuse_mod()
+! UNIFIED-USE: fir.global @_QMacc_declare_unified_modEgarr {acc.declare = #acc.declare<dataClause = acc_create>
+
+! DISCRETE-MOD: acc.declare_action = #acc.declare_action<postAlloc = @_QMacc_declare_unified_modEgarr_acc_declare_post_alloc>
+! DISCRETE-MOD: acc.declare_action = #acc.declare_action<preDealloc = @_QMacc_declare_unified_modEgarr_acc_declare_pre_dealloc>
+! DISCRETE-MOD: acc.declare_action = #acc.declare_action<postDealloc = @_QMacc_declare_unified_modEgarr_acc_declare_post_dealloc>
+! DISCRETE-MOD: func.func private @_QMacc_declare_unified_modFalloc_localElarr_acc_declare_post_alloc(
+! DISCRETE-MOD: func.func private @_QMacc_declare_unified_modFalloc_localElarr_acc_declare_pre_dealloc(
+! DISCRETE-MOD: func.func private @_QMacc_declare_unified_modFalloc_localElarr_acc_declare_post_dealloc(
+! DISCRETE-MOD: acc.global_ctor @_QMacc_declare_unified_modEgarr_acc_ctor
+! DISCRETE-MOD: func.func @_QMacc_declare_unified_modEgarr_acc_declare_post_alloc() attributes {acc.declare_action}
+! DISCRETE-MOD: func.func @_QMacc_declare_unified_modEgarr_acc_declare_post_dealloc() attributes {acc.declare_action}
+! DISCRETE-MOD: acc.global_dtor @_QMacc_declare_unified_modEgarr_acc_dtor
+
+! DISCRETE-USE: acc.declare_action = #acc.declare_action<postAlloc = @_QMacc_declare_unified_modEgarr_acc_declare_post_alloc>
+! DISCRETE-USE: func.func private @_QMacc_declare_unified_modEgarr_acc_declare_post_alloc()
+! DISCRETE-USE: func.func private @_QMacc_declare_unified_modEgarr_acc_declare_post_dealloc()



More information about the flang-commits mailing list