From 7364ff56bcfbdfd414d8a3bfb9257f7a3bbdb8a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Susan=20Tan=20=28=E3=82=B9-=E3=82=B6=E3=83=B3=E3=80=80?= =?UTF-8?q?=E3=82=BF=E3=83=B3=29?= Date: Thu, 15 Jan 2026 13:30:38 -0500 Subject: [PATCH] [acc] `acc declare` + `present` clause for COMMON blocks (#175588) Fix: `!$acc declare present(/COMMON/)` no longer adds `acc.declare(dataClause=acc_present)` attribute to the fir.global common. Lowering change: COMMON+present is lowered through the structured declare path (fir.address_of + acc.present operand) to preserve scope. --- flang/lib/Lower/OpenACC.cpp | 129 ++++++++++-------- ...acc-declare-common-present-in-function.f90 | 27 ++++ 2 files changed, 101 insertions(+), 55 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 8a7cb3e5d50d..d4e1ace270b3 100644 --- a/flang/lib/Lower/OpenACC.cpp +++ b/flang/lib/Lower/OpenACC.cpp @@ -887,6 +887,30 @@ 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 static void emitCtorDtorPair(mlir::OpBuilder &modBuilder, fir::FirOpBuilder &builder, @@ -907,6 +931,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.4: +/// - 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 static void genDeclareDataOperandOperations( const Fortran::parser::AccObjectList &objectList, @@ -922,37 +965,37 @@ 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::FindCommonBlockContaining(symbol)) { - 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) { - 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 || - std::is_same_v || - std::is_same_v< - EntryOp, - mlir::acc::DeclareDeviceResidentOp> || - std::is_same_v) { - emitCtorDtorPair(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; + 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) { + if constexpr (std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v< + EntryOp, mlir::acc::DeclareDeviceResidentOp>) { + emitCtorDtorPair(modBuilder, builder, loc, + globalOp, clause, + asFortranStr, ctorName); + } + }); + continue; + } } Fortran::semantics::MaybeExpr designator = Fortran::common::visit( [&](auto &&s) { return ea.Analyze(s); }, accObject.u); @@ -4018,31 +4061,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 static void emitCommonGlobal(Fortran::lower::AbstractConverter &converter, fir::FirOpBuilder &builder, @@ -4066,7 +4084,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 000000000000..5b2c8d3c00b9 --- /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-DAG: hlfir.declare +! CHECK-DAG: %[[ADDR:.*]] = fir.address_of(@com_){{.*}} : !fir.ref> +! CHECK-DAG: %[[PRESENT:.*]] = acc.present varPtr(%[[ADDR]] : !fir.ref>) -> !fir.ref> {name = "com"} +! CHECK-DAG: %[[TOK:.*]] = acc.declare_enter dataOperands(%[[PRESENT]] : !fir.ref>) +! CHECK: acc.declare_exit token(%[[TOK]]) dataOperands(%[[PRESENT]] : !fir.ref>) + end subroutine s +end program p + +