[flang-commits] [flang] [Flang] Preserve local host association attrs (PR #230001)
via flang-commits
flang-commits at lists.llvm.org
Thu Oct 8 02:37:15 PDT 2026
https://github.com/keepyixiao updated https://github.com/llvm/llvm-project/pull/230001
>From 7b67ea5a03662966ba4fda4b4c3ded8c382e16b7 Mon Sep 17 00:00:00 2001
From: nudt_yixiao <1272950933 at qq.com>
Date: Wed, 7 Oct 2026 22:14:15 +0800
Subject: [PATCH] [Flang] Preserve local host association attrs
Host-associated names may add VOLATILE or ASYNCHRONOUS locally, but
lowering only considered attributes from the ultimate symbol.
Propagate local association attributes to hlfir.declare.
Add regression tests for global and tuple host associations.
---
flang/lib/Lower/Bridge.cpp | 20 +++
flang/lib/Lower/ConvertVariable.cpp | 49 ++++++-
.../test/Lower/volatile-host-association.f90 | 135 ++++++++++++++++++
3 files changed, 200 insertions(+), 4 deletions(-)
create mode 100644 flang/test/Lower/volatile-host-association.f90
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 939d8d134552e..68b5a94bfcc6b 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -4617,6 +4617,26 @@ class FirConverter : public Fortran::lower::AbstractConverter {
Fortran::lower::AggregateStoreMap storeMap;
for (const Fortran::lower::pft::Variable &var :
Fortran::lower::pft::getScopeVariableList(scope)) {
+ if (var.hasSymbol()) {
+ const Fortran::semantics::Symbol &sym = var.getSymbol();
+ if (const auto *hostDetails =
+ sym.detailsIf<Fortran::semantics::HostAssocDetails>()) {
+ const Fortran::semantics::Symbol &hostSym = hostDetails->symbol();
+ const auto &attrs = sym.attrs();
+ const auto &hostAttrs = hostSym.attrs();
+ const bool addsAsynchronous =
+ attrs.test(Fortran::semantics::Attr::ASYNCHRONOUS) &&
+ !hostAttrs.test(Fortran::semantics::Attr::ASYNCHRONOUS);
+ const bool addsVolatile =
+ attrs.test(Fortran::semantics::Attr::VOLATILE) &&
+ !hostAttrs.test(Fortran::semantics::Attr::VOLATILE);
+ if (addsAsynchronous || addsVolatile) {
+ addSymbol(sym, getSymbolExtendedValue(hostSym, &localSymbols),
+ /*forced=*/true);
+ continue;
+ }
+ }
+ }
// Do no instantiate again variables from the block host
// that appears in specification of block variables.
if (!var.hasSymbol() || !lookupSymbol(var.getSymbol()))
diff --git a/flang/lib/Lower/ConvertVariable.cpp b/flang/lib/Lower/ConvertVariable.cpp
index 59f46a31256fe..f044fe9b578da 100644
--- a/flang/lib/Lower/ConvertVariable.cpp
+++ b/flang/lib/Lower/ConvertVariable.cpp
@@ -2550,6 +2550,49 @@ fir::FortranVariableFlagsAttr Fortran::lower::translateSymbolAttributes(
return fir::FortranVariableFlagsAttr::get(mlirContext, flags);
}
+/// Return local VOLATILE and ASYNCHRONOUS attributes for an associated name.
+/// These attributes may be added in a function or nested lexical scope without
+/// changing the attributes of the ultimate symbol.
+static fir::FortranVariableFlagsEnum
+getLocalAssociationFlags(Fortran::lower::AbstractConverter &converter,
+ const Fortran::semantics::Symbol &sym) {
+ fir::FortranVariableFlagsEnum flags = fir::FortranVariableFlagsEnum::None;
+ auto addFlags = [&](const Fortran::semantics::Symbol &localSym) {
+ const auto &attrs = localSym.attrs();
+ if (attrs.test(Fortran::semantics::Attr::ASYNCHRONOUS))
+ flags = flags | fir::FortranVariableFlagsEnum::asynchronous;
+ if (attrs.test(Fortran::semantics::Attr::VOLATILE))
+ flags = flags | fir::FortranVariableFlagsEnum::fortran_volatile;
+ };
+
+ // First collect attributes from the associated symbol itself. This handles
+ // associated names declared in nested lexical scopes such as BLOCK.
+ if (sym.has<Fortran::semantics::HostAssocDetails>() ||
+ sym.has<Fortran::semantics::UseDetails>())
+ addFlags(sym);
+
+ // Also inspect the current function scope. The symbol passed by lowering
+ // may be an association from an enclosing scope, while the current scope
+ // adds further VOLATILE or ASYNCHRONOUS attributes.
+ const Fortran::lower::pft::FunctionLikeUnit *funit =
+ converter.getCurrentFunctionUnit();
+ if (!funit)
+ return flags;
+
+ const Fortran::semantics::Scope &scope = funit->getScope();
+ auto iter = scope.find(sym.name());
+ if (iter == scope.end())
+ return flags;
+ const Fortran::semantics::Symbol &localSym = *iter->second;
+ if ((!localSym.has<Fortran::semantics::HostAssocDetails>() &&
+ !localSym.has<Fortran::semantics::UseDetails>()) ||
+ &localSym.GetUltimate() != &sym.GetUltimate())
+ return flags;
+
+ addFlags(localSym);
+ return flags;
+}
+
static bool
isCapturedInInternalProcedure(Fortran::lower::AbstractConverter &converter,
const Fortran::semantics::Symbol &sym) {
@@ -2610,6 +2653,7 @@ static void genDeclareSymbol(Fortran::lower::AbstractConverter &converter,
extraFlags = extraFlags | fir::FortranVariableFlagsEnum::internal_assoc;
if (converter.isVisibleCrayPointerTarget(sym))
extraFlags = extraFlags | fir::FortranVariableFlagsEnum::target;
+ extraFlags = extraFlags | getLocalAssociationFlags(converter, sym);
fir::FortranVariableFlagsAttr attributes =
Fortran::lower::translateSymbolAttributes(builder.getContext(), sym,
extraFlags);
@@ -2705,10 +2749,7 @@ void Fortran::lower::genDeclareSymbol(
const mlir::Location loc = genLocation(converter, sym);
if (isCapturedInInternalProcedure(converter, sym))
extraFlags = extraFlags | fir::FortranVariableFlagsEnum::internal_assoc;
- // FIXME: Using the ultimate symbol for translating symbol attributes will
- // lead to situations where the VOLATILE/ASYNCHRONOUS attributes are not
- // propagated to the hlfir.declare (these attributes can be added when
- // using module variables).
+ extraFlags = extraFlags | getLocalAssociationFlags(converter, sym);
fir::FortranVariableFlagsAttr attributes =
Fortran::lower::translateSymbolAttributes(
builder.getContext(), sym.GetUltimate(), extraFlags);
diff --git a/flang/test/Lower/volatile-host-association.f90 b/flang/test/Lower/volatile-host-association.f90
new file mode 100644
index 0000000000000..6336f41096783
--- /dev/null
+++ b/flang/test/Lower/volatile-host-association.f90
@@ -0,0 +1,135 @@
+! RUN: bbc --strict-fir-volatile-verifier %s -o - | FileCheck %s
+
+! Verify that VOLATILE and ASYNCHRONOUS attributes added to a host-associated
+! name are reflected in the declaration inside the internal procedure without
+! changing the declaration in the host procedure.
+
+subroutine local_volatile_global
+ integer :: n = 0
+ call inner
+contains
+ subroutine inner
+ volatile :: n
+ do while (n == 0)
+ end do
+ end subroutine
+end subroutine
+
+! CHECK-LABEL: func.func @_QPlocal_volatile_global()
+! CHECK: %[[ADDR:.*]] = fir.address_of(@_QFlocal_volatile_globalEn) : !fir.ref<i32>
+! CHECK-NOT: fir.volatile_cast %[[ADDR]]
+! CHECK: %[[HOST_DECLARE:.*]]:2 = hlfir.declare %[[ADDR]] uniq_name("_QFlocal_volatile_globalEn") fortran_attrs<internal_assoc> : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+
+! CHECK-LABEL: func.func private @_QFlocal_volatile_globalPinner()
+! CHECK: %[[ADDR:.*]] = fir.address_of(@_QFlocal_volatile_globalEn) : !fir.ref<i32>
+! CHECK: %[[VOLATILE_ADDR:.*]] = fir.volatile_cast %[[ADDR]] : (!fir.ref<i32>) -> !fir.ref<i32, volatile>
+! CHECK: %[[INNER_DECLARE:.*]]:2 = hlfir.declare %[[VOLATILE_ADDR]] uniq_name("_QFlocal_volatile_globalEn") fortran_attrs<volatile> : (!fir.ref<i32, volatile>) -> (!fir.ref<i32, volatile>, !fir.ref<i32, volatile>)
+! CHECK: fir.load %[[INNER_DECLARE]]#0 : !fir.ref<i32, volatile>
+
+subroutine local_volatile_tuple
+ integer :: n
+ n = 0
+ call inner
+contains
+ subroutine inner
+ volatile :: n
+ do while (n == 0)
+ end do
+ end subroutine
+end subroutine
+
+! CHECK-LABEL: func.func @_QPlocal_volatile_tuple()
+! CHECK: %[[HOST_ADDR:.*]] = fir.alloca i32
+! CHECK-NOT: fir.volatile_cast %[[HOST_ADDR]]
+! CHECK: %[[HOST_DECLARE:.*]]:2 = hlfir.declare %[[HOST_ADDR]] uniq_name("_QFlocal_volatile_tupleEn") fortran_attrs<internal_assoc> : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+! CHECK: fir.call @_QFlocal_volatile_tuplePinner({{.*}}) {{.*}} : (!fir.ref<tuple<!fir.ref<i32>>>) -> ()
+
+! CHECK-LABEL: func.func private @_QFlocal_volatile_tuplePinner(
+! CHECK-SAME: %[[HOST_TUPLE:.*]]: !fir.ref<tuple<!fir.ref<i32>>> {fir.host_assoc})
+! CHECK: %[[COORD:.*]] = fir.coordinate_of %[[HOST_TUPLE]], {{.*}} : (!fir.ref<tuple<!fir.ref<i32>>>, i32) -> !fir.llvm_ptr<!fir.ref<i32>>
+! CHECK: %[[TUPLE_ADDR:.*]] = fir.load %[[COORD]] : !fir.llvm_ptr<!fir.ref<i32>>
+! CHECK: %[[VOLATILE_ADDR:.*]] = fir.volatile_cast %[[TUPLE_ADDR]] : (!fir.ref<i32>) -> !fir.ref<i32, volatile>
+! CHECK: %[[INNER_DECLARE:.*]]:2 = hlfir.declare %[[VOLATILE_ADDR]] uniq_name("_QFlocal_volatile_tupleEn") fortran_attrs<volatile, host_assoc> : (!fir.ref<i32, volatile>) -> (!fir.ref<i32, volatile>, !fir.ref<i32, volatile>)
+! CHECK: fir.load %[[INNER_DECLARE]]#0 : !fir.ref<i32, volatile>
+
+subroutine local_asynchronous_tuple
+ integer :: n
+ n = 0
+ call inner
+contains
+ subroutine inner
+ asynchronous :: n
+ n = n + 1
+ end subroutine
+end subroutine
+
+! CHECK-LABEL: func.func private @_QFlocal_asynchronous_tuplePinner(
+! CHECK: %[[INNER_DECLARE:.*]]:2 = hlfir.declare {{.*}} uniq_name("_QFlocal_asynchronous_tupleEn") fortran_attrs<asynchronous, host_assoc>
+
+subroutine blk
+ integer :: n = 0
+ block
+ volatile :: n
+ do while (n == 0)
+ end do
+ end block
+end subroutine
+
+! CHECK-LABEL: func.func @_QPblk()
+! CHECK: %[[ADDR:.*]] = fir.address_of(@_QFblkEn) : !fir.ref<i32>
+! CHECK: %[[HOST_DECLARE:.*]]:2 = hlfir.declare %[[ADDR]] uniq_name("_QFblkEn") : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+! CHECK: %[[VOLATILE_ADDR:.*]] = fir.volatile_cast %[[HOST_DECLARE]]#0 : (!fir.ref<i32>) -> !fir.ref<i32, volatile>
+! CHECK: %[[BLOCK_DECLARE:.*]]:2 = hlfir.declare %[[VOLATILE_ADDR]] uniq_name("_QFblkEn") fortran_attrs<volatile> : (!fir.ref<i32, volatile>) -> (!fir.ref<i32, volatile>, !fir.ref<i32, volatile>)
+! CHECK: fir.load %[[BLOCK_DECLARE]]#0 : !fir.ref<i32, volatile>
+
+module m
+ integer :: n = 0
+end module
+
+subroutine poll
+ use m
+ volatile :: n
+ do while (n == 0)
+ end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPpoll()
+! CHECK: %[[ADDR:.*]] = fir.address_of(@_QMmEn) : !fir.ref<i32>
+! CHECK: %[[VOLATILE_ADDR:.*]] = fir.volatile_cast %[[ADDR]] : (!fir.ref<i32>) -> !fir.ref<i32, volatile>
+! CHECK: %[[DECLARE:.*]]:2 = hlfir.declare %[[VOLATILE_ADDR]] uniq_name("_QMmEn") fortran_attrs<volatile> : (!fir.ref<i32, volatile>) -> (!fir.ref<i32, volatile>, !fir.ref<i32, volatile>)
+! CHECK: fir.load %[[DECLARE]]#0 : !fir.ref<i32, volatile>
+
+module nested_assoc_m
+ integer, target :: n = 0
+end module
+
+subroutine nested_use_host
+ use nested_assoc_m, only : n
+ call plain_access
+ call volatile_poll
+contains
+ subroutine plain_access
+ n = n + 1
+ end subroutine
+
+ subroutine volatile_poll
+ volatile :: n
+ do while (n == 0)
+ end do
+ end subroutine
+end subroutine
+
+! The sibling procedure retains the module variable's TARGET attribute and
+! must not inherit VOLATILE from volatile_poll.
+! CHECK-LABEL: func.func private @_QFnested_use_hostPplain_access()
+! CHECK: %[[PLAIN_ADDR:.*]] = fir.address_of(@_QMnested_assoc_mEn) : !fir.ref<i32>
+! CHECK-NOT: fir.volatile_cast %[[PLAIN_ADDR]]
+! CHECK: %[[PLAIN_DECLARE:.*]]:2 = hlfir.declare %[[PLAIN_ADDR]] uniq_name("_QMnested_assoc_mEn") fortran_attrs<target> : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+
+! The local VOLATILE attribute is combined with TARGET from the ultimate
+! module symbol in volatile_poll.
+! CHECK-LABEL: func.func private @_QFnested_use_hostPvolatile_poll()
+! CHECK: %[[POLL_ADDR:.*]] = fir.address_of(@_QMnested_assoc_mEn) : !fir.ref<i32>
+! CHECK: %[[POLL_VOLATILE_ADDR:.*]] = fir.volatile_cast %[[POLL_ADDR]] : (!fir.ref<i32>) -> !fir.ref<i32, volatile>
+! CHECK: %[[POLL_DECLARE:.*]]:2 = hlfir.declare %[[POLL_VOLATILE_ADDR]] uniq_name("_QMnested_assoc_mEn") fortran_attrs<target, volatile> : (!fir.ref<i32, volatile>) -> (!fir.ref<i32, volatile>, !fir.ref<i32, volatile>)
+! CHECK: fir.load %[[POLL_DECLARE]]#0 : !fir.ref<i32, volatile>
More information about the flang-commits
mailing list