[flang-commits] [flang] [acc] `acc declare` + `present` clause for COMMON blocks (PR #175588)
Susan Tan ス-ザン タン via flang-commits
flang-commits at lists.llvm.org
Thu Jan 15 09:58:42 PST 2026
https://github.com/SusanTan updated https://github.com/llvm/llvm-project/pull/175588
>From a13b5395003787df27163ce1991415d36f4f97c3 Mon Sep 17 00:00:00 2001
From: Susan Tan <zujunt at nvidia.com>
Date: Mon, 12 Jan 2026 09:07:28 -0800
Subject: [PATCH 1/6] implement for common present
---
flang/lib/Lower/OpenACC.cpp | 72 ++++++++++++-------
...acc-declare-common-present-in-function.f90 | 27 +++++++
2 files changed, 73 insertions(+), 26 deletions(-)
create mode 100644 flang/test/Lower/OpenACC/acc-declare-common-present-in-function.f90
diff --git a/flang/lib/Lower/OpenACC.cpp b/flang/lib/Lower/OpenACC.cpp
index 52fee7baf9de1..88376a2937ad9 100644
--- a/flang/lib/Lower/OpenACC.cpp
+++ b/flang/lib/Lower/OpenACC.cpp
@@ -923,6 +923,31 @@ static void createDeclareGlobalOp(mlir::OpBuilder &modBuilder,
modBuilder.setInsertionPointAfter(declareGlobalOp);
}
+static fir::GlobalOp
+lookupGlobalBySymbolOrEquivalence(Fortran::lower::AbstractConverter &converter,
+ fir::FirOpBuilder &builder,
+ const Fortran::semantics::Symbol &sym) {
+ const Fortran::semantics::Symbol *commonBlock =
+ Fortran::semantics::FindCommonBlockContaining(sym);
+ std::string globalName = commonBlock ? converter.mangleName(*commonBlock)
+ : converter.mangleName(sym);
+ if (fir::GlobalOp g = builder.getNamedGlobal(globalName)) {
+ return g;
+ }
+ // Not found: if not a COMMON member, try equivalence members
+ if (!commonBlock) {
+ if (const Fortran::semantics::EquivalenceSet *eqSet =
+ Fortran::semantics::FindEquivalenceSet(sym)) {
+ for (const Fortran::semantics::EquivalenceObject &eqObj : *eqSet) {
+ std::string eqName = converter.mangleName(eqObj.symbol);
+ if (fir::GlobalOp g = builder.getNamedGlobal(eqName))
+ return g;
+ }
+ }
+ }
+ return {};
+}
+
template <typename EntryOp, typename ExitOp>
static void
emitCtorDtorPair(mlir::OpBuilder &modBuilder, fir::FirOpBuilder &builder,
@@ -961,6 +986,25 @@ static void genDeclareDataOperandOperations(
// Handle COMMON/global symbols via module-level ctor/dtor path.
if (symbol.detailsIf<Fortran::semantics::CommonBlockDetails>() ||
Fortran::semantics::FindCommonBlockContaining(symbol)) {
+ if (dataClause == mlir::acc::DataClause::acc_present) {
+ fir::GlobalOp globalOp =
+ lookupGlobalBySymbolOrEquivalence(converter, builder, symbol);
+ if (!globalOp)
+ llvm::report_fatal_error("could not retrieve global symbol");
+
+ fir::AddrOfOp addrOp = fir::AddrOfOp::create(
+ builder, operandLocation,
+ fir::ReferenceType::get(globalOp.getType()), globalOp.getSymbol());
+
+ asFortran << symbol.name().ToString();
+ EntryOp op = createDataEntryOp<EntryOp>(
+ builder, operandLocation, addrOp.getResTy(), asFortran, bounds,
+ structured, implicit, dataClause, addrOp.getResTy().getType(),
+ /*async=*/{}, /*asyncDeviceTypes=*/{}, /*asyncOnlyDeviceTypes=*/{});
+ dataOperands.push_back(op.getAccVar());
+ continue;
+ }
+
emitCommonGlobal(
converter, builder, accObject, dataClause,
[&](mlir::OpBuilder &modBuilder, [[maybe_unused]] mlir::Location loc,
@@ -4250,31 +4294,6 @@ genGlobalCtorsWithModifier(Fortran::lower::AbstractConverter &converter,
dataClause);
}
-static fir::GlobalOp
-lookupGlobalBySymbolOrEquivalence(Fortran::lower::AbstractConverter &converter,
- fir::FirOpBuilder &builder,
- const Fortran::semantics::Symbol &sym) {
- const Fortran::semantics::Symbol *commonBlock =
- Fortran::semantics::FindCommonBlockContaining(sym);
- std::string globalName = commonBlock ? converter.mangleName(*commonBlock)
- : converter.mangleName(sym);
- if (fir::GlobalOp g = builder.getNamedGlobal(globalName)) {
- return g;
- }
- // Not found: if not a COMMON member, try equivalence members
- if (!commonBlock) {
- if (const Fortran::semantics::EquivalenceSet *eqSet =
- Fortran::semantics::FindEquivalenceSet(sym)) {
- for (const Fortran::semantics::EquivalenceObject &eqObj : *eqSet) {
- std::string eqName = converter.mangleName(eqObj.symbol);
- if (fir::GlobalOp g = builder.getNamedGlobal(eqName))
- return g;
- }
- }
- }
- return {};
-}
-
template <typename EmitterFn>
static void emitCommonGlobal(Fortran::lower::AbstractConverter &converter,
fir::FirOpBuilder &builder,
@@ -4298,7 +4317,8 @@ static void emitCommonGlobal(Fortran::lower::AbstractConverter &converter,
return;
mlir::Location operandLocation = genOperandLocation(converter, obj);
- addDeclareAttr(builder, globalOp.getOperation(), clause);
+ if (clause != mlir::acc::DataClause::acc_present)
+ addDeclareAttr(builder, globalOp.getOperation(), clause);
mlir::OpBuilder modBuilder(builder.getModule().getBodyRegion());
modBuilder.setInsertionPointAfter(globalOp);
std::stringstream asFortran;
diff --git a/flang/test/Lower/OpenACC/acc-declare-common-present-in-function.f90 b/flang/test/Lower/OpenACC/acc-declare-common-present-in-function.f90
new file mode 100644
index 0000000000000..9983eca6c3d91
--- /dev/null
+++ b/flang/test/Lower/OpenACC/acc-declare-common-present-in-function.f90
@@ -0,0 +1,27 @@
+! RUN: bbc -fopenacc -emit-hlfir %s -o - | FileCheck %s
+
+! Verify that 'declare present' on a COMMON block inside a function is lowered
+! through the structured declare region (acc.present + declare enter/exit),
+! and does not stamp the fir.global common with acc.declare=acc_present.
+
+program p
+ implicit none
+ real :: pi
+ common /COM/ pi
+contains
+ subroutine s()
+ implicit none
+ real :: pi
+ common /COM/ pi
+!$acc declare present(/COM/)
+! CHECK: fir.global common @com_(dense<0> : vector<4xi8>) {alignment = 4 : i64} : !fir.array<4xi8>
+! CHECK-LABEL: func.func private @_QFPs()
+! CHECK: hlfir.declare
+! CHECK: %[[ADDR:.*]] = fir.address_of(@com_) : !fir.ref<!fir.array<4xi8>>
+! CHECK: %[[PRESENT:.*]] = acc.present varPtr(%[[ADDR]] : !fir.ref<!fir.array<4xi8>>) -> !fir.ref<!fir.array<4xi8>> {name = "com"}
+! CHECK: %[[TOK:.*]] = acc.declare_enter dataOperands(%[[PRESENT]] : !fir.ref<!fir.array<4xi8>>)
+! CHECK: acc.declare_exit token(%[[TOK]]) dataOperands(%[[PRESENT]] : !fir.ref<!fir.array<4xi8>>)
+ end subroutine s
+end program p
+
+
>From 732dff5e776c35eef166e78b47e57353ef1ae828 Mon Sep 17 00:00:00 2001
From: Susan Tan <zujunt at nvidia.com>
Date: Thu, 15 Jan 2026 09:12:19 -0800
Subject: [PATCH 2/6] add common function
---
flang/lib/Lower/OpenACC.cpp | 93 +++++++++++++++++++------------------
1 file changed, 48 insertions(+), 45 deletions(-)
diff --git a/flang/lib/Lower/OpenACC.cpp b/flang/lib/Lower/OpenACC.cpp
index 88376a2937ad9..0c9baea075cdc 100644
--- a/flang/lib/Lower/OpenACC.cpp
+++ b/flang/lib/Lower/OpenACC.cpp
@@ -968,6 +968,25 @@ emitCtorDtorPair(mlir::OpBuilder &modBuilder, fir::FirOpBuilder &builder,
/*implicit=*/false, asFortran);
}
+/// Return true iff this OpenACC clause is valid for lowering a global/COMMON
+/// symbol via module-level global ctor/dtor.
+///
+/// OpenACC 3.0:
+/// - 3000: In a Fortran module declaration section, only create, copyin,
+/// device_resident clauses are allowed.
+/// - 3001: link is also allowed.
+static bool isValidClauseForGlobalDeclare(mlir::acc::DataClause clause) {
+ switch (clause) {
+ case mlir::acc::DataClause::acc_create:
+ case mlir::acc::DataClause::acc_copyin:
+ case mlir::acc::DataClause::acc_declare_device_resident:
+ case mlir::acc::DataClause::acc_declare_link:
+ return true;
+ default:
+ return false;
+ }
+}
+
template <typename EntryOp, typename ExitOp>
static void genDeclareDataOperandOperations(
const Fortran::parser::AccObjectList &objectList,
@@ -986,53 +1005,37 @@ static void genDeclareDataOperandOperations(
// Handle COMMON/global symbols via module-level ctor/dtor path.
if (symbol.detailsIf<Fortran::semantics::CommonBlockDetails>() ||
Fortran::semantics::FindCommonBlockContaining(symbol)) {
- if (dataClause == mlir::acc::DataClause::acc_present) {
- fir::GlobalOp globalOp =
- lookupGlobalBySymbolOrEquivalence(converter, builder, symbol);
- if (!globalOp)
- llvm::report_fatal_error("could not retrieve global symbol");
-
- fir::AddrOfOp addrOp = fir::AddrOfOp::create(
- builder, operandLocation,
- fir::ReferenceType::get(globalOp.getType()), globalOp.getSymbol());
-
- asFortran << symbol.name().ToString();
- EntryOp op = createDataEntryOp<EntryOp>(
- builder, operandLocation, addrOp.getResTy(), asFortran, bounds,
- structured, implicit, dataClause, addrOp.getResTy().getType(),
- /*async=*/{}, /*asyncDeviceTypes=*/{}, /*asyncOnlyDeviceTypes=*/{});
- dataOperands.push_back(op.getAccVar());
+ // Only certain clauses are valid for module declaration sections (OpenACC
+ // 3.0 3000/3001). All other clauses (including present/deviceptr) must be
+ // handled via structured declare lowering.
+ if (isValidClauseForGlobalDeclare(dataClause)) {
+ emitCommonGlobal(
+ converter, builder, accObject, dataClause,
+ [&](mlir::OpBuilder &modBuilder,
+ [[maybe_unused]] mlir::Location loc,
+ [[maybe_unused]] fir::GlobalOp globalOp,
+ [[maybe_unused]] mlir::acc::DataClause clause,
+ std::stringstream &asFortranStr, const std::string &ctorName) {
+ // Compile-time guard: only instantiate the module-level ctor/dtor
+ // lowering for EntryOps that are supported in this path.
+ //
+ // Note: the runtime clause guard above
+ // (isValidClauseForGlobalDeclare) is not sufficient to prevent
+ // template instantiation failures for other EntryOps (e.g.
+ // DevicePtrOp has a different create signature).
+ if constexpr (std::is_same_v<EntryOp, mlir::acc::DeclareLinkOp> ||
+ std::is_same_v<EntryOp, mlir::acc::CreateOp> ||
+ std::is_same_v<EntryOp, mlir::acc::CopyinOp> ||
+ std::is_same_v<
+ EntryOp, mlir::acc::DeclareDeviceResidentOp>) {
+ // Module-level ctor/dtor path for valid global declare clauses.
+ emitCtorDtorPair<EntryOp, ExitOp>(modBuilder, builder, loc,
+ globalOp, clause,
+ asFortranStr, ctorName);
+ }
+ });
continue;
}
-
- emitCommonGlobal(
- converter, builder, accObject, dataClause,
- [&](mlir::OpBuilder &modBuilder, [[maybe_unused]] mlir::Location loc,
- [[maybe_unused]] fir::GlobalOp globalOp,
- [[maybe_unused]] mlir::acc::DataClause clause,
- std::stringstream &asFortranStr, const std::string &ctorName) {
- if constexpr (std::is_same_v<EntryOp, mlir::acc::DeclareLinkOp>) {
- createDeclareGlobalOp<
- mlir::acc::GlobalConstructorOp, mlir::acc::DeclareLinkOp,
- mlir::acc::DeclareEnterOp, mlir::acc::DeclareLinkOp>(
- modBuilder, builder, loc, globalOp, clause, ctorName,
- /*implicit=*/false, asFortranStr);
- } else if constexpr (std::is_same_v<EntryOp, mlir::acc::CreateOp> ||
- std::is_same_v<EntryOp, mlir::acc::CopyinOp> ||
- std::is_same_v<
- EntryOp,
- mlir::acc::DeclareDeviceResidentOp> ||
- std::is_same_v<ExitOp, mlir::acc::CopyoutOp>) {
- emitCtorDtorPair<EntryOp, ExitOp>(modBuilder, builder, loc,
- globalOp, clause, asFortranStr,
- ctorName);
- } else {
- // No module-level ctor/dtor for this clause (e.g., deviceptr,
- // present). Handled via structured declare region only.
- return;
- }
- });
- continue;
}
Fortran::semantics::MaybeExpr designator = Fortran::common::visit(
[&](auto &&s) { return ea.Analyze(s); }, accObject.u);
>From 9c8314e3a7377209ea992894264e35f1cae37911 Mon Sep 17 00:00:00 2001
From: Susan Tan <zujunt at nvidia.com>
Date: Thu, 15 Jan 2026 09:16:07 -0800
Subject: [PATCH 3/6] tweak on commnets
---
flang/lib/Lower/OpenACC.cpp | 21 +++++++++------------
1 file changed, 9 insertions(+), 12 deletions(-)
diff --git a/flang/lib/Lower/OpenACC.cpp b/flang/lib/Lower/OpenACC.cpp
index 0c9baea075cdc..1fa32598f6482 100644
--- a/flang/lib/Lower/OpenACC.cpp
+++ b/flang/lib/Lower/OpenACC.cpp
@@ -1002,12 +1002,17 @@ static void genDeclareDataOperandOperations(
std::stringstream asFortran;
mlir::Location operandLocation = genOperandLocation(converter, accObject);
Fortran::semantics::Symbol &symbol = getSymbolFromAccObject(accObject);
- // Handle COMMON/global symbols via module-level ctor/dtor path.
+ // OpenACC `declare` lowering for COMMON/global symbols has two paths:
+ // - Module-level global ctor/dtor: used only for clauses allowed in a
+ // module declaration section (OpenACC 3.0 3000/3001: create/copyin/
+ // device_resident/link). This materializes the mapping at program
+ // start/end via module-level ctor/dtor ops.
+ // - Structured declare: used for all other clauses (e.g.
+ // present/deviceptr),
+ // whose semantics are scope-dependent and are represented via a
+ // structured `acc.declare` region.
if (symbol.detailsIf<Fortran::semantics::CommonBlockDetails>() ||
Fortran::semantics::FindCommonBlockContaining(symbol)) {
- // Only certain clauses are valid for module declaration sections (OpenACC
- // 3.0 3000/3001). All other clauses (including present/deviceptr) must be
- // handled via structured declare lowering.
if (isValidClauseForGlobalDeclare(dataClause)) {
emitCommonGlobal(
converter, builder, accObject, dataClause,
@@ -1016,19 +1021,11 @@ static void genDeclareDataOperandOperations(
[[maybe_unused]] fir::GlobalOp globalOp,
[[maybe_unused]] mlir::acc::DataClause clause,
std::stringstream &asFortranStr, const std::string &ctorName) {
- // Compile-time guard: only instantiate the module-level ctor/dtor
- // lowering for EntryOps that are supported in this path.
- //
- // Note: the runtime clause guard above
- // (isValidClauseForGlobalDeclare) is not sufficient to prevent
- // template instantiation failures for other EntryOps (e.g.
- // DevicePtrOp has a different create signature).
if constexpr (std::is_same_v<EntryOp, mlir::acc::DeclareLinkOp> ||
std::is_same_v<EntryOp, mlir::acc::CreateOp> ||
std::is_same_v<EntryOp, mlir::acc::CopyinOp> ||
std::is_same_v<
EntryOp, mlir::acc::DeclareDeviceResidentOp>) {
- // Module-level ctor/dtor path for valid global declare clauses.
emitCtorDtorPair<EntryOp, ExitOp>(modBuilder, builder, loc,
globalOp, clause,
asFortranStr, ctorName);
>From f35ecafeb375f298fdaf30e457d3f3e8ab50a95d Mon Sep 17 00:00:00 2001
From: Susan Tan <zujunt at nvidia.com>
Date: Thu, 15 Jan 2026 09:27:05 -0800
Subject: [PATCH 4/6] tweak on test
---
.../OpenACC/acc-declare-common-present-in-function.f90 | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/flang/test/Lower/OpenACC/acc-declare-common-present-in-function.f90 b/flang/test/Lower/OpenACC/acc-declare-common-present-in-function.f90
index 9983eca6c3d91..5b2c8d3c00b9f 100644
--- a/flang/test/Lower/OpenACC/acc-declare-common-present-in-function.f90
+++ b/flang/test/Lower/OpenACC/acc-declare-common-present-in-function.f90
@@ -16,10 +16,10 @@ subroutine s()
!$acc declare present(/COM/)
! CHECK: fir.global common @com_(dense<0> : vector<4xi8>) {alignment = 4 : i64} : !fir.array<4xi8>
! CHECK-LABEL: func.func private @_QFPs()
-! CHECK: hlfir.declare
-! CHECK: %[[ADDR:.*]] = fir.address_of(@com_) : !fir.ref<!fir.array<4xi8>>
-! CHECK: %[[PRESENT:.*]] = acc.present varPtr(%[[ADDR]] : !fir.ref<!fir.array<4xi8>>) -> !fir.ref<!fir.array<4xi8>> {name = "com"}
-! CHECK: %[[TOK:.*]] = acc.declare_enter dataOperands(%[[PRESENT]] : !fir.ref<!fir.array<4xi8>>)
+! CHECK-DAG: hlfir.declare
+! CHECK-DAG: %[[ADDR:.*]] = fir.address_of(@com_){{.*}} : !fir.ref<!fir.array<4xi8>>
+! CHECK-DAG: %[[PRESENT:.*]] = acc.present varPtr(%[[ADDR]] : !fir.ref<!fir.array<4xi8>>) -> !fir.ref<!fir.array<4xi8>> {name = "com"}
+! CHECK-DAG: %[[TOK:.*]] = acc.declare_enter dataOperands(%[[PRESENT]] : !fir.ref<!fir.array<4xi8>>)
! CHECK: acc.declare_exit token(%[[TOK]]) dataOperands(%[[PRESENT]] : !fir.ref<!fir.array<4xi8>>)
end subroutine s
end program p
>From 35680ad15c59cd795c7ada708a78fd0b0b46b994 Mon Sep 17 00:00:00 2001
From: Susan Tan <zujunt at nvidia.com>
Date: Thu, 15 Jan 2026 09:28:07 -0800
Subject: [PATCH 5/6] remove braces
---
flang/lib/Lower/OpenACC.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/flang/lib/Lower/OpenACC.cpp b/flang/lib/Lower/OpenACC.cpp
index 1fa32598f6482..3aad1a1e7be3d 100644
--- a/flang/lib/Lower/OpenACC.cpp
+++ b/flang/lib/Lower/OpenACC.cpp
@@ -931,9 +931,8 @@ lookupGlobalBySymbolOrEquivalence(Fortran::lower::AbstractConverter &converter,
Fortran::semantics::FindCommonBlockContaining(sym);
std::string globalName = commonBlock ? converter.mangleName(*commonBlock)
: converter.mangleName(sym);
- if (fir::GlobalOp g = builder.getNamedGlobal(globalName)) {
+ if (fir::GlobalOp g = builder.getNamedGlobal(globalName))
return g;
- }
// Not found: if not a COMMON member, try equivalence members
if (!commonBlock) {
if (const Fortran::semantics::EquivalenceSet *eqSet =
>From 299cb78f182bc557a9ad8d038aa10e56e8e6fa42 Mon Sep 17 00:00:00 2001
From: Susan Tan <zujunt at nvidia.com>
Date: Thu, 15 Jan 2026 09:58:29 -0800
Subject: [PATCH 6/6] 3.4
---
flang/lib/Lower/OpenACC.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/flang/lib/Lower/OpenACC.cpp b/flang/lib/Lower/OpenACC.cpp
index 3d7f95a52f139..262e36ed9e74a 100644
--- a/flang/lib/Lower/OpenACC.cpp
+++ b/flang/lib/Lower/OpenACC.cpp
@@ -973,7 +973,7 @@ emitCtorDtorPair(mlir::OpBuilder &modBuilder, fir::FirOpBuilder &builder,
/// Return true iff this OpenACC clause is valid for lowering a global/COMMON
/// symbol via module-level global ctor/dtor.
///
-/// OpenACC 3.0:
+/// OpenACC 3.4:
/// - 3000: In a Fortran module declaration section, only create, copyin,
/// device_resident clauses are allowed.
/// - 3001: link is also allowed.
More information about the flang-commits
mailing list