[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